diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-08-30 10:22:43 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-08-30 12:36:28 +0000 |
commit | 271a6c3487a14599023a9106329505597638d793 (patch) | |
tree | e040d58ffc86c1480b79ca8528020ca9ec919bf8 /chromium/cc | |
parent | 7b2ffa587235a47d4094787d72f38102089f402a (diff) | |
download | qtwebengine-chromium-271a6c3487a14599023a9106329505597638d793.tar.gz |
BASELINE: Update Chromium to 77.0.3865.59
Change-Id: I1e89a5f3b009a9519a6705102ad65c92fe736f21
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/cc')
264 files changed, 8940 insertions, 4258 deletions
diff --git a/chromium/cc/BUILD.gn b/chromium/cc/BUILD.gn index b0fde3505e6..80c2ef7647c 100644 --- a/chromium/cc/BUILD.gn +++ b/chromium/cc/BUILD.gn @@ -88,6 +88,10 @@ cc_component("cc") { "layers/layer_position_constraint.h", "layers/layer_sticky_position_constraint.cc", "layers/layer_sticky_position_constraint.h", + "layers/mirror_layer.cc", + "layers/mirror_layer.h", + "layers/mirror_layer_impl.cc", + "layers/mirror_layer_impl.h", "layers/nine_patch_generator.cc", "layers/nine_patch_generator.h", "layers/nine_patch_layer.cc", @@ -132,6 +136,8 @@ cc_component("cc") { "layers/texture_layer_client.h", "layers/texture_layer_impl.cc", "layers/texture_layer_impl.h", + "layers/tile_size_calculator.cc", + "layers/tile_size_calculator.h", "layers/touch_action_region.cc", "layers/touch_action_region.h", "layers/ui_resource_layer.cc", @@ -234,8 +240,6 @@ cc_component("cc") { "tiles/image_decode_cache_utils.h", "tiles/mipmap_util.cc", "tiles/mipmap_util.h", - "tiles/paint_worklet_image_cache.cc", - "tiles/paint_worklet_image_cache.h", "tiles/picture_layer_tiling.cc", "tiles/picture_layer_tiling.h", "tiles/picture_layer_tiling_set.cc", @@ -284,10 +288,10 @@ cc_component("cc") { "trees/draw_property_utils.h", "trees/effect_node.cc", "trees/effect_node.h", - "trees/element_id.cc", - "trees/element_id.h", "trees/frame_rate_counter.cc", "trees/frame_rate_counter.h", + "trees/frame_sequence_tracker.cc", + "trees/frame_sequence_tracker.h", "trees/image_animation_controller.cc", "trees/image_animation_controller.h", "trees/latency_info_swap_promise.cc", @@ -502,10 +506,8 @@ cc_test_static_library("test_support") { "test/task_graph_runner_test_template.h", "test/test_hooks.cc", "test/test_hooks.h", - "test/test_image_factory.cc", - "test/test_image_factory.h", - "test/test_in_process_context_provider.cc", - "test/test_in_process_context_provider.h", + "test/test_layer_tree_frame_sink.cc", + "test/test_layer_tree_frame_sink.h", "test/test_layer_tree_host_base.cc", "test/test_layer_tree_host_base.h", "test/test_occlusion_tracker.h", @@ -605,6 +607,7 @@ cc_test("cc_unittests") { "layers/layer_list_iterator_unittest.cc", "layers/layer_position_constraint_unittest.cc", "layers/layer_unittest.cc", + "layers/mirror_layer_unittest.cc", "layers/nine_patch_generator_unittest.cc", "layers/nine_patch_layer_impl_unittest.cc", "layers/nine_patch_layer_unittest.cc", @@ -634,6 +637,7 @@ cc_test("cc_unittests") { "paint/discardable_image_map_unittest.cc", "paint/display_item_list_unittest.cc", "paint/filter_operations_unittest.cc", + "paint/image_transfer_cache_entry_unittest.cc", "paint/oop_pixeltest.cc", "paint/paint_cache_unittest.cc", "paint/paint_filter_unittest.cc", @@ -654,6 +658,7 @@ cc_test("cc_unittests") { "raster/synchronous_task_graph_runner_unittest.cc", "raster/task_graph_work_queue_unittest.cc", "resources/resource_pool_unittest.cc", + "scheduler/compositor_frame_reporter_unittest.cc", "scheduler/compositor_frame_reporting_controller_unittest.cc", "scheduler/compositor_timing_history_unittest.cc", "scheduler/scheduler_state_machine_unittest.cc", @@ -664,7 +669,6 @@ cc_test("cc_unittests") { "tiles/gpu_image_decode_cache_unittest.cc", "tiles/image_controller_unittest.cc", "tiles/mipmap_util_unittest.cc", - "tiles/paint_worklet_image_cache_unittest.cc", "tiles/picture_layer_tiling_set_unittest.cc", "tiles/picture_layer_tiling_unittest.cc", "tiles/software_image_decode_cache_unittest.cc", @@ -672,6 +676,7 @@ cc_test("cc_unittests") { "tiles/tile_manager_unittest.cc", "tiles/tile_priority_unittest.cc", "trees/damage_tracker_unittest.cc", + "trees/frame_sequence_tracker_unittest.cc", "trees/image_animation_controller_unittest.cc", "trees/layer_tree_frame_sink_unittest.cc", "trees/layer_tree_host_common_unittest.cc", @@ -679,6 +684,7 @@ cc_test("cc_unittests") { "trees/layer_tree_host_pixeltest_blending.cc", "trees/layer_tree_host_pixeltest_filters.cc", "trees/layer_tree_host_pixeltest_masks.cc", + "trees/layer_tree_host_pixeltest_mirror.cc", "trees/layer_tree_host_pixeltest_readback.cc", "trees/layer_tree_host_pixeltest_scrollbars.cc", "trees/layer_tree_host_pixeltest_synchronous.cc", @@ -771,6 +777,20 @@ cc_test("cc_unittests") { data_deps = [ "//third_party/mesa_headers", ] + + if (enable_vulkan) { + deps += [ + "//gpu/vulkan:test_support", + "//gpu/vulkan/init", + ] + + # TODO(samans): Support more configurations. + # CFI issue: https://crbug.com/967819 + # LSAN issue: https://crbug.com/971325 + if (use_x11 && !is_cfi && !is_lsan) { + defines = [ "ENABLE_CC_VULKAN_TESTS" ] + } + } } cc_test("cc_perftests") { diff --git a/chromium/cc/DEPS b/chromium/cc/DEPS index 0186be319b6..8d3576c0da9 100644 --- a/chromium/cc/DEPS +++ b/chromium/cc/DEPS @@ -22,6 +22,7 @@ include_rules = [ "+gpu/command_buffer/common/sync_token.h", "+gpu/command_buffer/common/texture_in_use_response.h", "+gpu/config/gpu_feature_info.h", + "+gpu/config/gpu_finch_features.h", "+gpu/vulkan", "+media", "+mojo/public/cpp/system/buffer.h", @@ -58,6 +59,8 @@ specific_include_rules = { ".*_(unit|pixel|perf)test.*\.cc": [ "+components/viz/service/display", "+components/viz/test", + "+gpu/command_buffer/common/command_buffer_id.h", + "+gpu/command_buffer/common/constants.h", ], "oop_pixeltest\.cc" : [ "+gpu/command_buffer/client", diff --git a/chromium/cc/README.md b/chromium/cc/README.md index 4c51342f0e8..e8ae01d7bfb 100644 --- a/chromium/cc/README.md +++ b/chromium/cc/README.md @@ -71,7 +71,7 @@ RenderPass has been drawn to. Chosen by cc's clients and can be used as a stable identifier across updates. For example, blink uses ElementIDs as a stable id for the object (opaque to cc) that is responsible for a composited animation. Some additional information in -[element_id.h](https://codesearch.chromium.org/chromium/src/cc/trees/element_id.h) +[element_id.h](https://codesearch.chromium.org/chromium/src/cc/paint/element_id.h) ### DirectRenderer An abstraction that provides an API for the Display to draw a fully-aggregated diff --git a/chromium/cc/animation/DEPS b/chromium/cc/animation/DEPS index bc229ac9da9..808b4349b0b 100644 --- a/chromium/cc/animation/DEPS +++ b/chromium/cc/animation/DEPS @@ -8,7 +8,6 @@ include_rules = [ "+cc/test", "+cc/trees/animation_options.h", "+cc/trees/animation_effect_timings.h", - "+cc/trees/element_id.h", "+cc/trees/mutator_host.h", "+cc/trees/mutator_host_client.h", "+cc/trees/property_animation_state.h", diff --git a/chromium/cc/animation/animation.h b/chromium/cc/animation/animation.h index 14a988505a9..12ad6058cdc 100644 --- a/chromium/cc/animation/animation.h +++ b/chromium/cc/animation/animation.h @@ -14,7 +14,7 @@ #include "cc/animation/animation_export.h" #include "cc/animation/element_animations.h" #include "cc/animation/keyframe_model.h" -#include "cc/trees/element_id.h" +#include "cc/paint/element_id.h" namespace cc { diff --git a/chromium/cc/animation/animation_events.h b/chromium/cc/animation/animation_events.h index 6e3fed34d34..584292197ae 100644 --- a/chromium/cc/animation/animation_events.h +++ b/chromium/cc/animation/animation_events.h @@ -11,8 +11,8 @@ #include "cc/animation/animation_curve.h" #include "cc/animation/animation_export.h" #include "cc/animation/keyframe_model.h" +#include "cc/paint/element_id.h" #include "cc/paint/filter_operations.h" -#include "cc/trees/element_id.h" #include "cc/trees/mutator_host.h" #include "ui/gfx/transform.h" diff --git a/chromium/cc/animation/animation_host.cc b/chromium/cc/animation/animation_host.cc index ccb6a42ba3c..b283103cc48 100644 --- a/chromium/cc/animation/animation_host.cc +++ b/chromium/cc/animation/animation_host.cc @@ -81,8 +81,7 @@ AnimationHost::AnimationHost(ThreadInstance thread_instance) thread_instance_(thread_instance), supports_scroll_animations_(false), needs_push_properties_(false), - mutator_(nullptr), - weak_factory_(this) { + mutator_(nullptr) { if (thread_instance_ == ThreadInstance::IMPL) { scroll_offset_animations_impl_ = std::make_unique<ScrollOffsetAnimationsImpl>(this); @@ -649,6 +648,18 @@ bool AnimationHost::HasTickingKeyframeModelForTesting( : false; } +void AnimationHost::ImplOnlyAutoScrollAnimationCreate( + ElementId element_id, + const gfx::ScrollOffset& target_offset, + const gfx::ScrollOffset& current_offset, + float autoscroll_velocity, + base::TimeDelta animation_start_offset) { + DCHECK(scroll_offset_animations_impl_); + scroll_offset_animations_impl_->AutoScrollAnimationCreate( + element_id, target_offset, current_offset, autoscroll_velocity, + animation_start_offset); +} + void AnimationHost::ImplOnlyScrollAnimationCreate( ElementId element_id, const gfx::ScrollOffset& target_offset, @@ -690,7 +701,7 @@ bool AnimationHost::IsImplOnlyScrollAnimating() const { } void AnimationHost::AddToTicking(scoped_refptr<Animation> animation) { - DCHECK(!base::ContainsValue(ticking_animations_, animation)); + DCHECK(!base::Contains(ticking_animations_, animation)); ticking_animations_.push_back(animation); } diff --git a/chromium/cc/animation/animation_host.h b/chromium/cc/animation/animation_host.h index 45fca14a5b7..2e3eb122c71 100644 --- a/chromium/cc/animation/animation_host.h +++ b/chromium/cc/animation/animation_host.h @@ -164,6 +164,13 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost, bool IsElementAnimating(ElementId element_id) const override; bool HasTickingKeyframeModelForTesting(ElementId element_id) const override; + void ImplOnlyAutoScrollAnimationCreate( + ElementId element_id, + const gfx::ScrollOffset& target_offset, + const gfx::ScrollOffset& current_offset, + float autoscroll_velocity, + base::TimeDelta animation_start_offset) override; + void ImplOnlyScrollAnimationCreate( ElementId element_id, const gfx::ScrollOffset& target_offset, @@ -255,7 +262,7 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost, bool current_frame_had_raf_ = false; bool next_frame_has_pending_raf_ = false; - base::WeakPtrFactory<AnimationHost> weak_factory_; + base::WeakPtrFactory<AnimationHost> weak_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/animation/element_animations.h b/chromium/cc/animation/element_animations.h index e2a46551787..28de076c312 100644 --- a/chromium/cc/animation/element_animations.h +++ b/chromium/cc/animation/element_animations.h @@ -12,7 +12,7 @@ #include "base/observer_list.h" #include "cc/animation/animation_export.h" #include "cc/animation/animation_target.h" -#include "cc/trees/element_id.h" +#include "cc/paint/element_id.h" #include "cc/trees/property_animation_state.h" #include "cc/trees/target_property.h" #include "ui/gfx/geometry/scroll_offset.h" diff --git a/chromium/cc/animation/keyframe_effect.cc b/chromium/cc/animation/keyframe_effect.cc index d1d55431893..221cf0b141e 100644 --- a/chromium/cc/animation/keyframe_effect.cc +++ b/chromium/cc/animation/keyframe_effect.cc @@ -276,7 +276,7 @@ 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_parition here instead of remove_if. Remove_if leaves + // 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(), diff --git a/chromium/cc/animation/keyframe_effect.h b/chromium/cc/animation/keyframe_effect.h index 3bcb0347439..e4b00292602 100644 --- a/chromium/cc/animation/keyframe_effect.h +++ b/chromium/cc/animation/keyframe_effect.h @@ -10,7 +10,7 @@ #include "cc/animation/animation_events.h" #include "cc/animation/animation_export.h" #include "cc/animation/element_animations.h" -#include "cc/trees/element_id.h" +#include "cc/paint/element_id.h" #include "cc/trees/mutator_host_client.h" #include "cc/trees/target_property.h" #include "ui/gfx/geometry/box_f.h" diff --git a/chromium/cc/animation/keyframe_model.cc b/chromium/cc/animation/keyframe_model.cc index d4b89dda44b..50b3cef1ea2 100644 --- a/chromium/cc/animation/keyframe_model.cc +++ b/chromium/cc/animation/keyframe_model.cc @@ -12,6 +12,7 @@ #include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" #include "cc/animation/animation_curve.h" +#include "cc/trees/target_property.h" namespace { @@ -50,9 +51,11 @@ std::unique_ptr<KeyframeModel> KeyframeModel::Create( std::unique_ptr<AnimationCurve> curve, int keyframe_model_id, int group_id, - int target_property_id) { + int target_property_id, + const std::string& custom_property_name) { return base::WrapUnique(new KeyframeModel(std::move(curve), keyframe_model_id, - group_id, target_property_id)); + group_id, target_property_id, + custom_property_name)); } std::unique_ptr<KeyframeModel> KeyframeModel::CreateImplInstance( @@ -61,7 +64,8 @@ std::unique_ptr<KeyframeModel> KeyframeModel::CreateImplInstance( // creating multiple controlling instances. DCHECK(!is_controlling_instance_); std::unique_ptr<KeyframeModel> to_return( - new KeyframeModel(curve_->Clone(), id_, group_, target_property_id_)); + new KeyframeModel(curve_->Clone(), id_, group_, target_property_id_, + custom_property_name_)); to_return->element_id_ = element_id_; to_return->run_state_ = initial_run_state; to_return->iterations_ = iterations_; @@ -81,7 +85,8 @@ std::unique_ptr<KeyframeModel> KeyframeModel::CreateImplInstance( KeyframeModel::KeyframeModel(std::unique_ptr<AnimationCurve> curve, int keyframe_model_id, int group_id, - int target_property_id) + int target_property_id, + const std::string& custom_property_name) : curve_(std::move(curve)), id_(keyframe_model_id), group_(group_id), @@ -97,7 +102,11 @@ KeyframeModel::KeyframeModel(std::unique_ptr<AnimationCurve> curve, is_controlling_instance_(false), is_impl_only_(false), affects_active_elements_(true), - affects_pending_elements_(true) {} + affects_pending_elements_(true), + custom_property_name_(custom_property_name) { + DCHECK(custom_property_name_.empty() || + target_property_id_ == TargetProperty::CSS_CUSTOM_PROPERTY); +} KeyframeModel::~KeyframeModel() { if (run_state_ == RUNNING || run_state_ == PAUSED) diff --git a/chromium/cc/animation/keyframe_model.h b/chromium/cc/animation/keyframe_model.h index 43a22d883ec..7c05245dc99 100644 --- a/chromium/cc/animation/keyframe_model.h +++ b/chromium/cc/animation/keyframe_model.h @@ -10,7 +10,7 @@ #include "base/optional.h" #include "base/time/time.h" #include "cc/animation/animation_export.h" -#include "cc/trees/element_id.h" +#include "cc/paint/element_id.h" namespace cc { @@ -57,11 +57,15 @@ class CC_ANIMATION_EXPORT KeyframeModel { enum class Phase { BEFORE, ACTIVE, AFTER }; + // The |custom_property_name| has a default value of an empty string, + // indicating that the animated property is a native property. When it is an + // animated custom property, it should be the property name. static std::unique_ptr<KeyframeModel> Create( std::unique_ptr<AnimationCurve> curve, int keyframe_model_id, int group_id, - int target_property_id); + int target_property_id, + const std::string& custom_property_name = ""); std::unique_ptr<KeyframeModel> CreateImplInstance( RunState initial_run_state) const; @@ -177,11 +181,16 @@ class CC_ANIMATION_EXPORT KeyframeModel { KeyframeModel::Phase CalculatePhaseForTesting( base::TimeDelta local_time) const; + const std::string& GetCustomPropertyNameForTesting() { + return custom_property_name_; + } + private: KeyframeModel(std::unique_ptr<AnimationCurve> curve, int keyframe_model_id, int group_id, - int target_property_id); + int target_property_id, + const std::string& custom_property_name); // Return local time for this keyframe model given the absolute monotonic // time. @@ -281,6 +290,10 @@ class CC_ANIMATION_EXPORT KeyframeModel { // longer affect any elements, and are deleted. bool affects_active_elements_; bool affects_pending_elements_; + + // Name of the animated custom property. Empty if it is an animated native + // property. + std::string custom_property_name_; }; } // namespace cc diff --git a/chromium/cc/animation/keyframe_model_unittest.cc b/chromium/cc/animation/keyframe_model_unittest.cc index 5c853eb1246..b5648c0c370 100644 --- a/chromium/cc/animation/keyframe_model_unittest.cc +++ b/chromium/cc/animation/keyframe_model_unittest.cc @@ -1384,5 +1384,19 @@ TEST(KeyframeModelTest, ToString) { keyframe_model->ToString()); } +TEST(KeyframeModelTest, CustomPropertyKeyframe) { + std::unique_ptr<KeyframeModel> keyframe_model = + KeyframeModel::Create(std::make_unique<FakeFloatAnimationCurve>(1), 1, 1, + TargetProperty::CSS_CUSTOM_PROPERTY, "foo"); + EXPECT_EQ(keyframe_model->GetCustomPropertyNameForTesting(), "foo"); +} + +TEST(KeyframeModelTest, NonCustomPropertyKeyframe) { + std::unique_ptr<KeyframeModel> keyframe_model = + KeyframeModel::Create(std::make_unique<FakeFloatAnimationCurve>(1), 1, 1, + TargetProperty::TRANSFORM); + EXPECT_EQ(keyframe_model->GetCustomPropertyNameForTesting(), ""); +} + } // namespace } // namespace cc diff --git a/chromium/cc/animation/keyframed_animation_curve.cc b/chromium/cc/animation/keyframed_animation_curve.cc index ed44328d053..67147cb86a4 100644 --- a/chromium/cc/animation/keyframed_animation_curve.cc +++ b/chromium/cc/animation/keyframed_animation_curve.cc @@ -541,8 +541,8 @@ gfx::SizeF KeyframedSizeAnimationCurve::GetValue(base::TimeDelta t) const { double progress = TransformedKeyframeProgress(keyframes_, scaled_duration(), t, i); - return gfx::Tween::SizeValueBetween(progress, keyframes_[i]->Value(), - keyframes_[i + 1]->Value()); + return gfx::Tween::SizeFValueBetween(progress, keyframes_[i]->Value(), + keyframes_[i + 1]->Value()); } } // namespace cc diff --git a/chromium/cc/animation/keyframed_animation_curve.h b/chromium/cc/animation/keyframed_animation_curve.h index 27e87ba3648..c843fc7f782 100644 --- a/chromium/cc/animation/keyframed_animation_curve.h +++ b/chromium/cc/animation/keyframed_animation_curve.h @@ -164,12 +164,15 @@ class CC_ANIMATION_EXPORT KeyframedColorAnimationCurve // BackgrounColorAnimationCurve implementation SkColor GetValue(base::TimeDelta t) const override; + using Keyframes = std::vector<std::unique_ptr<ColorKeyframe>>; + const Keyframes& keyframes_for_testing() const { return keyframes_; } + private: KeyframedColorAnimationCurve(); // Always sorted in order of increasing time. No two keyframes have the // same time. - std::vector<std::unique_ptr<ColorKeyframe>> keyframes_; + Keyframes keyframes_; std::unique_ptr<TimingFunction> timing_function_; double scaled_duration_; }; diff --git a/chromium/cc/animation/keyframed_animation_curve_unittest.cc b/chromium/cc/animation/keyframed_animation_curve_unittest.cc index 4b678bdfac6..60fa0054a2c 100644 --- a/chromium/cc/animation/keyframed_animation_curve_unittest.cc +++ b/chromium/cc/animation/keyframed_animation_curve_unittest.cc @@ -519,6 +519,19 @@ TEST(KeyframedAnimationCurveTest, UnsortedKeyframes) { EXPECT_FLOAT_EQ(8.f, curve->GetValue(base::TimeDelta::FromSecondsD(3.f))); } +// Tests that a linear timing function works as expected. +TEST(KeyframedAnimationCurveTest, LinearTimingFunction) { + std::unique_ptr<KeyframedFloatAnimationCurve> curve( + KeyframedFloatAnimationCurve::Create()); + curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 0.f, + LinearTimingFunction::Create())); + curve->AddKeyframe( + FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 1.f, nullptr)); + + EXPECT_FLOAT_EQ(0.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.f))); + EXPECT_FLOAT_EQ(0.75f, curve->GetValue(base::TimeDelta::FromSecondsD(0.75f))); +} + // Tests that a cubic bezier timing function works as expected. TEST(KeyframedAnimationCurveTest, CubicBezierTimingFunction) { std::unique_ptr<KeyframedFloatAnimationCurve> curve( @@ -1004,7 +1017,7 @@ TEST(KeyframedAnimationCurveTest, OneSizeKeyFrame) { TEST(KeyframedAnimationCurveTest, TwoSizeKeyFrame) { gfx::SizeF size_a = gfx::SizeF(100, 100); gfx::SizeF size_b = gfx::SizeF(100, 0); - gfx::SizeF size_midpoint = gfx::Tween::SizeValueBetween(0.5, size_a, size_b); + gfx::SizeF size_midpoint = gfx::Tween::SizeFValueBetween(0.5, size_a, size_b); std::unique_ptr<KeyframedSizeAnimationCurve> curve( KeyframedSizeAnimationCurve::Create()); curve->AddKeyframe(SizeKeyframe::Create(base::TimeDelta(), size_a, nullptr)); @@ -1024,8 +1037,10 @@ TEST(KeyframedAnimationCurveTest, ThreeSizeKeyFrame) { gfx::SizeF size_a = gfx::SizeF(100, 100); gfx::SizeF size_b = gfx::SizeF(100, 0); gfx::SizeF size_c = gfx::SizeF(200, 0); - gfx::SizeF size_midpoint1 = gfx::Tween::SizeValueBetween(0.5, size_a, size_b); - gfx::SizeF size_midpoint2 = gfx::Tween::SizeValueBetween(0.5, size_b, size_c); + gfx::SizeF size_midpoint1 = + gfx::Tween::SizeFValueBetween(0.5, size_a, size_b); + gfx::SizeF size_midpoint2 = + gfx::Tween::SizeFValueBetween(0.5, size_b, size_c); std::unique_ptr<KeyframedSizeAnimationCurve> curve( KeyframedSizeAnimationCurve::Create()); curve->AddKeyframe(SizeKeyframe::Create(base::TimeDelta(), size_a, nullptr)); diff --git a/chromium/cc/animation/scroll_offset_animation_curve.cc b/chromium/cc/animation/scroll_offset_animation_curve.cc index 4dedca4a205..0bc523ff275 100644 --- a/chromium/cc/animation/scroll_offset_animation_curve.cc +++ b/chromium/cc/animation/scroll_offset_animation_curve.cc @@ -82,7 +82,8 @@ ScrollOffsetAnimationCurve::~ScrollOffsetAnimationCurve() = default; base::TimeDelta ScrollOffsetAnimationCurve::SegmentDuration( const gfx::Vector2dF& delta, DurationBehavior behavior, - base::TimeDelta delayed_by) { + base::TimeDelta delayed_by, + float velocity) { double duration = kConstantDuration; if (!animation_duration_for_testing_) { switch (behavior) { @@ -101,6 +102,10 @@ base::TimeDelta ScrollOffsetAnimationCurve::SegmentDuration( kInverseDeltaMinDuration), kInverseDeltaMaxDuration); break; + case DurationBehavior::CONSTANT_VELOCITY: + duration = + std::abs(MaximumDimension(delta) / velocity * kDurationDivisor); + break; default: NOTREACHED(); } @@ -119,11 +124,13 @@ base::TimeDelta ScrollOffsetAnimationCurve::SegmentDuration( void ScrollOffsetAnimationCurve::SetInitialValue( const gfx::ScrollOffset& initial_value, - base::TimeDelta delayed_by) { + base::TimeDelta delayed_by, + float velocity) { initial_value_ = initial_value; has_set_initial_value_ = true; - total_animation_duration_ = SegmentDuration( - target_value_.DeltaFrom(initial_value_), duration_behavior_, delayed_by); + total_animation_duration_ = + SegmentDuration(target_value_.DeltaFrom(initial_value_), + duration_behavior_, delayed_by, velocity); } bool ScrollOffsetAnimationCurve::HasSetInitialValue() const { @@ -233,11 +240,11 @@ void ScrollOffsetAnimationCurve::UpdateTarget( gfx::Vector2dF old_delta = target_value_.DeltaFrom(initial_value_); gfx::Vector2dF new_delta = new_target.DeltaFrom(current_position); - // The last segement was of zero duration. + // The last segment was of zero duration. if ((total_animation_duration_ - last_retarget_).is_zero()) { DCHECK_EQ(t, last_retarget_); - total_animation_duration_ = - SegmentDuration(new_delta, duration_behavior_, delayed_by); + total_animation_duration_ = SegmentDuration(new_delta, duration_behavior_, + delayed_by, /*velocity*/ 0); target_value_ = new_target; return; } @@ -250,7 +257,8 @@ void ScrollOffsetAnimationCurve::UpdateTarget( // segment duration. This minimizes the "rubber-band" bouncing effect when // old_normalized_velocity is large and new_delta is small. base::TimeDelta new_duration = - std::min(SegmentDuration(new_delta, duration_behavior_, delayed_by), + std::min(SegmentDuration(new_delta, duration_behavior_, delayed_by, + /*velocity*/ 0), VelocityBasedDurationBound(old_delta, old_normalized_velocity, old_duration, new_delta)); diff --git a/chromium/cc/animation/scroll_offset_animation_curve.h b/chromium/cc/animation/scroll_offset_animation_curve.h index fca9af6942b..42b774b9c47 100644 --- a/chromium/cc/animation/scroll_offset_animation_curve.h +++ b/chromium/cc/animation/scroll_offset_animation_curve.h @@ -35,7 +35,9 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve { // Duration inversely proportional to scroll delta within certain bounds. // Used for mouse wheels, makes fast wheel flings feel "snappy" while // preserving smoothness of slow wheel movements. - INVERSE_DELTA + INVERSE_DELTA, + // Constant velocity; used for autoscrolls. + CONSTANT_VELOCITY, }; static std::unique_ptr<ScrollOffsetAnimationCurve> Create( const gfx::ScrollOffset& target_value, @@ -44,7 +46,8 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve { static base::TimeDelta SegmentDuration(const gfx::Vector2dF& delta, DurationBehavior behavior, - base::TimeDelta delayed_by); + base::TimeDelta delayed_by, + float velocity); ScrollOffsetAnimationCurve(const ScrollOffsetAnimationCurve&) = delete; ~ScrollOffsetAnimationCurve() override; @@ -52,8 +55,10 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationCurve : public AnimationCurve { ScrollOffsetAnimationCurve& operator=(const ScrollOffsetAnimationCurve&) = delete; + // Sets the initial offset and velocity (in pixels per second). void SetInitialValue(const gfx::ScrollOffset& initial_value, - base::TimeDelta delayed_by = base::TimeDelta()); + base::TimeDelta delayed_by = base::TimeDelta(), + float velocity = 0); bool HasSetInitialValue() const; gfx::ScrollOffset GetValue(base::TimeDelta t) const; gfx::ScrollOffset target_value() const { return target_value_; } diff --git a/chromium/cc/animation/scroll_offset_animation_curve_unittest.cc b/chromium/cc/animation/scroll_offset_animation_curve_unittest.cc index cd308a709b8..61bc09be18e 100644 --- a/chromium/cc/animation/scroll_offset_animation_curve_unittest.cc +++ b/chromium/cc/animation/scroll_offset_animation_curve_unittest.cc @@ -205,6 +205,33 @@ TEST(ScrollOffsetAnimationCurveTest, InverseDeltaDuration) { EXPECT_EQ(largeDeltaDuration, curve->Duration().InSecondsF()); } +TEST(ScrollOffsetAnimationCurveTest, ConstantVelocityDuration) { + // Testing autoscroll downwards for a scroller of length 1000px. + gfx::ScrollOffset current_offset(0.f, 0.f); + gfx::ScrollOffset target_offset(0.f, 1000.f); + std::unique_ptr<ScrollOffsetAnimationCurve> curve( + ScrollOffsetAnimationCurve::Create( + target_offset, LinearTimingFunction::Create(), + ScrollOffsetAnimationCurve::DurationBehavior::CONSTANT_VELOCITY)); + + const float autoscroll_velocity = 800.f; // pixels per second. + curve->SetInitialValue(current_offset, base::TimeDelta(), + autoscroll_velocity); + EXPECT_FLOAT_EQ(1.25f, curve->Duration().InSecondsF()); + + // Test scrolling down from half way. + current_offset = gfx::ScrollOffset(0.f, 500.f); + curve->SetInitialValue(current_offset, base::TimeDelta(), + autoscroll_velocity); + EXPECT_FLOAT_EQ(0.625f, curve->Duration().InSecondsF()); + + // Test scrolling down when max_offset is reached. + current_offset = gfx::ScrollOffset(0.f, 1000.f); + curve->SetInitialValue(current_offset, base::TimeDelta(), + autoscroll_velocity); + EXPECT_FLOAT_EQ(0.f, curve->Duration().InSecondsF()); +} + TEST(ScrollOffsetAnimationCurveTest, CurveWithDelay) { std::unique_ptr<ScrollOffsetAnimationCurve> curve( ScrollOffsetAnimationCurve::Create( @@ -244,7 +271,7 @@ TEST(ScrollOffsetAnimationCurveTest, CurveWithLargeDelay) { ScrollOffsetAnimationCurve::SegmentDuration( gfx::Vector2dF(0.f, 200.f), ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA, - base::TimeDelta::FromSecondsD(0.01)) + base::TimeDelta::FromSecondsD(0.01), /*velocity*/ 0) .InSecondsF(); EXPECT_EQ(duration, curve->Duration().InSecondsF()); @@ -255,7 +282,7 @@ TEST(ScrollOffsetAnimationCurveTest, CurveWithLargeDelay) { duration = ScrollOffsetAnimationCurve::SegmentDuration( gfx::Vector2dF(0.f, 500.f), ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA, - base::TimeDelta::FromSecondsD(0.01)) + base::TimeDelta::FromSecondsD(0.01), /*velocity*/ 0) .InSecondsF(); EXPECT_EQ(duration, curve->Duration().InSecondsF()); @@ -288,7 +315,7 @@ TEST(ScrollOffsetAnimationCurveTest, UpdateTargetZeroLastSegmentDuration) { ScrollOffsetAnimationCurve::SegmentDuration( gfx::Vector2dF(new_delta.x(), new_delta.y()), ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA, - base::TimeDelta()) + base::TimeDelta(), /*velocity*/ 0) .InSecondsF() + 0.05; curve->UpdateTarget(base::TimeDelta::FromSecondsD(0.05), @@ -309,7 +336,7 @@ TEST(ScrollOffsetAnimationCurveTest, UpdateTargetZeroLastSegmentDuration) { ScrollOffsetAnimationCurve::SegmentDuration( gfx::Vector2dF(new_delta.x(), new_delta.y()), ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA, - base::TimeDelta::FromSecondsD(0.15)) + base::TimeDelta::FromSecondsD(0.15), /*velocity*/ 0) .InSecondsF(); curve->UpdateTarget(base::TimeDelta::FromSecondsD(-0.1), gfx::ScrollOffset(0.f, 500.f)); diff --git a/chromium/cc/animation/scroll_offset_animations_impl.cc b/chromium/cc/animation/scroll_offset_animations_impl.cc index 896e17440be..5c7ff936114 100644 --- a/chromium/cc/animation/scroll_offset_animations_impl.cc +++ b/chromium/cc/animation/scroll_offset_animations_impl.cc @@ -34,6 +34,22 @@ ScrollOffsetAnimationsImpl::~ScrollOffsetAnimationsImpl() { animation_host_->RemoveAnimationTimeline(scroll_offset_timeline_.get()); } +void ScrollOffsetAnimationsImpl::AutoScrollAnimationCreate( + ElementId element_id, + const gfx::ScrollOffset& target_offset, + const gfx::ScrollOffset& current_offset, + float autoscroll_velocity, + base::TimeDelta animation_start_offset) { + std::unique_ptr<ScrollOffsetAnimationCurve> curve = + ScrollOffsetAnimationCurve::Create( + target_offset, LinearTimingFunction::Create(), + ScrollOffsetAnimationCurve::DurationBehavior::CONSTANT_VELOCITY); + curve->SetInitialValue(current_offset, base::TimeDelta(), + autoscroll_velocity); + ScrollAnimationCreateInternal(element_id, std::move(curve), + animation_start_offset); +} + void ScrollOffsetAnimationsImpl::ScrollAnimationCreate( ElementId element_id, const gfx::ScrollOffset& target_offset, @@ -42,10 +58,19 @@ void ScrollOffsetAnimationsImpl::ScrollAnimationCreate( base::TimeDelta animation_start_offset) { std::unique_ptr<ScrollOffsetAnimationCurve> curve = ScrollOffsetAnimationCurve::Create( - target_offset, CubicBezierTimingFunction::CreatePreset( - CubicBezierTimingFunction::EaseType::EASE_IN_OUT), + target_offset, + CubicBezierTimingFunction::CreatePreset( + CubicBezierTimingFunction::EaseType::EASE_IN_OUT), ScrollOffsetAnimationCurve::DurationBehavior::INVERSE_DELTA); curve->SetInitialValue(current_offset, delayed_by); + ScrollAnimationCreateInternal(element_id, std::move(curve), + animation_start_offset); +} + +void ScrollOffsetAnimationsImpl::ScrollAnimationCreateInternal( + ElementId element_id, + std::unique_ptr<AnimationCurve> curve, + base::TimeDelta animation_start_offset) { TRACE_EVENT_INSTANT1("cc", "ScrollAnimationCreate", TRACE_EVENT_SCOPE_THREAD, "Duration", curve->Duration().InMillisecondsF()); diff --git a/chromium/cc/animation/scroll_offset_animations_impl.h b/chromium/cc/animation/scroll_offset_animations_impl.h index 39d72f18cd6..4371deed459 100644 --- a/chromium/cc/animation/scroll_offset_animations_impl.h +++ b/chromium/cc/animation/scroll_offset_animations_impl.h @@ -32,6 +32,12 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationsImpl ScrollOffsetAnimationsImpl& operator=(const ScrollOffsetAnimationsImpl&) = delete; + void AutoScrollAnimationCreate(ElementId element_id, + const gfx::ScrollOffset& target_offset, + const gfx::ScrollOffset& current_offset, + float autoscroll_velocity, + base::TimeDelta animation_start_offset); + // |delayed_by| shrinks the duration of the // animation. |animation_start_offset| causes us to start the animation // partway through. @@ -73,6 +79,10 @@ class CC_ANIMATION_EXPORT ScrollOffsetAnimationsImpl bool IsAnimating() const; private: + void ScrollAnimationCreateInternal(ElementId element_id, + std::unique_ptr<AnimationCurve> curve, + base::TimeDelta animation_start_offset); + void ReattachScrollOffsetAnimationIfNeeded(ElementId element_id); AnimationHost* animation_host_; diff --git a/chromium/cc/animation/scroll_timeline.h b/chromium/cc/animation/scroll_timeline.h index 152c1a642db..66b39d562fc 100644 --- a/chromium/cc/animation/scroll_timeline.h +++ b/chromium/cc/animation/scroll_timeline.h @@ -9,7 +9,7 @@ #include "base/time/time.h" #include "cc/animation/animation_export.h" #include "cc/animation/keyframe_model.h" -#include "cc/trees/element_id.h" +#include "cc/paint/element_id.h" namespace cc { diff --git a/chromium/cc/animation/single_keyframe_effect_animation.h b/chromium/cc/animation/single_keyframe_effect_animation.h index 474250c8a9a..a79566ae28e 100644 --- a/chromium/cc/animation/single_keyframe_effect_animation.h +++ b/chromium/cc/animation/single_keyframe_effect_animation.h @@ -15,7 +15,7 @@ #include "cc/animation/animation_export.h" #include "cc/animation/element_animations.h" #include "cc/animation/keyframe_model.h" -#include "cc/trees/element_id.h" +#include "cc/paint/element_id.h" namespace cc { diff --git a/chromium/cc/animation/timing_function.cc b/chromium/cc/animation/timing_function.cc index 1c80aeeb28d..d3a66810330 100644 --- a/chromium/cc/animation/timing_function.cc +++ b/chromium/cc/animation/timing_function.cc @@ -105,23 +105,77 @@ double StepsTimingFunction::GetPreciseValue(double t, steps * t - std::floor(steps * t) == 0) { current_step -= 1; } + // Jumps may differ from steps based on the number of end-point + // discontinuities, which may be 0, 1 or 2. + int jumps = NumberOfJumps(); if (t >= 0 && current_step < 0) current_step = 0; - if (t <= 1 && current_step > steps) - current_step = steps; - return current_step / steps; + if (t <= 1 && current_step > jumps) + current_step = jumps; + return current_step / jumps; +} + +int StepsTimingFunction::NumberOfJumps() const { + switch (step_position_) { + case StepPosition::END: + case StepPosition::START: + case StepPosition::JUMP_END: + case StepPosition::JUMP_START: + return steps_; + + case StepPosition::JUMP_BOTH: + return steps_ + 1; + + case StepPosition::JUMP_NONE: + DCHECK_GT(steps_, 1); + return steps_ - 1; + + default: + NOTREACHED(); + return steps_; + } } float StepsTimingFunction::GetStepsStartOffset() const { switch (step_position_) { + case StepPosition::JUMP_BOTH: + case StepPosition::JUMP_START: case StepPosition::START: return 1; + + case StepPosition::JUMP_END: + case StepPosition::JUMP_NONE: case StepPosition::END: return 0; + default: NOTREACHED(); return 1; } } +std::unique_ptr<LinearTimingFunction> LinearTimingFunction::Create() { + return base::WrapUnique(new LinearTimingFunction()); +} + +LinearTimingFunction::LinearTimingFunction() = default; + +LinearTimingFunction::~LinearTimingFunction() = default; + +TimingFunction::Type LinearTimingFunction::GetType() const { + return Type::LINEAR; +} + +std::unique_ptr<TimingFunction> LinearTimingFunction::Clone() const { + return base::WrapUnique(new LinearTimingFunction(*this)); +} + +double LinearTimingFunction::Velocity(double x) const { + return 0; +} + +double LinearTimingFunction::GetValue(double t) const { + return t; +} + } // namespace cc diff --git a/chromium/cc/animation/timing_function.h b/chromium/cc/animation/timing_function.h index 4212bb28864..4422a4a6f7d 100644 --- a/chromium/cc/animation/timing_function.h +++ b/chromium/cc/animation/timing_function.h @@ -73,7 +73,18 @@ class CC_ANIMATION_EXPORT StepsTimingFunction : public TimingFunction { public: // step-timing-function values // https://drafts.csswg.org/css-easing-1/#typedef-step-timing-function - enum class StepPosition { START, END }; + enum class StepPosition { + START, // Discontinuity at progress = 0. + // Alias for jump-start. Maintaining a separate enumerated value + // for serialization. + END, // Discontinuity at progress = 1. + // Alias for jump-end. Maintaining a separate enumerated value + // for serialization. + JUMP_BOTH, // Discontinuities at progress = 0 and 1. + JUMP_END, // Discontinuity at progress = 1. + JUMP_NONE, // Continuous at progress = 0 and 1. + JUMP_START // Discontinuity at progress = 0. + }; static std::unique_ptr<StepsTimingFunction> Create( int steps, @@ -95,12 +106,35 @@ class CC_ANIMATION_EXPORT StepsTimingFunction : public TimingFunction { private: StepsTimingFunction(int steps, StepPosition step_position); + // The number of jumps is the number of discontinuities in the timing + // function. There is a subtle distinction between the number of steps and + // jumps. The number of steps is the number of intervals in the timing + // function. The number of jumps differs from the number of steps when either + // both or neither end point has a discontinuity. + // https://drafts.csswg.org/css-easing-1/#step-easing-functions + int NumberOfJumps() const; + float GetStepsStartOffset() const; int steps_; StepPosition step_position_; }; +class CC_ANIMATION_EXPORT LinearTimingFunction : public TimingFunction { + public: + static std::unique_ptr<LinearTimingFunction> Create(); + ~LinearTimingFunction() override; + + // TimingFunction implementation. + Type GetType() const override; + double GetValue(double t) const override; + std::unique_ptr<TimingFunction> Clone() const override; + double Velocity(double time) const override; + + private: + LinearTimingFunction(); +}; + } // namespace cc #endif // CC_ANIMATION_TIMING_FUNCTION_H_ diff --git a/chromium/cc/base/delayed_unique_notifier.cc b/chromium/cc/base/delayed_unique_notifier.cc index 741bc83c71f..3148c0f788f 100644 --- a/chromium/cc/base/delayed_unique_notifier.cc +++ b/chromium/cc/base/delayed_unique_notifier.cc @@ -18,8 +18,7 @@ DelayedUniqueNotifier::DelayedUniqueNotifier( : task_runner_(task_runner), closure_(std::move(closure)), delay_(delay), - notification_pending_(false), - weak_ptr_factory_(this) {} + notification_pending_(false) {} DelayedUniqueNotifier::~DelayedUniqueNotifier() = default; diff --git a/chromium/cc/base/delayed_unique_notifier.h b/chromium/cc/base/delayed_unique_notifier.h index 16669879d00..b70de390844 100644 --- a/chromium/cc/base/delayed_unique_notifier.h +++ b/chromium/cc/base/delayed_unique_notifier.h @@ -67,7 +67,7 @@ class CC_BASE_EXPORT DelayedUniqueNotifier { base::TimeTicks next_notification_time_; bool notification_pending_; - base::WeakPtrFactory<DelayedUniqueNotifier> weak_ptr_factory_; + base::WeakPtrFactory<DelayedUniqueNotifier> weak_ptr_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/base/devtools_instrumentation.cc b/chromium/cc/base/devtools_instrumentation.cc index fab22da704c..d348592276c 100644 --- a/chromium/cc/base/devtools_instrumentation.cc +++ b/chromium/cc/base/devtools_instrumentation.cc @@ -43,6 +43,9 @@ ScopedImageDecodeTask::ScopedImageDecodeTask(const void* image_ptr, ScopedImageDecodeTask::~ScopedImageDecodeTask() { TRACE_EVENT_END0(internal::CategoryName::kTimeline, internal::kImageDecodeTask); + if (suppress_metrics_) + return; + base::TimeDelta duration = base::TimeTicks::Now() - start_time_; switch (task_type_) { case kInRaster: diff --git a/chromium/cc/base/devtools_instrumentation.h b/chromium/cc/base/devtools_instrumentation.h index 56077e041e4..052bff459a5 100644 --- a/chromium/cc/base/devtools_instrumentation.h +++ b/chromium/cc/base/devtools_instrumentation.h @@ -76,10 +76,15 @@ class CC_BASE_EXPORT ScopedImageDecodeTask { ScopedImageDecodeTask& operator=(const ScopedImageDecodeTask&) = delete; + // Prevents logging duration metrics. Used in cases where a task performed + // uninteresting work or was terminated early. + void SuppressMetrics() { suppress_metrics_ = true; } + private: const DecodeType decode_type_; const TaskType task_type_; const base::TimeTicks start_time_; + bool suppress_metrics_ = false; }; class CC_BASE_EXPORT ScopedLayerTreeTask { diff --git a/chromium/cc/base/rolling_time_delta_history.cc b/chromium/cc/base/rolling_time_delta_history.cc index dde20a7bbe4..6973e42414a 100644 --- a/chromium/cc/base/rolling_time_delta_history.cc +++ b/chromium/cc/base/rolling_time_delta_history.cc @@ -29,6 +29,13 @@ void RollingTimeDeltaHistory::InsertSample(base::TimeDelta time) { percentile_cache_.clear(); } +void RollingTimeDeltaHistory::RemoveOldestSample() { + if (sample_set_.size() > 0) { + sample_set_.erase(chronological_sample_deque_.front()); + chronological_sample_deque_.pop_front(); + } +} + void RollingTimeDeltaHistory::Clear() { chronological_sample_deque_.clear(); sample_set_.clear(); diff --git a/chromium/cc/base/rolling_time_delta_history.h b/chromium/cc/base/rolling_time_delta_history.h index 75832323278..81aa8d263bd 100644 --- a/chromium/cc/base/rolling_time_delta_history.h +++ b/chromium/cc/base/rolling_time_delta_history.h @@ -28,6 +28,7 @@ class CC_BASE_EXPORT RollingTimeDeltaHistory { RollingTimeDeltaHistory& operator=(const RollingTimeDeltaHistory&) = delete; void InsertSample(base::TimeDelta time); + void RemoveOldestSample(); size_t sample_count() const { return sample_set_.size(); } void Clear(); diff --git a/chromium/cc/base/unique_notifier.cc b/chromium/cc/base/unique_notifier.cc index 52e7a498de3..5a3ada85187 100644 --- a/chromium/cc/base/unique_notifier.cc +++ b/chromium/cc/base/unique_notifier.cc @@ -15,8 +15,7 @@ UniqueNotifier::UniqueNotifier(base::SequencedTaskRunner* task_runner, base::RepeatingClosure closure) : task_runner_(task_runner), closure_(std::move(closure)), - notification_pending_(false), - weak_ptr_factory_(this) {} + notification_pending_(false) {} UniqueNotifier::~UniqueNotifier() = default; diff --git a/chromium/cc/base/unique_notifier.h b/chromium/cc/base/unique_notifier.h index ad2172b4213..2366b611b68 100644 --- a/chromium/cc/base/unique_notifier.h +++ b/chromium/cc/base/unique_notifier.h @@ -48,7 +48,7 @@ class CC_BASE_EXPORT UniqueNotifier { base::Lock lock_; bool notification_pending_; - base::WeakPtrFactory<UniqueNotifier> weak_ptr_factory_; + base::WeakPtrFactory<UniqueNotifier> weak_ptr_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/benchmarks/micro_benchmark_impl.cc b/chromium/cc/benchmarks/micro_benchmark_impl.cc index a37ad345277..2dacb125406 100644 --- a/chromium/cc/benchmarks/micro_benchmark_impl.cc +++ b/chromium/cc/benchmarks/micro_benchmark_impl.cc @@ -31,7 +31,7 @@ void MicroBenchmarkImpl::DidCompleteCommit(LayerTreeHostImpl* host) {} void MicroBenchmarkImpl::NotifyDone(std::unique_ptr<base::Value> result) { origin_task_runner_->PostTask( - FROM_HERE, base::BindOnce(std::move(callback_), base::Passed(&result))); + FROM_HERE, base::BindOnce(std::move(callback_), std::move(result))); is_done_ = true; } diff --git a/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc b/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc index 844f66aef1d..b3ca388b131 100644 --- a/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc +++ b/chromium/cc/benchmarks/rasterize_and_record_benchmark.cc @@ -74,8 +74,7 @@ RasterizeAndRecordBenchmark::RasterizeAndRecordBenchmark( record_repeat_count_(kDefaultRecordRepeatCount), settings_(std::move(value)), main_thread_benchmark_done_(false), - layer_tree_host_(nullptr), - weak_ptr_factory_(this) { + layer_tree_host_(nullptr) { base::DictionaryValue* settings = nullptr; settings_->GetAsDictionary(&settings); if (!settings) diff --git a/chromium/cc/benchmarks/rasterize_and_record_benchmark.h b/chromium/cc/benchmarks/rasterize_and_record_benchmark.h index efb6266b9fb..8ca7c588a1f 100644 --- a/chromium/cc/benchmarks/rasterize_and_record_benchmark.h +++ b/chromium/cc/benchmarks/rasterize_and_record_benchmark.h @@ -62,7 +62,7 @@ class RasterizeAndRecordBenchmark : public MicroBenchmark { LayerTreeHost* layer_tree_host_; - base::WeakPtrFactory<RasterizeAndRecordBenchmark> weak_ptr_factory_; + base::WeakPtrFactory<RasterizeAndRecordBenchmark> weak_ptr_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc b/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc index a1f8b53f202..93b5d5dfc4e 100644 --- a/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc +++ b/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc @@ -95,7 +95,7 @@ class FixedInvalidationPictureLayerTilingClient return base_client_->CreateTile(info); } - gfx::Size CalculateTileSize(const gfx::Size& content_bounds) const override { + gfx::Size CalculateTileSize(const gfx::Size& content_bounds) override { return base_client_->CalculateTileSize(content_bounds); } @@ -116,6 +116,10 @@ class FixedInvalidationPictureLayerTilingClient return base_client_->RequiresHighResToDraw(); } + const PaintWorkletRecordMap& GetPaintWorkletRecords() const override { + return base_client_->GetPaintWorkletRecords(); + } + private: PictureLayerTilingClient* base_client_; Region invalidation_; diff --git a/chromium/cc/benchmarks/unittest_only_benchmark.cc b/chromium/cc/benchmarks/unittest_only_benchmark.cc index 4e32942140a..efb46c63b90 100644 --- a/chromium/cc/benchmarks/unittest_only_benchmark.cc +++ b/chromium/cc/benchmarks/unittest_only_benchmark.cc @@ -14,9 +14,7 @@ namespace cc { UnittestOnlyBenchmark::UnittestOnlyBenchmark(std::unique_ptr<base::Value> value, DoneCallback callback) - : MicroBenchmark(std::move(callback)), - create_impl_benchmark_(false), - weak_ptr_factory_(this) { + : MicroBenchmark(std::move(callback)), create_impl_benchmark_(false) { if (!value) return; diff --git a/chromium/cc/benchmarks/unittest_only_benchmark.h b/chromium/cc/benchmarks/unittest_only_benchmark.h index fa0f63d6f25..7ba9a9e1ded 100644 --- a/chromium/cc/benchmarks/unittest_only_benchmark.h +++ b/chromium/cc/benchmarks/unittest_only_benchmark.h @@ -28,7 +28,7 @@ class CC_EXPORT UnittestOnlyBenchmark : public MicroBenchmark { void RecordImplResults(std::unique_ptr<base::Value> results); bool create_impl_benchmark_; - base::WeakPtrFactory<UnittestOnlyBenchmark> weak_ptr_factory_; + base::WeakPtrFactory<UnittestOnlyBenchmark> weak_ptr_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/debug/debug_colors.cc b/chromium/cc/debug/debug_colors.cc index d8fc52a0da8..c16f4fbec8b 100644 --- a/chromium/cc/debug/debug_colors.cc +++ b/chromium/cc/debug/debug_colors.cc @@ -194,6 +194,25 @@ SkColor DebugColors::PaintRectFillColor(int step) { return FadedGreen(60, step); } +static SkColor FadedBlue(int initial_value, int step) { + DCHECK_GE(step, 0); + DCHECK_LE(step, DebugColors::kFadeSteps); + int value = step * initial_value / DebugColors::kFadeSteps; + return SkColorSetARGB(value, 0, 0, 255); +} +/// Layout Shift rects in blue. +SkColor DebugColors::LayoutShiftRectBorderColor() { + return SkColorSetARGB(0, 0, 0, 255); +} +int DebugColors::LayoutShiftRectBorderWidth() { + // We don't want any border showing for the layout shift debug rects so we set + // the border width to be equal to 0. + return 0; +} +SkColor DebugColors::LayoutShiftRectFillColor(int step) { + return FadedBlue(60, step); +} + // Property-changed rects in blue. SkColor DebugColors::PropertyChangedRectBorderColor() { return SkColorSetARGB(255, 0, 0, 255); diff --git a/chromium/cc/debug/debug_colors.h b/chromium/cc/debug/debug_colors.h index 69a73b1d47d..8e13fcbb8d4 100644 --- a/chromium/cc/debug/debug_colors.h +++ b/chromium/cc/debug/debug_colors.h @@ -72,6 +72,10 @@ class CC_DEBUG_EXPORT DebugColors { static int PaintRectBorderWidth(); static SkColor PaintRectFillColor(int step); + static SkColor LayoutShiftRectBorderColor(); + static int LayoutShiftRectBorderWidth(); + static SkColor LayoutShiftRectFillColor(int step); + static SkColor PropertyChangedRectBorderColor(); static int PropertyChangedRectBorderWidth(); static SkColor PropertyChangedRectFillColor(); diff --git a/chromium/cc/input/input_handler.h b/chromium/cc/input/input_handler.h index 0c41e257c7d..80e9bf42b20 100644 --- a/chromium/cc/input/input_handler.h +++ b/chromium/cc/input/input_handler.h @@ -15,8 +15,9 @@ #include "cc/input/scroll_state.h" #include "cc/input/scrollbar.h" #include "cc/input/touch_action.h" -#include "cc/trees/element_id.h" +#include "cc/paint/element_id.h" #include "cc/trees/swap_promise_monitor.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" #include "ui/events/types/scroll_types.h" namespace gfx { @@ -94,7 +95,8 @@ class CC_EXPORT InputHandlerClient { float page_scale_factor, float min_page_scale_factor, float max_page_scale_factor) = 0; - virtual void DeliverInputForBeginFrame() = 0; + virtual void DeliverInputForBeginFrame(const viz::BeginFrameArgs& args) = 0; + virtual void DeliverInputForHighLatencyMode() = 0; protected: InputHandlerClient() = default; @@ -223,8 +225,8 @@ class CC_EXPORT InputHandler { virtual bool IsCurrentlyScrollingViewport() const = 0; // Whether the layer under |viewport_point| is the currently scrolling layer. - virtual bool IsCurrentlyScrollingLayerAt(const gfx::Point& viewport_point, - ScrollInputType type) const = 0; + virtual bool IsCurrentlyScrollingLayerAt( + const gfx::Point& viewport_point) const = 0; virtual EventListenerProperties GetEventListenerProperties( EventListenerClass event_class) const = 0; diff --git a/chromium/cc/input/scroll_state.h b/chromium/cc/input/scroll_state.h index 11114c0293b..658d356dc91 100644 --- a/chromium/cc/input/scroll_state.h +++ b/chromium/cc/input/scroll_state.h @@ -99,6 +99,8 @@ class CC_EXPORT ScrollState { bool is_scroll_chain_cut() const { return data_.is_scroll_chain_cut; } + double delta_granularity() const { return data_.delta_granularity; } + LayerTreeImpl* layer_tree_impl() { return layer_tree_impl_; } ScrollStateData* data() { return &data_; } diff --git a/chromium/cc/input/scrollbar.h b/chromium/cc/input/scrollbar.h index ec23fef5f0d..7d0647ff78e 100644 --- a/chromium/cc/input/scrollbar.h +++ b/chromium/cc/input/scrollbar.h @@ -10,13 +10,21 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" -static const int kPixelsPerLineStep = 40; -static const float kMinFractionToStepWhenPaging = 0.875f; +static constexpr int kPixelsPerLineStep = 40; +static constexpr float kMinFractionToStepWhenPaging = 0.875f; + +// Autoscrolling (on the main thread) happens by applying a delta every 50ms. +// Hence, pixels per second for a autoscroll cc animation can be calculated as: +// autoscroll velocity = delta / 0.05 sec = delta x 20 +static constexpr float kAutoscrollMultiplier = 20.f; +static constexpr base::TimeDelta kInitialAutoscrollTimerDelay = + base::TimeDelta::FromMilliseconds(250); namespace cc { enum ScrollbarOrientation { HORIZONTAL, VERTICAL }; enum ScrollDirection { SCROLL_BACKWARD, SCROLL_FORWARD }; +enum AutoScrollState { NO_AUTOSCROLL, AUTOSCROLL_FORWARD, AUTOSCROLL_BACKWARD }; enum ScrollbarPart { THUMB, diff --git a/chromium/cc/input/scrollbar_animation_controller.cc b/chromium/cc/input/scrollbar_animation_controller.cc index 4b63b4bf9b7..bed51a0282d 100644 --- a/chromium/cc/input/scrollbar_animation_controller.cc +++ b/chromium/cc/input/scrollbar_animation_controller.cc @@ -55,8 +55,7 @@ ScrollbarAnimationController::ScrollbarAnimationController( show_scrollbars_on_scroll_gesture_(false), need_thinning_animation_(false), is_mouse_down_(false), - tickmarks_showing_(false), - weak_factory_(this) {} + tickmarks_showing_(false) {} ScrollbarAnimationController::ScrollbarAnimationController( ElementId scroll_element_id, @@ -78,8 +77,7 @@ ScrollbarAnimationController::ScrollbarAnimationController( show_scrollbars_on_scroll_gesture_(true), need_thinning_animation_(true), is_mouse_down_(false), - tickmarks_showing_(false), - weak_factory_(this) { + tickmarks_showing_(false) { vertical_controller_ = SingleScrollbarAnimationControllerThinning::Create( scroll_element_id, ScrollbarOrientation::VERTICAL, client, thinning_duration); diff --git a/chromium/cc/input/scrollbar_animation_controller.h b/chromium/cc/input/scrollbar_animation_controller.h index f7a26c523f4..67a9ed5c8fc 100644 --- a/chromium/cc/input/scrollbar_animation_controller.h +++ b/chromium/cc/input/scrollbar_animation_controller.h @@ -169,7 +169,7 @@ class CC_EXPORT ScrollbarAnimationController { std::unique_ptr<SingleScrollbarAnimationControllerThinning> horizontal_controller_; - base::WeakPtrFactory<ScrollbarAnimationController> weak_factory_; + base::WeakPtrFactory<ScrollbarAnimationController> weak_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/input/scrollbar_controller.cc b/chromium/cc/input/scrollbar_controller.cc index d29a2574cc6..46f4b21dbca 100644 --- a/chromium/cc/input/scrollbar_controller.cc +++ b/chromium/cc/input/scrollbar_controller.cc @@ -6,19 +6,40 @@ #include <algorithm> +#include "base/cancelable_callback.h" #include "cc/base/math_util.h" #include "cc/input/scrollbar.h" #include "cc/input/scrollbar_controller.h" #include "cc/trees/layer_tree_impl.h" +#include "cc/trees/scroll_node.h" namespace cc { +ScrollbarController::~ScrollbarController() { + if (cancelable_autoscroll_task_) { + cancelable_autoscroll_task_->Cancel(); + cancelable_autoscroll_task_.reset(); + } +} + ScrollbarController::ScrollbarController( LayerTreeHostImpl* layer_tree_host_impl) : layer_tree_host_impl_(layer_tree_host_impl), scrollbar_scroll_is_active_(false), thumb_drag_in_progress_(false), + autoscroll_state_(AutoScrollState::NO_AUTOSCROLL), currently_captured_scrollbar_(nullptr), - previous_pointer_position_(gfx::PointF(0, 0)) {} + previous_pointer_position_(gfx::PointF(0, 0)), + cancelable_autoscroll_task_(nullptr) {} + +void ScrollbarController::WillBeginImplFrame() { + // TODO(arakeri): Revisit this when addressing crbug.com/967004. The + // animations need to be aborted/restarted based on the pointer location (i.e + // leaving/entering the track/arrows, reaching the track end etc). The + // autoscroll_state_ however, needs to be reset on pointer changes. + if (autoscroll_state_ != AutoScrollState::NO_AUTOSCROLL && + ShouldCancelTrackAutoscroll()) + layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort(); +} // Performs hit test and prepares scroll deltas that will be used by GSB and // GSU. @@ -35,11 +56,14 @@ InputHandlerPointerResult ScrollbarController::HandleMouseDown( currently_captured_scrollbar_ = layer_impl->ToScrollbarLayer(); scroll_result.type = PointerResultType::kScrollbarScroll; layer_tree_host_impl_->active_tree()->UpdateScrollbarGeometries(); - scroll_result.scroll_offset = - GetScrollDeltaFromPointerDown(position_in_widget); + ScrollbarPart scrollbar_part = + GetScrollbarPartFromPointerDown(position_in_widget); + scroll_result.scroll_offset = GetScrollOffsetForScrollbarPart( + scrollbar_part, currently_captured_scrollbar_->orientation()); previous_pointer_position_ = position_in_widget; scrollbar_scroll_is_active_ = true; - if (thumb_drag_in_progress_) { + if (scrollbar_part == ScrollbarPart::THUMB) { + thumb_drag_in_progress_ = true; scroll_result.scroll_units = ui::input_types::ScrollGranularity::kScrollByPrecisePixel; } else { @@ -49,12 +73,27 @@ InputHandlerPointerResult ScrollbarController::HandleMouseDown( ui::input_types::ScrollGranularity::kScrollByPixel; } + // Thumb drag is the only scrollbar manipulation that cannot produce an + // autoscroll. All other interactions like clicking on arrows/trackparts have + // the potential of initiating an autoscroll (if held down long enough). + if (!scroll_result.scroll_offset.IsZero() && !thumb_drag_in_progress_) { + cancelable_autoscroll_task_ = std::make_unique<base::CancelableClosure>( + base::Bind(&ScrollbarController::StartAutoScrollAnimation, + base::Unretained(this), scroll_result.scroll_offset, + currently_captured_scrollbar_->scroll_element_id())); + layer_tree_host_impl_->task_runner_provider() + ->ImplThreadTaskRunner() + ->PostDelayedTask(FROM_HERE, cancelable_autoscroll_task_->callback(), + kInitialAutoscrollTimerDelay); + } return scroll_result; } // Performs hit test and prepares scroll deltas that will be used by GSU. InputHandlerPointerResult ScrollbarController::HandleMouseMove( const gfx::PointF position_in_widget) { + const gfx::PointF previous_pointer_position = previous_pointer_position_; + previous_pointer_position_ = position_in_widget; InputHandlerPointerResult scroll_result; if (!thumb_drag_in_progress_) return scroll_result; @@ -64,10 +103,10 @@ InputHandlerPointerResult ScrollbarController::HandleMouseMove( currently_captured_scrollbar_->orientation(); if (orientation == ScrollbarOrientation::VERTICAL) scroll_result.scroll_offset.set_y(position_in_widget.y() - - previous_pointer_position_.y()); + previous_pointer_position.y()); else scroll_result.scroll_offset.set_x(position_in_widget.x() - - previous_pointer_position_.x()); + previous_pointer_position.x()); LayerImpl* owner_scroll_layer = layer_tree_host_impl_->active_tree()->ScrollableLayerByElementId( @@ -129,7 +168,6 @@ InputHandlerPointerResult ScrollbarController::HandleMouseMove( (scrollbar_track_length - scrollbar_thumb_length)) * layer_tree_host_impl_->active_tree()->device_scale_factor(); scroll_result.scroll_offset.Scale(scaled_scroller_to_scrollbar_ratio); - previous_pointer_position_ = position_in_widget; // Thumb drags have more granularity and are purely dependent on the pointer // movement. Hence we use kPrecisePixel when dragging the thumb. scroll_result.scroll_units = @@ -138,6 +176,95 @@ InputHandlerPointerResult ScrollbarController::HandleMouseMove( return scroll_result; } +bool ScrollbarController::ShouldCancelTrackAutoscroll() { + // Should only ever be called if an autoscroll is in progress. + DCHECK(autoscroll_state_ != AutoScrollState::NO_AUTOSCROLL); + + layer_tree_host_impl_->active_tree()->UpdateScrollbarGeometries(); + const ScrollbarOrientation orientation = + currently_captured_scrollbar_->orientation(); + const gfx::Rect thumb_quad = + currently_captured_scrollbar_->ComputeThumbQuadRect(); + + bool clipped; + gfx::PointF scroller_relative_position( + GetScrollbarRelativePosition(previous_pointer_position_, &clipped)); + + if (clipped) + return false; + + // Based on the orientation of the scrollbar and the direction of the + // autoscroll, the code below makes a decision of whether the track autoscroll + // should be canceled or not. + int thumb_start = 0; + int thumb_end = 0; + int pointer_position = 0; + if (orientation == ScrollbarOrientation::VERTICAL) { + thumb_start = thumb_quad.y(); + thumb_end = thumb_quad.y() + thumb_quad.height(); + pointer_position = scroller_relative_position.y(); + } else { + thumb_start = thumb_quad.x(); + thumb_end = thumb_quad.x() + thumb_quad.width(); + pointer_position = scroller_relative_position.x(); + } + + if ((autoscroll_state_ == AutoScrollState::AUTOSCROLL_FORWARD && + thumb_end > pointer_position) || + (autoscroll_state_ == AutoScrollState::AUTOSCROLL_BACKWARD && + thumb_start < pointer_position)) + return true; + + return false; +} + +void ScrollbarController::StartAutoScrollAnimation( + gfx::ScrollOffset scroll_offset, + ElementId element_id) { + // scroll_node is set up while handling GSB. If there's no node to scroll, we + // don't need to create any animation for it. + ScrollTree& scroll_tree = + layer_tree_host_impl_->active_tree()->property_trees()->scroll_tree; + ScrollNode* scroll_node = scroll_tree.FindNodeFromElementId(element_id); + + if (!(scroll_node && scrollbar_scroll_is_active_)) + return; + + layer_tree_host_impl_->active_tree()->UpdateScrollbarGeometries(); + ScrollbarOrientation orientation = + currently_captured_scrollbar_->orientation(); + // TODO(arakeri): The animation needs to be readjusted if the scroller length + // changes. Tracked here: crbug.com/972485 + float scroll_layer_length = + currently_captured_scrollbar_->scroll_layer_length(); + + gfx::ScrollOffset current_offset = + scroll_tree.current_scroll_offset(scroll_node->element_id); + gfx::Vector2dF target_offset; + + // Determine the max offset for the scroll based on the scrolling direction. + // Negative scroll_delta indicates backwards scrolling whereas a positive + // scroll_delta indicates forwards scrolling. + float scroll_delta = 0; + if (orientation == ScrollbarOrientation::VERTICAL) { + DCHECK_NE(scroll_offset.y(), 0); + scroll_delta = scroll_offset.y(); + float final_offset = scroll_delta < 0 ? 0 : scroll_layer_length; + target_offset = gfx::Vector2dF(current_offset.x(), final_offset); + } else { + DCHECK_NE(scroll_offset.x(), 0); + scroll_delta = scroll_offset.x(); + float final_offset = scroll_delta < 0 ? 0 : scroll_layer_length; + target_offset = gfx::Vector2dF(final_offset, current_offset.y()); + } + + autoscroll_state_ = scroll_delta < 0 ? AutoScrollState::AUTOSCROLL_BACKWARD + : AutoScrollState::AUTOSCROLL_FORWARD; + float autoscroll_velocity = std::abs(scroll_delta) * kAutoscrollMultiplier; + layer_tree_host_impl_->AutoScrollAnimationCreate(scroll_node, target_offset, + autoscroll_velocity); +} + // Performs hit test and prepares scroll deltas that will be used by GSE. InputHandlerPointerResult ScrollbarController::HandleMouseUp( const gfx::PointF position_in_widget) { @@ -146,7 +273,20 @@ InputHandlerPointerResult ScrollbarController::HandleMouseUp( scrollbar_scroll_is_active_ = false; scroll_result.type = PointerResultType::kScrollbarScroll; } + + // TODO(arakeri): This needs to be moved to ScrollOffsetAnimationsImpl as it + // has knowledge about what type of animation is running. crbug.com/976353 + // Only abort the animation if it is an "autoscroll" animation. + if (autoscroll_state_ != AutoScrollState::NO_AUTOSCROLL) + layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort(); + + if (cancelable_autoscroll_task_) { + cancelable_autoscroll_task_->Cancel(); + cancelable_autoscroll_task_.reset(); + } + thumb_drag_in_progress_ = false; + autoscroll_state_ = AutoScrollState::NO_AUTOSCROLL; return scroll_result; } @@ -195,31 +335,41 @@ int ScrollbarController::GetScrollDeltaForScrollbarPart( layer_tree_host_impl_->active_tree()->device_scale_factor(); } -// Determines the scroll offsets based on hit test results. -gfx::ScrollOffset ScrollbarController::GetScrollDeltaFromPointerDown( - const gfx::PointF position_in_widget) { - const ScrollbarOrientation orientation = - currently_captured_scrollbar_->orientation(); - - // position_in_widget needs to be transformed and made relative to the - // scrollbar layer because hit testing assumes layer relative coordinates. - ScrollbarPart scrollbar_part = ScrollbarPart::NO_PART; +gfx::PointF ScrollbarController::GetScrollbarRelativePosition( + const gfx::PointF position_in_widget, + bool* clipped) { gfx::Transform inverse_screen_space_transform( gfx::Transform::kSkipInitialization); if (!currently_captured_scrollbar_->ScreenSpaceTransform().GetInverse( &inverse_screen_space_transform)) - return gfx::ScrollOffset(0, 0); + return gfx::PointF(0, 0); - bool clipped; - gfx::PointF scroller_relative_position(MathUtil::ProjectPoint( - inverse_screen_space_transform, position_in_widget, &clipped)); + return gfx::PointF(MathUtil::ProjectPoint(inverse_screen_space_transform, + position_in_widget, clipped)); +} + +// Determines the ScrollbarPart based on the position_in_widget. +ScrollbarPart ScrollbarController::GetScrollbarPartFromPointerDown( + const gfx::PointF position_in_widget) { + // position_in_widget needs to be transformed and made relative to the + // scrollbar layer because hit testing assumes layer relative coordinates. + bool clipped = false; + + const gfx::PointF scroller_relative_position( + GetScrollbarRelativePosition(position_in_widget, &clipped)); if (clipped) - return gfx::ScrollOffset(0, 0); + return ScrollbarPart::NO_PART; - scrollbar_part = currently_captured_scrollbar_->IdentifyScrollbarPart( + return currently_captured_scrollbar_->IdentifyScrollbarPart( scroller_relative_position); +} +// Determines the scroll offsets based on the ScrollbarPart and the scrollbar +// orientation. +gfx::ScrollOffset ScrollbarController::GetScrollOffsetForScrollbarPart( + const ScrollbarPart scrollbar_part, + const ScrollbarOrientation orientation) { float scroll_delta = GetScrollDeltaForScrollbarPart(scrollbar_part); // See CreateScrollStateForGesture for more information on how these values @@ -232,9 +382,6 @@ gfx::ScrollOffset ScrollbarController::GetScrollDeltaFromPointerDown( return orientation == ScrollbarOrientation::VERTICAL ? gfx::ScrollOffset(0, scroll_delta) // Down arrow : gfx::ScrollOffset(scroll_delta, 0); // Right arrow - } else if (scrollbar_part == ScrollbarPart::THUMB) { - // Offsets are calculated in HandleMouseMove. - thumb_drag_in_progress_ = true; } else if (scrollbar_part == ScrollbarPart::BACK_TRACK) { return orientation == ScrollbarOrientation::VERTICAL ? gfx::ScrollOffset(0, -scroll_delta) // Track click up diff --git a/chromium/cc/input/scrollbar_controller.h b/chromium/cc/input/scrollbar_controller.h index 3ce462461bf..550e63ef1ba 100644 --- a/chromium/cc/input/scrollbar_controller.h +++ b/chromium/cc/input/scrollbar_controller.h @@ -18,7 +18,7 @@ namespace cc { class CC_EXPORT ScrollbarController { public: explicit ScrollbarController(LayerTreeHostImpl*); - virtual ~ScrollbarController() = default; + virtual ~ScrollbarController(); InputHandlerPointerResult HandleMouseDown( const gfx::PointF position_in_widget); @@ -26,14 +26,38 @@ class CC_EXPORT ScrollbarController { const gfx::PointF position_in_widget); InputHandlerPointerResult HandleMouseUp(const gfx::PointF position_in_widget); + // scroll_offset is the delta from the initial click. This is needed to + // determine whether we should set up the autoscrolling in the forwards or the + // backwards direction and the speed of the animation. + void StartAutoScrollAnimation(gfx::ScrollOffset scroll_offset, + ElementId element_id); + bool ScrollbarScrollIsActive() { return scrollbar_scroll_is_active_; } + ScrollbarOrientation orientation() { + return currently_captured_scrollbar_->orientation(); + } + + void WillBeginImplFrame(); + private: - // Returns a gfx::ScrollOffset object which contains scroll deltas for the - // synthetic Gesture events. - gfx::ScrollOffset GetScrollDeltaFromPointerDown( + // Returns the hit tested ScrollbarPart based on the position_in_widget. + ScrollbarPart GetScrollbarPartFromPointerDown( const gfx::PointF position_in_widget); + + // Returns scroll offsets based on which ScrollbarPart was hit tested. + gfx::ScrollOffset GetScrollOffsetForScrollbarPart( + const ScrollbarPart scrollbar_part, + const ScrollbarOrientation orientation); + LayerImpl* GetLayerHitByPoint(const gfx::PointF position_in_widget); int GetScrollDeltaForScrollbarPart(ScrollbarPart scrollbar_part); + // Makes position_in_widget relative to the scrollbar. + gfx::PointF GetScrollbarRelativePosition(const gfx::PointF position_in_widget, + bool* clipped); + + // Decides whether a track autoscroll should be aborted. + bool ShouldCancelTrackAutoscroll(); + LayerTreeHostImpl* layer_tree_host_impl_; // Used to safeguard against firing GSE without firing GSB and GSU. For @@ -43,10 +67,17 @@ class CC_EXPORT ScrollbarController { // Used to tell if the scrollbar thumb is getting dragged. bool thumb_drag_in_progress_; + + // "Autoscroll" here means the continuous scrolling that occurs when the + // pointer is held down on a hit-testable area of the scrollbar such as an + // arrows of the track itself. + AutoScrollState autoscroll_state_; const ScrollbarLayerImplBase* currently_captured_scrollbar_; // This is relative to the RenderWidget's origin. gfx::PointF previous_pointer_position_; + + std::unique_ptr<base::CancelableClosure> cancelable_autoscroll_task_; }; } // namespace cc diff --git a/chromium/cc/layers/append_quads_data.h b/chromium/cc/layers/append_quads_data.h index a681a97b489..02545c2383b 100644 --- a/chromium/cc/layers/append_quads_data.h +++ b/chromium/cc/layers/append_quads_data.h @@ -11,6 +11,7 @@ #include "base/optional.h" #include "cc/cc_export.h" #include "components/viz/common/surfaces/surface_id.h" +#include "ui/gfx/geometry/rect.h" namespace cc { @@ -44,6 +45,10 @@ class CC_EXPORT AppendQuadsData { // active CompositorFrames so that this CompositorFrame can // activate. std::vector<viz::SurfaceId> activation_dependencies; + + // If the layer is a MirrorLayer, this will represent its visible boundaries + // in target space. + gfx::Rect mirror_rect; }; } // namespace cc diff --git a/chromium/cc/layers/draw_properties.cc b/chromium/cc/layers/draw_properties.cc index 0cff4994c1d..b998cff755b 100644 --- a/chromium/cc/layers/draw_properties.cc +++ b/chromium/cc/layers/draw_properties.cc @@ -6,10 +6,7 @@ namespace cc { -DrawProperties::DrawProperties() - : opacity(0.f), - screen_space_transform_is_animating(false), - is_clipped(false) {} +DrawProperties::DrawProperties() = default; DrawProperties::~DrawProperties() = default; diff --git a/chromium/cc/layers/draw_properties.h b/chromium/cc/layers/draw_properties.h index 1b96435e988..b241728724c 100644 --- a/chromium/cc/layers/draw_properties.h +++ b/chromium/cc/layers/draw_properties.h @@ -35,19 +35,19 @@ struct CC_EXPORT DrawProperties { // DrawProperties::opacity may be different than LayerImpl::opacity, // particularly in the case when a RenderSurface re-parents the layer's // opacity, or when opacity is compounded by the hierarchy. - float opacity; + float opacity = 0.f; // Whether the layer has a potentially animating transform in its chain of // transforms to the screen. This is essentially a cache of the transform // node's potentially-animated status. - bool screen_space_transform_is_animating; + bool screen_space_transform_is_animating = false; // True if the layer needs to be clipped by clip_rect. - bool is_clipped; + bool is_clipped = false; // If set, it makes the layer's rounded corner not trigger a render surface if // possible. - bool is_fast_rounded_corner; + bool is_fast_rounded_corner = false; // This rect is a bounding box around what part of the layer is visible, in // the layer's coordinate space. diff --git a/chromium/cc/layers/heads_up_display_layer.cc b/chromium/cc/layers/heads_up_display_layer.cc index e291e6ee99a..d1ac009c6de 100644 --- a/chromium/cc/layers/heads_up_display_layer.cc +++ b/chromium/cc/layers/heads_up_display_layer.cc @@ -82,6 +82,7 @@ void HeadsUpDisplayLayer::PushPropertiesTo(LayerImpl* layer) { layer_impl->SetHUDTypeface(typeface_); layer_impl->SetLayoutShiftRects(layout_shift_rects_); + layout_shift_rects_.clear(); } } // namespace cc diff --git a/chromium/cc/layers/heads_up_display_layer_impl.cc b/chromium/cc/layers/heads_up_display_layer_impl.cc index 75431c15d8f..f98f34286bb 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.cc +++ b/chromium/cc/layers/heads_up_display_layer_impl.cc @@ -107,8 +107,7 @@ HeadsUpDisplayLayerImpl::HeadsUpDisplayLayerImpl(LayerTreeImpl* tree_impl, : LayerImpl(tree_impl, id), internal_contents_scale_(1.f), fps_graph_(60.0, 80.0), - paint_time_graph_(16.0, 48.0), - fade_step_(0) {} + paint_time_graph_(16.0, 48.0) {} HeadsUpDisplayLayerImpl::~HeadsUpDisplayLayerImpl() { ReleaseResources(); @@ -188,7 +187,7 @@ void HeadsUpDisplayLayerImpl::AppendQuads(viz::RenderPass* render_pass, viz::SharedQuadState* shared_quad_state = render_pass->CreateAndAppendSharedQuadState(); PopulateScaledSharedQuadState(shared_quad_state, internal_contents_scale_, - internal_contents_scale_, contents_opaque()); + contents_opaque()); // Appends a dummy quad here, which will be updated later once the resource // is ready in UpdateHudTexture(). We don't add a TextureDrawQuad directly @@ -321,14 +320,13 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( auto backing = std::make_unique<HudSoftwareBacking>(); backing->layer_tree_frame_sink = layer_tree_frame_sink; backing->shared_bitmap_id = viz::SharedBitmap::GenerateId(); - base::MappedReadOnlyRegion mapped_region = + base::MappedReadOnlyRegion shm = viz::bitmap_allocation::AllocateSharedBitmap(pool_resource.size(), pool_resource.format()); - backing->shared_mapping = std::move(mapped_region.mapping); + backing->shared_mapping = std::move(shm.mapping); - layer_tree_frame_sink->DidAllocateSharedBitmap( - viz::bitmap_allocation::ToMojoHandle(std::move(mapped_region.region)), - backing->shared_bitmap_id); + layer_tree_frame_sink->DidAllocateSharedBitmap(std::move(shm.region), + backing->shared_bitmap_id); pool_resource.set_software_backing(std::move(backing)); } @@ -483,7 +481,7 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture( /*background_color=*/SK_ColorTRANSPARENT, vertex_opacity, /*flipped=*/false, /*nearest_neighbor=*/false, /*secure_output_only=*/false, - ui::ProtectedVideoType::kClear); + gfx::ProtectedVideoType::kClear); ValidateQuadResources(quad); break; } @@ -532,6 +530,7 @@ void HeadsUpDisplayLayerImpl::PushPropertiesTo(LayerImpl* layer) { layer_impl->SetHUDTypeface(typeface_); layer_impl->SetLayoutShiftRects(layout_shift_rects_); + layout_shift_rects_.clear(); } void HeadsUpDisplayLayerImpl::UpdateHudContents() { @@ -983,6 +982,7 @@ void HeadsUpDisplayLayerImpl::DrawDebugRects( const std::vector<DebugRect>& debug_rects = debug_rect_history->debug_rects(); std::vector<DebugRect> new_paint_rects; + std::vector<DebugRect> new_layout_shift_rects; for (size_t i = 0; i < debug_rects.size(); ++i) { SkColor stroke_color = 0; @@ -992,8 +992,8 @@ void HeadsUpDisplayLayerImpl::DrawDebugRects( switch (debug_rects[i].type) { case LAYOUT_SHIFT_RECT_TYPE: - // TODO(rnasri@): Handle layout shift rects drawing. - break; + new_layout_shift_rects.push_back(debug_rects[i]); + continue; case PAINT_RECT_TYPE: new_paint_rects.push_back(debug_rects[i]); continue; @@ -1051,17 +1051,32 @@ void HeadsUpDisplayLayerImpl::DrawDebugRects( if (new_paint_rects.size()) { paint_rects_.swap(new_paint_rects); - fade_step_ = DebugColors::kFadeSteps; + paint_rects_fade_step_ = DebugColors::kFadeSteps; } - if (fade_step_ > 0) { - fade_step_--; + if (paint_rects_fade_step_ > 0) { + paint_rects_fade_step_--; for (size_t i = 0; i < paint_rects_.size(); ++i) { DrawDebugRect(canvas, &flags, paint_rects_[i], - DebugColors::PaintRectBorderColor(fade_step_), - DebugColors::PaintRectFillColor(fade_step_), + DebugColors::PaintRectBorderColor(paint_rects_fade_step_), + DebugColors::PaintRectFillColor(paint_rects_fade_step_), DebugColors::PaintRectBorderWidth(), ""); } } + + if (new_layout_shift_rects.size()) { + layout_shift_debug_rects_.swap(new_layout_shift_rects); + layout_shift_rects_fade_step_ = DebugColors::kFadeSteps; + } + if (layout_shift_rects_fade_step_ > 0) { + layout_shift_rects_fade_step_--; + for (size_t i = 0; i < layout_shift_debug_rects_.size(); ++i) { + DrawDebugRect( + canvas, &flags, layout_shift_debug_rects_[i], + DebugColors::LayoutShiftRectBorderColor(), + DebugColors::LayoutShiftRectFillColor(layout_shift_rects_fade_step_), + DebugColors::LayoutShiftRectBorderWidth(), ""); + } + } } const char* HeadsUpDisplayLayerImpl::LayerTypeAsString() const { diff --git a/chromium/cc/layers/heads_up_display_layer_impl.h b/chromium/cc/layers/heads_up_display_layer_impl.h index 4b1f4bf6ee2..e0784ff4b21 100644 --- a/chromium/cc/layers/heads_up_display_layer_impl.h +++ b/chromium/cc/layers/heads_up_display_layer_impl.h @@ -61,7 +61,9 @@ class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl { gfx::Rect GetEnclosingRectInTargetSpace() const override; - bool IsAnimatingHUDContents() const { return fade_step_ > 0; } + bool IsAnimatingHUDContents() const { + return paint_rects_fade_step_ > 0 || layout_shift_rects_fade_step_ > 0; + } void SetHUDTypeface(sk_sp<SkTypeface> typeface); void SetLayoutShiftRects(const std::vector<gfx::Rect>& rects); @@ -158,8 +160,10 @@ class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl { Graph fps_graph_; Graph paint_time_graph_; MemoryHistory::Entry memory_entry_; - int fade_step_; + int paint_rects_fade_step_ = 0; + int layout_shift_rects_fade_step_ = 0; std::vector<DebugRect> paint_rects_; + std::vector<DebugRect> layout_shift_debug_rects_; base::TimeTicks time_of_last_graph_update_; }; diff --git a/chromium/cc/layers/layer.cc b/chromium/cc/layers/layer.cc index 24db5b0a3cc..4fcd802446e 100644 --- a/chromium/cc/layers/layer.cc +++ b/chromium/cc/layers/layer.cc @@ -23,8 +23,8 @@ #include "cc/layers/layer_impl.h" #include "cc/layers/picture_layer.h" #include "cc/tiles/frame_viewer_instrumentation.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_host.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/mutator_host.h" @@ -71,7 +71,8 @@ Layer::Inputs::Inputs(int layer_id) has_will_change_transform_hint(false), trilinear_filtering(false), hide_layer_and_subtree(false), - overscroll_behavior(OverscrollBehavior::kOverscrollBehaviorTypeAuto) {} + overscroll_behavior(OverscrollBehavior::kOverscrollBehaviorTypeAuto), + mirror_count(0) {} Layer::Inputs::~Inputs() = default; @@ -101,6 +102,7 @@ Layer::Layer() may_contain_video_(false), needs_show_scrollbars_(false), has_transform_node_(false), + has_clip_node_(false), subtree_has_copy_request_(false), safe_opaque_background_color_(0), compositing_reasons_(0), @@ -214,7 +216,7 @@ bool Layer::IsPropertyChangeAllowed() const { } void Layer::CaptureContent(const gfx::Rect& rect, - std::vector<NodeHolder>* content) {} + std::vector<NodeId>* content) {} sk_sp<SkPicture> Layer::GetPicture() const { return nullptr; @@ -535,10 +537,63 @@ void Layer::SetMasksToBounds(bool masks_to_bounds) { SetSubtreePropertyChanged(); } +void Layer::SetClipRect(const gfx::Rect& clip_rect) { + DCHECK(IsPropertyChangeAllowed()); + if (inputs_.clip_rect == clip_rect) + return; + inputs_.clip_rect = clip_rect; + + // If the clip bounds have been cleared, the property trees needs a rebuild. + const bool force_rebuild = clip_rect.IsEmpty() || !has_clip_node_; + + SetSubtreePropertyChanged(); + if (clip_tree_index() != ClipTree::kInvalidNodeId && !force_rebuild) { + PropertyTrees* property_trees = layer_tree_host_->property_trees(); + gfx::RectF effective_clip_rect = EffectiveClipRect(); + if (ClipNode* node = property_trees->clip_tree.Node(clip_tree_index())) { + node->clip = effective_clip_rect; + node->clip += offset_to_transform_parent(); + property_trees->clip_tree.set_needs_update(true); + } + if (HasRoundedCorner() && + effect_tree_index() != EffectTree::kInvalidNodeId) { + if (EffectNode* node = + property_trees->effect_tree.Node(effect_tree_index())) { + node->rounded_corner_bounds = + gfx::RRectF(effective_clip_rect, corner_radii()); + node->effect_changed = true; + property_trees->effect_tree.set_needs_update(true); + } + } + } else { + SetPropertyTreesNeedRebuild(); + } + SetNeedsCommit(); +} + +gfx::RectF Layer::EffectiveClipRect() { + // If this does not have a clip rect set, then the subtree is clipped by + // the bounds. + const gfx::RectF layer_bounds = gfx::RectF(gfx::SizeF(bounds())); + if (clip_rect().IsEmpty()) + return layer_bounds; + + const gfx::RectF clip_rect_f(clip_rect()); + + // Layer needs to clip to its bounds as well apply a clip rect. Intersect the + // two to get the effective clip. + if (masks_to_bounds() || mask_layer() || filters().HasFilterThatMovesPixels()) + return gfx::IntersectRects(layer_bounds, clip_rect_f); + + // Clip rect is the only clip effecting the layer. + return clip_rect_f; +} + void Layer::SetMaskLayer(PictureLayer* mask_layer) { DCHECK(IsPropertyChangeAllowed()); if (inputs_.mask_layer.get() == mask_layer) return; + DCHECK(!layer_tree_host_ || !layer_tree_host_->IsUsingLayerLists()); if (inputs_.mask_layer.get()) { DCHECK_EQ(this, inputs_.mask_layer->parent()); inputs_.mask_layer->RemoveFromParent(); @@ -551,15 +606,8 @@ void Layer::SetMaskLayer(PictureLayer* mask_layer) { inputs_.mask_layer->RemoveFromParent(); DCHECK(!inputs_.mask_layer->parent()); inputs_.mask_layer->SetParent(this); - if (inputs_.filters.IsEmpty() && inputs_.backdrop_filters.IsEmpty() && - (!layer_tree_host_ || - layer_tree_host_->GetSettings().enable_mask_tiling)) { - inputs_.mask_layer->SetLayerMaskType( - Layer::LayerMaskType::MULTI_TEXTURE_MASK); - } else { - inputs_.mask_layer->SetLayerMaskType( - Layer::LayerMaskType::SINGLE_TEXTURE_MASK); - } + inputs_.mask_layer->SetLayerMaskType( + Layer::LayerMaskType::SINGLE_TEXTURE_MASK); } SetSubtreePropertyChanged(); SetNeedsFullTreeSync(); @@ -570,10 +618,6 @@ void Layer::SetFilters(const FilterOperations& filters) { if (inputs_.filters == filters) return; inputs_.filters = filters; - if (inputs_.mask_layer && !filters.IsEmpty()) { - inputs_.mask_layer->SetLayerMaskType( - Layer::LayerMaskType::SINGLE_TEXTURE_MASK); - } SetSubtreePropertyChanged(); SetPropertyTreesNeedRebuild(); SetNeedsCommit(); @@ -585,13 +629,6 @@ void Layer::SetBackdropFilters(const FilterOperations& filters) { return; inputs_.backdrop_filters = filters; - // We will not set the mask type to MULTI_TEXTURE_MASK if the mask layer's - // filters are removed, because we do not want to reraster if the filters are - // being animated. - if (inputs_.mask_layer && !filters.IsEmpty()) { - inputs_.mask_layer->SetLayerMaskType( - Layer::LayerMaskType::SINGLE_TEXTURE_MASK); - } SetSubtreePropertyChanged(); SetPropertyTreesNeedRebuild(); SetNeedsCommit(); @@ -627,7 +664,17 @@ void Layer::SetRoundedCorner(const gfx::RoundedCornersF& corner_radii) { inputs_.corner_radii = corner_radii; SetSubtreePropertyChanged(); SetNeedsCommit(); - SetPropertyTreesNeedRebuild(); + PropertyTrees* property_trees = layer_tree_host_->property_trees(); + EffectNode* node = nullptr; + if (effect_tree_index() != EffectTree::kInvalidNodeId && + (node = property_trees->effect_tree.Node(effect_tree_index()))) { + node->rounded_corner_bounds = + gfx::RRectF(EffectiveClipRect(), corner_radii); + node->effect_changed = true; + property_trees->effect_tree.set_needs_update(true); + } else { + SetPropertyTreesNeedRebuild(); + } } void Layer::SetIsFastRoundedCorner(bool enable) { @@ -1140,6 +1187,23 @@ void Layer::SetCacheRenderSurface(bool cache) { SetNeedsCommit(); } +RenderSurfaceReason Layer::GetRenderSurfaceReason() const { + if (!layer_tree_host_) + return RenderSurfaceReason::kNone; + PropertyTrees* property_trees = layer_tree_host_->property_trees(); + DCHECK(!property_trees->needs_rebuild); + EffectNode* effect_node = + property_trees->effect_tree.Node(this->effect_tree_index()); + + // Effect node can also be the effect node of an ancestor layer. + // Check if this effect node was created for this layer specifically. + if (!effect_node || + (parent_ && this->effect_tree_index() == parent_->effect_tree_index())) { + return RenderSurfaceReason::kNone; + } + return effect_node->render_surface_reason; +} + void Layer::SetForceRenderSurfaceForTesting(bool force) { DCHECK(IsPropertyChangeAllowed()); if (force_render_surface_for_testing_ == force) @@ -1244,6 +1308,7 @@ void Layer::SetOffsetToTransformParent(gfx::Vector2dF offset) { return; offset_to_transform_parent_ = offset; SetNeedsPushProperties(); + SetSubtreePropertyChanged(); } void Layer::InvalidatePropertyTreesIndices() { @@ -1458,6 +1523,7 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { layer->SetMasksToBounds(inputs_.masks_to_bounds); layer->SetNonFastScrollableRegion(inputs_.non_fast_scrollable_region); layer->SetTouchActionRegion(inputs_.touch_action_region); + layer->SetMirrorCount(inputs_.mirror_count); // TODO(sunxd): Pass the correct region for wheel event handlers, see // https://crbug.com/841364. EventListenerProperties mouse_wheel_props = @@ -1651,6 +1717,27 @@ void Layer::SetTrilinearFiltering(bool trilinear_filtering) { SetNeedsCommit(); } +void Layer::IncrementMirrorCount() { + SetMirrorCount(mirror_count() + 1); +} + +void Layer::DecrementMirrorCount() { + SetMirrorCount(mirror_count() - 1); +} + +void Layer::SetMirrorCount(int mirror_count) { + if (inputs_.mirror_count == mirror_count) + return; + + DCHECK_LE(0, mirror_count); + bool was_mirrored = inputs_.mirror_count > 0; + inputs_.mirror_count = mirror_count; + bool is_mirrored = inputs_.mirror_count > 0; + if (was_mirrored != is_mirrored) + SetPropertyTreesNeedRebuild(); + SetNeedsPushProperties(); +} + ElementListType Layer::GetElementTypeForAnimation() const { return ElementListType::ACTIVE; } diff --git a/chromium/cc/layers/layer.h b/chromium/cc/layers/layer.h index 04740451da9..ba71871f25e 100644 --- a/chromium/cc/layers/layer.h +++ b/chromium/cc/layers/layer.h @@ -26,9 +26,10 @@ #include "cc/layers/layer_collections.h" #include "cc/layers/layer_position_constraint.h" #include "cc/layers/touch_action_region.h" +#include "cc/paint/element_id.h" #include "cc/paint/filter_operations.h" #include "cc/paint/paint_record.h" -#include "cc/trees/element_id.h" +#include "cc/trees/effect_node.h" #include "cc/trees/property_tree.h" #include "cc/trees/target_property.h" #include "third_party/skia/include/core/SkColor.h" @@ -77,7 +78,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { // describe how the mask would be generated as a texture in that case. enum LayerMaskType { NOT_MASK = 0, - MULTI_TEXTURE_MASK, SINGLE_TEXTURE_MASK, }; @@ -215,6 +215,15 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { void SetMasksToBounds(bool masks_to_bounds); bool masks_to_bounds() const { return inputs_.masks_to_bounds; } + // Set or get the clip rect for this layer. |clip_rect| is relative to |this| + // layer. If you are trying to clip the subtree to the bounds of this layer, + // SetMasksToBounds() would be a better alternative. + void SetClipRect(const gfx::Rect& clip_rect); + const gfx::Rect& clip_rect() const { return inputs_.clip_rect; } + + // Returns the bounds which is clipped by the clip rect. + gfx::RectF EffectiveClipRect(); + // Set or get a layer that is not an ancestor of this layer, but which should // be clipped to this layer's bounds if SetMasksToBounds() is set to true. // The parent layer does *not* retain ownership of a reference on this layer. @@ -250,6 +259,8 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { // Set or get the rounded corner radii which is applied to the layer and its // subtree (as if they are together as a single composited entity) when // blitting into their target. Setting this makes the layer masked to bounds. + // If the layer has a clip of its own, the rounded corner will be applied + // along the layer's clip rect corners. void SetRoundedCorner(const gfx::RoundedCornersF& corner_radii); const gfx::RoundedCornersF& corner_radii() const { return inputs_.corner_radii; @@ -321,6 +332,10 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { return inputs_.backdrop_filter_bounds; } + const ElementId backdrop_mask_element_id() const { + return inputs_.backdrop_mask_element_id; + } + void SetBackdropFilterQuality(const float quality); float backdrop_filter_quality() const { return inputs_.backdrop_filter_quality; @@ -508,6 +523,11 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { void SetCacheRenderSurface(bool cache_render_surface); bool cache_render_surface() const { return cache_render_surface_; } + // If the layer induces a render surface, this returns the cause for the + // render surface. If the layer does not induce a render surface, this returns + // kNone. + RenderSurfaceReason GetRenderSurfaceReason() const; + // Set or get if the layer and its subtree will be drawn through an // intermediate texture, called a RenderSurface. This mimics the need // for a RenderSurface that is caused by compositing effects such as masks @@ -603,6 +623,9 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { void SetHasTransformNode(bool val) { has_transform_node_ = val; } bool has_transform_node() { return has_transform_node_; } + // This value indicates whether a clip node was created for |this| layer. + void SetHasClipNode(bool val) { has_clip_node_ = val; } + // Sets that the content shown in this layer may be a video. This may be used // by the system compositor to distinguish between animations updating the // screen and video, which the user would be watching. This allows @@ -634,13 +657,18 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { void SetTrilinearFiltering(bool trilinear_filtering); bool trilinear_filtering() const { return inputs_.trilinear_filtering; } + // Increments/decrements/gets number of layers mirroring this layer. + void IncrementMirrorCount(); + void DecrementMirrorCount(); + int mirror_count() const { return inputs_.mirror_count; } + // Called on the scroll layer to trigger showing the overlay scrollbars. void ShowScrollbars() { needs_show_scrollbars_ = true; } // Captures text content within the given |rect| and returns the associated - // NodeHolder in content. + // NodeId in |content|. virtual void CaptureContent(const gfx::Rect& rect, - std::vector<NodeHolder>* content); + std::vector<NodeId>* content); // For tracing. Gets a recorded rasterization of this layer's contents that // can be displayed inside representations of this layer. May return null, in @@ -757,6 +785,9 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { // layer's subtree, including itself. This causes the layer's subtree to be // considered damaged and re-displayed to the user. void SetSubtreePropertyChanged(); + void ClearSubtreePropertyChangedForTesting() { + subtree_property_changed_ = false; + } bool subtree_property_changed() const { return subtree_property_changed_; } // Internal to property tree construction. Returns ElementListType::ACTIVE @@ -913,6 +944,8 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { // they are marked as needing to be rebuilt. void UpdateScrollOffset(const gfx::ScrollOffset&); + void SetMirrorCount(int mirror_count); + // Encapsulates all data, callbacks or interfaces received from the embedder. struct Inputs { explicit Inputs(int layer_id); @@ -926,6 +959,7 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { gfx::Size bounds; bool masks_to_bounds; + gfx::Rect clip_rect; scoped_refptr<PictureLayer> mask_layer; @@ -960,6 +994,7 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { FilterOperations filters; FilterOperations backdrop_filters; base::Optional<gfx::RRectF> backdrop_filter_bounds; + ElementId backdrop_mask_element_id; gfx::PointF filters_origin; float backdrop_filter_quality; @@ -1030,6 +1065,8 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { OverscrollBehavior overscroll_behavior; base::Optional<SnapContainerData> snap_container_data; + + int mirror_count; }; Layer* parent_; @@ -1058,6 +1095,7 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { bool may_contain_video_ : 1; bool needs_show_scrollbars_ : 1; bool has_transform_node_ : 1; + bool has_clip_node_ : 1; // This value is valid only when LayerTreeHost::has_copy_request() is true bool subtree_has_copy_request_ : 1; SkColor safe_opaque_background_color_; diff --git a/chromium/cc/layers/layer_impl.cc b/chromium/cc/layers/layer_impl.cc index 86ccf0fc4aa..67a48733e6c 100644 --- a/chromium/cc/layers/layer_impl.cc +++ b/chromium/cc/layers/layer_impl.cc @@ -80,7 +80,8 @@ LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, scrollbars_hidden_(false), needs_show_scrollbars_(false), raster_even_if_not_drawn_(false), - has_transform_node_(false) { + has_transform_node_(false), + mirror_count_(0) { DCHECK_GT(layer_id_, 0); DCHECK(layer_tree_impl_); @@ -150,22 +151,32 @@ void LayerImpl::PopulateSharedQuadState(viz::SharedQuadState* state, } void LayerImpl::PopulateScaledSharedQuadState(viz::SharedQuadState* state, - float layer_to_content_scale_x, - float layer_to_content_scale_y, + float layer_to_content_scale, bool contents_opaque) const { + gfx::Size scaled_bounds = + gfx::ScaleToCeiledSize(bounds(), layer_to_content_scale); + gfx::Rect scaled_visible_layer_rect = + gfx::ScaleToEnclosingRect(visible_layer_rect(), layer_to_content_scale); + scaled_visible_layer_rect.Intersect(gfx::Rect(scaled_bounds)); + + PopulateScaledSharedQuadStateWithContentRects( + state, layer_to_content_scale, gfx::Rect(scaled_bounds), + scaled_visible_layer_rect, contents_opaque); +} + +void LayerImpl::PopulateScaledSharedQuadStateWithContentRects( + viz::SharedQuadState* state, + float layer_to_content_scale, + const gfx::Rect& content_rect, + const gfx::Rect& visible_content_rect, + bool contents_opaque) const { gfx::Transform scaled_draw_transform = draw_properties_.target_space_transform; - scaled_draw_transform.Scale(SK_MScalar1 / layer_to_content_scale_x, - SK_MScalar1 / layer_to_content_scale_y); - gfx::Size scaled_bounds = gfx::ScaleToCeiledSize( - bounds(), layer_to_content_scale_x, layer_to_content_scale_y); - gfx::Rect scaled_visible_layer_rect = gfx::ScaleToEnclosingRect( - visible_layer_rect(), layer_to_content_scale_x, layer_to_content_scale_y); - scaled_visible_layer_rect.Intersect(gfx::Rect(scaled_bounds)); + scaled_draw_transform.Scale(SK_MScalar1 / layer_to_content_scale, + SK_MScalar1 / layer_to_content_scale); EffectNode* effect_node = GetEffectTree().Node(effect_tree_index_); - state->SetAll(scaled_draw_transform, gfx::Rect(scaled_bounds), - scaled_visible_layer_rect, + state->SetAll(scaled_draw_transform, content_rect, visible_content_rect, draw_properties().rounded_corner_bounds, draw_properties().clip_rect, draw_properties().is_clipped, contents_opaque, draw_properties().opacity, @@ -307,6 +318,25 @@ void LayerImpl::SetScrollable(const gfx::Size& bounds) { NoteLayerPropertyChanged(); } +void LayerImpl::SetTouchActionRegion(TouchActionRegion region) { + // Avoid recalculating the cached |all_touch_action_regions_| value. + if (touch_action_region_ == region) + return; + touch_action_region_ = std::move(region); + all_touch_action_regions_ = nullptr; +} + +const Region& LayerImpl::GetAllTouchActionRegions() const { + if (!all_touch_action_regions_) { + all_touch_action_regions_ = + std::make_unique<Region>(touch_action_region_.GetAllRegions()); + } else { + // Ensure the cached value of |all_touch_action_regions_| is up to date. + DCHECK_EQ(touch_action_region_.GetAllRegions(), *all_touch_action_regions_); + } + return *all_touch_action_regions_; +} + std::unique_ptr<LayerImpl> LayerImpl::CreateLayerImpl( LayerTreeImpl* tree_impl) { return LayerImpl::Create(tree_impl, layer_id_); @@ -337,6 +367,10 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { layer->hit_testable_ = hit_testable_; layer->non_fast_scrollable_region_ = non_fast_scrollable_region_; layer->touch_action_region_ = touch_action_region_; + layer->all_touch_action_regions_ = + all_touch_action_regions_ + ? std::make_unique<Region>(*all_touch_action_regions_) + : nullptr; layer->wheel_event_handler_region_ = wheel_event_handler_region_; layer->background_color_ = background_color_; layer->safe_opaque_background_color_ = safe_opaque_background_color_; @@ -345,6 +379,7 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { layer->clip_tree_index_ = clip_tree_index_; layer->scroll_tree_index_ = scroll_tree_index_; layer->has_will_change_transform_hint_ = has_will_change_transform_hint_; + layer->mirror_count_ = mirror_count_; layer->scrollbars_hidden_ = scrollbars_hidden_; if (needs_show_scrollbars_) layer->needs_show_scrollbars_ = needs_show_scrollbars_; @@ -435,9 +470,8 @@ std::unique_ptr<base::DictionaryValue> LayerImpl::LayerAsJson() const { if (scrollable()) result->SetBoolean("Scrollable", true); - if (!touch_action_region_.region().IsEmpty()) { - std::unique_ptr<base::Value> region = - touch_action_region_.region().AsValue(); + if (!GetAllTouchActionRegions().IsEmpty()) { + std::unique_ptr<base::Value> region = GetAllTouchActionRegions().AsValue(); result->Set("TouchRegion", std::move(region)); } @@ -516,7 +550,6 @@ void LayerImpl::ResetChangeTracking() { needs_push_properties_ = false; update_rect_.SetRect(0, 0, 0, 0); - damage_rect_.SetRect(0, 0, 0, 0); } bool LayerImpl::IsActive() const { @@ -696,12 +729,16 @@ void LayerImpl::SetElementId(ElementId element_id) { layer_tree_impl_->AddToElementLayerList(element_id_, this); } +void LayerImpl::SetMirrorCount(int mirror_count) { + mirror_count_ = mirror_count; +} + void LayerImpl::SetUpdateRect(const gfx::Rect& update_rect) { update_rect_ = update_rect; } -void LayerImpl::AddDamageRect(const gfx::Rect& damage_rect) { - damage_rect_.Union(damage_rect); +gfx::Rect LayerImpl::GetDamageRect() const { + return gfx::Rect(); } void LayerImpl::SetCurrentScrollOffset(const gfx::ScrollOffset& scroll_offset) { @@ -802,9 +839,9 @@ void LayerImpl::AsValueInto(base::trace_event::TracedValue* state) const { MathUtil::MapQuad(ScreenSpaceTransform(), gfx::QuadF(gfx::RectF(gfx::Rect(bounds()))), &clipped); MathUtil::AddToTracedValue("layer_quad", layer_quad, state); - if (!touch_action_region_.region().IsEmpty()) { - state->BeginArray("touch_action_region_region"); - touch_action_region_.region().AsValueInto(state); + if (!GetAllTouchActionRegions().IsEmpty()) { + state->BeginArray("all_touch_action_regions"); + GetAllTouchActionRegions().AsValueInto(state); state->EndArray(); } if (!wheel_event_handler_region_.IsEmpty()) { @@ -925,11 +962,6 @@ float LayerImpl::GetIdealContentsScale() const { float device_scale = layer_tree_impl()->device_scale_factor(); float default_scale = page_scale * device_scale; - if (!layer_tree_impl() - ->settings() - .layer_transforms_should_scale_layer_contents) { - return default_scale; - } const auto& transform = ScreenSpaceTransform(); if (transform.HasPerspective()) { diff --git a/chromium/cc/layers/layer_impl.h b/chromium/cc/layers/layer_impl.h index b98cfd6ad3c..be26a60ae36 100644 --- a/chromium/cc/layers/layer_impl.h +++ b/chromium/cc/layers/layer_impl.h @@ -30,8 +30,8 @@ #include "cc/layers/performance_properties.h" #include "cc/layers/render_surface_impl.h" #include "cc/layers/touch_action_region.h" +#include "cc/paint/element_id.h" #include "cc/tiles/tile_priority.h" -#include "cc/trees/element_id.h" #include "cc/trees/target_property.h" #include "components/viz/common/quads/shared_quad_state.h" #include "third_party/skia/include/core/SkColor.h" @@ -129,13 +129,19 @@ class CC_EXPORT LayerImpl { void PopulateSharedQuadState(viz::SharedQuadState* state, bool contents_opaque) const; - // If using this, you need to override GetEnclosingRectInTargetSpace() to + // If using these two, you need to override GetEnclosingRectInTargetSpace() to // use GetScaledEnclosingRectInTargetSpace(). To do otherwise may result in // inconsistent values, and drawing/clipping problems. void PopulateScaledSharedQuadState(viz::SharedQuadState* state, - float layer_to_content_scale_x, - float layer_to_content_scale_y, + float layer_to_content_scale, bool contents_opaque) const; + void PopulateScaledSharedQuadStateWithContentRects( + viz::SharedQuadState* state, + float layer_to_content_scale, + const gfx::Rect& content_rect, + const gfx::Rect& content_visible_rect, + bool contents_opaque) const; + // WillDraw must be called before AppendQuads. If WillDraw returns false, // AppendQuads and DidDraw will not be called. If WillDraw returns true, // DidDraw is guaranteed to be called before another WillDraw or before @@ -194,6 +200,9 @@ class CC_EXPORT LayerImpl { void SetElementId(ElementId element_id); ElementId element_id() const { return element_id_; } + void SetMirrorCount(int mirror_count); + int mirror_count() const { return mirror_count_; } + bool IsAffectedByPageScale() const; bool Is3dSorted() const { return GetSortingContextId() != 0; } @@ -311,12 +320,14 @@ class CC_EXPORT LayerImpl { return non_fast_scrollable_region_; } - void SetTouchActionRegion(TouchActionRegion touch_action_region) { - touch_action_region_ = std::move(touch_action_region); - } + void SetTouchActionRegion(TouchActionRegion); const TouchActionRegion& touch_action_region() const { return touch_action_region_; } + const Region& GetAllTouchActionRegions() const; + bool has_touch_action_regions() const { + return !touch_action_region_.IsEmpty(); + } // Set or get the region that contains wheel event handler. // The |wheel_event_handler_region| specify the area where wheel event handler @@ -332,8 +343,10 @@ class CC_EXPORT LayerImpl { void SetUpdateRect(const gfx::Rect& update_rect); const gfx::Rect& update_rect() const { return update_rect_; } - void AddDamageRect(const gfx::Rect& damage_rect); - const gfx::Rect& damage_rect() const { return damage_rect_; } + // Denotes an area that is damaged and needs redraw. This is in the layer's + // space. By default returns empty rect, but can be overridden by subclasses + // as appropriate. + virtual gfx::Rect GetDamageRect() const; virtual std::unique_ptr<base::DictionaryValue> LayerAsJson() const; // TODO(pdr): This should be removed because there is no longer a tree @@ -349,7 +362,7 @@ class CC_EXPORT LayerImpl { // from property_trees changes in animaiton. bool LayerPropertyChangedNotFromPropertyTrees() const; - void ResetChangeTracking(); + virtual void ResetChangeTracking(); virtual SimpleEnclosedRegion VisibleOpaqueRegion() const; @@ -437,8 +450,6 @@ class CC_EXPORT LayerImpl { // PopulateScaledSharedQuadStateQuadState() for more details. gfx::Rect GetScaledEnclosingRectInTargetSpace(float scale) const; - void UpdatePropertyTreeForAnimationIfNeeded(ElementId element_id); - float GetIdealContentsScale() const; void NoteLayerPropertyChanged(); @@ -569,10 +580,6 @@ class CC_EXPORT LayerImpl { // This is in the layer's space. gfx::Rect update_rect_; - // Denotes an area that is damaged and needs redraw. This is in the layer's - // space. - gfx::Rect damage_rect_; - // Group of properties that need to be computed based on the layer tree // hierarchy before layers can be drawn. DrawProperties draw_properties_; @@ -581,6 +588,10 @@ class CC_EXPORT LayerImpl { std::unique_ptr<base::trace_event::TracedValue> owned_debug_info_; base::trace_event::TracedValue* debug_info_; + // Cache of all regions represented by any touch action from + // |touch_action_region_|. + mutable std::unique_ptr<Region> all_touch_action_regions_; + bool has_will_change_transform_hint_ : 1; bool needs_push_properties_ : 1; bool is_scrollbar_ : 1; @@ -598,6 +609,10 @@ class CC_EXPORT LayerImpl { bool raster_even_if_not_drawn_ : 1; bool has_transform_node_ : 1; + + // Number of layers mirroring this layer. If greater than zero, forces a + // render pass for the layer so it can be embedded by the mirroring layer. + int mirror_count_; }; } // namespace cc diff --git a/chromium/cc/layers/layer_impl_test_properties.h b/chromium/cc/layers/layer_impl_test_properties.h index 0d2c134b2b4..b0818808729 100644 --- a/chromium/cc/layers/layer_impl_test_properties.h +++ b/chromium/cc/layers/layer_impl_test_properties.h @@ -50,6 +50,7 @@ struct CC_EXPORT LayerImplTestProperties { FilterOperations filters; FilterOperations backdrop_filters; base::Optional<gfx::RRectF> backdrop_filter_bounds; + ElementId backdrop_mask_element_id; float backdrop_filter_quality; gfx::PointF filters_origin; SkBlendMode blend_mode; diff --git a/chromium/cc/layers/layer_impl_unittest.cc b/chromium/cc/layers/layer_impl_unittest.cc index 9200f7a7be1..4dc4d5b24af 100644 --- a/chromium/cc/layers/layer_impl_unittest.cc +++ b/chromium/cc/layers/layer_impl_unittest.cc @@ -391,7 +391,6 @@ TEST(LayerImplTest, PerspectiveTransformHasReasonableScale) { std::unique_ptr<LayerTreeFrameSink> layer_tree_frame_sink = FakeLayerTreeFrameSink::Create3d(); LayerTreeSettings settings; - settings.layer_transforms_should_scale_layer_contents = true; FakeLayerTreeHostImpl host_impl(settings, &task_runner_provider, &task_graph_runner); auto owned_layer = LayerImpl::Create(host_impl.active_tree(), 1); @@ -604,6 +603,31 @@ TEST_F(LayerImplScrollTest, ScrollUserUnscrollableLayer) { EXPECT_VECTOR_EQ(gfx::Vector2dF(30.5f, 5), layer()->CurrentScrollOffset()); } +// |LayerImpl::all_touch_action_regions_| is a cache of all regions on +// |LayerImpl::touch_action_region_| and must be invalidated on changes. +TEST_F(LayerImplScrollTest, TouchActionRegionCacheInvalidation) { + host_impl().CreatePendingTree(); + std::unique_ptr<LayerImpl> pending_layer = + LayerImpl::Create(host_impl().pending_tree(), 2); + + TouchActionRegion region; + region.Union(kTouchActionNone, gfx::Rect(0, 0, 50, 50)); + pending_layer->SetTouchActionRegion(region); + + // The values for GetAllTouchActionRegions should be correct on both layers. + // Note that querying GetAllTouchActionRegions will update the cached value + // in |LayerImpl::all_touch_action_regions_|. + EXPECT_EQ(pending_layer->GetAllTouchActionRegions(), region.GetAllRegions()); + EXPECT_EQ(layer()->GetAllTouchActionRegions(), Region()); + + pending_layer->PushPropertiesTo(layer()); + + // After pushing properties, the value for GetAllTouchActionRegions should + // not be stale. + EXPECT_EQ(pending_layer->GetAllTouchActionRegions(), region.GetAllRegions()); + EXPECT_EQ(layer()->GetAllTouchActionRegions(), region.GetAllRegions()); +} + TEST_F(CommitToPendingTreeLayerImplScrollTest, PushPropertiesToMirrorsCurrentScrollOffset) { gfx::ScrollOffset scroll_offset(10, 5); diff --git a/chromium/cc/layers/layer_sticky_position_constraint.h b/chromium/cc/layers/layer_sticky_position_constraint.h index 8eedea825c7..d3075d95b74 100644 --- a/chromium/cc/layers/layer_sticky_position_constraint.h +++ b/chromium/cc/layers/layer_sticky_position_constraint.h @@ -7,7 +7,7 @@ #include "cc/cc_export.h" -#include "cc/trees/element_id.h" +#include "cc/paint/element_id.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" @@ -33,7 +33,7 @@ struct CC_EXPORT LayerStickyPositionConstraint { // The rectangle in which the sticky box is able to be positioned. This may be // smaller than the scroller viewport due to things like padding. - gfx::RectF constraint_box_rect; + gfx::Rect constraint_box_rect; // The rectangle corresponding to original layout position of the sticky box // relative to the scroll ancestor. The sticky box is only offset once the diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc index 0a02373e4d9..bd3b89aac8c 100644 --- a/chromium/cc/layers/layer_unittest.cc +++ b/chromium/cc/layers/layer_unittest.cc @@ -27,6 +27,7 @@ #include "cc/test/layer_test_common.h" #include "cc/test/stub_layer_tree_host_single_thread_client.h" #include "cc/test/test_task_graph_runner.h" +#include "cc/trees/clip_node.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/single_thread_proxy.h" #include "cc/trees/transform_node.h" @@ -59,14 +60,14 @@ using ::testing::_; code_to_test; \ root->layer_tree_host()->BuildPropertyTreesForTesting(); \ EXPECT_TRUE(root->subtree_property_changed()); \ - EXPECT_TRUE(base::ContainsKey( \ + EXPECT_TRUE(base::Contains( \ root->layer_tree_host()->LayersThatShouldPushProperties(), root.get())); \ EXPECT_TRUE(child->subtree_property_changed()); \ - EXPECT_TRUE(base::ContainsKey( \ + EXPECT_TRUE(base::Contains( \ child->layer_tree_host()->LayersThatShouldPushProperties(), \ child.get())); \ EXPECT_TRUE(grand_child->subtree_property_changed()); \ - EXPECT_TRUE(base::ContainsKey( \ + EXPECT_TRUE(base::Contains( \ grand_child->layer_tree_host()->LayersThatShouldPushProperties(), \ grand_child.get())); @@ -74,14 +75,14 @@ using ::testing::_; code_to_test; \ root->layer_tree_host()->BuildPropertyTreesForTesting(); \ EXPECT_FALSE(root->subtree_property_changed()); \ - EXPECT_FALSE(base::ContainsKey( \ + EXPECT_FALSE(base::Contains( \ root->layer_tree_host()->LayersThatShouldPushProperties(), root.get())); \ EXPECT_FALSE(child->subtree_property_changed()); \ - EXPECT_FALSE(base::ContainsKey( \ + EXPECT_FALSE(base::Contains( \ child->layer_tree_host()->LayersThatShouldPushProperties(), \ child.get())); \ EXPECT_FALSE(grand_child->subtree_property_changed()); \ - EXPECT_FALSE(base::ContainsKey( \ + EXPECT_FALSE(base::Contains( \ grand_child->layer_tree_host()->LayersThatShouldPushProperties(), \ grand_child.get())); @@ -350,15 +351,8 @@ TEST_F(LayerTest, LayerPropertyChangedForSubtree) { child2->PushPropertiesTo(child2_impl.get()); grand_child->PushPropertiesTo(grand_child_impl.get())); - EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1); - EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetRoundedCorner({1, 2, 3, 4})); - EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET( - root->PushPropertiesTo(root_impl.get()); - child->PushPropertiesTo(child_impl.get()); - child2->PushPropertiesTo(child2_impl.get()); - grand_child->PushPropertiesTo(grand_child_impl.get())); - - EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1); + EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(2); + root->SetRoundedCorner({1, 2, 3, 4}); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetIsFastRoundedCorner(true)); EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET( root->PushPropertiesTo(root_impl.get()); @@ -367,14 +361,6 @@ TEST_F(LayerTest, LayerPropertyChangedForSubtree) { grand_child->PushPropertiesTo(grand_child_impl.get())); EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1); - EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetRoundedCorner({0, 0, 0, 0})); - EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET( - root->PushPropertiesTo(root_impl.get()); - child->PushPropertiesTo(child_impl.get()); - child2->PushPropertiesTo(child2_impl.get()); - grand_child->PushPropertiesTo(grand_child_impl.get())); - - EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetDoubleSided(false)); EXECUTE_AND_VERIFY_SUBTREE_CHANGES_RESET( root->PushPropertiesTo(root_impl.get()); @@ -586,7 +572,7 @@ TEST_F(LayerTest, ReorderChildren) { EXPECT_EQ(child2, parent->children()[2]); for (const auto& child : parent->children()) { - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host_->LayersThatShouldPushProperties(), child.get())); EXPECT_TRUE(child->subtree_property_changed()); } @@ -758,8 +744,8 @@ TEST_F(LayerTest, DeleteRemovedScrollParent) { SimulateCommitForLayer(child1.get()); EXPECT_SET_NEEDS_COMMIT(1, child1->SetScrollParent(nullptr)); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host_->LayersThatShouldPushProperties(), child1.get())); + EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(), + child1.get())); EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, layer_tree_host_->SetRootLayer(nullptr)); } @@ -1011,6 +997,7 @@ TEST_F(LayerTest, CheckPropertyChangeCausesCorrectBehavior) { 1, test_layer->SetTransformOrigin(gfx::Point3F(1.23f, 4.56f, 0.f))); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetBackgroundColor(SK_ColorLTGRAY)); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetMasksToBounds(true)); + EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetClipRect(gfx::Rect(1, 2, 3, 4))); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetRoundedCorner({1, 2, 3, 4})); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetIsFastRoundedCorner(true)); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetOpacity(0.5f)); @@ -1654,6 +1641,298 @@ TEST_F(LayerTest, SetElementIdNotUsingLayerLists) { test_layer->SetLayerTreeHost(nullptr); } +// Verifies that mirror count is pushed to the LayerImpl. +TEST_F(LayerTest, MirrorCountIsPushed) { + scoped_refptr<Layer> test_layer = Layer::Create(); + std::unique_ptr<LayerImpl> impl_layer = + LayerImpl::Create(host_impl_.active_tree(), 1); + test_layer->SetLayerTreeHost(layer_tree_host_.get()); + EXPECT_EQ(0, test_layer->mirror_count()); + EXPECT_EQ(0, impl_layer->mirror_count()); + + test_layer->IncrementMirrorCount(); + EXPECT_EQ(1, test_layer->mirror_count()); + EXPECT_EQ(0, impl_layer->mirror_count()); + + test_layer->PushPropertiesTo(impl_layer.get()); + EXPECT_EQ(1, test_layer->mirror_count()); + EXPECT_EQ(1, impl_layer->mirror_count()); + + test_layer->SetLayerTreeHost(nullptr); +} + +// Verifies that when mirror count of the layer is incremented or decremented, +// SetPropertyTreesNeedRebuild() and SetNeedsPushProperties() are called +// appropriately. +TEST_F(LayerTest, UpdateMirrorCount) { + scoped_refptr<Layer> test_layer = Layer::Create(); + test_layer->SetLayerTreeHost(layer_tree_host_.get()); + layer_tree_host_->property_trees()->needs_rebuild = false; + layer_tree_host_->ClearLayersThatShouldPushProperties(); + EXPECT_EQ(0, test_layer->mirror_count()); + EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild); + EXPECT_EQ(0u, layer_tree_host_->LayersThatShouldPushProperties().size()); + + // Incrementing mirror count from zero should trigger property trees rebuild. + test_layer->IncrementMirrorCount(); + EXPECT_EQ(1, test_layer->mirror_count()); + EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild); + EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(), + test_layer.get())); + + layer_tree_host_->property_trees()->needs_rebuild = false; + layer_tree_host_->ClearLayersThatShouldPushProperties(); + + // Incrementing mirror count from non-zero should not trigger property trees + // rebuild. + test_layer->IncrementMirrorCount(); + EXPECT_EQ(2, test_layer->mirror_count()); + EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild); + EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(), + test_layer.get())); + + layer_tree_host_->ClearLayersThatShouldPushProperties(); + + // Decrementing mirror count to non-zero should not trigger property trees + // rebuild. + test_layer->DecrementMirrorCount(); + EXPECT_EQ(1, test_layer->mirror_count()); + EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild); + EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(), + test_layer.get())); + + layer_tree_host_->ClearLayersThatShouldPushProperties(); + + // Decrementing mirror count to zero should trigger property trees rebuild. + test_layer->DecrementMirrorCount(); + EXPECT_EQ(0, test_layer->mirror_count()); + EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild); + EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(), + test_layer.get())); + + test_layer->SetLayerTreeHost(nullptr); +} + +TEST_F(LayerTest, UpdatingClipRect) { + const gfx::Size kRootSize(200, 200); + const gfx::Vector2dF kParentOffset(10.f, 20.f); + const gfx::Size kLayerSize(100, 100); + const gfx::Rect kClipRect(50, 25, 100, 100); + const gfx::Rect kUpdatedClipRect_1(10, 20, 150, 200); + const gfx::Rect kUpdatedClipRect_2(20, 20, 50, 100); + const gfx::Rect kUpdatedClipRect_3(50, 25, 100, 80); + const gfx::Rect kUpdatedClipRect_4(10, 10, 200, 200); + + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> parent = Layer::Create(); + scoped_refptr<Layer> clipped_1 = Layer::Create(); + scoped_refptr<Layer> clipped_2 = Layer::Create(); + scoped_refptr<Layer> clipped_3 = Layer::Create(); + scoped_refptr<Layer> clipped_4 = Layer::Create(); + + EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(AtLeast(1)); + EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1)); + layer_tree_host_->SetRootLayer(root); + root->AddChild(parent); + parent->AddChild(clipped_1); + parent->AddChild(clipped_2); + parent->AddChild(clipped_3); + parent->AddChild(clipped_4); + + root->SetBounds(kRootSize); + parent->SetBounds(kRootSize); + clipped_1->SetBounds(kLayerSize); + clipped_2->SetBounds(kLayerSize); + clipped_3->SetBounds(kLayerSize); + clipped_4->SetBounds(kLayerSize); + + // This should introduce the |offset_from_transform_parent| component. + parent->SetPosition(gfx::PointF() + kParentOffset); + + clipped_1->SetClipRect(kClipRect); + clipped_2->SetClipRect(kClipRect); + clipped_3->SetClipRect(kClipRect); + clipped_4->SetClipRect(kClipRect); + EXPECT_EQ(clipped_1->clip_rect(), kClipRect); + EXPECT_EQ(clipped_2->clip_rect(), kClipRect); + EXPECT_EQ(clipped_3->clip_rect(), kClipRect); + EXPECT_EQ(clipped_4->clip_rect(), kClipRect); + + root->layer_tree_host()->BuildPropertyTreesForTesting(); + ClipNode* node_1 = layer_tree_host_->property_trees()->clip_tree.Node( + clipped_1->clip_tree_index()); + ClipNode* node_2 = layer_tree_host_->property_trees()->clip_tree.Node( + clipped_2->clip_tree_index()); + ClipNode* node_3 = layer_tree_host_->property_trees()->clip_tree.Node( + clipped_3->clip_tree_index()); + ClipNode* node_4 = layer_tree_host_->property_trees()->clip_tree.Node( + clipped_4->clip_tree_index()); + + EXPECT_EQ(gfx::RectF(kClipRect) + kParentOffset, node_1->clip); + EXPECT_EQ(gfx::RectF(kClipRect) + kParentOffset, node_2->clip); + EXPECT_EQ(gfx::RectF(kClipRect) + kParentOffset, node_3->clip); + EXPECT_EQ(gfx::RectF(kClipRect) + kParentOffset, node_4->clip); + + // The following layer properties should result in the layer being clipped to + // its bounds along with being clipped by the clip rect. Check if the final + // rect on the clip node is set correctly. + + // Setting clip to layer bounds. + clipped_1->SetMasksToBounds(true); + + // Setting a mask. + FakeContentLayerClient client; + scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); + clipped_2->SetMaskLayer(mask.get()); + + // Setting a filter that moves pixels. + FilterOperations move_pixel_filters; + move_pixel_filters.Append( + FilterOperation::CreateBlurFilter(2, SkBlurImageFilter::kClamp_TileMode)); + ASSERT_TRUE(move_pixel_filters.HasFilterThatMovesPixels()); + clipped_3->SetFilters(move_pixel_filters); + + clipped_1->SetClipRect(kUpdatedClipRect_1); + clipped_2->SetClipRect(kUpdatedClipRect_2); + clipped_3->SetClipRect(kUpdatedClipRect_3); + clipped_4->SetClipRect(kUpdatedClipRect_4); + + node_1 = layer_tree_host_->property_trees()->clip_tree.Node( + clipped_1->clip_tree_index()); + node_2 = layer_tree_host_->property_trees()->clip_tree.Node( + clipped_2->clip_tree_index()); + node_3 = layer_tree_host_->property_trees()->clip_tree.Node( + clipped_3->clip_tree_index()); + node_4 = layer_tree_host_->property_trees()->clip_tree.Node( + clipped_4->clip_tree_index()); + + EXPECT_EQ(node_1->clip, + gfx::IntersectRects(gfx::RectF(kUpdatedClipRect_1), + gfx::RectF(gfx::SizeF(kLayerSize))) + + kParentOffset); + EXPECT_EQ(node_2->clip, + gfx::IntersectRects(gfx::RectF(kUpdatedClipRect_2), + gfx::RectF(gfx::SizeF(kLayerSize))) + + kParentOffset); + EXPECT_EQ(node_3->clip, + gfx::IntersectRects(gfx::RectF(kUpdatedClipRect_3), + gfx::RectF(gfx::SizeF(kLayerSize))) + + kParentOffset); + EXPECT_EQ(node_4->clip, gfx::RectF(kUpdatedClipRect_4) + kParentOffset); +} + +TEST_F(LayerTest, UpdatingRoundedCorners) { + const gfx::Size kRootSize(200, 200); + const gfx::Size kLayerSize(100, 100); + const gfx::Rect kClipRect(50, 25, 100, 100); + const gfx::Rect kUpdatedClipRect(10, 20, 30, 40); + const gfx::RoundedCornersF kRoundedCorners(5); + const gfx::RoundedCornersF kUpdatedRoundedCorners(10); + + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> layer_1 = Layer::Create(); + scoped_refptr<Layer> layer_2 = Layer::Create(); + scoped_refptr<Layer> layer_3 = Layer::Create(); + scoped_refptr<Layer> layer_4 = Layer::Create(); + scoped_refptr<Layer> layer_5 = Layer::Create(); + + EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(AtLeast(1)); + EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1)); + layer_tree_host_->SetRootLayer(root); + root->AddChild(layer_1); + root->AddChild(layer_2); + root->AddChild(layer_3); + root->AddChild(layer_4); + root->AddChild(layer_5); + + root->SetBounds(kRootSize); + layer_1->SetBounds(kLayerSize); + layer_2->SetBounds(kLayerSize); + layer_3->SetBounds(kLayerSize); + layer_4->SetBounds(kLayerSize); + layer_5->SetBounds(kLayerSize); + + layer_1->SetClipRect(kClipRect); + layer_2->SetClipRect(kClipRect); + layer_3->SetClipRect(kClipRect); + layer_4->SetClipRect(kClipRect); + layer_1->SetRoundedCorner(kRoundedCorners); + layer_2->SetRoundedCorner(kRoundedCorners); + layer_3->SetRoundedCorner(kRoundedCorners); + layer_4->SetRoundedCorner(kRoundedCorners); + layer_5->SetRoundedCorner(kRoundedCorners); + EXPECT_EQ(layer_1->corner_radii(), kRoundedCorners); + EXPECT_EQ(layer_2->corner_radii(), kRoundedCorners); + EXPECT_EQ(layer_3->corner_radii(), kRoundedCorners); + EXPECT_EQ(layer_4->corner_radii(), kRoundedCorners); + EXPECT_EQ(layer_5->corner_radii(), kRoundedCorners); + + root->layer_tree_host()->BuildPropertyTreesForTesting(); + EffectNode* node_1 = layer_tree_host_->property_trees()->effect_tree.Node( + layer_1->effect_tree_index()); + EffectNode* node_2 = layer_tree_host_->property_trees()->effect_tree.Node( + layer_2->effect_tree_index()); + EffectNode* node_3 = layer_tree_host_->property_trees()->effect_tree.Node( + layer_3->effect_tree_index()); + EffectNode* node_4 = layer_tree_host_->property_trees()->effect_tree.Node( + layer_4->effect_tree_index()); + EffectNode* node_5 = layer_tree_host_->property_trees()->effect_tree.Node( + layer_5->effect_tree_index()); + + EXPECT_EQ(gfx::RRectF(gfx::RectF(kClipRect), kRoundedCorners), + node_1->rounded_corner_bounds); + EXPECT_EQ(gfx::RRectF(gfx::RectF(kClipRect), kRoundedCorners), + node_2->rounded_corner_bounds); + EXPECT_EQ(gfx::RRectF(gfx::RectF(kClipRect), kRoundedCorners), + node_3->rounded_corner_bounds); + EXPECT_EQ(gfx::RRectF(gfx::RectF(kClipRect), kRoundedCorners), + node_4->rounded_corner_bounds); + EXPECT_EQ(gfx::RRectF(gfx::RectF(gfx::Rect(kLayerSize)), kRoundedCorners), + node_5->rounded_corner_bounds); + + // Setting clip to layer bounds. + layer_1->SetMasksToBounds(true); + + // Setting a mask. + FakeContentLayerClient client; + scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); + layer_2->SetMaskLayer(mask.get()); + + layer_1->SetRoundedCorner(kUpdatedRoundedCorners); + layer_2->SetRoundedCorner(kUpdatedRoundedCorners); + layer_3->SetRoundedCorner(kUpdatedRoundedCorners); + // Updates the clip rect instead of rounded corners. + layer_4->SetClipRect(kUpdatedClipRect); + layer_5->SetRoundedCorner(kUpdatedRoundedCorners); + + node_1 = layer_tree_host_->property_trees()->effect_tree.Node( + layer_1->effect_tree_index()); + node_2 = layer_tree_host_->property_trees()->effect_tree.Node( + layer_2->effect_tree_index()); + node_3 = layer_tree_host_->property_trees()->effect_tree.Node( + layer_3->effect_tree_index()); + node_4 = layer_tree_host_->property_trees()->effect_tree.Node( + layer_4->effect_tree_index()); + node_5 = layer_tree_host_->property_trees()->effect_tree.Node( + layer_5->effect_tree_index()); + + EXPECT_EQ(gfx::RRectF(gfx::RectF(gfx::IntersectRects(gfx::Rect(kLayerSize), + kClipRect)), + kUpdatedRoundedCorners), + node_1->rounded_corner_bounds); + EXPECT_EQ(gfx::RRectF(gfx::RectF(gfx::IntersectRects(gfx::Rect(kLayerSize), + kClipRect)), + kUpdatedRoundedCorners), + node_2->rounded_corner_bounds); + EXPECT_EQ(gfx::RRectF(gfx::RectF(kClipRect), kUpdatedRoundedCorners), + node_3->rounded_corner_bounds); + EXPECT_EQ(gfx::RRectF(gfx::RectF(kUpdatedClipRect), kRoundedCorners), + node_4->rounded_corner_bounds); + EXPECT_EQ( + gfx::RRectF(gfx::RectF(gfx::Rect(kLayerSize)), kUpdatedRoundedCorners), + node_5->rounded_corner_bounds); +} + class LayerTestWithLayerLists : public LayerTest { protected: void SetUp() override { diff --git a/chromium/cc/layers/mirror_layer.cc b/chromium/cc/layers/mirror_layer.cc new file mode 100644 index 00000000000..b89e300d2d6 --- /dev/null +++ b/chromium/cc/layers/mirror_layer.cc @@ -0,0 +1,49 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/layers/mirror_layer.h" + +#include "cc/layers/mirror_layer_impl.h" + +namespace cc { + +std::unique_ptr<LayerImpl> MirrorLayer::CreateLayerImpl( + LayerTreeImpl* tree_impl) { + return MirrorLayerImpl::Create(tree_impl, id()); +} + +void MirrorLayer::PushPropertiesTo(LayerImpl* layer) { + Layer::PushPropertiesTo(layer); + + auto* mirror_layer = static_cast<MirrorLayerImpl*>(layer); + mirror_layer->SetMirroredLayerId(mirrored_layer_->id()); +} + +void MirrorLayer::SetLayerTreeHost(LayerTreeHost* host) { +#if DCHECK_IS_ON() + if (host && host != layer_tree_host()) { + for (auto* p = parent(); p; p = p->parent()) + DCHECK_NE(p, mirrored_layer_.get()); + } +#endif + + Layer::SetLayerTreeHost(host); +} + +scoped_refptr<MirrorLayer> MirrorLayer::Create( + scoped_refptr<Layer> mirrored_layer) { + return base::WrapRefCounted(new MirrorLayer(std::move(mirrored_layer))); +} + +MirrorLayer::MirrorLayer(scoped_refptr<Layer> mirrored_layer) + : mirrored_layer_(std::move(mirrored_layer)) { + DCHECK(mirrored_layer_); + mirrored_layer_->IncrementMirrorCount(); +} + +MirrorLayer::~MirrorLayer() { + mirrored_layer_->DecrementMirrorCount(); +} + +} // namespace cc diff --git a/chromium/cc/layers/mirror_layer.h b/chromium/cc/layers/mirror_layer.h new file mode 100644 index 00000000000..9915dcf525d --- /dev/null +++ b/chromium/cc/layers/mirror_layer.h @@ -0,0 +1,42 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_LAYERS_MIRROR_LAYER_H_ +#define CC_LAYERS_MIRROR_LAYER_H_ + +#include "base/memory/scoped_refptr.h" +#include "cc/cc_export.h" +#include "cc/layers/layer.h" + +namespace cc { + +// A layer that can mirror contents of another Layer. +class CC_EXPORT MirrorLayer : public Layer { + public: + static scoped_refptr<MirrorLayer> Create(scoped_refptr<Layer> mirrored_layer); + + MirrorLayer(const MirrorLayer&) = delete; + MirrorLayer& operator=(const MirrorLayer&) = delete; + + Layer* mirrored_layer() const { return mirrored_layer_.get(); } + + // Layer overrides. + std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; + void PushPropertiesTo(LayerImpl* layer) override; + void SetLayerTreeHost(LayerTreeHost* host) override; + + protected: + explicit MirrorLayer(scoped_refptr<Layer> mirrored_layer); + + private: + ~MirrorLayer() override; + + // A reference to a layer that is mirrored by this layer. |mirrored_layer_| + // cannot be an ancestor of |this|. + scoped_refptr<Layer> mirrored_layer_; +}; + +} // namespace cc + +#endif // CC_LAYERS_MIRROR_LAYER_H_ diff --git a/chromium/cc/layers/mirror_layer_impl.cc b/chromium/cc/layers/mirror_layer_impl.cc new file mode 100644 index 00000000000..97099507d86 --- /dev/null +++ b/chromium/cc/layers/mirror_layer_impl.cc @@ -0,0 +1,92 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/layers/mirror_layer_impl.h" + +#include "cc/layers/append_quads_data.h" +#include "cc/trees/effect_node.h" +#include "cc/trees/layer_tree_impl.h" +#include "cc/trees/occlusion.h" +#include "components/viz/common/quads/render_pass_draw_quad.h" + +namespace cc { + +MirrorLayerImpl::MirrorLayerImpl(LayerTreeImpl* tree_impl, int id) + : LayerImpl(tree_impl, id) {} + +MirrorLayerImpl::~MirrorLayerImpl() = default; + +std::unique_ptr<LayerImpl> MirrorLayerImpl::CreateLayerImpl( + LayerTreeImpl* tree_impl) { + return MirrorLayerImpl::Create(tree_impl, id()); +} + +void MirrorLayerImpl::AppendQuads(viz::RenderPass* render_pass, + AppendQuadsData* append_quads_data) { + // TODO(mohsen): Currently, effects on the mirrored layer (e.g mask and + // opacity) are ignored. Consider applying them here. + + auto* mirrored_layer = layer_tree_impl()->LayerById(mirrored_layer_id_); + auto* mirrored_render_surface = + GetEffectTree().GetRenderSurface(mirrored_layer->effect_tree_index()); + gfx::Rect content_rect = mirrored_render_surface->content_rect(); + + gfx::Rect unoccluded_content_rect = + draw_properties().occlusion_in_content_space.GetUnoccludedContentRect( + content_rect); + if (unoccluded_content_rect.IsEmpty()) + return; + + append_quads_data->mirror_rect = drawable_content_rect(); + + const bool contents_opaque = false; + viz::SharedQuadState* shared_quad_state = + render_pass->CreateAndAppendSharedQuadState(); + PopulateScaledSharedQuadStateWithContentRects( + shared_quad_state, mirrored_layer->GetIdealContentsScale(), content_rect, + content_rect, contents_opaque); + + AppendDebugBorderQuad(render_pass, content_rect, shared_quad_state, + append_quads_data); + + viz::ResourceId mask_resource_id = 0; + gfx::RectF mask_uv_rect; + gfx::Size mask_texture_size; + + auto* mirrored_effect_node = mirrored_render_surface->OwningEffectNode(); + auto* quad = render_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>(); + quad->SetNew(shared_quad_state, content_rect, unoccluded_content_rect, + mirrored_layer_id_, mask_resource_id, mask_uv_rect, + mask_texture_size, /*mask_applies_to_backdrop=*/false, + mirrored_effect_node->surface_contents_scale, + mirrored_effect_node->filters_origin, + gfx::RectF(gfx::Rect(content_rect.size())), + !layer_tree_impl()->settings().enable_edge_anti_aliasing, 0.f); +} + +void MirrorLayerImpl::PushPropertiesTo(LayerImpl* layer) { + LayerImpl::PushPropertiesTo(layer); + + auto* mirror_layer = static_cast<MirrorLayerImpl*>(layer); + mirror_layer->SetMirroredLayerId(mirrored_layer_id_); +} + +gfx::Rect MirrorLayerImpl::GetDamageRect() const { + // TOOD(mohsen): Currently, the whole layer is marked as damaged. We should + // only consider the damage from the mirrored layer. + return gfx::Rect(bounds()); +} + +gfx::Rect MirrorLayerImpl::GetEnclosingRectInTargetSpace() const { + const LayerImpl* mirrored_layer = + layer_tree_impl()->LayerById(mirrored_layer_id_); + return GetScaledEnclosingRectInTargetSpace( + mirrored_layer->GetIdealContentsScale()); +} + +const char* MirrorLayerImpl::LayerTypeAsString() const { + return "cc::MirrorLayerImpl"; +} + +} // namespace cc diff --git a/chromium/cc/layers/mirror_layer_impl.h b/chromium/cc/layers/mirror_layer_impl.h new file mode 100644 index 00000000000..b2e62041014 --- /dev/null +++ b/chromium/cc/layers/mirror_layer_impl.h @@ -0,0 +1,63 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_LAYERS_MIRROR_LAYER_IMPL_H_ +#define CC_LAYERS_MIRROR_LAYER_IMPL_H_ + +#include <memory> + +#include "base/memory/ptr_util.h" +#include "cc/cc_export.h" +#include "cc/layers/layer_impl.h" + +namespace cc { + +// This type of layer is used to mirror contents of another layer (specified by +// |mirrored_layer_id_|) by forcing a render pass for the mirrored layer and +// adding a RenderPassDrawQuad in the compositor frame for this layer referring +// to that render pass. The mirroring layer should not be a descendant of the +// mirrored layer (in terms of the effect tree). Due to ordering requirements +// for render passes in the compositor frame, the render pass containing +// mirroring layer should appear after the render pass created for the mirrored +// layer. Currently, render passes are in reverse-draw order of the effect tree, +// so we should be careful that this reverse-draw order does not conflict with +// render pass ordering requirement mentioned above. +// TODO(mohsen): If necessary, reorder render passes in compositor frame such +// that the render pass containing mirroring layer appears after the render pass +// created for the mirrored layer. +class CC_EXPORT MirrorLayerImpl : public LayerImpl { + public: + static std::unique_ptr<MirrorLayerImpl> Create(LayerTreeImpl* tree_impl, + int id) { + return base::WrapUnique(new MirrorLayerImpl(tree_impl, id)); + } + + MirrorLayerImpl(const MirrorLayerImpl&) = delete; + MirrorLayerImpl& operator=(const MirrorLayerImpl&) = delete; + + ~MirrorLayerImpl() override; + + void SetMirroredLayerId(int id) { mirrored_layer_id_ = id; } + int mirrored_layer_id() const { return mirrored_layer_id_; } + + // LayerImpl overrides. + std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override; + void AppendQuads(viz::RenderPass* render_pass, + AppendQuadsData* append_quads_data) override; + void PushPropertiesTo(LayerImpl* layer) override; + gfx::Rect GetDamageRect() const override; + gfx::Rect GetEnclosingRectInTargetSpace() const override; + + protected: + MirrorLayerImpl(LayerTreeImpl* tree_impl, int id); + + private: + const char* LayerTypeAsString() const override; + + int mirrored_layer_id_ = 0; +}; + +} // namespace cc + +#endif // CC_LAYERS_MIRROR_LAYER_IMPL_H_ diff --git a/chromium/cc/layers/mirror_layer_unittest.cc b/chromium/cc/layers/mirror_layer_unittest.cc new file mode 100644 index 00000000000..9630d80571d --- /dev/null +++ b/chromium/cc/layers/mirror_layer_unittest.cc @@ -0,0 +1,141 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <memory> + +#include "cc/animation/animation_host.h" +#include "cc/layers/mirror_layer.h" +#include "cc/layers/mirror_layer_impl.h" +#include "cc/test/fake_impl_task_runner_provider.h" +#include "cc/test/fake_layer_tree_host.h" +#include "cc/test/fake_layer_tree_host_client.h" +#include "cc/test/fake_layer_tree_host_impl.h" +#include "cc/test/test_task_graph_runner.h" +#include "cc/trees/tree_synchronizer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +class MirrorLayerTest : public testing::Test { + public: + MirrorLayerTest() : host_impl_(&task_runner_provider_, &task_graph_runner_) {} + + // Synchronizes |layer_tree_host_| and |host_impl_| and pushes surface ids. + void SynchronizeTrees() { + TreeSynchronizer::PushLayerProperties(layer_tree_host_.get(), + host_impl_.pending_tree()); + } + + protected: + void SetUp() override { + animation_host_ = AnimationHost::CreateForTesting(ThreadInstance::MAIN); + layer_tree_host_ = FakeLayerTreeHost::Create( + &fake_client_, &task_graph_runner_, animation_host_.get()); + layer_tree_host_->SetViewportSizeAndScale(gfx::Size(10, 10), 1.f, + viz::LocalSurfaceIdAllocation()); + host_impl_.CreatePendingTree(); + } + + void TearDown() override { + layer_tree_host_->SetRootLayer(nullptr); + layer_tree_host_ = nullptr; + } + + FakeLayerTreeHostClient fake_client_; + FakeImplTaskRunnerProvider task_runner_provider_; + TestTaskGraphRunner task_graph_runner_; + std::unique_ptr<AnimationHost> animation_host_; + std::unique_ptr<FakeLayerTreeHost> layer_tree_host_; + FakeLayerTreeHostImpl host_impl_; +}; + +// This test verifies that MirrorLayer properties are pushed across to +// MirrorLayerImpl. +TEST_F(MirrorLayerTest, PushProperties) { + auto root = Layer::Create(); + layer_tree_host_->SetRootLayer(root); + + auto mirrored = Layer::Create(); + root->AddChild(mirrored); + auto mirror = MirrorLayer::Create(mirrored); + root->AddChild(mirror); + + EXPECT_EQ(1, mirrored->mirror_count()); + EXPECT_EQ(mirrored.get(), mirror->mirrored_layer()); + + auto root_impl = LayerImpl::Create(host_impl_.pending_tree(), root->id()); + auto mirrored_impl = + LayerImpl::Create(host_impl_.pending_tree(), mirrored->id()); + auto mirror_impl = + MirrorLayerImpl::Create(host_impl_.pending_tree(), mirror->id()); + + // Verify that impl layers have default property values. + EXPECT_EQ(0, mirrored_impl->mirror_count()); + EXPECT_EQ(0, mirror_impl->mirrored_layer_id()); + + SynchronizeTrees(); + + // Verify that property values are pushed to impl layers. + EXPECT_EQ(1, mirrored_impl->mirror_count()); + EXPECT_EQ(mirrored_impl->id(), mirror_impl->mirrored_layer_id()); +} + +// This test verifies adding/removing mirror layers updates mirror count +// properly and sets appropriate bits on the layer tree host. +TEST_F(MirrorLayerTest, MirrorCount) { + auto mirrored = Layer::Create(); + mirrored->SetLayerTreeHost(layer_tree_host_.get()); + + layer_tree_host_->property_trees()->needs_rebuild = false; + layer_tree_host_->ClearLayersThatShouldPushProperties(); + EXPECT_EQ(0, mirrored->mirror_count()); + + // Creating the first mirror layer should trigger property trees rebuild. + auto mirror1 = MirrorLayer::Create(mirrored); + EXPECT_EQ(1, mirrored->mirror_count()); + EXPECT_EQ(mirrored.get(), mirror1->mirrored_layer()); + EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild); + EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(), + mirrored.get())); + + layer_tree_host_->property_trees()->needs_rebuild = false; + layer_tree_host_->ClearLayersThatShouldPushProperties(); + + // Creating a second mirror layer should not trigger property trees rebuild. + auto mirror2 = MirrorLayer::Create(mirrored); + EXPECT_EQ(2, mirrored->mirror_count()); + EXPECT_EQ(mirrored.get(), mirror2->mirrored_layer()); + EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild); + EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(), + mirrored.get())); + + layer_tree_host_->property_trees()->needs_rebuild = false; + layer_tree_host_->ClearLayersThatShouldPushProperties(); + + // Destroying one of the mirror layers should not trigger property trees + // rebuild. + mirror1->RemoveFromParent(); + mirror1 = nullptr; + EXPECT_EQ(1, mirrored->mirror_count()); + EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild); + EXPECT_EQ(1u, layer_tree_host_->LayersThatShouldPushProperties().size()); + + layer_tree_host_->property_trees()->needs_rebuild = false; + layer_tree_host_->ClearLayersThatShouldPushProperties(); + + // Destroying the only remaining mirror layer should trigger property trees + // rebuild. + mirror2->RemoveFromParent(); + mirror2 = nullptr; + EXPECT_EQ(0, mirrored->mirror_count()); + EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild); + EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(), + mirrored.get())); + + mirrored->SetLayerTreeHost(nullptr); +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/layers/nine_patch_generator.cc b/chromium/cc/layers/nine_patch_generator.cc index 7677f48bd11..30214034a44 100644 --- a/chromium/cc/layers/nine_patch_generator.cc +++ b/chromium/cc/layers/nine_patch_generator.cc @@ -362,7 +362,7 @@ void NinePatchGenerator::AppendQuads(LayerImpl* layer_impl, image_rect.bottom_right(), SK_ColorTRANSPARENT, vertex_opacity, flipped, nearest_neighbor_, /*secure_output_only=*/false, - ui::ProtectedVideoType::kClear); + gfx::ProtectedVideoType::kClear); layer_impl->ValidateQuadResources(quad); } } diff --git a/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc b/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc index c7e055e541d..3a865b28741 100644 --- a/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc +++ b/chromium/cc/layers/painted_overlay_scrollbar_layer_impl.cc @@ -162,7 +162,7 @@ void PaintedOverlayScrollbarLayerImpl::AppendTrackQuads( shared_quad_state, scaled_track_quad_rect, scaled_visible_track_quad_rect, needs_blending, track_resource_id, premultipled_alpha, uv_top_left, uv_bottom_right, SK_ColorTRANSPARENT, opacity, flipped, nearest_neighbor, - /*secure_output_only=*/false, ui::ProtectedVideoType::kClear); + /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear); ValidateQuadResources(quad); } diff --git a/chromium/cc/layers/painted_scrollbar_layer.cc b/chromium/cc/layers/painted_scrollbar_layer.cc index deda561b810..83df997f2b0 100644 --- a/chromium/cc/layers/painted_scrollbar_layer.cc +++ b/chromium/cc/layers/painted_scrollbar_layer.cc @@ -148,18 +148,14 @@ void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() { } void PaintedScrollbarLayer::UpdateInternalContentScale() { - float scale = layer_tree_host()->device_scale_factor(); - if (layer_tree_host() - ->GetSettings() - .layer_transforms_should_scale_layer_contents) { - gfx::Transform transform; - transform = draw_property_utils::ScreenSpaceTransform( - this, layer_tree_host()->property_trees()->transform_tree); - - gfx::Vector2dF transform_scales = - MathUtil::ComputeTransform2dScaleComponents(transform, scale); - scale = std::max(transform_scales.x(), transform_scales.y()); - } + gfx::Transform transform; + transform = draw_property_utils::ScreenSpaceTransform( + this, layer_tree_host()->property_trees()->transform_tree); + + gfx::Vector2dF transform_scales = MathUtil::ComputeTransform2dScaleComponents( + transform, layer_tree_host()->device_scale_factor()); + float scale = std::max(transform_scales.x(), transform_scales.y()); + bool changed = false; changed |= UpdateProperty(scale, &internal_contents_scale_); changed |= diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.cc b/chromium/cc/layers/painted_scrollbar_layer_impl.cc index 0788e665719..c6426b6c4f8 100644 --- a/chromium/cc/layers/painted_scrollbar_layer_impl.cc +++ b/chromium/cc/layers/painted_scrollbar_layer_impl.cc @@ -100,7 +100,7 @@ void PaintedScrollbarLayerImpl::AppendQuads( viz::SharedQuadState* shared_quad_state = render_pass->CreateAndAppendSharedQuadState(); PopulateScaledSharedQuadState(shared_quad_state, internal_contents_scale_, - internal_contents_scale_, contents_opaque()); + contents_opaque()); AppendDebugBorderQuad(render_pass, gfx::Rect(internal_content_bounds_), shared_quad_state, append_quads_data); @@ -129,7 +129,7 @@ void PaintedScrollbarLayerImpl::AppendQuads( thumb_resource_id, premultipled_alpha, uv_top_left, uv_bottom_right, SK_ColorTRANSPARENT, opacity, flipped, nearest_neighbor, /*secure_output_only=*/false, - ui::ProtectedVideoType::kClear); + gfx::ProtectedVideoType::kClear); ValidateQuadResources(quad); } @@ -149,7 +149,7 @@ void PaintedScrollbarLayerImpl::AppendQuads( track_resource_id, premultipled_alpha, uv_top_left, uv_bottom_right, SK_ColorTRANSPARENT, opacity, flipped, nearest_neighbor, /*secure_output_only=*/false, - ui::ProtectedVideoType::kClear); + gfx::ProtectedVideoType::kClear); ValidateQuadResources(quad); } } diff --git a/chromium/cc/layers/painted_scrollbar_layer_unittest.cc b/chromium/cc/layers/painted_scrollbar_layer_unittest.cc index 53ce3fabcef..bea0320631f 100644 --- a/chromium/cc/layers/painted_scrollbar_layer_unittest.cc +++ b/chromium/cc/layers/painted_scrollbar_layer_unittest.cc @@ -9,6 +9,7 @@ #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_scrollbar.h" #include "cc/test/test_task_graph_runner.h" +#include "cc/trees/layer_tree_host_common.h" #include "testing/gmock/include/gmock/gmock.h" using ::testing::Mock; @@ -43,6 +44,11 @@ TEST(PaintedScrollbarLayerTest, NeedsPaint) { scrollbar_layer->SetBounds(gfx::Size(100, 100)); layer_tree_host->SetRootLayer(scrollbar_layer); + + LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs( + scrollbar_layer.get(), scrollbar_layer->bounds()); + LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); + EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host.get()); // Request no paint, but expect them to be painted because they have not diff --git a/chromium/cc/layers/picture_layer.cc b/chromium/cc/layers/picture_layer.cc index 5447882cf61..049f7382668 100644 --- a/chromium/cc/layers/picture_layer.cc +++ b/chromium/cc/layers/picture_layer.cc @@ -82,7 +82,7 @@ void PictureLayer::PushPropertiesTo(LayerImpl* base_layer) { } layer_impl->UpdateRasterSource(recording_source_->CreateRasterSource(), - &last_updated_invalidation_, nullptr); + &last_updated_invalidation_, nullptr, nullptr); DCHECK(last_updated_invalidation_.IsEmpty()); } @@ -92,10 +92,6 @@ void PictureLayer::SetLayerTreeHost(LayerTreeHost* host) { if (!host) return; - if (!host->GetSettings().enable_mask_tiling && - mask_type_ == LayerMaskType::MULTI_TEXTURE_MASK) - mask_type_ = LayerMaskType::SINGLE_TEXTURE_MASK; - if (!recording_source_) recording_source_.reset(new RecordingSource); recording_source_->SetSlowdownRasterScaleFactor( @@ -165,11 +161,6 @@ bool PictureLayer::Update() { } void PictureLayer::SetLayerMaskType(LayerMaskType mask_type) { - // We do not allow converting SINGLE_TEXTURE_MASK to MULTI_TEXTURE_MASK in - // order to avoid rerastering when a mask's transform is being animated. - if (mask_type_ == LayerMaskType::SINGLE_TEXTURE_MASK && - mask_type == LayerMaskType::MULTI_TEXTURE_MASK) - return; mask_type_ = mask_type; } @@ -249,7 +240,7 @@ void PictureLayer::RunMicroBenchmark(MicroBenchmark* benchmark) { } void PictureLayer::CaptureContent(const gfx::Rect& rect, - std::vector<NodeHolder>* content) { + std::vector<NodeId>* content) { if (!DrawsContent()) return; diff --git a/chromium/cc/layers/picture_layer.h b/chromium/cc/layers/picture_layer.h index 48ea87225a1..097782aa4cf 100644 --- a/chromium/cc/layers/picture_layer.h +++ b/chromium/cc/layers/picture_layer.h @@ -48,7 +48,7 @@ class CC_EXPORT PictureLayer : public Layer { bool HasNonAAPaint() const override; void RunMicroBenchmark(MicroBenchmark* benchmark) override; void CaptureContent(const gfx::Rect& rect, - std::vector<NodeHolder>* content) override; + std::vector<NodeId>* content) override; ContentLayerClient* client() { return picture_layer_inputs_.client; } diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc index b9cbcd3241a..23c71d964ec 100644 --- a/chromium/cc/layers/picture_layer_impl.cc +++ b/chromium/cc/layers/picture_layer_impl.cc @@ -48,23 +48,6 @@ const float kMaxScaleRatioDuringPinch = 2.0f; // tiling's scale if the desired scale is within this ratio. const float kSnapToExistingTilingRatio = 1.2f; -// Even for really wide viewports, at some point GPU raster should use -// less than 4 tiles to fill the viewport. This is set to 256 as a -// sane minimum for now, but we might want to tune this for low-end. -const int kMinHeightForGpuRasteredTile = 256; - -// When making odd-sized tiles, round them up to increase the chances -// of using the same tile size. -const int kTileRoundUp = 64; - -// Round GPU default tile sizes to a multiple of 32. This helps prevent -// rounding errors during compositing. -const int kGpuDefaultTileRoundUp = 32; - -// For performance reasons and to support compressed tile textures, tile -// width and height should be an even multiple of 4 in size. -const int kTileMinimalAlignment = 4; - // Large contents scale can cause overflow issues. Cap the ideal contents scale // by this constant, since scales larger than this are usually not correct or // their scale doesn't matter as long as it's large. Content scales usually @@ -92,55 +75,6 @@ gfx::Rect SafeIntersectRects(const gfx::Rect& one, const gfx::Rect& two) { static_cast<int>(rb - ry)); } -// This function converts the given |device_pixels_size| to the expected size -// of content which was generated to fill it at 100%. This takes into account -// the ceil operations that occur as device pixels are converted to/from DIPs -// (content size must be a whole number of DIPs). -gfx::Size ApplyDsfAdjustment(gfx::Size device_pixels_size, float dsf) { - gfx::Size content_size_in_dips = - gfx::ScaleToCeiledSize(device_pixels_size, 1.0f / dsf); - gfx::Size content_size_in_dps = - gfx::ScaleToCeiledSize(content_size_in_dips, dsf); - return content_size_in_dps; -} - -// For GPU rasterization, we pick an ideal tile size using the viewport so we -// don't need any settings. The current approach uses 4 tiles to cover the -// viewport vertically. -gfx::Size CalculateGpuTileSize(const gfx::Size& base_tile_size, - const gfx::Size& content_bounds, - const gfx::Size& max_tile_size) { - int tile_width = base_tile_size.width(); - - // Increase the height proportionally as the width decreases, and pad by our - // border texels to make the tiles exactly match the viewport. - int divisor = 4; - if (content_bounds.width() <= base_tile_size.width() / 2) - divisor = 2; - if (content_bounds.width() <= base_tile_size.width() / 4) - divisor = 1; - int tile_height = - MathUtil::UncheckedRoundUp(base_tile_size.height(), divisor) / divisor; - - // Grow default sizes to account for overlapping border texels. - tile_width += 2 * PictureLayerTiling::kBorderTexels; - tile_height += 2 * PictureLayerTiling::kBorderTexels; - - // Round GPU default tile sizes to a multiple of kGpuDefaultTileAlignment. - // This helps prevent rounding errors in our CA path. https://crbug.com/632274 - tile_width = MathUtil::UncheckedRoundUp(tile_width, kGpuDefaultTileRoundUp); - tile_height = MathUtil::UncheckedRoundUp(tile_height, kGpuDefaultTileRoundUp); - - tile_height = std::max(tile_height, kMinHeightForGpuRasteredTile); - - if (!max_tile_size.IsEmpty()) { - tile_width = std::min(tile_width, max_tile_size.width()); - tile_height = std::min(tile_height, max_tile_size.height()); - } - - return gfx::Size(tile_width, tile_height); -} - } // namespace PictureLayerImpl::PictureLayerImpl(LayerTreeImpl* tree_impl, @@ -164,13 +98,19 @@ PictureLayerImpl::PictureLayerImpl(LayerTreeImpl* tree_impl, nearest_neighbor_(false), use_transformed_rasterization_(false), is_directly_composited_image_(false), - can_use_lcd_text_(true) { + can_use_lcd_text_(true), + tile_size_calculator_(this) { layer_tree_impl()->RegisterPictureLayerImpl(this); } PictureLayerImpl::~PictureLayerImpl() { if (twin_layer_) twin_layer_->twin_layer_ = nullptr; + // We only track PaintWorklet-containing PictureLayerImpls on the pending + // tree. However this deletion may happen outside the commit flow when we are + // on the recycle tree instead, so just check !IsActiveTree(). + if (!paint_worklet_records_.empty() && !layer_tree_impl()->IsActiveTree()) + layer_tree_impl()->NotifyLayerHasPaintWorkletsChanged(this, false); layer_tree_impl()->UnregisterPictureLayerImpl(this); // Unregister for all images on the current raster source. @@ -223,8 +163,8 @@ void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) { layer_tree_impl()->create_low_res_tiling() ? 2u : 1u); layer_impl->set_gpu_raster_max_texture_size(gpu_raster_max_texture_size_); - layer_impl->UpdateRasterSource(raster_source_, &invalidation_, - tilings_.get()); + layer_impl->UpdateRasterSource(raster_source_, &invalidation_, tilings_.get(), + &paint_worklet_records_); DCHECK(invalidation_.IsEmpty()); // After syncing a solid color layer, the active layer has no tilings. @@ -258,23 +198,19 @@ void PictureLayerImpl::AppendQuads(viz::RenderPass* render_pass, render_pass->CreateAndAppendSharedQuadState(); if (raster_source_->IsSolidColor()) { - // TODO(sunxd): Solid color non-mask layers are forced to have contents - // scale = 1. This is a workaround to temperarily fix - // https://crbug.com/796558. - // We need to investigate into the ca layers logic and remove this - // workaround after fixing the bug. - float max_contents_scale = - !(mask_type_ == Layer::LayerMaskType::MULTI_TEXTURE_MASK) - ? 1 - : CanHaveTilings() ? ideal_contents_scale_ - : std::min(kMaxIdealContentsScale, - std::max(GetIdealContentsScale(), - MinimumContentsScale())); + // TODO(979672): This is still hard-coded at 1.0. This has some history: + // - for crbug.com/769319, the contents scale was allowed to change, to + // avoid blurring on high-dpi screens. + // - for crbug.com/796558, the max device scale was hard-coded back to 1.0 + // for single-tile masks, to avoid problems with transforms. + // To avoid those transform/scale bugs, this is currently left at 1.0. See + // crbug.com/979672 for more context and test links. + float max_contents_scale = 1; // The downstream CA layers use shared_quad_state to generate resources of // the right size even if it is a solid color picture layer. PopulateScaledSharedQuadState(shared_quad_state, max_contents_scale, - max_contents_scale, contents_opaque()); + contents_opaque()); AppendDebugBorderQuad(render_pass, gfx::Rect(bounds()), shared_quad_state, append_quads_data); @@ -297,9 +233,13 @@ void PictureLayerImpl::AppendQuads(viz::RenderPass* render_pass, } float device_scale_factor = layer_tree_impl()->device_scale_factor(); - float max_contents_scale = MaximumTilingContentsScale(); + // If we don't have tilings, we're likely going to append a checkerboard quad + // the size of the layer. In that case, use scale 1 for more stable + // to-screen-space mapping. + float max_contents_scale = + tilings_->num_tilings() ? MaximumTilingContentsScale() : 1.f; PopulateScaledSharedQuadState(shared_quad_state, max_contents_scale, - max_contents_scale, contents_opaque()); + contents_opaque()); Occlusion scaled_occlusion; if (mask_type_ == Layer::LayerMaskType::NOT_MASK) { scaled_occlusion = @@ -495,7 +435,7 @@ void PictureLayerImpl::AppendQuads(viz::RenderPass* render_pass, float alpha = (SkColorGetA(draw_info.solid_color()) * (1.0f / 255.0f)) * shared_quad_state->opacity; - if (mask_type_ == Layer::LayerMaskType::MULTI_TEXTURE_MASK || + if (mask_type_ != Layer::LayerMaskType::NOT_MASK || alpha >= std::numeric_limits<float>::epsilon()) { auto* quad = render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>(); @@ -731,7 +671,8 @@ PictureLayerImpl* PictureLayerImpl::GetPendingOrActiveTwinLayer() const { void PictureLayerImpl::UpdateRasterSource( scoped_refptr<RasterSource> raster_source, Region* new_invalidation, - const PictureLayerTilingSet* pending_set) { + const PictureLayerTilingSet* pending_set, + const PaintWorkletRecordMap* pending_paint_worklet_records) { // The bounds and the pile size may differ if the pile wasn't updated (ie. // PictureLayer::Update didn't happen). In that case the pile will be empty. DCHECK(raster_source->GetSize().IsEmpty() || @@ -747,9 +688,16 @@ void PictureLayerImpl::UpdateRasterSource( // Unregister for all images on the current raster source, if the recording // was updated. - if (recording_updated) + if (recording_updated) { UnregisterAnimatedImages(); + // When the display list changes, the set of PaintWorklets may also change. + if (pending_paint_worklet_records) + paint_worklet_records_ = *pending_paint_worklet_records; + else + SetPaintWorkletInputs(raster_source->GetPaintWorkletInputs()); + } + // The |raster_source_| is initially null, so have to check for that for the // first frame. bool could_have_tilings = raster_source_.get() && CanHaveTilings(); @@ -821,7 +769,7 @@ bool PictureLayerImpl::UpdateCanUseLCDTextAfterCommit() { void PictureLayerImpl::NotifyTileStateChanged(const Tile* tile) { if (layer_tree_impl()->IsActiveTree()) - AddDamageRect(tile->enclosing_layer_rect()); + damage_rect_.Union(tile->enclosing_layer_rect()); if (tile->draw_info().NeedsRaster()) { PictureLayerTiling* tiling = tilings_->FindTilingWithScaleKey(tile->contents_scale_key()); @@ -830,6 +778,15 @@ void PictureLayerImpl::NotifyTileStateChanged(const Tile* tile) { } } +gfx::Rect PictureLayerImpl::GetDamageRect() const { + return damage_rect_; +} + +void PictureLayerImpl::ResetChangeTracking() { + LayerImpl::ResetChangeTracking(); + damage_rect_.SetRect(0, 0, 0, 0); +} + void PictureLayerImpl::DidBeginTracing() { raster_source_->DidBeginTracing(); } @@ -907,6 +864,10 @@ bool PictureLayerImpl::RequiresHighResToDraw() const { return layer_tree_impl()->RequiresHighResToDraw(); } +const PaintWorkletRecordMap& PictureLayerImpl::GetPaintWorkletRecords() const { + return paint_worklet_records_; +} + gfx::Rect PictureLayerImpl::GetEnclosingRectInTargetSpace() const { return GetScaledEnclosingRectInTargetSpace(MaximumTilingContentsScale()); } @@ -944,90 +905,9 @@ bool PictureLayerImpl::ShouldAnimate(PaintImage::Id paint_image_id) const { return false; } -gfx::Size PictureLayerImpl::CalculateTileSize( - const gfx::Size& content_bounds) const { - int max_texture_size = layer_tree_impl()->max_texture_size(); - - if (mask_type_ == Layer::LayerMaskType::SINGLE_TEXTURE_MASK) { - // Masks are not tiled, so if we can't cover the whole mask with one tile, - // we shouldn't have such a tiling at all. - DCHECK_LE(content_bounds.width(), max_texture_size); - DCHECK_LE(content_bounds.height(), max_texture_size); - return content_bounds; - } - - int default_tile_width = 0; - int default_tile_height = 0; - if (layer_tree_impl()->use_gpu_rasterization()) { - gfx::Size max_tile_size = - layer_tree_impl()->settings().max_gpu_raster_tile_size; - - // Calculate |base_tile_size based| on |gpu_raster_max_texture_size_|, - // adjusting for ceil operations that may occur due to DSF. - gfx::Size base_tile_size = ApplyDsfAdjustment( - gpu_raster_max_texture_size_, layer_tree_impl()->device_scale_factor()); - - // Set our initial size assuming a |base_tile_size| equal to our - // |viewport_size|. - gfx::Size default_tile_size = - CalculateGpuTileSize(base_tile_size, content_bounds, max_tile_size); - - // Use half-width GPU tiles when the content_width is greater than our - // calculated tile size. - if (content_bounds.width() > default_tile_size.width()) { - // Divide width by 2 and round up. - base_tile_size.set_width((base_tile_size.width() + 1) / 2); - default_tile_size = - CalculateGpuTileSize(base_tile_size, content_bounds, max_tile_size); - } - - default_tile_width = default_tile_size.width(); - default_tile_height = default_tile_size.height(); - } else { - // For CPU rasterization we use tile-size settings. - const LayerTreeSettings& settings = layer_tree_impl()->settings(); - int max_untiled_content_width = settings.max_untiled_layer_size.width(); - int max_untiled_content_height = settings.max_untiled_layer_size.height(); - default_tile_width = settings.default_tile_size.width(); - default_tile_height = settings.default_tile_size.height(); - - // If the content width is small, increase tile size vertically. - // If the content height is small, increase tile size horizontally. - // If both are less than the untiled-size, use a single tile. - if (content_bounds.width() < default_tile_width) - default_tile_height = max_untiled_content_height; - if (content_bounds.height() < default_tile_height) - default_tile_width = max_untiled_content_width; - if (content_bounds.width() < max_untiled_content_width && - content_bounds.height() < max_untiled_content_height) { - default_tile_height = max_untiled_content_height; - default_tile_width = max_untiled_content_width; - } - } - - int tile_width = default_tile_width; - int tile_height = default_tile_height; - - // Clamp the tile width/height to the content width/height to save space. - if (content_bounds.width() < default_tile_width) { - tile_width = std::min(tile_width, content_bounds.width()); - tile_width = MathUtil::UncheckedRoundUp(tile_width, kTileRoundUp); - tile_width = std::min(tile_width, default_tile_width); - } - if (content_bounds.height() < default_tile_height) { - tile_height = std::min(tile_height, content_bounds.height()); - tile_height = MathUtil::UncheckedRoundUp(tile_height, kTileRoundUp); - tile_height = std::min(tile_height, default_tile_height); - } - - // Ensure that tile width and height are properly aligned. - tile_width = MathUtil::UncheckedRoundUp(tile_width, kTileMinimalAlignment); - tile_height = MathUtil::UncheckedRoundUp(tile_height, kTileMinimalAlignment); - - // Under no circumstance should we be larger than the max texture size. - tile_width = std::min(tile_width, max_texture_size); - tile_height = std::min(tile_height, max_texture_size); - return gfx::Size(tile_width, tile_height); +gfx::Size PictureLayerImpl::CalculateTileSize(const gfx::Size& content_bounds) { + content_bounds_ = content_bounds; + return tile_size_calculator_.CalculateTileSize(); } void PictureLayerImpl::GetContentsResourceId( @@ -1470,6 +1350,8 @@ float PictureLayerImpl::MinimumContentsScale() const { } float PictureLayerImpl::MaximumContentsScale() const { + if (bounds().IsEmpty()) + return 0; // When mask tiling is disabled or the mask is single textured, masks can not // have tilings that would become larger than the max_texture_size since they // use a single tile for the entire tiling. Other layers can have tilings such @@ -1478,9 +1360,8 @@ float PictureLayerImpl::MaximumContentsScale() const { static_cast<float>(mask_type_ == Layer::LayerMaskType::SINGLE_TEXTURE_MASK ? layer_tree_impl()->max_texture_size() : std::numeric_limits<int>::max()); - float max_scale_width = max_dimension / bounds().width(); - float max_scale_height = max_dimension / bounds().height(); - float max_scale = std::min(max_scale_width, max_scale_height); + int higher_dimension = std::max(bounds().width(), bounds().height()); + float max_scale = max_dimension / higher_dimension; // We require that multiplying the layer size by the contents scale and // ceiling produces a value <= |max_dimension|. Because for large layer @@ -1721,6 +1602,13 @@ PictureLayerImpl::InvalidateRegionForImages( return ImageInvalidationResult::kInvalidated; } +void PictureLayerImpl::SetPaintWorkletRecord( + scoped_refptr<PaintWorkletInput> input, + sk_sp<PaintRecord> record) { + DCHECK(paint_worklet_records_.find(input) != paint_worklet_records_.end()); + paint_worklet_records_[input] = std::move(record); +} + void PictureLayerImpl::RegisterAnimatedImages() { if (!raster_source_ || !raster_source_->GetDisplayItemList()) return; @@ -1749,4 +1637,45 @@ void PictureLayerImpl::UnregisterAnimatedImages() { controller->UnregisterAnimationDriver(data.paint_image_id, this); } +void PictureLayerImpl::SetPaintWorkletInputs( + const std::vector<scoped_refptr<PaintWorkletInput>>& inputs) { + bool had_paint_worklets = !paint_worklet_records_.empty(); + PaintWorkletRecordMap new_records; + for (const auto& input : inputs) { + // Attempt to re-use an existing PaintRecord if possible. + new_records[input] = std::move(paint_worklet_records_[input]); + } + paint_worklet_records_.swap(new_records); + + // The pending tree tracks which PictureLayerImpls have PaintWorkletInputs as + // an optimization to avoid walking all picture layers. + bool has_paint_worklets = !paint_worklet_records_.empty(); + if ((has_paint_worklets != had_paint_worklets) && + layer_tree_impl()->IsPendingTree()) { + layer_tree_impl()->NotifyLayerHasPaintWorkletsChanged(this, + has_paint_worklets); + } +} + +std::unique_ptr<base::DictionaryValue> PictureLayerImpl::LayerAsJson() const { + auto result = LayerImpl::LayerAsJson(); + auto dictionary = std::make_unique<base::DictionaryValue>(); + if (raster_source_) { + dictionary->SetBoolean("IsSolidColor", raster_source_->IsSolidColor()); + auto list = std::make_unique<base::ListValue>(); + list->AppendInteger(raster_source_->GetSize().width()); + list->AppendInteger(raster_source_->GetSize().height()); + dictionary->Set("Size", std::move(list)); + dictionary->SetBoolean("HasRecordings", raster_source_->HasRecordings()); + + const auto& display_list = raster_source_->GetDisplayItemList(); + size_t op_count = display_list ? display_list->TotalOpCount() : 0; + size_t bytes_used = display_list ? display_list->BytesUsed() : 0; + dictionary->SetInteger("OpCount", op_count); + dictionary->SetInteger("BytesUsed", bytes_used); + } + result->Set("RasterSource", std::move(dictionary)); + return result; +} + } // namespace cc diff --git a/chromium/cc/layers/picture_layer_impl.h b/chromium/cc/layers/picture_layer_impl.h index 82309bd8d4c..c7fa5fa0d74 100644 --- a/chromium/cc/layers/picture_layer_impl.h +++ b/chromium/cc/layers/picture_layer_impl.h @@ -15,6 +15,7 @@ #include "cc/cc_export.h" #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" +#include "cc/layers/tile_size_calculator.h" #include "cc/paint/image_id.h" #include "cc/tiles/picture_layer_tiling.h" #include "cc/tiles/picture_layer_tiling_set.h" @@ -51,22 +52,25 @@ class CC_EXPORT PictureLayerImpl void AppendQuads(viz::RenderPass* render_pass, AppendQuadsData* append_quads_data) override; void NotifyTileStateChanged(const Tile* tile) override; + gfx::Rect GetDamageRect() const override; + void ResetChangeTracking() override; void ResetRasterScale(); void DidBeginTracing() override; void ReleaseResources() override; void ReleaseTileResources() override; void RecreateTileResources() override; Region GetInvalidationRegionForDebugging() override; + gfx::Rect GetEnclosingRectInTargetSpace() const override; // PictureLayerTilingClient overrides. std::unique_ptr<Tile> CreateTile(const Tile::CreateInfo& info) override; - gfx::Size CalculateTileSize(const gfx::Size& content_bounds) const override; + gfx::Size CalculateTileSize(const gfx::Size& content_bounds) override; const Region* GetPendingInvalidation() override; const PictureLayerTiling* GetPendingOrActiveTwinTiling( const PictureLayerTiling* tiling) const override; bool HasValidTilePriorities() const override; bool RequiresHighResToDraw() const override; - gfx::Rect GetEnclosingRectInTargetSpace() const override; + const PaintWorkletRecordMap& GetPaintWorkletRecords() const override; // ImageAnimationController::AnimationDriver overrides. bool ShouldAnimate(PaintImage::Id paint_image_id) const override; @@ -74,9 +78,16 @@ class CC_EXPORT PictureLayerImpl void set_gpu_raster_max_texture_size(gfx::Size gpu_raster_max_texture_size) { gpu_raster_max_texture_size_ = gpu_raster_max_texture_size; } - void UpdateRasterSource(scoped_refptr<RasterSource> raster_source, - Region* new_invalidation, - const PictureLayerTilingSet* pending_set); + + gfx::Size gpu_raster_max_texture_size() { + return gpu_raster_max_texture_size_; + } + + void UpdateRasterSource( + scoped_refptr<RasterSource> raster_source, + Region* new_invalidation, + const PictureLayerTilingSet* pending_set, + const PaintWorkletRecordMap* pending_paint_worklet_records); bool UpdateTiles(); // Returns true if the LCD state changed. bool UpdateCanUseLCDTextAfterCommit(); @@ -123,10 +134,23 @@ class CC_EXPORT PictureLayerImpl ImageInvalidationResult InvalidateRegionForImages( const PaintImageIdFlatSet& images_to_invalidate); - bool RasterSourceUsesLCDTextForTesting() const { return can_use_lcd_text_; } + bool can_use_lcd_text() const { return can_use_lcd_text_; } const Region& InvalidationForTesting() const { return invalidation_; } + // Set the paint result (PaintRecord) for a given PaintWorkletInput. + void SetPaintWorkletRecord(scoped_refptr<PaintWorkletInput>, + sk_sp<PaintRecord>); + + // Retrieve the map of PaintWorkletInputs to their painted results + // (PaintRecords). If a PaintWorkletInput has not been painted yet, it will + // map to nullptr. + const PaintWorkletRecordMap& GetPaintWorkletRecordMap() const { + return paint_worklet_records_; + } + + gfx::Size content_bounds() { return content_bounds_; } + protected: PictureLayerImpl(LayerTreeImpl* tree_impl, int id, @@ -159,6 +183,12 @@ class CC_EXPORT PictureLayerImpl void RegisterAnimatedImages(); void UnregisterAnimatedImages(); + std::unique_ptr<base::DictionaryValue> LayerAsJson() const override; + + // Set the collection of PaintWorkletInputs that are part of this layer. + void SetPaintWorkletInputs( + const std::vector<scoped_refptr<PaintWorkletInput>>& inputs); + PictureLayerImpl* twin_layer_; std::unique_ptr<PictureLayerTilingSet> tilings_; @@ -207,6 +237,20 @@ class CC_EXPORT PictureLayerImpl // of comparing pointers, since objects pointed to are not guaranteed to // exist. std::vector<PictureLayerTiling*> last_append_quads_tilings_; + + // The set of PaintWorkletInputs that are part of this PictureLayerImpl, and + // their painted results (if any). During commit, Blink hands us a set of + // PaintWorkletInputs that are part of this layer. These are then painted + // asynchronously on a worklet thread, triggered from + // |LayerTreeHostImpl::UpdateSyncTreeAfterCommitOrImplSideInvalidation|. + PaintWorkletRecordMap paint_worklet_records_; + + gfx::Size content_bounds_; + TileSizeCalculator tile_size_calculator_; + + // Denotes an area that is damaged and needs redraw. This is in the layer's + // space. + gfx::Rect damage_rect_; }; } // namespace cc diff --git a/chromium/cc/layers/picture_layer_impl_unittest.cc b/chromium/cc/layers/picture_layer_impl_unittest.cc index d98fed6c1d0..1949b3524f2 100644 --- a/chromium/cc/layers/picture_layer_impl_unittest.cc +++ b/chromium/cc/layers/picture_layer_impl_unittest.cc @@ -31,6 +31,7 @@ #include "cc/test/layer_test_common.h" #include "cc/test/skia_common.h" #include "cc/test/test_layer_tree_host_base.h" +#include "cc/test/test_paint_worklet_input.h" #include "cc/test/test_task_graph_runner.h" #include "cc/tiles/tiling_set_raster_queue_all.h" #include "cc/tiles/tiling_set_raster_queue_required.h" @@ -80,7 +81,6 @@ class PictureLayerImplTest : public TestLayerTreeHostBase { LayerTreeSettings CreateSettings() override { LayerTreeSettings settings; settings.commit_to_active_tree = false; - settings.layer_transforms_should_scale_layer_contents = true; settings.create_low_res_tiling = true; return settings; } @@ -721,8 +721,7 @@ TEST_F(PictureLayerImplTest, ScaledBoundsOverflowInt) { gfx::Rect(layer_bounds); viz::SharedQuadState state; active_layer()->PopulateScaledSharedQuadState( - &state, adjusted_scale, adjusted_scale, - active_layer()->contents_opaque()); + &state, adjusted_scale, active_layer()->contents_opaque()); } TEST_F(PictureLayerImplTest, PinchGestureTilings) { @@ -1158,7 +1157,7 @@ TEST_F(PictureLayerImplTest, DontAddLowResForSmallLayers) { // Mask layers dont create low res since they always fit on one tile. std::unique_ptr<FakePictureLayerImpl> mask = - FakePictureLayerImpl::CreateSingleTextureMaskWithRasterSource( + FakePictureLayerImpl::CreateMaskWithRasterSource( host_impl()->pending_tree(), 3, pending_raster_source); mask->SetBounds(layer_bounds); mask->SetDrawsContent(true); @@ -1191,7 +1190,7 @@ TEST_F(PictureLayerImplTest, HugeMasksGetScaledDown) { SetupPendingTree(valid_raster_source); std::unique_ptr<FakePictureLayerImpl> mask_ptr = - FakePictureLayerImpl::CreateSingleTextureMaskWithRasterSource( + FakePictureLayerImpl::CreateMaskWithRasterSource( host_impl()->pending_tree(), 3, valid_raster_source); mask_ptr->SetBounds(layer_bounds); mask_ptr->SetDrawsContent(true); @@ -1324,7 +1323,7 @@ TEST_F(PictureLayerImplTest, ScaledMaskLayer) { SetupPendingTree(valid_raster_source); std::unique_ptr<FakePictureLayerImpl> mask_ptr = - FakePictureLayerImpl::CreateSingleTextureMaskWithRasterSource( + FakePictureLayerImpl::CreateMaskWithRasterSource( host_impl()->pending_tree(), 3, valid_raster_source); mask_ptr->SetBounds(layer_bounds); mask_ptr->SetDrawsContent(true); @@ -3375,7 +3374,7 @@ TEST_F(PictureLayerImplTest, IgnoreOcclusionOnSolidColorMask) { scoped_refptr<FakeRasterSource> pending_raster_source = FakeRasterSource::CreateFilledSolidColor(layer_bounds); SetupPendingTree(std::move(pending_raster_source), gfx::Size(), Region(), - Layer::LayerMaskType::MULTI_TEXTURE_MASK); + Layer::LayerMaskType::SINGLE_TEXTURE_MASK); host_impl()->pending_tree()->SetDeviceScaleFactor(2.f); ActivateTree(); @@ -4934,7 +4933,7 @@ TEST_F(PictureLayerImplTest, UpdateLCDInvalidatesPendingTree) { FakeRasterSource::CreateFilledLCD(layer_bounds); SetupPendingTreeWithFixedTileSize(pending_raster_source, tile_size, Region()); - EXPECT_TRUE(pending_layer()->RasterSourceUsesLCDTextForTesting()); + EXPECT_TRUE(pending_layer()->can_use_lcd_text()); EXPECT_TRUE(pending_layer()->HighResTiling()->has_tiles()); std::vector<Tile*> tiles = pending_layer()->HighResTiling()->AllTilesForTesting(); @@ -4942,7 +4941,7 @@ TEST_F(PictureLayerImplTest, UpdateLCDInvalidatesPendingTree) { pending_layer()->SetContentsOpaque(false); pending_layer()->UpdateCanUseLCDTextAfterCommit(); - EXPECT_FALSE(pending_layer()->RasterSourceUsesLCDTextForTesting()); + EXPECT_FALSE(pending_layer()->can_use_lcd_text()); EXPECT_TRUE(pending_layer()->HighResTiling()->has_tiles()); std::vector<Tile*> new_tiles = pending_layer()->HighResTiling()->AllTilesForTesting(); @@ -5536,5 +5535,88 @@ TEST_F(PictureLayerImplTest, AnimatedImages) { EXPECT_FALSE(active_layer()->ShouldAnimate(image2.stable_id())); } +TEST_F(PictureLayerImplTest, PaintWorkletInputs) { + gfx::Size layer_bounds(1000, 1000); + + // Set up a raster source with 2 PaintWorkletInputs. + auto recording_source = FakeRecordingSource::CreateRecordingSource( + gfx::Rect(layer_bounds), layer_bounds); + scoped_refptr<TestPaintWorkletInput> input1 = + base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100, 100)); + PaintImage image1 = CreatePaintWorkletPaintImage(input1); + scoped_refptr<TestPaintWorkletInput> input2 = + base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(50, 50)); + PaintImage image2 = CreatePaintWorkletPaintImage(input2); + recording_source->add_draw_image(image1, gfx::Point(100, 100)); + recording_source->add_draw_image(image2, gfx::Point(500, 500)); + recording_source->Rerecord(); + scoped_refptr<RasterSource> raster_source = + recording_source->CreateRasterSource(); + + // All inputs should be registered on the pending layer. + SetupPendingTree(raster_source, gfx::Size(), Region(gfx::Rect(layer_bounds))); + EXPECT_EQ(pending_layer()->GetPaintWorkletRecordMap().size(), 2u); + EXPECT_TRUE(pending_layer()->GetPaintWorkletRecordMap().contains(input1)); + EXPECT_TRUE(pending_layer()->GetPaintWorkletRecordMap().contains(input2)); + + // Specify a record for one of the inputs. + sk_sp<PaintRecord> record1 = sk_make_sp<PaintOpBuffer>(); + pending_layer()->SetPaintWorkletRecord(input1, record1); + + // Now activate and make sure the active layer is registered as well, with the + // appropriate record. + ActivateTree(); + EXPECT_EQ(active_layer()->GetPaintWorkletRecordMap().size(), 2u); + auto it = active_layer()->GetPaintWorkletRecordMap().find(input1); + ASSERT_NE(it, active_layer()->GetPaintWorkletRecordMap().end()); + EXPECT_EQ(it->second, record1); + EXPECT_TRUE(active_layer()->GetPaintWorkletRecordMap().contains(input2)); + + // Committing new PaintWorkletInputs (in a new raster source) should replace + // the previous ones. + recording_source = FakeRecordingSource::CreateRecordingSource( + gfx::Rect(layer_bounds), layer_bounds); + scoped_refptr<TestPaintWorkletInput> input3 = + base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(12, 12)); + PaintImage image3 = CreatePaintWorkletPaintImage(input3); + recording_source->add_draw_image(image3, gfx::Point(10, 10)); + recording_source->Rerecord(); + raster_source = recording_source->CreateRasterSource(); + + SetupPendingTree(raster_source, gfx::Size(), Region(gfx::Rect(layer_bounds))); + EXPECT_EQ(pending_layer()->GetPaintWorkletRecordMap().size(), 1u); + EXPECT_TRUE(pending_layer()->GetPaintWorkletRecordMap().contains(input3)); +} + +TEST_F(PictureLayerImplTest, NoTilingsUsesScaleOne) { + std::unique_ptr<viz::RenderPass> render_pass = viz::RenderPass::Create(); + + gfx::Size layer_bounds(1000, 10000); + scoped_refptr<FakeRasterSource> active_raster_source = + FakeRasterSource::CreateEmpty(layer_bounds); + SetupPendingTree(active_raster_source); + ActivateTree(); + + active_layer()->SetContentsOpaque(true); + active_layer()->draw_properties().visible_layer_rect = + gfx::Rect(0, 0, 1000, 1000); + active_layer()->UpdateTiles(); + + ASSERT_FALSE(active_layer()->HighResTiling()); + + AppendQuadsData data; + active_layer()->WillDraw(DRAW_MODE_HARDWARE, nullptr); + active_layer()->AppendQuads(render_pass.get(), &data); + active_layer()->DidDraw(nullptr); + + // One checkerboard quad. + EXPECT_EQ(1u, render_pass->quad_list.size()); + + auto* shared_quad_state = render_pass->quad_list.begin()->shared_quad_state; + // We should use scale 1 here, so the layer rect should be full layer bounds + // and the transform should be identity. + EXPECT_RECT_EQ(gfx::Rect(1000, 10000), shared_quad_state->quad_layer_rect); + EXPECT_TRUE(shared_quad_state->quad_to_target_transform.IsIdentity()); +} } // namespace } // namespace cc diff --git a/chromium/cc/layers/render_surface_impl.cc b/chromium/cc/layers/render_surface_impl.cc index 6acd6825c0b..ca739ac2085 100644 --- a/chromium/cc/layers/render_surface_impl.cc +++ b/chromium/cc/layers/render_surface_impl.cc @@ -13,9 +13,9 @@ #include "cc/base/math_util.h" #include "cc/debug/debug_colors.h" #include "cc/layers/append_quads_data.h" +#include "cc/paint/element_id.h" #include "cc/paint/filter_operations.h" #include "cc/trees/damage_tracker.h" -#include "cc/trees/draw_property_utils.h" #include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/occlusion.h" @@ -126,6 +126,13 @@ LayerImpl* RenderSurfaceImpl::MaskLayer() { return layer_tree_impl_->LayerById(mask_layer_id); } +LayerImpl* RenderSurfaceImpl::BackdropMaskLayer() const { + ElementId mask_element_id = OwningEffectNode()->backdrop_mask_element_id; + if (!mask_element_id) + return nullptr; + return layer_tree_impl_->LayerByElementId(mask_element_id); +} + bool RenderSurfaceImpl::HasMask() const { return OwningEffectNode()->mask_layer_id != Layer::INVALID_ID; } @@ -416,12 +423,19 @@ void RenderSurfaceImpl::AppendQuads(DrawMode draw_mode, GetDebugBorderWidth()); } + DCHECK(!(MaskLayer() && BackdropMaskLayer())) + << "Can't support both a mask_layer and a backdrop_mask_layer"; + bool mask_applies_to_backdrop = BackdropMaskLayer(); + PictureLayerImpl* mask_layer = + mask_applies_to_backdrop + ? static_cast<PictureLayerImpl*>(BackdropMaskLayer()) + : static_cast<PictureLayerImpl*>(MaskLayer()); + viz::ResourceId mask_resource_id = 0; gfx::Size mask_texture_size; gfx::RectF mask_uv_rect; gfx::Vector2dF surface_contents_scale = OwningEffectNode()->surface_contents_scale; - PictureLayerImpl* mask_layer = static_cast<PictureLayerImpl*>(MaskLayer()); // Resourceless mode does not support masks. if (draw_mode != DRAW_MODE_RESOURCELESS_SOFTWARE && mask_layer && mask_layer->DrawsContent() && !mask_layer->bounds().IsEmpty()) { @@ -435,10 +449,6 @@ void RenderSurfaceImpl::AppendQuads(DrawMode draw_mode, "mask_layer_gpu_memory_usage", mask_layer->GPUMemoryUsageInBytes()); - if (mask_layer->mask_type() == Layer::LayerMaskType::MULTI_TEXTURE_MASK) { - TileMaskLayer(render_pass, shared_quad_state, unoccluded_content_rect); - return; - } gfx::SizeF mask_uv_size; mask_layer->GetContentsResourceId(&mask_resource_id, &mask_texture_size, &mask_uv_size); @@ -457,199 +467,10 @@ void RenderSurfaceImpl::AppendQuads(DrawMode draw_mode, auto* quad = render_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>(); quad->SetNew(shared_quad_state, content_rect(), unoccluded_content_rect, id(), mask_resource_id, mask_uv_rect, mask_texture_size, - surface_contents_scale, FiltersOrigin(), tex_coord_rect, + mask_applies_to_backdrop, surface_contents_scale, + FiltersOrigin(), tex_coord_rect, !layer_tree_impl_->settings().enable_edge_anti_aliasing, OwningEffectNode()->backdrop_filter_quality); } -void RenderSurfaceImpl::TileMaskLayer( - viz::RenderPass* render_pass, - viz::SharedQuadState* shared_quad_state, - const gfx::Rect& unoccluded_content_rect) { - DCHECK(MaskLayer()); - DCHECK(Filters().IsEmpty()); - - LayerImpl* mask_layer = MaskLayer(); - gfx::Vector2dF owning_layer_to_surface_contents_scale = - OwningEffectNode()->surface_contents_scale; - - // Use the picture layer's AppendQuads logic to generate TileDrawQuads. These - // DrawQuads are used to generate tiled RenderPassDrawQuad for the mask. - std::unique_ptr<viz::RenderPass> temp_render_pass = viz::RenderPass::Create(); - AppendQuadsData temp_append_quads_data; - mask_layer->AppendQuads(temp_render_pass.get(), &temp_append_quads_data); - - auto* temp_quad = temp_render_pass->quad_list.front(); - if (!temp_quad) - return; - - // There are two spaces we are dealing with here: - // 1. quad space: This is the space where the draw quads generated by the - // PictureLayerImpl's logic are in. In other words, this is the space where - // the |temp_quad|'s rect is in. - // 2. surface space: This is the contents space of |this| render surface. - // Since |mask_layer|'s target is the render surface it's masking, the surface - // space is also the target space for the quads generated by - // PictureLayerImpl's logic. - - gfx::Transform quad_space_to_surface_space_transform = - temp_quad->shared_quad_state->quad_to_target_transform; - // This transform should be a 2d scale + offset, so would be invertible. - gfx::Transform surface_space_to_quad_space_transform; - bool invertible = quad_space_to_surface_space_transform.GetInverse( - &surface_space_to_quad_space_transform); - DCHECK(invertible) << "RenderSurfaceImpl::TileMaskLayer created quads with " - "non-invertible transform."; - - // While converting from the TileDrawQuads to RenderPassDrawQuads, we keep the - // quad rects in the same space, and modify every other rect that is not in - // quad space accordingly. - - // The |shared_quad_state| being passed in is generated with |this| render - // surface's draw properties. It holds a transform from the surface contents - // space to the surface target space. We want to change the origin space to - // match the |mask_layer|'s quad space, so we must include the transform from - // the quad space to the surface contents space. Then the transform is from - // the |mask_layer|'s quad space to our target space. - shared_quad_state->quad_to_target_transform.PreconcatTransform( - quad_space_to_surface_space_transform); - - // Next, we need to modify the rects on |shared_quad_state| that are in - // surface's "quad space" (surface space) to quad space. - shared_quad_state->quad_layer_rect = MathUtil::ProjectEnclosingClippedRect( - surface_space_to_quad_space_transform, - shared_quad_state->quad_layer_rect); - shared_quad_state->visible_quad_layer_rect = - MathUtil::ProjectEnclosingClippedRect( - surface_space_to_quad_space_transform, - shared_quad_state->visible_quad_layer_rect); - - // The |shared_quad_state|'s |quad_layer_rect| and |visible_quad_layer_rect| - // is set from content_rect(). content_rect() defines the size of the source - // texture to be masked. PictureLayerImpl's generated |quad_layer_rect| and - // |visible_quad_layer_rect| is from the mask layer's |bounds| and - // |visible_layer_rect|. These rect defines the size of the mask texture. The - // intersection of the two rects is the rect we can draw. - shared_quad_state->quad_layer_rect.Intersect( - temp_quad->shared_quad_state->quad_layer_rect); - shared_quad_state->visible_quad_layer_rect.Intersect( - temp_quad->shared_quad_state->visible_quad_layer_rect); - - // Cache content_rect() and |unoccluded_content_rect| in quad space. - gfx::Rect content_rect_in_quad_space = MathUtil::MapEnclosingClippedRect( - surface_space_to_quad_space_transform, content_rect()); - gfx::Rect unoccluded_content_rect_in_quad_space = - MathUtil::MapEnclosingClippedRect(surface_space_to_quad_space_transform, - unoccluded_content_rect); - - // Generate RenderPassDrawQuads based on the temporary quads created by - // |mask_layer|. - for (auto* temp_quad : temp_render_pass->quad_list) { - gfx::Rect temp_quad_rect = temp_quad->rect; - // If the |temp_quad_rect| is entirely outside render surface's - // content_rect(), ignore the quad. - if (!temp_quad_rect.Intersects(content_rect_in_quad_space)) - continue; - - // We only care about the quads that are inside the content_rect(). - gfx::Rect quad_rect = - gfx::IntersectRects(temp_quad_rect, content_rect_in_quad_space); - - gfx::Rect visible_quad_rect = - gfx::IntersectRects(quad_rect, unoccluded_content_rect_in_quad_space); - if (visible_quad_rect.IsEmpty()) - continue; - - // |tex_coord_rect| is non-normalized sub-rect of the render surface's - // texture that is being masked. Its origin is (0,0) and it is in surface - // space. For example the |tex_coord_rect| for the entire texture would be - // (0,0 content_rect.width X content_rect.height). - - // In order to calculate the |tex_coord_rect|, we calculate what quad's rect - // would be masking in the surface contents space, then remove the offset. - gfx::RectF tex_coord_rect = MathUtil::MapClippedRect( - quad_space_to_surface_space_transform, gfx::RectF(quad_rect)); - tex_coord_rect.Offset(-content_rect().OffsetFromOrigin()); - - constexpr float backdrop_filter_quality = 1.0; - switch (temp_quad->material) { - case viz::DrawQuad::Material::kTiledContent: { - DCHECK_EQ(1U, temp_quad->resources.count); - // When the |temp_quad| is actually a texture, we need to calculate - // |mask_uv_rect|. The |mask_uv_rect| is the normalized sub-rect for - // applying the mask's texture. To get |mask_uv_rect|, we need the newly - // calculated |quad_rect| in the texture's space, then normalized by the - // texture's size. - - // We are applying the |temp_quad|'s texture as a mask, so we start with - // the |tex_coord_rect| of the |temp_quad|. - gfx::RectF temp_tex_coord_rect = - viz::TileDrawQuad::MaterialCast(temp_quad)->tex_coord_rect; - - // The |quad_rect| is in the same space as |temp_quad_rect|. Calculate - // the scale transform between the texture space and the quad space. - float scale_x = temp_tex_coord_rect.width() / temp_quad_rect.width(); - float scale_y = temp_tex_coord_rect.height() / temp_quad_rect.height(); - // Start by setting up the correct size of mask_uv_rect in texture - // space. - gfx::RectF mask_uv_rect(quad_rect.width() * scale_x, - quad_rect.height() * scale_y); - - // Now figure out what is the correct offset. Start with the original - // temp_tex_coord_rect's offset. - mask_uv_rect.Offset(temp_tex_coord_rect.OffsetFromOrigin()); - // Next figure out what offset to apply by checking the difference in - // offset between |temp_quad_rect| and |quad_rect| which is - // intersected by the content_rect(). - gfx::Vector2dF offset(quad_rect.OffsetFromOrigin()); - offset -= temp_quad_rect.OffsetFromOrigin(); - // Convert the difference in offset into texture space. - offset.Scale(scale_x, scale_y); - mask_uv_rect.Offset(offset); - - // |mask_uv_rect| is normalized to [0..1] by the |mask_texture_size|. - gfx::Size mask_texture_size = - viz::TileDrawQuad::MaterialCast(temp_quad)->texture_size; - mask_uv_rect.Scale(1.f / mask_texture_size.width(), - 1.f / mask_texture_size.height()); - - auto* quad = - render_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>(); - quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, id(), - temp_quad->resources.ids[0], mask_uv_rect, - mask_texture_size, owning_layer_to_surface_contents_scale, - FiltersOrigin(), tex_coord_rect, - !layer_tree_impl_->settings().enable_edge_anti_aliasing, - backdrop_filter_quality); - } break; - case viz::DrawQuad::Material::kSolidColor: { - SkColor temp_color = - viz::SolidColorDrawQuad::MaterialCast(temp_quad)->color; - // Check the alpha channel of the color. We apply the mask by - // multiplying with the alpha channel, so if the alpha channel is - // transparent, we skip this quad. - if (SkColorGetA(temp_color) == SK_AlphaTRANSPARENT) - continue; - SkAlpha solid = SK_AlphaOPAQUE; - DCHECK_EQ(SkColorGetA(temp_color), solid); - - auto* quad = - render_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>(); - quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, id(), 0, - gfx::RectF(), gfx::Size(), - owning_layer_to_surface_contents_scale, FiltersOrigin(), - tex_coord_rect, - !layer_tree_impl_->settings().enable_edge_anti_aliasing, - backdrop_filter_quality); - } break; - case viz::DrawQuad::Material::kDebugBorder: - NOTIMPLEMENTED(); - break; - default: - NOTREACHED(); - break; - } - } -} - } // namespace cc diff --git a/chromium/cc/layers/render_surface_impl.h b/chromium/cc/layers/render_surface_impl.h index 89a270cf933..38fcaea82b9 100644 --- a/chromium/cc/layers/render_surface_impl.h +++ b/chromium/cc/layers/render_surface_impl.h @@ -30,6 +30,7 @@ class FilterOperations; class Occlusion; class LayerImpl; class LayerTreeImpl; +class PictureLayerImpl; class CC_EXPORT RenderSurfaceImpl { public: @@ -160,6 +161,7 @@ class CC_EXPORT RenderSurfaceImpl { const FilterOperations& Filters() const; const FilterOperations& BackdropFilters() const; base::Optional<gfx::RRectF> BackdropFilterBounds() const; + LayerImpl* BackdropMaskLayer() const; gfx::PointF FiltersOrigin() const; gfx::Transform SurfaceScale() const; @@ -180,6 +182,9 @@ class CC_EXPORT RenderSurfaceImpl { gfx::Rect GetDamageRect() const; std::unique_ptr<viz::RenderPass> CreateRenderPass(); + viz::ResourceId GetMaskResourceFromLayer(PictureLayerImpl* mask_layer, + gfx::Size* mask_texture_size, + gfx::RectF* mask_uv_rect) const; void AppendQuads(DrawMode draw_mode, viz::RenderPass* render_pass, AppendQuadsData* append_quads_data); diff --git a/chromium/cc/layers/render_surface_impl_unittest.cc b/chromium/cc/layers/render_surface_impl_unittest.cc index 89c6177b3b0..eed5071141d 100644 --- a/chromium/cc/layers/render_surface_impl_unittest.cc +++ b/chromium/cc/layers/render_surface_impl_unittest.cc @@ -76,7 +76,6 @@ static std::unique_ptr<viz::RenderPass> DoAppendQuadsWithScaledMask( FakeRasterSource::CreateFilledSolidColor(layer_size); LayerTreeSettings settings; - settings.layer_transforms_should_scale_layer_contents = true; LayerTestCommon::LayerImplTest impl(settings); std::unique_ptr<LayerImpl> root = LayerImpl::Create(impl.host_impl()->active_tree(), 2); @@ -147,7 +146,7 @@ TEST(RenderSurfaceLayerImplTest, ResourcelessAppendQuadsSkipMask) { TEST(RenderSurfaceLayerImplTest, AppendQuadsWithSolidColorMaskAndDeviceScaleFactor) { std::unique_ptr<viz::RenderPass> render_pass = DoAppendQuadsWithScaledMask( - DRAW_MODE_HARDWARE, 2.f, Layer::LayerMaskType::MULTI_TEXTURE_MASK); + DRAW_MODE_HARDWARE, 2.f, Layer::LayerMaskType::SINGLE_TEXTURE_MASK); DCHECK(render_pass->quad_list.front()); const viz::RenderPassDrawQuad* quad = viz::RenderPassDrawQuad::MaterialCast(render_pass->quad_list.front()); diff --git a/chromium/cc/layers/render_surface_unittest.cc b/chromium/cc/layers/render_surface_unittest.cc index 577594b0abe..5eac17c49ed 100644 --- a/chromium/cc/layers/render_surface_unittest.cc +++ b/chromium/cc/layers/render_surface_unittest.cc @@ -21,6 +21,8 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/transform.h" +#include "cc/test/fake_raster_source.h" + namespace cc { namespace { @@ -63,7 +65,7 @@ class FakePictureLayerImplForRenderSurfaceTest : public FakePictureLayerImpl { render_pass->CreateAndAppendSharedQuadState(); float max_contents_scale = 1.f; PopulateScaledSharedQuadState(shared_quad_state, max_contents_scale, - max_contents_scale, contents_opaque()); + contents_opaque()); bool needs_blending = false; for (const auto& rect : quad_rects_) { auto* quad = render_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>(); @@ -76,7 +78,7 @@ class FakePictureLayerImplForRenderSurfaceTest : public FakePictureLayerImpl { FakePictureLayerImplForRenderSurfaceTest(LayerTreeImpl* tree_impl, int id) : FakePictureLayerImpl(tree_impl, id, - Layer::LayerMaskType::MULTI_TEXTURE_MASK) {} + Layer::LayerMaskType::SINGLE_TEXTURE_MASK) {} std::vector<gfx::Rect> quad_rects_; }; @@ -239,75 +241,6 @@ TEST(RenderSurfaceTest, SanityCheckSurfaceCreatesCorrectRenderPass) { EXPECT_EQ(origin, pass->transform_to_root_target); } -TEST(RenderSurfaceTest, SanityCheckSurfaceDropsOccludedRenderPassDrawQuads) { - FakeImplTaskRunnerProvider task_runner_provider; - TestTaskGraphRunner task_graph_runner; - std::unique_ptr<LayerTreeFrameSink> layer_tree_frame_sink = - FakeLayerTreeFrameSink::Create3d(); - FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner); - // Set a big enough viewport to show the entire render pass. - host_impl.active_tree()->SetDeviceViewportSize(gfx::Size(1000, 1000)); - - std::unique_ptr<LayerImpl> root_layer = - LayerImpl::Create(host_impl.active_tree(), 1); - - int owning_layer_id = 2; - std::unique_ptr<LayerImpl> owning_layer = - LayerImpl::Create(host_impl.active_tree(), owning_layer_id); - - int mask_layer_id = 3; - std::unique_ptr<FakePictureLayerImplForRenderSurfaceTest> mask_layer = - FakePictureLayerImplForRenderSurfaceTest::CreateMask( - host_impl.active_tree(), mask_layer_id); - mask_layer->SetBounds(gfx::Size(200, 100)); - mask_layer->SetDrawsContent(true); - std::vector<gfx::Rect> quad_rects; - quad_rects.push_back(gfx::Rect(0, 0, 100, 100)); - quad_rects.push_back(gfx::Rect(100, 0, 100, 100)); - mask_layer->SetQuadRectsForTesting(quad_rects); - - owning_layer->SetBounds(gfx::Size(200, 100)); - owning_layer->SetDrawsContent(true); - owning_layer->test_properties()->SetMaskLayer(std::move(mask_layer)); - root_layer->test_properties()->AddChild(std::move(owning_layer)); - host_impl.active_tree()->SetRootLayerForTesting(std::move(root_layer)); - host_impl.SetVisible(true); - host_impl.InitializeFrameSink(layer_tree_frame_sink.get()); - host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting(); - host_impl.active_tree()->UpdateDrawProperties(); - - ASSERT_TRUE( - GetRenderSurface(host_impl.active_tree()->LayerById(owning_layer_id))); - RenderSurfaceImpl* render_surface = - GetRenderSurface(host_impl.active_tree()->LayerById(owning_layer_id)); - - gfx::Rect content_rect(0, 0, 200, 100); - gfx::Rect occluded(0, 0, 100, 100); - - render_surface->SetContentRectForTesting(content_rect); - render_surface->set_occlusion_in_content_space( - Occlusion(gfx::Transform(), SimpleEnclosedRegion(occluded), - SimpleEnclosedRegion(occluded))); - - std::unique_ptr<viz::RenderPass> render_pass = viz::RenderPass::Create(); - AppendQuadsData append_quads_data; - - render_surface->AppendQuads(DRAW_MODE_HARDWARE, render_pass.get(), - &append_quads_data); - - ASSERT_EQ(1u, render_pass->shared_quad_state_list.size()); - viz::SharedQuadState* shared_quad_state = - render_pass->shared_quad_state_list.front(); - - EXPECT_EQ(content_rect, - gfx::Rect(shared_quad_state->visible_quad_layer_rect)); - - // The quad (0, 0, 100, 100) is occluded and should be dropped. - ASSERT_EQ(1u, render_pass->quad_list.size()); - EXPECT_EQ(gfx::Rect(100, 0, 100, 100).ToString(), - render_pass->quad_list.front()->rect.ToString()); -} - TEST(RenderSurfaceTest, SanityCheckSurfaceIgnoreMaskLayerOcclusion) { FakeImplTaskRunnerProvider task_runner_provider; TestTaskGraphRunner task_graph_runner; @@ -328,7 +261,11 @@ TEST(RenderSurfaceTest, SanityCheckSurfaceIgnoreMaskLayerOcclusion) { std::unique_ptr<FakePictureLayerImplForRenderSurfaceTest> mask_layer = FakePictureLayerImplForRenderSurfaceTest::CreateMask( host_impl.active_tree(), mask_layer_id); + mask_layer->SetBounds(gfx::Size(200, 100)); + scoped_refptr<FakeRasterSource> raster_source( + FakeRasterSource::CreateFilled(mask_layer->bounds())); + mask_layer->SetRasterSourceOnActive(raster_source, Region()); mask_layer->SetDrawsContent(true); std::vector<gfx::Rect> quad_rects; quad_rects.push_back(gfx::Rect(0, 0, 100, 100)); @@ -345,10 +282,9 @@ TEST(RenderSurfaceTest, SanityCheckSurfaceIgnoreMaskLayerOcclusion) { host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting(); host_impl.active_tree()->UpdateDrawProperties(); - ASSERT_TRUE( - GetRenderSurface(host_impl.active_tree()->LayerById(owning_layer_id))); RenderSurfaceImpl* render_surface = GetRenderSurface(host_impl.active_tree()->LayerById(owning_layer_id)); + ASSERT_TRUE(render_surface); gfx::Rect content_rect(0, 0, 200, 100); gfx::Rect occluded(0, 0, 200, 100); @@ -373,14 +309,9 @@ TEST(RenderSurfaceTest, SanityCheckSurfaceIgnoreMaskLayerOcclusion) { EXPECT_EQ(content_rect, gfx::Rect(shared_quad_state->visible_quad_layer_rect)); - - // Neither of the two quads should be occluded since mask occlusion is - // ignored. - ASSERT_EQ(2u, render_pass->quad_list.size()); - EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(), + ASSERT_EQ(1u, render_pass->quad_list.size()); + EXPECT_EQ(gfx::Rect(0, 0, 200, 100).ToString(), render_pass->quad_list.front()->rect.ToString()); - EXPECT_EQ(gfx::Rect(100, 0, 100, 100).ToString(), - render_pass->quad_list.back()->rect.ToString()); } } // namespace diff --git a/chromium/cc/layers/scrollbar_layer_unittest.cc b/chromium/cc/layers/scrollbar_layer_unittest.cc index 3320d74868f..6a2dd986d9d 100644 --- a/chromium/cc/layers/scrollbar_layer_unittest.cc +++ b/chromium/cc/layers/scrollbar_layer_unittest.cc @@ -31,6 +31,7 @@ #include "cc/test/test_task_graph_runner.h" #include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_host.h" +#include "cc/trees/layer_tree_host_common.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/occlusion_tracker.h" #include "cc/trees/scroll_node.h" @@ -1076,6 +1077,10 @@ class ScrollbarLayerTestResourceCreationAndRelease : public ScrollbarLayerTest { layer_tree_host_->SetRootLayer(layer_tree_root); + LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs( + layer_tree_root.get(), layer_tree_host_->device_viewport_size()); + LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); + scrollbar_layer->SetIsDrawable(true); scrollbar_layer->SetBounds(gfx::Size(100, 100)); layer_tree_root->SetScrollable(gfx::Size(100, 200)); @@ -1134,6 +1139,10 @@ TEST_F(ScrollbarLayerTestResourceCreationAndRelease, TestResourceUpdate) { layer_tree_host_->SetRootLayer(layer_tree_root); + LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs( + layer_tree_root.get(), layer_tree_host_->device_viewport_size()); + LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); + scrollbar_layer->SetIsDrawable(true); scrollbar_layer->SetBounds(gfx::Size(100, 15)); scrollbar_layer->SetPosition(gfx::PointF(scrollbar_location)); @@ -1303,6 +1312,11 @@ class ScaledScrollbarLayerTestResourceCreation : public ScrollbarLayerTest { EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get()); + LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs( + layer_tree_root.get(), layer_tree_host_->device_viewport_size()); + inputs.device_scale_factor = test_scale; + LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); + layer_tree_host_->SetViewportSizeAndScale( layer_tree_host_->device_viewport_size(), test_scale, layer_tree_host_->local_surface_id_allocation_from_parent()); @@ -1368,6 +1382,10 @@ class ScaledScrollbarLayerTestScaledRasterization : public ScrollbarLayerTest { scrollbar_layer->fake_scrollbar()->set_location(scrollbar_rect.origin()); scrollbar_layer->fake_scrollbar()->set_track_rect(scrollbar_rect); + LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs( + layer_tree_root.get(), layer_tree_host_->device_viewport_size()); + inputs.device_scale_factor = test_scale; + LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); layer_tree_host_->SetViewportSizeAndScale( layer_tree_host_->device_viewport_size(), test_scale, layer_tree_host_->local_surface_id_allocation_from_parent()); diff --git a/chromium/cc/layers/surface_layer_impl.cc b/chromium/cc/layers/surface_layer_impl.cc index 9d4a37a49d5..6ef4249c51e 100644 --- a/chromium/cc/layers/surface_layer_impl.cc +++ b/chromium/cc/layers/surface_layer_impl.cc @@ -143,7 +143,7 @@ void SurfaceLayerImpl::AppendQuads(viz::RenderPass* render_pass, render_pass->CreateAndAppendSharedQuadState(); PopulateScaledSharedQuadState(shared_quad_state, device_scale_factor, - device_scale_factor, contents_opaque()); + contents_opaque()); if (surface_range_.IsValid()) { auto* quad = render_pass->CreateAndAppendDrawQuad<viz::SurfaceDrawQuad>(); diff --git a/chromium/cc/layers/texture_layer.cc b/chromium/cc/layers/texture_layer.cc index e97162e7b1e..4e1c25208f4 100644 --- a/chromium/cc/layers/texture_layer.cc +++ b/chromium/cc/layers/texture_layer.cc @@ -23,8 +23,7 @@ scoped_refptr<TextureLayer> TextureLayer::CreateForMailbox( return scoped_refptr<TextureLayer>(new TextureLayer(client)); } -TextureLayer::TextureLayer(TextureLayerClient* client) - : client_(client), weak_ptr_factory_(this) {} +TextureLayer::TextureLayer(TextureLayerClient* client) : client_(client) {} TextureLayer::~TextureLayer() = default; diff --git a/chromium/cc/layers/texture_layer.h b/chromium/cc/layers/texture_layer.h index dc9bd63f500..9166bd26472 100644 --- a/chromium/cc/layers/texture_layer.h +++ b/chromium/cc/layers/texture_layer.h @@ -175,6 +175,11 @@ class CC_EXPORT TextureLayer : public Layer, SharedBitmapIdRegistrar { const viz::SharedBitmapId& id, scoped_refptr<CrossThreadSharedBitmap> bitmap) override; + viz::TransferableResource current_transferable_resource() const { + return holder_ref_ ? holder_ref_->holder()->resource() + : viz::TransferableResource(); + } + protected: explicit TextureLayer(TextureLayerClient* client); ~TextureLayer() override; @@ -222,7 +227,7 @@ class CC_EXPORT TextureLayer : public Layer, SharedBitmapIdRegistrar { // TextureLayerImpl. std::vector<viz::SharedBitmapId> to_unregister_bitmap_ids_; - base::WeakPtrFactory<TextureLayer> weak_ptr_factory_; + base::WeakPtrFactory<TextureLayer> weak_ptr_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/layers/texture_layer_impl.cc b/chromium/cc/layers/texture_layer_impl.cc index 6944908fbb2..3f82dafccf0 100644 --- a/chromium/cc/layers/texture_layer_impl.cc +++ b/chromium/cc/layers/texture_layer_impl.cc @@ -110,8 +110,7 @@ void TextureLayerImpl::AppendQuads(viz::RenderPass* render_pass, LayerTreeFrameSink* sink = layer_tree_impl()->layer_tree_frame_sink(); for (const auto& pair : to_register_bitmaps_) { - sink->DidAllocateSharedBitmap(viz::bitmap_allocation::ToMojoHandle( - pair.second->shared_region().Duplicate()), + sink->DidAllocateSharedBitmap(pair.second->shared_region().Duplicate(), pair.first); } // All |to_register_bitmaps_| have been registered above, so we can move them @@ -150,7 +149,7 @@ void TextureLayerImpl::AppendQuads(viz::RenderPass* render_pass, resource_id_, premultiplied_alpha_, uv_top_left_, uv_bottom_right_, bg_color, vertex_opacity_, flipped_, nearest_neighbor_, /*secure_output_only=*/false, - ui::ProtectedVideoType::kClear); + gfx::ProtectedVideoType::kClear); quad->set_resource_size_in_pixels(transferable_resource_.size); ValidateQuadResources(quad); } diff --git a/chromium/cc/layers/texture_layer_unittest.cc b/chromium/cc/layers/texture_layer_unittest.cc index 7aec79fdf9a..b581c5a54ba 100644 --- a/chromium/cc/layers/texture_layer_unittest.cc +++ b/chromium/cc/layers/texture_layer_unittest.cc @@ -32,6 +32,7 @@ #include "cc/test/layer_test_common.h" #include "cc/test/layer_tree_test.h" #include "cc/test/stub_layer_tree_host_single_thread_client.h" +#include "cc/test/test_layer_tree_frame_sink.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_impl.h" @@ -45,7 +46,6 @@ #include "components/viz/common/resources/transferable_resource.h" #include "components/viz/service/display/software_output_device.h" #include "components/viz/test/fake_output_surface.h" -#include "components/viz/test/test_layer_tree_frame_sink.h" #include "gpu/GLES2/gl2extchromium.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -581,7 +581,7 @@ TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_MainReleasedFirst) { class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { public: - std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( const viz::RendererSettings& renderer_settings, double refresh_rate, scoped_refptr<viz::ContextProvider> compositor_context_provider, @@ -591,7 +591,7 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { bool synchronous_composite = !HasImplThread() && !layer_tree_host()->GetSettings().single_thread_proxy_scheduler; - return std::make_unique<viz::TestLayerTreeFrameSink>( + return std::make_unique<TestLayerTreeFrameSink>( compositor_context_provider, std::move(worker_context_provider), gpu_memory_buffer_manager(), renderer_settings, ImplThreadTaskRunner(), synchronous_composite, disable_display_vsync, refresh_rate); @@ -1489,7 +1489,7 @@ class SoftwareTextureLayerTest : public LayerTreeTest { LayerTreeTest::SetupTree(); } - std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( const viz::RendererSettings& renderer_settings, double refresh_rate, scoped_refptr<viz::ContextProvider> compositor_context_provider, @@ -1499,7 +1499,7 @@ class SoftwareTextureLayerTest : public LayerTreeTest { bool synchronous_composite = !HasImplThread() && !layer_tree_host()->GetSettings().single_thread_proxy_scheduler; - auto sink = std::make_unique<viz::TestLayerTreeFrameSink>( + auto sink = std::make_unique<TestLayerTreeFrameSink>( nullptr, nullptr, gpu_memory_buffer_manager(), renderer_settings, ImplThreadTaskRunner(), synchronous_composite, disable_display_vsync, refresh_rate); @@ -1519,7 +1519,7 @@ class SoftwareTextureLayerTest : public LayerTreeTest { scoped_refptr<Layer> root_; scoped_refptr<SolidColorLayer> solid_color_layer_; scoped_refptr<TextureLayer> texture_layer_; - viz::TestLayerTreeFrameSink* frame_sink_ = nullptr; + TestLayerTreeFrameSink* frame_sink_ = nullptr; int num_frame_sinks_created_ = 0; }; diff --git a/chromium/cc/layers/tile_size_calculator.cc b/chromium/cc/layers/tile_size_calculator.cc new file mode 100644 index 00000000000..6c071ca3708 --- /dev/null +++ b/chromium/cc/layers/tile_size_calculator.cc @@ -0,0 +1,221 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/layers/tile_size_calculator.h" + +#include "cc/base/math_util.h" +#include "cc/layers/picture_layer_impl.h" +#include "cc/trees/layer_tree_impl.h" + +namespace cc { +namespace { +// Even for really wide viewports, at some point GPU raster should use +// less than 4 tiles to fill the viewport. This is set to 256 as a +// sane minimum for now, but we might want to tune this for low-end. +const int kMinHeightForGpuRasteredTile = 256; + +// When making odd-sized tiles, round them up to increase the chances +// of using the same tile size. +const int kTileRoundUp = 64; + +// Round GPU default tile sizes to a multiple of 32. This helps prevent +// rounding errors during compositing. +const int kGpuDefaultTileRoundUp = 32; + +// For performance reasons and to support compressed tile textures, tile +// width and height should be an even multiple of 4 in size. +const int kTileMinimalAlignment = 4; + +// This function converts the given |device_pixels_size| to the expected size +// of content which was generated to fill it at 100%. This takes into account +// the ceil operations that occur as device pixels are converted to/from DIPs +// (content size must be a whole number of DIPs). +gfx::Size ApplyDsfAdjustment(const gfx::Size& device_pixels_size, float dsf) { + gfx::Size content_size_in_dips = + gfx::ScaleToCeiledSize(device_pixels_size, 1.0f / dsf); + gfx::Size content_size_in_dps = + gfx::ScaleToCeiledSize(content_size_in_dips, dsf); + return content_size_in_dps; +} + +// For GPU rasterization, we pick an ideal tile size using the viewport so we +// don't need any settings. The current approach uses 4 tiles to cover the +// viewport vertically. +gfx::Size CalculateGpuTileSize(const gfx::Size& base_tile_size, + const gfx::Size& content_bounds, + const gfx::Size& max_tile_size) { + int tile_width = base_tile_size.width(); + + // Increase the height proportionally as the width decreases, and pad by our + // border texels to make the tiles exactly match the viewport. + int divisor = 4; + if (content_bounds.width() <= base_tile_size.width() / 2) + divisor = 2; + if (content_bounds.width() <= base_tile_size.width() / 4) + divisor = 1; + int tile_height = + MathUtil::UncheckedRoundUp(base_tile_size.height(), divisor) / divisor; + + // Grow default sizes to account for overlapping border texels. + tile_width += 2 * PictureLayerTiling::kBorderTexels; + tile_height += 2 * PictureLayerTiling::kBorderTexels; + + // Round GPU default tile sizes to a multiple of kGpuDefaultTileAlignment. + // This helps prevent rounding errors in our CA path. https://crbug.com/632274 + tile_width = MathUtil::UncheckedRoundUp(tile_width, kGpuDefaultTileRoundUp); + tile_height = MathUtil::UncheckedRoundUp(tile_height, kGpuDefaultTileRoundUp); + + tile_height = std::max(tile_height, kMinHeightForGpuRasteredTile); + + if (!max_tile_size.IsEmpty()) { + tile_width = std::min(tile_width, max_tile_size.width()); + tile_height = std::min(tile_height, max_tile_size.height()); + } + + return gfx::Size(tile_width, tile_height); +} + +} // namespace + +// AffectingParams. +bool TileSizeCalculator::AffectingParams::operator==( + const AffectingParams& other) { + return max_texture_size == other.max_texture_size && + use_gpu_rasterization == other.use_gpu_rasterization && + device_scale_factor == other.device_scale_factor && + max_tile_size == other.max_tile_size && + gpu_raster_max_texture_size == other.gpu_raster_max_texture_size && + max_untiled_layer_size == other.max_untiled_layer_size && + default_tile_size == other.default_tile_size && + layer_content_bounds == other.layer_content_bounds; +} + +// TileSizeCalculator. +TileSizeCalculator::TileSizeCalculator(PictureLayerImpl* layer_impl) + : layer_impl_(layer_impl) {} + +bool TileSizeCalculator::IsAffectingParamsChanged() { + AffectingParams new_params = GetAffectingParams(); + + if (affecting_params_ == new_params) + return false; + + affecting_params_ = new_params; + return true; +} + +TileSizeCalculator::AffectingParams TileSizeCalculator::GetAffectingParams() { + AffectingParams params; + LayerTreeImpl* layer_tree_impl = layer_impl()->layer_tree_impl(); + params.max_texture_size = layer_tree_impl->max_texture_size(); + params.use_gpu_rasterization = layer_tree_impl->use_gpu_rasterization(); + params.max_tile_size = layer_tree_impl->settings().max_gpu_raster_tile_size; + params.gpu_raster_max_texture_size = + layer_impl()->gpu_raster_max_texture_size(); + params.device_scale_factor = layer_tree_impl->device_scale_factor(); + params.max_untiled_layer_size = + layer_tree_impl->settings().max_untiled_layer_size; + params.default_tile_size = layer_tree_impl->settings().default_tile_size; + params.layer_content_bounds = layer_impl()->content_bounds(); + return params; +} + +gfx::Size TileSizeCalculator::CalculateTileSize() { + gfx::Size content_bounds = layer_impl()->content_bounds(); + + if (layer_impl()->mask_type() == Layer::LayerMaskType::SINGLE_TEXTURE_MASK) { + // Masks are not tiled, so if we can't cover the whole mask with one tile, + // we shouldn't have such a tiling at all. + DCHECK_LE(content_bounds.width(), + layer_impl()->layer_tree_impl()->max_texture_size()); + DCHECK_LE(content_bounds.height(), + layer_impl()->layer_tree_impl()->max_texture_size()); + return content_bounds; + } + + // If |affecting_params_| is already computed and not changed, return + // pre-calculated tile size. + if (!IsAffectingParamsChanged()) + return tile_size_; + + int default_tile_width = 0; + int default_tile_height = 0; + if (affecting_params_.use_gpu_rasterization) { + gfx::Size max_tile_size = affecting_params_.max_tile_size; + + // Calculate |base_tile_size| based on |gpu_raster_max_texture_size|, + // adjusting for ceil operations that may occur due to DSF. + gfx::Size base_tile_size = + ApplyDsfAdjustment(affecting_params_.gpu_raster_max_texture_size, + affecting_params_.device_scale_factor); + + // Set our initial size assuming a |base_tile_size| equal to our + // |viewport_size|. + gfx::Size default_tile_size = + CalculateGpuTileSize(base_tile_size, content_bounds, max_tile_size); + + // Use half-width GPU tiles when the content_width is greater than our + // calculated tile size. + if (content_bounds.width() > default_tile_size.width()) { + // Divide width by 2 and round up. + base_tile_size.set_width((base_tile_size.width() + 1) / 2); + default_tile_size = + CalculateGpuTileSize(base_tile_size, content_bounds, max_tile_size); + } + + default_tile_width = default_tile_size.width(); + default_tile_height = default_tile_size.height(); + } else { + // For CPU rasterization we use tile-size settings. + int max_untiled_content_width = + affecting_params_.max_untiled_layer_size.width(); + int max_untiled_content_height = + affecting_params_.max_untiled_layer_size.height(); + default_tile_width = affecting_params_.default_tile_size.width(); + default_tile_height = affecting_params_.default_tile_size.height(); + + // If the content width is small, increase tile size vertically. + // If the content height is small, increase tile size horizontally. + // If both are less than the untiled-size, use a single tile. + if (content_bounds.width() < default_tile_width) + default_tile_height = max_untiled_content_height; + if (content_bounds.height() < default_tile_height) + default_tile_width = max_untiled_content_width; + if (content_bounds.width() < max_untiled_content_width && + content_bounds.height() < max_untiled_content_height) { + default_tile_height = max_untiled_content_height; + default_tile_width = max_untiled_content_width; + } + } + + int tile_width = default_tile_width; + int tile_height = default_tile_height; + + // Clamp the tile width/height to the content width/height to save space. + if (content_bounds.width() < default_tile_width) { + tile_width = std::min(tile_width, content_bounds.width()); + tile_width = MathUtil::UncheckedRoundUp(tile_width, kTileRoundUp); + tile_width = std::min(tile_width, default_tile_width); + } + if (content_bounds.height() < default_tile_height) { + tile_height = std::min(tile_height, content_bounds.height()); + tile_height = MathUtil::UncheckedRoundUp(tile_height, kTileRoundUp); + tile_height = std::min(tile_height, default_tile_height); + } + + // Ensure that tile width and height are properly aligned. + tile_width = MathUtil::UncheckedRoundUp(tile_width, kTileMinimalAlignment); + tile_height = MathUtil::UncheckedRoundUp(tile_height, kTileMinimalAlignment); + + // Under no circumstance should we be larger than the max texture size. + tile_width = std::min(tile_width, affecting_params_.max_texture_size); + tile_height = std::min(tile_height, affecting_params_.max_texture_size); + + // Store the calculated tile size. + tile_size_ = gfx::Size(tile_width, tile_height); + + return tile_size_; +} + +} // namespace cc diff --git a/chromium/cc/layers/tile_size_calculator.h b/chromium/cc/layers/tile_size_calculator.h new file mode 100644 index 00000000000..cd455b922d2 --- /dev/null +++ b/chromium/cc/layers/tile_size_calculator.h @@ -0,0 +1,50 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_LAYERS_TILE_SIZE_CALCULATOR_H_ +#define CC_LAYERS_TILE_SIZE_CALCULATOR_H_ + +#include "cc/cc_export.h" +#include "ui/gfx/geometry/size.h" + +namespace cc { + +class PictureLayerImpl; + +// This class calculates the tile size only when the |affecting_params_| +// or |content_bounds_| is changed. +class CC_EXPORT TileSizeCalculator { + public: + explicit TileSizeCalculator(PictureLayerImpl* layer_impl); + + gfx::Size CalculateTileSize(); + + private: + struct AffectingParams { + int max_texture_size = 0; + bool use_gpu_rasterization = false; + float device_scale_factor = 0.0f; + gfx::Size max_tile_size; + gfx::Size gpu_raster_max_texture_size; + gfx::Size max_untiled_layer_size; + gfx::Size default_tile_size; + gfx::Size layer_content_bounds; + + bool operator==(const AffectingParams& other); + }; + + PictureLayerImpl* layer_impl() const { return layer_impl_; } + AffectingParams GetAffectingParams(); + bool IsAffectingParamsChanged(); + + PictureLayerImpl* layer_impl_; + + AffectingParams affecting_params_; + + gfx::Size tile_size_; +}; + +} // namespace cc + +#endif // CC_LAYERS_TILE_SIZE_CALCULATOR_H_ diff --git a/chromium/cc/layers/touch_action_region.cc b/chromium/cc/layers/touch_action_region.cc index 75da3cac02c..a84bd0e9a75 100644 --- a/chromium/cc/layers/touch_action_region.cc +++ b/chromium/cc/layers/touch_action_region.cc @@ -8,26 +8,22 @@ namespace cc { -TouchActionRegion::TouchActionRegion() : region_(std::make_unique<Region>()) {} +TouchActionRegion::TouchActionRegion() {} TouchActionRegion::TouchActionRegion( - const TouchActionRegion& touch_action_region) - : map_(touch_action_region.map_), - region_(std::make_unique<Region>(touch_action_region.region())) {} + const TouchActionRegion& touch_action_region) = default; TouchActionRegion::TouchActionRegion(TouchActionRegion&& touch_action_region) = default; -TouchActionRegion::TouchActionRegion( - const base::flat_map<TouchAction, Region>& region_map) - : map_(region_map), region_(std::make_unique<Region>()) { - for (const auto& pair : region_map) { - region_->Union(pair.second); - } -} - TouchActionRegion::~TouchActionRegion() = default; +Region TouchActionRegion::GetAllRegions() const { + Region all_regions; + for (const auto& pair : map_) + all_regions.Union(pair.second); + return all_regions; +} + void TouchActionRegion::Union(TouchAction touch_action, const gfx::Rect& rect) { - region_->Union(rect); map_[touch_action].Union(rect); } @@ -53,7 +49,6 @@ TouchAction TouchActionRegion::GetAllowedTouchAction( TouchActionRegion& TouchActionRegion::operator=( const TouchActionRegion& other) { - *region_ = *other.region_; map_ = other.map_; return *this; } diff --git a/chromium/cc/layers/touch_action_region.h b/chromium/cc/layers/touch_action_region.h index 4322646a36d..6a8911a5f72 100644 --- a/chromium/cc/layers/touch_action_region.h +++ b/chromium/cc/layers/touch_action_region.h @@ -19,13 +19,15 @@ class CC_EXPORT TouchActionRegion { TouchActionRegion(); TouchActionRegion(const TouchActionRegion& touch_action_region); TouchActionRegion(TouchActionRegion&& touch_action_region); - explicit TouchActionRegion(const base::flat_map<TouchAction, Region>&); ~TouchActionRegion(); void Union(TouchAction, const gfx::Rect&); - const Region& region() const { return *region_; } + // Return all regions with any touch action. + Region GetAllRegions() const; const Region& GetRegionForTouchAction(TouchAction) const; + bool IsEmpty() const { return map_.empty(); } + // Returns the touch actions that we are sure will be allowed at the point // by finding the intersection of all touch actions whose regions contain the // given point. If the map is empty, |kTouchActionAuto| is returned since no @@ -37,10 +39,7 @@ class CC_EXPORT TouchActionRegion { bool operator==(const TouchActionRegion& other) const; private: - // TODO(hayleyferr): Region could be owned directly instead of in a - // unique_ptr if Region supported efficient moving. base::flat_map<TouchAction, Region> map_; - std::unique_ptr<Region> region_; }; } // namespace cc diff --git a/chromium/cc/layers/touch_action_region_unittest.cc b/chromium/cc/layers/touch_action_region_unittest.cc index 1157982e6b6..e41631901e3 100644 --- a/chromium/cc/layers/touch_action_region_unittest.cc +++ b/chromium/cc/layers/touch_action_region_unittest.cc @@ -63,25 +63,5 @@ TEST(TouchActionRegionTest, GetAllowedTouchActionSingleMapEntry) { touch_action_region.GetAllowedTouchAction(gfx::Point(60, 60))); } -TEST(TouchActionRegionTest, ConstructorFromMap) { - gfx::Rect rect1(0, 0, 50, 50); - gfx::Rect rect2(50, 0, 50, 50); - base::flat_map<TouchAction, Region> region_map; - region_map[kTouchActionPanX].Union(rect1); - region_map[kTouchActionPanUp].Union(rect2); - TouchActionRegion touch_action_region(region_map); - - Region expected_region1(rect1); - Region expected_region2(rect2); - Region expected_region3(rect1); - expected_region3.Union(rect2); - - EXPECT_EQ(touch_action_region.GetRegionForTouchAction(kTouchActionPanX), - expected_region1); - EXPECT_EQ(touch_action_region.GetRegionForTouchAction(kTouchActionPanUp), - expected_region2); - EXPECT_EQ(touch_action_region.region(), expected_region3); -} - } // namespace } // namespace cc diff --git a/chromium/cc/layers/ui_resource_layer_impl.cc b/chromium/cc/layers/ui_resource_layer_impl.cc index 7eae103e9aa..cb3dea6c5bd 100644 --- a/chromium/cc/layers/ui_resource_layer_impl.cc +++ b/chromium/cc/layers/ui_resource_layer_impl.cc @@ -130,7 +130,7 @@ void UIResourceLayerImpl::AppendQuads(viz::RenderPass* render_pass, quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, needs_blending, resource, premultiplied_alpha, uv_top_left_, uv_bottom_right_, SK_ColorTRANSPARENT, vertex_opacity_, flipped, nearest_neighbor, - /*secure_output_only=*/false, ui::ProtectedVideoType::kClear); + /*secure_output_only=*/false, gfx::ProtectedVideoType::kClear); ValidateQuadResources(quad); } 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 4459afbbc22..02c682614f1 100644 --- a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc +++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.cc @@ -96,8 +96,7 @@ AsyncLayerTreeFrameSink::AsyncLayerTreeFrameSink( params->client_name)), submit_begin_frame_histogram_(GetHistogramNamed( "GraphicsPipeline.%s.SubmitCompositorFrameAfterBeginFrame", - params->client_name)), - weak_factory_(this) { + params->client_name)) { // We should not create hit test data provider if we want to use cc layer tree // to generated data. if (features::IsVizHitTestingSurfaceLayerEnabled()) @@ -174,8 +173,6 @@ void AsyncLayerTreeFrameSink::SubmitCompositorFrame( DCHECK(frame.metadata.begin_frame_ack.has_damage); DCHECK_LE(viz::BeginFrameArgs::kStartingFrameNumber, frame.metadata.begin_frame_ack.sequence_number); - TRACE_EVENT1("cc,benchmark", "AsyncLayerTreeFrameSink::SubmitCompositorFrame", - "source_frame_number_", source_frame_number_); // It's possible to request an immediate composite from cc which will bypass // BeginFrame. In that case, we cannot collect full graphics pipeline data. @@ -203,12 +200,6 @@ void AsyncLayerTreeFrameSink::SubmitCompositorFrame( } } - TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - "SubmitCompositorFrame", local_surface_id_.hash()); - bool tracing_enabled; - TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"), - &tracing_enabled); - base::Optional<viz::HitTestRegionList> hit_test_region_list; if (hit_test_data_provider_) hit_test_region_list = hit_test_data_provider_->GetHitTestData(frame); @@ -272,9 +263,7 @@ void AsyncLayerTreeFrameSink::SubmitCompositorFrame( "SubmitHitTestData"); compositor_frame_sink_ptr_->SubmitCompositorFrame( - local_surface_id_, std::move(frame), std::move(hit_test_region_list), - tracing_enabled ? base::TimeTicks::Now().since_origin().InMicroseconds() - : 0); + local_surface_id_, std::move(frame), std::move(hit_test_region_list), 0); } void AsyncLayerTreeFrameSink::DidNotProduceFrame( @@ -293,10 +282,10 @@ void AsyncLayerTreeFrameSink::DidNotProduceFrame( } void AsyncLayerTreeFrameSink::DidAllocateSharedBitmap( - mojo::ScopedSharedBufferHandle buffer, + base::ReadOnlySharedMemoryRegion region, const viz::SharedBitmapId& id) { DCHECK(compositor_frame_sink_ptr_); - compositor_frame_sink_ptr_->DidAllocateSharedBitmap(std::move(buffer), id); + compositor_frame_sink_ptr_->DidAllocateSharedBitmap(std::move(region), id); } void AsyncLayerTreeFrameSink::DidDeleteSharedBitmap( @@ -319,9 +308,10 @@ void AsyncLayerTreeFrameSink::DidReceiveCompositorFrameAck( void AsyncLayerTreeFrameSink::OnBeginFrame( const viz::BeginFrameArgs& args, - const viz::PresentationFeedbackMap& feedbacks) { - for (const auto& pair : feedbacks) { - client_->DidPresentCompositorFrame(pair.first, pair.second); + const viz::FrameTimingDetailsMap& timing_details) { + for (const auto& pair : timing_details) { + client_->DidPresentCompositorFrame(pair.first, + pair.second.presentation_feedback); } DCHECK_LE(pipeline_reporting_frame_times_.size(), 25u); 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 5ed49023a30..1b176d0d9da 100644 --- a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h +++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink.h @@ -9,13 +9,14 @@ #include <string> #include <vector> +#include "base/memory/read_only_shared_memory_region.h" #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" #include "cc/mojo_embedder/mojo_embedder_export.h" #include "cc/trees/layer_tree_frame_sink.h" #include "components/viz/common/frame_sinks/begin_frame_source.h" +#include "components/viz/common/frame_timing_details_map.h" #include "components/viz/common/gpu/context_provider.h" -#include "components/viz/common/presentation_feedback_map.h" #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h" #include "components/viz/common/surfaces/surface_id.h" #include "mojo/public/cpp/bindings/binding.h" @@ -123,7 +124,7 @@ class CC_MOJO_EMBEDDER_EXPORT AsyncLayerTreeFrameSink bool hit_test_data_changed, bool show_hit_test_borders) override; void DidNotProduceFrame(const viz::BeginFrameAck& ack) override; - void DidAllocateSharedBitmap(mojo::ScopedSharedBufferHandle buffer, + void DidAllocateSharedBitmap(base::ReadOnlySharedMemoryRegion region, const viz::SharedBitmapId& id) override; void DidDeleteSharedBitmap(const viz::SharedBitmapId& id) override; void ForceAllocateNewId() override; @@ -137,7 +138,7 @@ class CC_MOJO_EMBEDDER_EXPORT AsyncLayerTreeFrameSink void DidReceiveCompositorFrameAck( const std::vector<viz::ReturnedResource>& resources) override; void OnBeginFrame(const viz::BeginFrameArgs& begin_frame_args, - const viz::PresentationFeedbackMap& feedbacks) override; + const viz::FrameTimingDetailsMap& timing_details) override; void OnBeginFramePausedChanged(bool paused) override; void ReclaimResources( const std::vector<viz::ReturnedResource>& resources) override; @@ -187,7 +188,7 @@ class CC_MOJO_EMBEDDER_EXPORT AsyncLayerTreeFrameSink // Histogram metrics used to record // GraphicsPipeline.ClientName.SubmitCompositorFrameAfterBeginFrame base::HistogramBase* const submit_begin_frame_histogram_; - base::WeakPtrFactory<AsyncLayerTreeFrameSink> weak_factory_; + base::WeakPtrFactory<AsyncLayerTreeFrameSink> weak_factory_{this}; }; } // namespace mojo_embedder diff --git a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc index 0a6f1d6ce29..8205b650973 100644 --- a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc +++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc @@ -292,15 +292,15 @@ TEST_F(AsyncLayerTreeFrameSinkSimpleTest, HitTestRegionListDuplicate) { quad3_root_1->SetNew(shared_quad_state3_root, /*rect=*/rect3_root, /*visible_rect=*/rect3_root, /*render_pass_id=*/3, /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(), - gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, - 1.0f); + /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1), + gfx::PointF(), gfx::RectF(), false, 1.0f); auto* quad3_root_2 = pass3_root->quad_list.AllocateAndConstruct<viz::RenderPassDrawQuad>(); quad3_root_2->SetNew(shared_quad_state3_root, /*rect=*/rect3_root, /*visible_rect=*/rect3_root, /*render_pass_id=*/4, /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(), - gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, - 1.0f); + /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1), + gfx::PointF(), gfx::RectF(), false, 1.0f); pass_list.push_back(std::move(pass3_root)); SendRenderPassList(&pass_list, /*hit_test_data_changed=*/false); @@ -396,15 +396,15 @@ TEST_F(AsyncLayerTreeFrameSinkSimpleTest, quad2_root_1->SetNew(shared_quad_state2_root, /*rect=*/rect2_root, /*visible_rect=*/rect2_root, /*render_pass_id=*/2, /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(), - gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, - 1.0f); + /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1), + gfx::PointF(), gfx::RectF(), false, 1.0f); auto* quad2_root_2 = pass2_root->quad_list.AllocateAndConstruct<viz::RenderPassDrawQuad>(); quad2_root_2->SetNew(shared_quad_state2_root, /*rect=*/rect2_root, /*visible_rect=*/rect2_root, /*render_pass_id=*/3, /*mask_resource_id=*/0, gfx::RectF(), gfx::Size(), - gfx::Vector2dF(1, 1), gfx::PointF(), gfx::RectF(), false, - 1.0f); + /*mask_applies_to_backdrop=*/false, gfx::Vector2dF(1, 1), + gfx::PointF(), gfx::RectF(), false, 1.0f); pass_list.push_back(std::move(pass2_root)); SendRenderPassList(&pass_list, /*hit_test_data_changed=*/true); diff --git a/chromium/cc/paint/BUILD.gn b/chromium/cc/paint/BUILD.gn index 6c428fbabfa..705951c1e6b 100644 --- a/chromium/cc/paint/BUILD.gn +++ b/chromium/cc/paint/BUILD.gn @@ -18,6 +18,8 @@ cc_component("paint") { "display_item_list.h", "draw_image.cc", "draw_image.h", + "element_id.cc", + "element_id.h", "filter_operation.cc", "filter_operation.h", "filter_operations.cc", @@ -29,8 +31,7 @@ cc_component("paint") { "image_provider.h", "image_transfer_cache_entry.cc", "image_transfer_cache_entry.h", - "node_holder.cc", - "node_holder.h", + "node_id.h", "paint_cache.cc", "paint_cache.h", "paint_canvas.h", @@ -60,6 +61,8 @@ cc_component("paint") { "paint_shader.cc", "paint_shader.h", "paint_worklet_input.h", + "paint_worklet_job.cc", + "paint_worklet_job.h", "paint_worklet_layer_painter.h", "raw_memory_transfer_cache_entry.cc", "raw_memory_transfer_cache_entry.h", @@ -79,7 +82,6 @@ cc_component("paint") { "skottie_wrapper.h", "solid_color_analyzer.cc", "solid_color_analyzer.h", - "text_holder.h", "transfer_cache_deserialize_helper.h", "transfer_cache_entry.cc", "transfer_cache_entry.h", diff --git a/chromium/cc/paint/DEPS b/chromium/cc/paint/DEPS index 6d60ad8e8d6..844d096658c 100644 --- a/chromium/cc/paint/DEPS +++ b/chromium/cc/paint/DEPS @@ -1,4 +1,16 @@ +# cc/paint can only depends on some specific folders, so explicitly +# write include rules here to be clear. +include_rules = [ + "-cc", + "+cc/base", + "+cc/debug", + "+cc/paint", +] + specific_include_rules = { + ".*(test|_fuzzer)\.cc": [ + "+cc", + ], "paint_op_buffer_fuzzer.cc": [ "+components/viz/test", "+gpu/command_buffer", diff --git a/chromium/cc/paint/discardable_image_map.cc b/chromium/cc/paint/discardable_image_map.cc index 23ccb20940a..fcf04a19e44 100644 --- a/chromium/cc/paint/discardable_image_map.cc +++ b/chromium/cc/paint/discardable_image_map.cc @@ -103,6 +103,9 @@ class DiscardableImageGenerator { TakeAnimatedImagesMetadata() { return std::move(animated_images_metadata_); } + std::vector<scoped_refptr<PaintWorkletInput>> TakePaintWorkletInputs() { + return std::move(paint_worklet_inputs_); + } void RecordColorHistograms() const { if (color_stats_total_image_count_ > 0) { @@ -354,9 +357,12 @@ class DiscardableImageGenerator { SkIRect src_irect; src_rect.roundOut(&src_irect); - if (!paint_image.IsPaintWorklet()) { + if (paint_image.IsPaintWorklet()) { + paint_worklet_inputs_.push_back(paint_image.paint_worklet_input()); + } else { // Make a note if any image was originally specified in a non-sRGB color - // space. + // space. PaintWorklets do not have the concept of a color space, so + // should not be used to accumulate either counter. SkColorSpace* source_color_space = paint_image.color_space(); color_stats_total_pixel_count_ += image_rect.size().GetCheckedArea(); color_stats_total_image_count_++; @@ -389,12 +395,20 @@ class DiscardableImageGenerator { paint_image.reset_animation_sequence_id()); } - // If we are iterating images in a record shader, only track them if they - // are animated. We defer decoding of images in record shaders to skia, but - // we still need to track animated images to invalidate and advance the - // animation in cc. - bool add_image = - !only_gather_animated_images_ || paint_image.ShouldAnimate(); + bool add_image = true; + if (paint_image.IsPaintWorklet()) { + // PaintWorklet-backed images don't go through the image decode pipeline + // (they are painted pre-raster from LayerTreeHostImpl), so do not need to + // be added to the |image_set_|. + add_image = false; + } else if (only_gather_animated_images_) { + // If we are iterating images in a record shader, only track them if they + // are animated. We defer decoding of images in record shaders to skia, + // but we still need to track animated images to invalidate and advance + // the animation in cc. + add_image = paint_image.ShouldAnimate(); + } + if (add_image) { image_set_.emplace_back( DrawImage(std::move(paint_image), src_irect, filter_quality, matrix), @@ -406,6 +420,7 @@ class DiscardableImageGenerator { base::flat_map<PaintImage::Id, DiscardableImageMap::Rects> image_id_to_rects_; std::vector<DiscardableImageMap::AnimatedImageMetadata> animated_images_metadata_; + std::vector<scoped_refptr<PaintWorkletInput>> paint_worklet_inputs_; base::flat_map<PaintImage::Id, PaintImage::DecodingMode> decoding_mode_map_; bool only_gather_animated_images_ = false; @@ -434,6 +449,7 @@ void DiscardableImageMap::Generate(const PaintOpBuffer* paint_op_buffer, generator.RecordColorHistograms(); image_id_to_rects_ = generator.TakeImageIdToRectsMap(); animated_images_metadata_ = generator.TakeAnimatedImagesMetadata(); + paint_worklet_inputs_ = generator.TakePaintWorkletInputs(); decoding_mode_map_ = generator.TakeDecodingModeMap(); all_images_are_srgb_ = generator.all_images_are_srgb(); auto images = generator.TakeImages(); diff --git a/chromium/cc/paint/discardable_image_map.h b/chromium/cc/paint/discardable_image_map.h index a9d1bb96d09..dd494fa3fd6 100644 --- a/chromium/cc/paint/discardable_image_map.h +++ b/chromium/cc/paint/discardable_image_map.h @@ -70,6 +70,11 @@ class CC_PAINT_EXPORT DiscardableImageMap { base::flat_map<PaintImage::Id, PaintImage::DecodingMode> TakeDecodingModeMap(); + const std::vector<scoped_refptr<PaintWorkletInput>>& paint_worklet_inputs() + const { + return paint_worklet_inputs_; + } + private: friend class ScopedMetadataGenerator; friend class DiscardableImageMapTest; @@ -86,6 +91,8 @@ class CC_PAINT_EXPORT DiscardableImageMap { bool all_images_are_srgb_ = false; RTree<DrawImage> images_rtree_; + + std::vector<scoped_refptr<PaintWorkletInput>> paint_worklet_inputs_; }; } // namespace cc diff --git a/chromium/cc/paint/discardable_image_map_unittest.cc b/chromium/cc/paint/discardable_image_map_unittest.cc index 8e4a0734995..e7ecf7ee498 100644 --- a/chromium/cc/paint/discardable_image_map_unittest.cc +++ b/chromium/cc/paint/discardable_image_map_unittest.cc @@ -732,6 +732,39 @@ TEST_F(DiscardableImageMapTest, GathersAnimatedImages) { EXPECT_DCHECK_DEATH(images[2]->frame_index()); } +TEST_F(DiscardableImageMapTest, GathersPaintWorklets) { + gfx::Rect visible_rect(1000, 1000); + FakeContentLayerClient content_layer_client; + content_layer_client.set_bounds(visible_rect.size()); + + gfx::Size image_size(100, 100); + PaintImage static_image = CreateDiscardablePaintImage(image_size); + scoped_refptr<TestPaintWorkletInput> input = + base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(image_size)); + PaintImage paint_worklet_image = CreatePaintWorkletPaintImage(input); + + PaintFlags flags; + content_layer_client.add_draw_image(static_image, gfx::Point(0, 0), flags); + content_layer_client.add_draw_image(paint_worklet_image, gfx::Point(100, 100), + flags); + + scoped_refptr<DisplayItemList> display_list = + content_layer_client.PaintContentsToDisplayList( + ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + display_list->GenerateDiscardableImagesMetadata(); + + const auto& paint_worklet_inputs = + display_list->discardable_image_map().paint_worklet_inputs(); + ASSERT_EQ(paint_worklet_inputs.size(), 1u); + EXPECT_EQ(paint_worklet_inputs[0], input); + + // PaintWorklets are not considered discardable images. + std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect( + display_list->discardable_image_map(), visible_rect); + ASSERT_EQ(images.size(), 1u); + EXPECT_EQ(images[0].image, static_image); +} + TEST_F(DiscardableImageMapTest, CapturesImagesInPaintRecordShaders) { // Create the record to use in the shader. auto shader_record = sk_make_sp<PaintOpBuffer>(); @@ -884,19 +917,6 @@ TEST_F(DiscardableImageMapTest, EmbeddedShaderWithAnimatedImages) { ImageAnalysisState::kAnimatedImages); } -TEST_F(DiscardableImageMapTest, BuildPaintWorkletImage) { - gfx::SizeF size(100, 50); - scoped_refptr<TestPaintWorkletInput> input = - base::MakeRefCounted<TestPaintWorkletInput>(size); - PaintImage paint_image = PaintImageBuilder::WithDefault() - .set_id(1) - .set_paint_worklet_input(std::move(input)) - .TakePaintImage(); - EXPECT_TRUE(paint_image.paint_worklet_input()); - EXPECT_EQ(paint_image.width(), size.width()); - EXPECT_EQ(paint_image.height(), size.height()); -} - TEST_F(DiscardableImageMapTest, DecodingModeHintsBasic) { gfx::Rect visible_rect(100, 100); PaintImage unspecified_image = diff --git a/chromium/cc/paint/display_item_list.cc b/chromium/cc/paint/display_item_list.cc index c82e1d2d13f..403c58fa674 100644 --- a/chromium/cc/paint/display_item_list.cc +++ b/chromium/cc/paint/display_item_list.cc @@ -32,10 +32,10 @@ bool GetCanvasClipBounds(SkCanvas* canvas, gfx::Rect* clip_bounds) { } void FillTextContent(const PaintOpBuffer* buffer, - std::vector<NodeHolder>* content) { + std::vector<NodeId>* content) { for (auto* op : PaintOpBuffer::Iterator(buffer)) { if (op->GetType() == PaintOpType::DrawTextBlob) { - content->push_back(static_cast<DrawTextBlobOp*>(op)->node_holder); + content->push_back(static_cast<DrawTextBlobOp*>(op)->node_id); } else if (op->GetType() == PaintOpType::DrawRecord) { FillTextContent(static_cast<DrawRecordOp*>(op)->record.get(), content); } @@ -44,12 +44,12 @@ void FillTextContent(const PaintOpBuffer* buffer, void FillTextContentByOffsets(const PaintOpBuffer* buffer, const std::vector<size_t>& offsets, - std::vector<NodeHolder>* content) { + std::vector<NodeId>* content) { if (!buffer) return; for (auto* op : PaintOpBuffer::OffsetIterator(buffer, &offsets)) { if (op->GetType() == PaintOpType::DrawTextBlob) { - content->push_back(static_cast<DrawTextBlobOp*>(op)->node_holder); + content->push_back(static_cast<DrawTextBlobOp*>(op)->node_id); } else if (op->GetType() == PaintOpType::DrawRecord) { FillTextContent(static_cast<DrawRecordOp*>(op)->record.get(), content); } @@ -82,7 +82,7 @@ void DisplayItemList::Raster(SkCanvas* canvas, } void DisplayItemList::CaptureContent(const gfx::Rect& rect, - std::vector<NodeHolder>* content) const { + std::vector<NodeId>* content) const { std::vector<size_t> offsets; rtree_.Search(rect, &offsets); FillTextContentByOffsets(&paint_op_buffer_, offsets, content); diff --git a/chromium/cc/paint/display_item_list.h b/chromium/cc/paint/display_item_list.h index d11f49ad6a1..af922d371b7 100644 --- a/chromium/cc/paint/display_item_list.h +++ b/chromium/cc/paint/display_item_list.h @@ -63,9 +63,9 @@ class CC_PAINT_EXPORT DisplayItemList void Raster(SkCanvas* canvas, ImageProvider* image_provider = nullptr) const; // Captures the DrawTextBlobOp within |rect| and returns the associated - // NodeHolder in |content|. + // NodeId in |content|. void CaptureContent(const gfx::Rect& rect, - std::vector<NodeHolder>* content) const; + std::vector<NodeId>* content) const; void StartPaint() { #if DCHECK_IS_ON() @@ -165,6 +165,7 @@ class CC_PAINT_EXPORT DisplayItemList int NumSlowPaths() const { return paint_op_buffer_.numSlowPaths(); } bool HasNonAAPaint() const { return paint_op_buffer_.HasNonAAPaint(); } + bool HasText() const { return paint_op_buffer_.HasText(); } // This gives the total number of PaintOps. size_t TotalOpCount() const { return paint_op_buffer_.total_op_count(); } diff --git a/chromium/cc/trees/element_id.cc b/chromium/cc/paint/element_id.cc index 6690378fc16..15d78835bca 100644 --- a/chromium/cc/trees/element_id.cc +++ b/chromium/cc/paint/element_id.cc @@ -1,8 +1,8 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "cc/trees/element_id.h" +#include "cc/paint/element_id.h" #include <inttypes.h> #include <limits> diff --git a/chromium/cc/trees/element_id.h b/chromium/cc/paint/element_id.h index e8275c521cc..59e01582126 100644 --- a/chromium/cc/trees/element_id.h +++ b/chromium/cc/paint/element_id.h @@ -1,9 +1,9 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. +// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CC_TREES_ELEMENT_ID_H_ -#define CC_TREES_ELEMENT_ID_H_ +#ifndef CC_PAINT_ELEMENT_ID_H_ +#define CC_PAINT_ELEMENT_ID_H_ #include <stddef.h> @@ -13,7 +13,7 @@ #include <memory> #include "base/hash/hash.h" -#include "cc/cc_export.h" +#include "cc/paint/paint_export.h" namespace base { class Value; @@ -44,7 +44,7 @@ using ElementIdType = uint64_t; // to the layer tree and uses element ids as a stable identifier for animation // targets. A Layer's element id can change over the Layer's lifetime because // non-default ElementIds are only set during an animation's lifetime. -struct CC_EXPORT ElementId { +struct CC_PAINT_EXPORT ElementId { explicit ElementId(ElementIdType id) : id_(id) { DCHECK_NE(id, kInvalidElementId); } @@ -75,15 +75,16 @@ struct CC_EXPORT ElementId { ElementIdType id_; }; -ElementId CC_EXPORT LayerIdToElementIdForTesting(int layer_id); +ElementId CC_PAINT_EXPORT LayerIdToElementIdForTesting(int layer_id); -struct CC_EXPORT ElementIdHash { +struct CC_PAINT_EXPORT ElementIdHash { size_t operator()(ElementId key) const; }; // Stream operator so ElementId can be used in assertion statements. -CC_EXPORT std::ostream& operator<<(std::ostream& out, const ElementId& id); +CC_PAINT_EXPORT std::ostream& operator<<(std::ostream& out, + const ElementId& id); } // namespace cc -#endif // CC_TREES_ELEMENT_ID_H_ +#endif // CC_PAINT_ELEMENT_ID_H_ diff --git a/chromium/cc/paint/image_provider.cc b/chromium/cc/paint/image_provider.cc index e30c8bb5e7d..dd471cbfdf6 100644 --- a/chromium/cc/paint/image_provider.cc +++ b/chromium/cc/paint/image_provider.cc @@ -13,16 +13,13 @@ ImageProvider::ScopedResult::ScopedResult() = default; ImageProvider::ScopedResult::ScopedResult(DecodedDrawImage image) : image_(std::move(image)) {} +ImageProvider::ScopedResult::ScopedResult(sk_sp<PaintRecord> record) + : record_(std::move(record)) {} + ImageProvider::ScopedResult::ScopedResult(DecodedDrawImage image, DestructionCallback callback) : image_(std::move(image)), destruction_callback_(std::move(callback)) {} -ImageProvider::ScopedResult::ScopedResult(sk_sp<PaintRecord> record, - DestructionCallback callback) - : record_(std::move(record)), destruction_callback_(std::move(callback)) { - DCHECK(!destruction_callback_.is_null()); -} - ImageProvider::ScopedResult::ScopedResult(ScopedResult&& other) : image_(std::move(other.image_)), record_(std::move(other.record_)), diff --git a/chromium/cc/paint/image_provider.h b/chromium/cc/paint/image_provider.h index 2b5e5f70713..973f8f78a9d 100644 --- a/chromium/cc/paint/image_provider.h +++ b/chromium/cc/paint/image_provider.h @@ -27,8 +27,8 @@ class CC_PAINT_EXPORT ImageProvider { ScopedResult(); explicit ScopedResult(DecodedDrawImage image); + explicit ScopedResult(sk_sp<PaintRecord> record); ScopedResult(DecodedDrawImage image, DestructionCallback callback); - ScopedResult(sk_sp<PaintRecord> record, DestructionCallback callback); ScopedResult(const ScopedResult&) = delete; ScopedResult(ScopedResult&& other); ~ScopedResult(); diff --git a/chromium/cc/paint/image_transfer_cache_entry.cc b/chromium/cc/paint/image_transfer_cache_entry.cc index 9e96fdbc3a3..964f5321c23 100644 --- a/chromium/cc/paint/image_transfer_cache_entry.cc +++ b/chromium/cc/paint/image_transfer_cache_entry.cc @@ -4,11 +4,14 @@ #include "cc/paint/image_transfer_cache_entry.h" +#include <array> +#include <type_traits> #include <utility> #include "base/bind_helpers.h" #include "base/logging.h" #include "base/numerics/checked_math.h" +#include "base/numerics/safe_conversions.h" #include "cc/paint/paint_op_reader.h" #include "cc/paint/paint_op_writer.h" #include "third_party/skia/include/core/SkColorSpace.h" @@ -23,6 +26,74 @@ namespace cc { namespace { +// Creates a SkImage backed by the YUV textures corresponding to |plane_images|. +// The layout is specified by |plane_images_format|). The backend textures are +// first extracted out of the |plane_images| (and work is flushed on each one). +// Note that we assume that the image is opaque (no alpha plane). Then, a +// SkImage is created out of those textures using the +// SkImage::MakeFromYUVATextures() API. Finally, |image_color_space| is the +// color space of the resulting image after applying |yuv_color_space| +// (converting from YUV to RGB). This is assumed to be sRGB if nullptr. +// +// On success, the resulting SkImage is +// returned. On failure, nullptr is returned (e.g., if one of the backend +// textures is invalid or a Skia error occurs). +sk_sp<SkImage> MakeYUVImageFromUploadedPlanes( + GrContext* context, + const std::vector<sk_sp<SkImage>>& plane_images, + YUVDecodeFormat plane_images_format, + SkYUVColorSpace yuv_color_space, + sk_sp<SkColorSpace> image_color_space) { + // 1) Extract the textures. + DCHECK_NE(YUVDecodeFormat::kUnknown, plane_images_format); + DCHECK_EQ(NumberOfPlanesForYUVDecodeFormat(plane_images_format), + plane_images.size()); + DCHECK_LE(plane_images.size(), + base::checked_cast<size_t>(SkYUVASizeInfo::kMaxCount)); + std::array<GrBackendTexture, SkYUVASizeInfo::kMaxCount> + plane_backend_textures; + for (size_t plane = 0u; plane < plane_images.size(); plane++) { + plane_backend_textures[plane] = plane_images[plane]->getBackendTexture( + true /* flushPendingGrContextIO */); + if (!plane_backend_textures[plane].isValid()) { + DLOG(ERROR) << "Invalid backend texture found"; + return nullptr; + } + } + + // 2) Create the YUV image. + SkYUVAIndex plane_indices[SkYUVAIndex::kIndexCount]; + if (plane_images_format == YUVDecodeFormat::kYUV3) { + plane_indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR}; + plane_indices[SkYUVAIndex::kU_Index] = {1, SkColorChannel::kR}; + plane_indices[SkYUVAIndex::kV_Index] = {2, SkColorChannel::kR}; + } else if (plane_images_format == YUVDecodeFormat::kYVU3) { + plane_indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR}; + plane_indices[SkYUVAIndex::kU_Index] = {2, SkColorChannel::kR}; + plane_indices[SkYUVAIndex::kV_Index] = {1, SkColorChannel::kR}; + } else if (plane_images_format == YUVDecodeFormat::kYUV2) { + plane_indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR}; + plane_indices[SkYUVAIndex::kU_Index] = {1, SkColorChannel::kR}; + plane_indices[SkYUVAIndex::kV_Index] = {1, SkColorChannel::kG}; + } else { + // TODO(crbug.com/910276): handle and test non-opaque images. + NOTREACHED(); + DLOG(ERROR) << "Unsupported planar format"; + return nullptr; + } + plane_indices[SkYUVAIndex::kA_Index] = {-1, SkColorChannel::kR}; + sk_sp<SkImage> image = SkImage::MakeFromYUVATextures( + context, yuv_color_space, plane_backend_textures.data(), plane_indices, + plane_images[0]->dimensions(), kTopLeft_GrSurfaceOrigin, + std::move(image_color_space)); + if (!image) { + DLOG(ERROR) << "Could not create YUV image"; + return nullptr; + } + + return image; +} + // TODO(ericrk): Replace calls to this with calls to SkImage::makeTextureImage, // once that function handles colorspaces. https://crbug.com/834837 sk_sp<SkImage> MakeTextureImage(GrContext* context, @@ -63,30 +134,50 @@ sk_sp<SkImage> MakeTextureImage(GrContext* context, } // namespace +size_t NumberOfPlanesForYUVDecodeFormat(YUVDecodeFormat format) { + switch (format) { + case YUVDecodeFormat::kYUVA4: + return 4u; + case YUVDecodeFormat::kYUV3: + case YUVDecodeFormat::kYVU3: + return 3u; + case YUVDecodeFormat::kYUV2: + return 2u; + case YUVDecodeFormat::kUnknown: + return 0u; + } +} + ClientImageTransferCacheEntry::ClientImageTransferCacheEntry( const SkPixmap* pixmap, const SkColorSpace* target_color_space, bool needs_mips) - : id_(s_next_id_.GetNext()), + : needs_mips_(needs_mips), + id_(GetNextId()), pixmap_(pixmap), target_color_space_(target_color_space), - needs_mips_(needs_mips) { + decoded_color_space_(nullptr) { size_t target_color_space_size = target_color_space ? target_color_space->writeToMemory(nullptr) : 0u; size_t pixmap_color_space_size = pixmap_->colorSpace() ? pixmap_->colorSpace()->writeToMemory(nullptr) : 0u; + // x64 has 8-byte alignment for uint64_t even though x86 has 4-byte + // alignment. Always use 8 byte alignment. + const size_t align = sizeof(uint64_t); + // Compute and cache the size of the data. base::CheckedNumeric<uint32_t> safe_size; safe_size += PaintOpWriter::HeaderBytes(); + safe_size += sizeof(uint32_t); // is_yuv safe_size += sizeof(uint32_t); // color type safe_size += sizeof(uint32_t); // width safe_size += sizeof(uint32_t); // height safe_size += sizeof(uint32_t); // has mips - safe_size += sizeof(uint64_t) + alignof(uint64_t); // pixels size - safe_size += target_color_space_size + sizeof(uint64_t) + alignof(uint64_t); - safe_size += pixmap_color_space_size + sizeof(uint64_t) + alignof(uint64_t); + safe_size += sizeof(uint64_t) + align; // pixels size + alignment + safe_size += target_color_space_size + sizeof(uint64_t) + align; + safe_size += pixmap_color_space_size + sizeof(uint64_t) + align; // Include 4 bytes of padding so we can always align our data pointer to a // 4-byte boundary. safe_size += 4; @@ -94,6 +185,50 @@ ClientImageTransferCacheEntry::ClientImageTransferCacheEntry( size_ = safe_size.ValueOrDie(); } +ClientImageTransferCacheEntry::ClientImageTransferCacheEntry( + const SkPixmap* y_pixmap, + const SkPixmap* u_pixmap, + const SkPixmap* v_pixmap, + const SkColorSpace* decoded_color_space, + SkYUVColorSpace yuv_color_space, + bool needs_mips) + : needs_mips_(needs_mips), + num_planes_(3), + id_(GetNextId()), + pixmap_(nullptr), + target_color_space_(nullptr), + yuv_pixmaps_({y_pixmap, u_pixmap, v_pixmap, nullptr}), + decoded_color_space_(decoded_color_space), + yuv_color_space_(yuv_color_space) { + DCHECK(IsYuv()); + size_t decoded_color_space_size = + decoded_color_space ? decoded_color_space->writeToMemory(nullptr) : 0u; + + // x64 has 8-byte alignment for uint64_t even though x86 has 4-byte + // alignment. Always use 8 byte alignment. + const size_t align = sizeof(uint64_t); + + // Compute and cache the size of the data. + base::CheckedNumeric<uint32_t> safe_size; + safe_size += PaintOpWriter::HeaderBytes(); + safe_size += sizeof(uint32_t); // is_yuv + safe_size += sizeof(uint32_t); // num_planes + safe_size += sizeof(uint32_t); // has mips + safe_size += sizeof(uint32_t); // yuv_color_space + safe_size += decoded_color_space_size + align; + safe_size += num_planes_ * sizeof(uint64_t); // plane widths + safe_size += num_planes_ * sizeof(uint64_t); // plane heights + safe_size += + num_planes_ * (sizeof(uint64_t) + align); // pixels size + alignment + // Include 4 bytes of padding before each plane data chunk so we can always + // align our data pointer to a 4-byte boundary. + safe_size += 4 * num_planes_; + safe_size += y_pixmap->computeByteSize(); + safe_size += u_pixmap->computeByteSize(); + safe_size += v_pixmap->computeByteSize(); + size_ = safe_size.ValueOrDie(); +} + ClientImageTransferCacheEntry::~ClientImageTransferCacheEntry() = default; // static @@ -107,21 +242,64 @@ uint32_t ClientImageTransferCacheEntry::Id() const { return id_; } +void ClientImageTransferCacheEntry::ValidateYUVDataBeforeSerializing() const { + DCHECK(!pixmap_); + DCHECK_LE(yuv_pixmaps_->size(), + static_cast<uint32_t>(SkYUVASizeInfo::kMaxCount)); + for (uint32_t i = 0; i < num_planes_; ++i) { + DCHECK(yuv_pixmaps_->at(i)); + const SkPixmap* plane = yuv_pixmaps_->at(i); + DCHECK_GT(plane->width(), 0); + DCHECK_GT(plane->height(), 0); + } + DCHECK(yuv_color_space_); +} + bool ClientImageTransferCacheEntry::Serialize(base::span<uint8_t> data) const { DCHECK_GE(data.size(), SerializedSize()); - DCHECK_GT(pixmap_->width(), 0); - DCHECK_GT(pixmap_->height(), 0); - // We don't need to populate the SerializeOptions here since the writer is // only used for serializing primitives. PaintOp::SerializeOptions options(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, false, false, 0, 0, SkMatrix::I()); PaintOpWriter writer(data.data(), data.size(), options); + writer.Write(static_cast<uint32_t>(IsYuv() ? 1 : 0)); + + if (IsYuv()) { + ValidateYUVDataBeforeSerializing(); + writer.Write(num_planes_); + writer.Write(static_cast<uint32_t>(needs_mips_ ? 1 : 0)); + writer.Write(yuv_color_space_); + writer.Write(decoded_color_space_); + for (uint32_t i = 0; i < num_planes_; ++i) { + DCHECK(yuv_pixmaps_->at(i)); + const SkPixmap* plane = yuv_pixmaps_->at(i); + writer.Write(plane->width()); + writer.Write(plane->height()); + size_t plane_size = plane->computeByteSize(); + if (plane_size == SIZE_MAX) + return false; + writer.WriteSize(plane_size); + writer.AlignMemory(4); + writer.WriteData(plane_size, plane->addr()); + } + + // Size can't be 0 after serialization unless the writer has become invalid. + if (writer.size() == 0u) + return false; + + return true; + } + + DCHECK_GT(pixmap_->width(), 0); + DCHECK_GT(pixmap_->height(), 0); + writer.Write(pixmap_->colorType()); writer.Write(pixmap_->width()); writer.Write(pixmap_->height()); writer.Write(static_cast<uint32_t>(needs_mips_ ? 1 : 0)); size_t pixmap_size = pixmap_->computeByteSize(); + if (pixmap_size == SIZE_MAX) + return false; writer.WriteSize(pixmap_size); // TODO(enne): we should consider caching these in some form. writer.Write(pixmap_->colorSpace()); @@ -147,61 +325,37 @@ ServiceImageTransferCacheEntry& ServiceImageTransferCacheEntry::operator=( bool ServiceImageTransferCacheEntry::BuildFromHardwareDecodedImage( GrContext* context, std::vector<sk_sp<SkImage>> plane_images, + YUVDecodeFormat plane_images_format, size_t buffer_byte_size, - bool needs_mips, - sk_sp<SkColorSpace> target_color_space) { + bool needs_mips) { context_ = context; - - // 1) Extract the planar textures from |plane_images|. - std::vector<GrBackendTexture> plane_backend_textures(3u); - DCHECK_EQ(3u, plane_images.size()); - for (size_t plane = 0; plane < 3u; plane++) { - plane_backend_textures[plane] = plane_images[plane]->getBackendTexture( - true /* flushPendingGrContextIO */); - if (!plane_backend_textures[plane].isValid()) { - DLOG(ERROR) << "Invalid backend texture found"; - return false; - } - if (needs_mips) { - // TODO(andrescj): generate mipmaps when requested. This will require some - // resource management: we either let Skia own the new textures or we take - // ownership and delete them in |destroy_callback|. - NOTIMPLEMENTED(); + yuv_color_space_ = SkYUVColorSpace::kJPEG_SkYUVColorSpace; + + // 1) Generate mipmap chains if requested. + if (needs_mips) { + for (size_t plane = 0; plane < plane_images.size(); plane++) { + plane_images[plane] = plane_images[plane]->makeTextureImage( + context_, nullptr /* dstColorSpace */, GrMipMapped::kYes); + if (!plane_images[plane]) { + DLOG(ERROR) << "Could not generate mipmap chain for plane " << plane; + return false; + } } } plane_images_ = std::move(plane_images); - - // 2) Create a SkImage backed by the YUV textures extracted above. There are - // two assumptions here: - // - // - SkYUVColorSpace::kJPEG_SkYUVColorSpace is used for the YUV-to-RGB - // matrix. - // - The color space of the resulting image is sRGB. - // - // TODO(andrescj): support other YUV-to-RGB conversions and embedded color - // profiles. - SkYUVAIndex plane_indices[] = { - SkYUVAIndex{0, SkColorChannel::kR}, SkYUVAIndex{1, SkColorChannel::kR}, - SkYUVAIndex{2, SkColorChannel::kR}, SkYUVAIndex{-1, SkColorChannel::kR}}; - image_ = SkImage::MakeFromYUVATextures( - context_, SkYUVColorSpace::kJPEG_SkYUVColorSpace, - plane_backend_textures.data(), plane_indices, - plane_images_[0]->dimensions(), kTopLeft_GrSurfaceOrigin); - if (!image_) { - DLOG(ERROR) << "Could not create YUV SkImage"; - return false; - } - - // 3) Perform color space conversion if necessary. - if (target_color_space) - image_ = image_->makeColorSpace(target_color_space); - if (!image_) { - DLOG(ERROR) << "Could not do color space conversion"; + plane_images_format_ = plane_images_format; + + // 2) Create a SkImage backed by |plane_images|. + // TODO(andrescj): support embedded color profiles for hardware decodes and + // pass the color space to MakeYUVImageFromUploadedPlanes. + image_ = MakeYUVImageFromUploadedPlanes( + context_, plane_images_, plane_images_format_, yuv_color_space_, + SkColorSpace::MakeSRGB()); + if (!image_) return false; - } - // 4) Fill out the rest of the information. - has_mips_ = false; + // 3) Fill out the rest of the information. + has_mips_ = needs_mips; size_ = buffer_byte_size; fits_on_gpu_ = true; return true; @@ -222,6 +376,91 @@ bool ServiceImageTransferCacheEntry::Deserialize( PaintOp::DeserializeOptions options(nullptr, nullptr, nullptr, &scratch_buffer); PaintOpReader reader(data.data(), data.size(), options); + uint32_t image_is_yuv; + reader.Read(&image_is_yuv); + if (!!image_is_yuv) { + uint32_t num_planes; + reader.Read(&num_planes); + // YUV or YUVA + // TODO(crbug.com/986575): consider serializing a YUVDecodeFormat. + if (num_planes != 3u && num_planes != 4u) + return false; + plane_images_format_ = + num_planes == 3u ? YUVDecodeFormat::kYUV3 : YUVDecodeFormat::kYUVA4; + uint32_t needs_mips; + reader.Read(&needs_mips); + has_mips_ = needs_mips; + SkYUVColorSpace yuv_color_space; + reader.Read(&yuv_color_space); + yuv_color_space_ = yuv_color_space; + sk_sp<SkColorSpace> decoded_color_space; + reader.Read(&decoded_color_space); + + // Match GrTexture::onGpuMemorySize so that memory traces agree. + auto gr_mips = has_mips_ ? GrMipMapped::kYes : GrMipMapped::kNo; + // Read in each plane and reconstruct pixmaps. + for (uint32_t i = 0; i < num_planes; i++) { + uint32_t plane_width; + reader.Read(&plane_width); + uint32_t plane_height; + reader.Read(&plane_height); + // Because Skia does not support YUV rasterization from software planes, + // we require that each pixmap fits in a GPU texture. In the + // GpuImageDecodeCache, we veto YUV decoding if the planes would be too + // big. + uint32_t max_size = static_cast<uint32_t>(context_->maxTextureSize()); + // We compute this for each plane in case a malicious renderer tries to + // send very large U or V planes. + fits_on_gpu_ = plane_width <= max_size && plane_height <= max_size; + if (!fits_on_gpu_) + return false; + + size_t plane_bytes; + reader.ReadSize(&plane_bytes); + constexpr SkColorType yuv_plane_color_type = kGray_8_SkColorType; + SkImageInfo plane_pixmap_info = + SkImageInfo::Make(plane_width, plane_height, yuv_plane_color_type, + kPremul_SkAlphaType, decoded_color_space); + if (plane_pixmap_info.computeMinByteSize() > plane_bytes) + return false; + // Align data to a 4-byte boundary, to match what we did when writing. + reader.AlignMemory(4); + const volatile void* plane_pixel_data = + reader.ExtractReadableMemory(plane_bytes); + if (!reader.valid()) + return false; + DCHECK(SkIsAlign4(reinterpret_cast<uintptr_t>(plane_pixel_data))); + const size_t plane_size = GrContext::ComputeTextureSize( + yuv_plane_color_type, plane_width, plane_height, gr_mips); + size_ += plane_size; + plane_sizes_.push_back(plane_size); + + // Const-cast away the "volatile" on |pixel_data|. We specifically + // understand that a malicious caller may change our pixels under us, and + // are OK with this as the worst case scenario is visual corruption. + SkPixmap plane_pixmap(plane_pixmap_info, + const_cast<const void*>(plane_pixel_data), + plane_pixmap_info.minRowBytes()); + + // Nothing should read the colorspace of individual planes because that + // information is stored in image_, so we pass nullptr. + sk_sp<SkImage> plane = + MakeSkImage(plane_pixmap, plane_width, plane_height, + nullptr /* target_color_space */); + if (!plane) + return false; + DCHECK(plane->isTextureBacked()); + // |plane_images_| must be set for use in EnsureMips(), memory dumps, and + // unit tests. + plane_images_.push_back(std::move(plane)); + } + + image_ = MakeYUVImageFromUploadedPlanes( + context_, plane_images_, plane_images_format_, yuv_color_space_, + decoded_color_space); + return !!image_; + } + SkColorType color_type = kUnknown_SkColorType; reader.Read(&color_type); @@ -272,10 +511,11 @@ bool ServiceImageTransferCacheEntry::Deserialize( // this as the worst case scenario is visual corruption. SkPixmap pixmap(image_info, const_cast<const void*>(pixel_data), image_info.minRowBytes()); - return MakeSkImage(pixmap, width, height, target_color_space); + image_ = MakeSkImage(pixmap, width, height, target_color_space); + return !!image_; } -bool ServiceImageTransferCacheEntry::MakeSkImage( +sk_sp<SkImage> ServiceImageTransferCacheEntry::MakeSkImage( const SkPixmap& pixmap, uint32_t width, uint32_t height, @@ -286,41 +526,74 @@ bool ServiceImageTransferCacheEntry::MakeSkImage( // a software or GPU SkImage. uint32_t max_size = context_->maxTextureSize(); fits_on_gpu_ = width <= max_size && height <= max_size; + sk_sp<SkImage> image; if (fits_on_gpu_) { - sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr); + image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr); if (!image) - return false; - image_ = MakeTextureImage(context_, std::move(image), target_color_space, - has_mips_ ? GrMipMapped::kYes : GrMipMapped::kNo); + return nullptr; + image = MakeTextureImage(context_, std::move(image), target_color_space, + has_mips_ ? GrMipMapped::kYes : GrMipMapped::kNo); } else { sk_sp<SkImage> original = SkImage::MakeFromRaster(pixmap, [](const void*, void*) {}, nullptr); if (!original) - return false; + return nullptr; if (target_color_space) { - image_ = original->makeColorSpace(target_color_space); + image = original->makeColorSpace(target_color_space); // If color space conversion is a noop, use original data. - if (image_ == original) - image_ = SkImage::MakeRasterCopy(pixmap); + if (image == original) + image = SkImage::MakeRasterCopy(pixmap); } else { // No color conversion to do, use original data. - image_ = SkImage::MakeRasterCopy(pixmap); + image = SkImage::MakeRasterCopy(pixmap); } } - return !!image_; + // Make sure the GPU work to create the backing texture is issued. + if (image) + image->getBackendTexture(true /* flushPendingGrContextIO */); + + return image; +} + +const sk_sp<SkImage>& ServiceImageTransferCacheEntry::GetPlaneImage( + size_t index) const { + DCHECK_GE(index, 0u); + DCHECK_LT(index, plane_images_.size()); + DCHECK(plane_images_.at(index)); + return plane_images_.at(index); } void ServiceImageTransferCacheEntry::EnsureMips() { if (has_mips_) return; - if (!plane_images_.empty()) { - // TODO(andrescj): generate mipmaps for hardware-accelerated decodes when - // requested. This will require some resource management: we either let Skia - // own the new textures or we take ownership and delete them in - // |destroy_callback_|. - NOTIMPLEMENTED(); + if (is_yuv()) { + DCHECK(image_); + DCHECK_LE(yuv_color_space_, SkYUVColorSpace::kLastEnum_SkYUVColorSpace); + DCHECK_NE(YUVDecodeFormat::kUnknown, plane_images_format_); + DCHECK_EQ(NumberOfPlanesForYUVDecodeFormat(plane_images_format_), + plane_images_.size()); + std::vector<sk_sp<SkImage>> mipped_planes; + for (size_t plane = 0; plane < plane_images_.size(); plane++) { + DCHECK(plane_images_.at(plane)); + sk_sp<SkImage> mipped_plane = plane_images_.at(plane)->makeTextureImage( + context_, nullptr /* dstColorSpace */, GrMipMapped::kYes); + if (!mipped_plane) + return; + mipped_planes.push_back(std::move(mipped_plane)); + } + // Keeping a separate vector for the planes as mips are added means that we + // are consistent: either all planes have mips or none do. + for (size_t plane = 0; plane < mipped_planes.size(); plane++) { + plane_images_.at(plane) = std::move(mipped_planes.at(plane)); + } + mipped_planes.clear(); + image_ = MakeYUVImageFromUploadedPlanes( + context_, plane_images_, plane_images_format_, yuv_color_space_, + image_->refColorSpace() /* image_color_space */); + has_mips_ = true; + return; } has_mips_ = true; diff --git a/chromium/cc/paint/image_transfer_cache_entry.h b/chromium/cc/paint/image_transfer_cache_entry.h index e55a60d894f..7aa5bf82ec2 100644 --- a/chromium/cc/paint/image_transfer_cache_entry.h +++ b/chromium/cc/paint/image_transfer_cache_entry.h @@ -14,6 +14,7 @@ #include "base/containers/span.h" #include "cc/paint/transfer_cache_entry.h" #include "third_party/skia/include/core/SkRefCnt.h" +#include "third_party/skia/include/core/SkYUVASizeInfo.h" class GrContext; class SkColorSpace; @@ -25,6 +26,17 @@ namespace cc { static constexpr uint32_t kInvalidImageTransferCacheEntryId = static_cast<uint32_t>(-1); +enum class YUVDecodeFormat { + kYUV3, // e.g., YUV 4:2:0, 4:2:2, or 4:4:4 as 3 planes. + kYUVA4, // e.g., YUV 4:2:0 as 3 planes plus an alpha plane. + kYVU3, // e.g., YVU 4:2:0, 4:2:2, or 4:4:4 as 3 planes. + kYUV2, // e.g., YUV 4:2:0 as NV12 (2 planes). + kUnknown, + kMaxValue = kUnknown, +}; + +CC_PAINT_EXPORT size_t NumberOfPlanesForYUVDecodeFormat(YUVDecodeFormat format); + // Client/ServiceImageTransferCacheEntry implement a transfer cache entry // for transferring image data. On the client side, this is a CPU SkPixmap, // on the service side the image is uploaded and is a GPU SkImage. @@ -34,6 +46,13 @@ class CC_PAINT_EXPORT ClientImageTransferCacheEntry explicit ClientImageTransferCacheEntry(const SkPixmap* pixmap, const SkColorSpace* target_color_space, bool needs_mips); + explicit ClientImageTransferCacheEntry( + const SkPixmap* y_pixmap, + const SkPixmap* u_pixmap, + const SkPixmap* v_pixmap, + const SkColorSpace* decoded_color_space, + SkYUVColorSpace yuv_color_space, + bool needs_mips); ~ClientImageTransferCacheEntry() final; uint32_t Id() const final; @@ -42,13 +61,31 @@ class CC_PAINT_EXPORT ClientImageTransferCacheEntry uint32_t SerializedSize() const final; bool Serialize(base::span<uint8_t> data) const final; + static uint32_t GetNextId() { return s_next_id_.GetNext(); } + bool IsYuv() const { return !!yuv_pixmaps_; } + private: + const bool needs_mips_ = false; + const uint32_t num_planes_ = 1; uint32_t id_; - const SkPixmap* const pixmap_; - const SkColorSpace* const target_color_space_; - const bool needs_mips_; uint32_t size_ = 0; static base::AtomicSequenceNumber s_next_id_; + + // RGBX-only members. + const SkPixmap* const pixmap_; + const SkColorSpace* const + target_color_space_; // Unused for YUV because Skia handles colorspaces + // at raster. + + // YUVA-only members. + base::Optional<std::array<const SkPixmap*, SkYUVASizeInfo::kMaxCount>> + yuv_pixmaps_; + const SkColorSpace* const decoded_color_space_; + SkYUVColorSpace yuv_color_space_; + + // DCHECKs that the appropriate data members are set or not set and have + // positive size dimensions. + void ValidateYUVDataBeforeSerializing() const; }; class CC_PAINT_EXPORT ServiceImageTransferCacheEntry @@ -64,23 +101,20 @@ class CC_PAINT_EXPORT ServiceImageTransferCacheEntry // Populates this entry using the result of a hardware decode. The assumption // is that |plane_images| are backed by textures that are in turn backed by a // buffer (dmabuf in Chrome OS) containing the planes of the decoded image. + // |plane_images_format| indicates the planar layout of |plane_images|. // |buffer_byte_size| is the size of the buffer. We assume the following: // - // - |plane_images| represents a YUV 4:2:0 triplanar image. // - The backing textures don't have mipmaps. We will generate the mipmaps if // |needs_mips| is true. // - The conversion from YUV to RGB will be performed assuming a JPEG image. - // - The colorspace of the resulting RGB image is sRGB. We will convert from - // this to |target_color_space| (if non-null). + // - The colorspace of the resulting RGB image is sRGB. // // Returns true if the entry can be built, false otherwise. - // - // TODO(andrescj): actually generate the mipmaps when |needs_mips| is true. bool BuildFromHardwareDecodedImage(GrContext* context, std::vector<sk_sp<SkImage>> plane_images, + YUVDecodeFormat plane_images_format, size_t buffer_byte_size, - bool needs_mips, - sk_sp<SkColorSpace> target_color_space); + bool needs_mips); // ServiceTransferCacheEntry implementation: size_t CachedSize() const final; @@ -95,15 +129,26 @@ class CC_PAINT_EXPORT ServiceImageTransferCacheEntry // Ensures the cached image has mips. void EnsureMips(); + // Used in tests and for registering each texture for memory dumps. + const sk_sp<SkImage>& GetPlaneImage(size_t index) const; + const std::vector<size_t>& GetPlaneCachedSizes() const { + return plane_sizes_; + } + bool is_yuv() const { return !plane_images_.empty(); } + size_t num_planes() const { return is_yuv() ? plane_images_.size() : 1u; } + private: - bool MakeSkImage(const SkPixmap& pixmap, - uint32_t width, - uint32_t height, - sk_sp<SkColorSpace> target_color_space); + sk_sp<SkImage> MakeSkImage(const SkPixmap& pixmap, + uint32_t width, + uint32_t height, + sk_sp<SkColorSpace> target_color_space); GrContext* context_ = nullptr; std::vector<sk_sp<SkImage>> plane_images_; + YUVDecodeFormat plane_images_format_ = YUVDecodeFormat::kUnknown; + std::vector<size_t> plane_sizes_; sk_sp<SkImage> image_; + SkYUVColorSpace yuv_color_space_; bool has_mips_ = false; size_t size_ = 0; bool fits_on_gpu_ = false; diff --git a/chromium/cc/paint/image_transfer_cache_entry_unittest.cc b/chromium/cc/paint/image_transfer_cache_entry_unittest.cc new file mode 100644 index 00000000000..87adbdfcee4 --- /dev/null +++ b/chromium/cc/paint/image_transfer_cache_entry_unittest.cc @@ -0,0 +1,328 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdint.h> + +#include <algorithm> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/logging.h" +#include "base/memory/scoped_refptr.h" +#include "build/build_config.h" +#include "cc/paint/image_transfer_cache_entry.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/core/SkImageInfo.h" +#include "third_party/skia/include/core/SkPixmap.h" +#include "third_party/skia/include/core/SkRefCnt.h" +#include "third_party/skia/include/gpu/GrBackendSurface.h" +#include "third_party/skia/include/gpu/GrContext.h" +#include "third_party/skia/include/gpu/GrTypes.h" +#include "third_party/skia/include/gpu/gl/GrGLInterface.h" +#include "third_party/skia/include/gpu/gl/GrGLTypes.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_context.h" +#include "ui/gl/gl_context_egl.h" +#include "ui/gl/gl_share_group.h" +#include "ui/gl/gl_surface.h" +#include "ui/gl/init/create_gr_gl_interface.h" +#include "ui/gl/init/gl_factory.h" + +namespace cc { +namespace { + +void MarkTextureAsReleased(SkImage::ReleaseContext context) { + auto* released = static_cast<bool*>(context); + DCHECK(!*released); + *released = true; +} + +// Checks if all the pixels in |image| are |expected_color|. +bool CheckImageIsSolidColor(const sk_sp<SkImage>& image, + SkColor expected_color) { + DCHECK_GE(image->width(), 1); + DCHECK_GE(image->height(), 1); + SkBitmap bitmap; + if (!bitmap.tryAllocN32Pixels(image->width(), image->height())) + return false; + SkPixmap pixmap; + if (!bitmap.peekPixels(&pixmap)) + return false; + if (!image->readPixels(pixmap, 0 /* srcX */, 0 /* srcY */)) + return false; + for (int y = 0; y < image->height(); y++) { + for (int x = 0; x < image->width(); x++) { + if (bitmap.getColor(x, y) != expected_color) + return false; + } + } + return true; +} + +class ImageTransferCacheEntryTest + : public testing::TestWithParam<YUVDecodeFormat> { + public: + void SetUp() override { + // Initialize a GL GrContext for Skia. + surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size()); + ASSERT_TRUE(surface_); + share_group_ = base::MakeRefCounted<gl::GLShareGroup>(); + gl_context_ = base::MakeRefCounted<gl::GLContextEGL>(share_group_.get()); + ASSERT_TRUE(gl_context_); + ASSERT_TRUE( + gl_context_->Initialize(surface_.get(), gl::GLContextAttribs())); + ASSERT_TRUE(gl_context_->MakeCurrent(surface_.get())); + sk_sp<GrGLInterface> interface(gl::init::CreateGrGLInterface( + *gl_context_->GetVersionInfo(), false /* use_version_es2 */)); + gr_context_ = GrContext::MakeGL(std::move(interface)); + ASSERT_TRUE(gr_context_); + } + + // Creates the textures for a 64x64 YUV 4:2:0 image where all the samples in + // all planes are 255u. This corresponds to an RGB color of (255, 121, 255) + // assuming the JPEG YUV-to-RGB conversion formulas. Returns a list of + // SkImages backed by the textures. Note that the number of textures depends + // on the format (obtained using GetParam()). |release_flags| is set to a list + // of boolean flags initialized to false. Each flag corresponds to a plane (in + // the same order as the returned SkImages). When the texture for that plane + // is released by Skia, that flag will be set to true. Returns an empty vector + // on failure. + std::vector<sk_sp<SkImage>> CreateTestYUVImage( + std::unique_ptr<bool[]>* release_flags) { + std::vector<sk_sp<SkImage>> plane_images; + *release_flags = nullptr; + if (GetParam() == YUVDecodeFormat::kYUV3 || + GetParam() == YUVDecodeFormat::kYVU3) { + *release_flags = + std::unique_ptr<bool[]>(new bool[3]{false, false, false}); + plane_images = { + CreateSolidPlane(gr_context(), 64, 64, GL_R8_EXT, SkColors::kWhite, + release_flags->get()), + CreateSolidPlane(gr_context(), 32, 32, GL_R8_EXT, SkColors::kWhite, + release_flags->get() + 1), + CreateSolidPlane(gr_context(), 32, 32, GL_R8_EXT, SkColors::kWhite, + release_flags->get() + 2)}; + } else if (GetParam() == YUVDecodeFormat::kYUV2) { + *release_flags = std::unique_ptr<bool[]>(new bool[2]{false, false}); + plane_images = { + CreateSolidPlane(gr_context(), 64, 64, GL_R8_EXT, SkColors::kWhite, + release_flags->get()), + CreateSolidPlane(gr_context(), 32, 32, GL_RG8_EXT, SkColors::kWhite, + release_flags->get() + 1)}; + } else { + NOTREACHED(); + return {}; + } + if (std::all_of(plane_images.cbegin(), plane_images.cend(), + [](sk_sp<SkImage> plane) { return !!plane; })) { + return plane_images; + } + return {}; + } + + void DeletePendingTextures() { + DCHECK(gr_context_); + for (const auto& texture : textures_to_free_) { + if (texture.isValid()) + gr_context_->deleteBackendTexture(texture); + } + gr_context_->flush(); + textures_to_free_.clear(); + } + + void TearDown() override { + DeletePendingTextures(); + gr_context_.reset(); + surface_->PrepareToDestroy(gl_context_->IsCurrent(surface_.get())); + surface_.reset(); + gl_context_.reset(); + share_group_.reset(); + } + + GrContext* gr_context() const { return gr_context_.get(); } + + private: + // Uploads a texture corresponding to a single plane in a YUV image. All the + // samples in the plane are set to |color|. The texture is not owned by Skia: + // when Skia doesn't need it anymore, MarkTextureAsReleased() will be called. + sk_sp<SkImage> CreateSolidPlane(GrContext* gr_context, + int width, + int height, + GrGLenum texture_format, + const SkColor4f& color, + bool* released) { + GrBackendTexture allocated_texture = gr_context->createBackendTexture( + width, height, GrBackendFormat::MakeGL(texture_format, GL_TEXTURE_2D), + color, GrMipMapped::kNo, GrRenderable::kNo); + if (!allocated_texture.isValid()) + return nullptr; + textures_to_free_.push_back(allocated_texture); + GrGLTextureInfo allocated_texture_info; + if (!allocated_texture.getGLTextureInfo(&allocated_texture_info)) + return nullptr; + DCHECK_EQ(width, allocated_texture.width()); + DCHECK_EQ(height, allocated_texture.height()); + DCHECK(!allocated_texture.hasMipMaps()); + DCHECK(allocated_texture_info.fTarget == GL_TEXTURE_2D); + if (texture_format == GL_RG8_EXT) { + // TODO(crbug.com/985458): using GL_RGBA8_EXT is a workaround so that we + // can make a SkImage out of a GL_RG8_EXT texture. Revisit this once Skia + // supports a corresponding SkColorType. + allocated_texture = GrBackendTexture( + width, height, GrMipMapped::kNo, + GrGLTextureInfo{GL_TEXTURE_2D, allocated_texture_info.fID, + GL_RGBA8_EXT}); + } + *released = false; + return SkImage::MakeFromTexture( + gr_context, allocated_texture, kTopLeft_GrSurfaceOrigin, + texture_format == GL_RG8_EXT ? kRGBA_8888_SkColorType + : kAlpha_8_SkColorType, + kOpaque_SkAlphaType, nullptr /* colorSpace */, MarkTextureAsReleased, + released); + } + + std::vector<GrBackendTexture> textures_to_free_; + scoped_refptr<gl::GLSurface> surface_; + scoped_refptr<gl::GLShareGroup> share_group_; + scoped_refptr<gl::GLContext> gl_context_; + sk_sp<GrContext> gr_context_; +}; + +TEST_P(ImageTransferCacheEntryTest, HardwareDecodedNoMipsAtCreation) { + std::unique_ptr<bool[]> release_flags; + std::vector<sk_sp<SkImage>> plane_images = CreateTestYUVImage(&release_flags); + ASSERT_EQ(NumberOfPlanesForYUVDecodeFormat(GetParam()), plane_images.size()); + + // Create a service-side image cache entry backed by these planes and do not + // request generating mipmap chains. The |buffer_byte_size| is only used for + // accounting, so we just set it to 0u. + auto entry(std::make_unique<ServiceImageTransferCacheEntry>()); + EXPECT_TRUE(entry->BuildFromHardwareDecodedImage( + gr_context(), std::move(plane_images), + GetParam() /* plane_images_format */, 0u /* buffer_byte_size */, + false /* needs_mips */)); + + // We didn't request generating mipmap chains, so the textures we created + // above should stay alive until after the cache entry is deleted. + EXPECT_TRUE(std::none_of(release_flags.get(), + release_flags.get() + plane_images.size(), + [](bool released) { return released; })); + entry.reset(); + EXPECT_TRUE(std::all_of(release_flags.get(), + release_flags.get() + plane_images.size(), + [](bool released) { return released; })); +} + +TEST_P(ImageTransferCacheEntryTest, HardwareDecodedMipsAtCreation) { +#if defined(OS_ANDROID) + // TODO(crbug.com/985458): this test is failing on Android for NV12 and we + // don't understand why yet. Revisit this once Skia supports an RG8 + // SkColorType. + if (GetParam() == YUVDecodeFormat::kYUV2) + return; +#endif + std::unique_ptr<bool[]> release_flags; + std::vector<sk_sp<SkImage>> plane_images = CreateTestYUVImage(&release_flags); + ASSERT_EQ(NumberOfPlanesForYUVDecodeFormat(GetParam()), plane_images.size()); + + // Create a service-side image cache entry backed by these planes and request + // generating mipmap chains at creation time. The |buffer_byte_size| is only + // used for accounting, so we just set it to 0u. + auto entry(std::make_unique<ServiceImageTransferCacheEntry>()); + EXPECT_TRUE(entry->BuildFromHardwareDecodedImage( + gr_context(), std::move(plane_images), + GetParam() /* plane_images_format */, 0u /* buffer_byte_size */, + true /* needs_mips */)); + + // We requested generating mipmap chains at creation time, so the textures we + // created above should be released by now. + EXPECT_TRUE(std::all_of(release_flags.get(), + release_flags.get() + plane_images.size(), + [](bool released) { return released; })); + DeletePendingTextures(); + + // Make sure that when we read the pixels from the YUV image, we get the + // correct RGB color corresponding to the planes created previously. This + // basically checks that after deleting the original YUV textures, the new + // YUV image is backed by the correct mipped planes. + ASSERT_TRUE(entry->image()); + EXPECT_TRUE( + CheckImageIsSolidColor(entry->image(), SkColorSetRGB(255, 121, 255))); +} + +TEST_P(ImageTransferCacheEntryTest, HardwareDecodedMipsAfterCreation) { +#if defined(OS_ANDROID) + // TODO(crbug.com/985458): this test is failing on Android for NV12 and we + // don't understand why yet. Revisit this once Skia supports an RG8 + // SkColorType. + if (GetParam() == YUVDecodeFormat::kYUV2) + return; +#endif + std::unique_ptr<bool[]> release_flags; + std::vector<sk_sp<SkImage>> plane_images = CreateTestYUVImage(&release_flags); + ASSERT_EQ(NumberOfPlanesForYUVDecodeFormat(GetParam()), plane_images.size()); + + // Create a service-side image cache entry backed by these planes and do not + // request generating mipmap chains at creation time. The |buffer_byte_size| + // is only used for accounting, so we just set it to 0u. + auto entry(std::make_unique<ServiceImageTransferCacheEntry>()); + EXPECT_TRUE(entry->BuildFromHardwareDecodedImage( + gr_context(), std::move(plane_images), + GetParam() /* plane_images_format */, 0u /* buffer_byte_size */, + false /* needs_mips */)); + + // We didn't request generating mip chains, so the textures we created above + // should stay alive for now. + EXPECT_TRUE(std::none_of(release_flags.get(), + release_flags.get() + plane_images.size(), + [](bool released) { return released; })); + + // Now request generating the mip chains. + entry->EnsureMips(); + + // Now the original textures should have been released. + EXPECT_TRUE(std::all_of(release_flags.get(), + release_flags.get() + plane_images.size(), + [](bool released) { return released; })); + DeletePendingTextures(); + + // Make sure that when we read the pixels from the YUV image, we get the + // correct RGB color corresponding to the planes created previously. This + // basically checks that after deleting the original YUV textures, the new + // YUV image is backed by the correct mipped planes. + ASSERT_TRUE(entry->image()); + EXPECT_TRUE( + CheckImageIsSolidColor(entry->image(), SkColorSetRGB(255, 121, 255))); +} + +std::string TestParamToString( + const testing::TestParamInfo<YUVDecodeFormat>& param_info) { + switch (param_info.param) { + case YUVDecodeFormat::kYUV3: + return "YUV3"; + case YUVDecodeFormat::kYVU3: + return "YVU3"; + case YUVDecodeFormat::kYUV2: + return "YUV2"; + default: + NOTREACHED(); + return ""; + } +} + +INSTANTIATE_TEST_SUITE_P(, + ImageTransferCacheEntryTest, + ::testing::Values(YUVDecodeFormat::kYUV3, + YUVDecodeFormat::kYVU3, + YUVDecodeFormat::kYUV2), + TestParamToString); + +} // namespace +} // namespace cc diff --git a/chromium/cc/paint/node_holder.cc b/chromium/cc/paint/node_holder.cc deleted file mode 100644 index 1e610134ba1..00000000000 --- a/chromium/cc/paint/node_holder.cc +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/paint/node_holder.h" -#include "base/no_destructor.h" - -namespace cc { - -NodeHolder::NodeHolder() : is_empty(true) {} - -NodeHolder::NodeHolder(scoped_refptr<TextHolder> holder) - : text_holder(holder), type(Type::kTextHolder), is_empty(false) {} - -NodeHolder::NodeHolder(int id) : id(id), type(Type::kID), is_empty(false) {} - -NodeHolder::NodeHolder(const NodeHolder& other) { - text_holder = other.text_holder; - id = other.id; - type = other.type; - is_empty = other.is_empty; -} - -NodeHolder::~NodeHolder() = default; - -// static -const NodeHolder& NodeHolder::EmptyNodeHolder() { - static const base::NoDestructor<NodeHolder> s; - return *s; -} - -bool operator==(const NodeHolder& l, const NodeHolder& r) { - if (l.is_empty != r.is_empty) { - return false; - } else if (l.is_empty) { - return true; - } else { - switch (l.type) { - case NodeHolder::Type::kTextHolder: - return l.text_holder == r.text_holder; - case NodeHolder::Type::kID: - return l.id == r.id; - } - } -} - -bool operator!=(const NodeHolder& l, const NodeHolder& r) { - return !(l == r); -} - -} // namespace cc diff --git a/chromium/cc/paint/node_holder.h b/chromium/cc/paint/node_holder.h deleted file mode 100644 index 399d65feaaf..00000000000 --- a/chromium/cc/paint/node_holder.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_PAINT_NODE_HOLDER_H_ -#define CC_PAINT_NODE_HOLDER_H_ - -#include "base/memory/scoped_refptr.h" -#include "cc/paint/paint_export.h" -#include "cc/paint/text_holder.h" - -namespace cc { - -// This struct is used to hold the information of node that PaintOp associates -// with, either the TextHolder or the Id could be set, but only one will finally -// be supported base on the performance impact. -// This is used for ContentCapture in blink to capture on-screen content, the -// NodeHolder shall be set to DrawTextBlobOp when blink paints the main text -// content of page; for ContentCapture, please refer to -// third_party/blink/renderer/core/content_capture/README.md -struct CC_PAINT_EXPORT NodeHolder { - NodeHolder(); - explicit NodeHolder(scoped_refptr<TextHolder> text_holder); - explicit NodeHolder(int id); - NodeHolder(const NodeHolder& node_holder); - virtual ~NodeHolder(); - - // Returns the empty NodeHolder, shall be used for content that is not - // considered part of the main text content of the page. - static const NodeHolder& EmptyNodeHolder(); - - enum class Type : bool { kTextHolder = false, kID = true }; - - // Only valid if is_empty is false. - scoped_refptr<TextHolder> text_holder; - int id; - Type type; - bool is_empty; -}; - -bool operator==(const NodeHolder& l, const NodeHolder& r); - -bool operator!=(const NodeHolder& l, const NodeHolder& r); - -} // namespace cc - -#endif // CC_PAINT_NODE_HOLDER_H_ diff --git a/chromium/cc/paint/node_id.h b/chromium/cc/paint/node_id.h new file mode 100644 index 00000000000..02a9d8127a9 --- /dev/null +++ b/chromium/cc/paint/node_id.h @@ -0,0 +1,22 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_PAINT_NODE_ID_H_ +#define CC_PAINT_NODE_ID_H_ + +namespace cc { +// The NodeId is used to associate the DOM node with PaintOp, its peer in +// blink is DOMNodeId. +// +// This is used for ContentCapture in blink to capture on-screen content, the +// NodeId shall be set to DrawTextBlobOp when blink paints the main text +// content of page; for ContentCapture, please refer to +// third_party/blink/renderer/core/content_capture/README.md +using NodeId = int; + +static const NodeId kInvalidNodeId = 0; + +} // namespace cc + +#endif // CC_PAINT_NODE_ID_H_ diff --git a/chromium/cc/paint/oop_pixeltest.cc b/chromium/cc/paint/oop_pixeltest.cc index 9af0e518b94..a71704a78d8 100644 --- a/chromium/cc/paint/oop_pixeltest.cc +++ b/chromium/cc/paint/oop_pixeltest.cc @@ -18,8 +18,8 @@ #include "cc/raster/playback_image_provider.h" #include "cc/raster/raster_source.h" #include "cc/test/pixel_test_utils.h" -#include "cc/test/test_in_process_context_provider.h" #include "cc/tiles/gpu_image_decode_cache.h" +#include "components/viz/test/test_in_process_context_provider.h" #include "gpu/command_buffer/client/gles2_implementation.h" #include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/client/raster_implementation.h" @@ -78,7 +78,7 @@ class OopPixelTest : public testing::Test, void SetUp() override { InitializeOOPContext(); gles2_context_provider_ = - base::MakeRefCounted<TestInProcessContextProvider>( + base::MakeRefCounted<viz::TestInProcessContextProvider>( /*enable_oop_rasterization=*/false, /*support_locking=*/true); gpu::ContextResult result = gles2_context_provider_->BindToCurrentThread(); DCHECK_EQ(result, gpu::ContextResult::kSuccess); @@ -103,7 +103,7 @@ class OopPixelTest : public testing::Test, oop_image_cache_.reset(); raster_context_provider_ = - base::MakeRefCounted<TestInProcessContextProvider>( + base::MakeRefCounted<viz::TestInProcessContextProvider>( /*enable_oop_rasterization=*/true, /*support_locking=*/true, &gr_shader_cache_, &activity_flags_); gpu::ContextResult result = raster_context_provider_->BindToCurrentThread(); @@ -153,7 +153,7 @@ class OopPixelTest : public testing::Test, SkBitmap Raster(scoped_refptr<DisplayItemList> display_item_list, const RasterOptions& options) { GURL url("https://example.com/foo"); - TestInProcessContextProvider::ScopedRasterContextLock lock( + viz::TestInProcessContextProvider::ScopedRasterContextLock lock( raster_context_provider_.get(), url.possibly_invalid_spec().c_str()); PlaybackImageProvider image_provider(oop_image_cache_.get(), @@ -258,7 +258,7 @@ class OopPixelTest : public testing::Test, SkBitmap RasterExpectedBitmap( scoped_refptr<DisplayItemList> display_item_list, const RasterOptions& options) { - TestInProcessContextProvider::ScopedRasterContextLock lock( + viz::TestInProcessContextProvider::ScopedRasterContextLock lock( gles2_context_provider_.get()); gles2_context_provider_->GrContext()->resetContext(); @@ -344,8 +344,8 @@ class OopPixelTest : public testing::Test, protected: enum { kWorkingSetSize = 64 * 1024 * 1024 }; - scoped_refptr<TestInProcessContextProvider> raster_context_provider_; - scoped_refptr<TestInProcessContextProvider> gles2_context_provider_; + scoped_refptr<viz::TestInProcessContextProvider> raster_context_provider_; + scoped_refptr<viz::TestInProcessContextProvider> gles2_context_provider_; std::unique_ptr<GpuImageDecodeCache> gpu_image_cache_; std::unique_ptr<GpuImageDecodeCache> oop_image_cache_; gl::DisableNullDrawGLBindings enable_pixel_output_; diff --git a/chromium/cc/paint/paint_canvas.h b/chromium/cc/paint/paint_canvas.h index 0f0c694eff1..a8201032901 100644 --- a/chromium/cc/paint/paint_canvas.h +++ b/chromium/cc/paint/paint_canvas.h @@ -8,6 +8,7 @@ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "build/build_config.h" +#include "cc/paint/node_id.h" #include "cc/paint/paint_export.h" #include "cc/paint/paint_image.h" #include "third_party/skia/include/core/SkCanvas.h" @@ -22,8 +23,6 @@ class SkottieWrapper; class PaintFlags; class PaintOpBuffer; -struct NodeHolder; - using PaintRecord = PaintOpBuffer; // PaintCanvas is the cc/paint wrapper of SkCanvas. It has a more restricted @@ -165,8 +164,8 @@ class CC_PAINT_EXPORT PaintCanvas { virtual void drawTextBlob(sk_sp<SkTextBlob> blob, SkScalar x, SkScalar y, - const PaintFlags& flags, - const NodeHolder& holder) = 0; + NodeId node_id, + const PaintFlags& flags) = 0; // Unlike SkCanvas::drawPicture, this only plays back the PaintRecord and does // not add an additional clip. This is closer to SkPicture::playback. diff --git a/chromium/cc/paint/paint_flags.cc b/chromium/cc/paint/paint_flags.cc index dcdb3c3a7f8..7e138218e96 100644 --- a/chromium/cc/paint/paint_flags.cc +++ b/chromium/cc/paint/paint_flags.cc @@ -129,7 +129,6 @@ SkPaint PaintFlags::ToSkPaint() const { paint.setShader(shader_->GetSkShader()); paint.setMaskFilter(mask_filter_); paint.setColorFilter(color_filter_); - paint.setDrawLooper(draw_looper_); if (image_filter_) paint.setImageFilter(image_filter_->cached_sk_filter_); paint.setColor(color_); diff --git a/chromium/cc/paint/paint_flags.h b/chromium/cc/paint/paint_flags.h index 64acb7861b4..c1f1cb5ce2f 100644 --- a/chromium/cc/paint/paint_flags.h +++ b/chromium/cc/paint/paint_flags.h @@ -146,8 +146,19 @@ class CC_PAINT_EXPORT PaintFlags { bool IsSimpleOpacity() const; bool SupportsFoldingAlpha() const; + // SkPaint does not support loopers, so callers of SkToPaint need + // to check for loopers manually (see getLooper()). SkPaint ToSkPaint() const; + template <typename Proc> + void DrawToSk(SkCanvas* canvas, Proc proc) const { + SkPaint paint = ToSkPaint(); + if (const sk_sp<SkDrawLooper>& looper = getLooper()) + looper->apply(canvas, paint, proc); + else + proc(canvas, paint); + } + bool IsValid() const; bool operator==(const PaintFlags& other) const; bool operator!=(const PaintFlags& other) const { return !(*this == other); } diff --git a/chromium/cc/paint/paint_image.h b/chromium/cc/paint/paint_image.h index 2f68f070c16..4090b7495dd 100644 --- a/chromium/cc/paint/paint_image.h +++ b/chromium/cc/paint/paint_image.h @@ -258,8 +258,8 @@ class CC_PAINT_EXPORT PaintImage { sk_sp<SkImage> GetSkImageForFrame(size_t index, GeneratorClientId client_id) const; - PaintWorkletInput* paint_worklet_input() const { - return paint_worklet_input_.get(); + const scoped_refptr<PaintWorkletInput>& paint_worklet_input() const { + return paint_worklet_input_; } std::string ToString() const; diff --git a/chromium/cc/paint/paint_image_unittest.cc b/chromium/cc/paint/paint_image_unittest.cc index 82793a61ddd..bc1a06d80ac 100644 --- a/chromium/cc/paint/paint_image_unittest.cc +++ b/chromium/cc/paint/paint_image_unittest.cc @@ -8,6 +8,7 @@ #include "cc/paint/paint_image_builder.h" #include "cc/test/fake_paint_image_generator.h" #include "cc/test/skia_common.h" +#include "cc/test/test_paint_worklet_input.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { @@ -156,4 +157,17 @@ TEST(PaintImageTest, DecodeToYuv420NoAlpha) { yuv_generator->reset_frames_decoded(); } +TEST(PaintImageTest, BuildPaintWorkletImage) { + gfx::SizeF size(100, 50); + scoped_refptr<TestPaintWorkletInput> input = + base::MakeRefCounted<TestPaintWorkletInput>(size); + PaintImage paint_image = PaintImageBuilder::WithDefault() + .set_id(1) + .set_paint_worklet_input(std::move(input)) + .TakePaintImage(); + EXPECT_TRUE(paint_image.paint_worklet_input()); + EXPECT_EQ(paint_image.width(), size.width()); + EXPECT_EQ(paint_image.height(), size.height()); +} + } // namespace cc diff --git a/chromium/cc/paint/paint_op_buffer.cc b/chromium/cc/paint/paint_op_buffer.cc index 1359b6362e6..4223876fd47 100644 --- a/chromium/cc/paint/paint_op_buffer.cc +++ b/chromium/cc/paint/paint_op_buffer.cc @@ -45,7 +45,6 @@ SkRect AdjustSrcRectForScale(SkRect original, SkSize scale_adjustment) { original.width() * x_scale, original.height() * y_scale); } - } // namespace #define TYPES(M) \ @@ -1026,7 +1025,7 @@ PaintOp* DrawTextBlobOp::Deserialize(const volatile void* input, void* output, size_t output_size, const DeserializeOptions& options) { - DCHECK_GE(output_size, sizeof(DrawTextBlobOp) - sizeof(NodeHolder)); + DCHECK_GE(output_size, sizeof(DrawTextBlobOp) - sizeof(NodeId)); DrawTextBlobOp* op = new (output) DrawTextBlobOp; PaintOpReader helper(input, input_size, options); @@ -1199,8 +1198,9 @@ void DrawDRRectOp::RasterWithFlags(const DrawDRRectOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params) { - SkPaint paint = flags->ToSkPaint(); - canvas->drawDRRect(op->outer, op->inner, paint); + flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { + c->drawDRRect(op->outer, op->inner, p); + }); } void DrawImageOp::RasterWithFlags(const DrawImageOp* op, @@ -1316,8 +1316,9 @@ void DrawIRectOp::RasterWithFlags(const DrawIRectOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params) { - SkPaint paint = flags->ToSkPaint(); - canvas->drawIRect(op->rect, paint); + flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { + c->drawIRect(op->rect, p); + }); } void DrawLineOp::RasterWithFlags(const DrawLineOp* op, @@ -1325,23 +1326,27 @@ void DrawLineOp::RasterWithFlags(const DrawLineOp* op, SkCanvas* canvas, const PlaybackParams& params) { SkPaint paint = flags->ToSkPaint(); - canvas->drawLine(op->x0, op->y0, op->x1, op->y1, paint); + flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { + c->drawLine(op->x0, op->y0, op->x1, op->y1, p); + }); } void DrawOvalOp::RasterWithFlags(const DrawOvalOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params) { - SkPaint paint = flags->ToSkPaint(); - canvas->drawOval(op->oval, paint); + flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { + c->drawOval(op->oval, p); + }); } void DrawPathOp::RasterWithFlags(const DrawPathOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params) { - SkPaint paint = flags->ToSkPaint(); - canvas->drawPath(op->path, paint); + flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { + c->drawPath(op->path, p); + }); } void DrawRecordOp::Raster(const DrawRecordOp* op, @@ -1357,16 +1362,18 @@ void DrawRectOp::RasterWithFlags(const DrawRectOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params) { - SkPaint paint = flags->ToSkPaint(); - canvas->drawRect(op->rect, paint); + flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { + c->drawRect(op->rect, p); + }); } void DrawRRectOp::RasterWithFlags(const DrawRRectOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params) { - SkPaint paint = flags->ToSkPaint(); - canvas->drawRRect(op->rrect, paint); + flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { + c->drawRRect(op->rrect, p); + }); } void DrawSkottieOp::Raster(const DrawSkottieOp* op, @@ -1379,8 +1386,9 @@ void DrawTextBlobOp::RasterWithFlags(const DrawTextBlobOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params) { - SkPaint paint = flags->ToSkPaint(); - canvas->drawTextBlob(op->blob.get(), op->x, op->y, paint); + flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { + c->drawTextBlob(op->blob.get(), op->x, op->y, p); + }); } void RestoreOp::Raster(const RestoreOp* op, @@ -1779,7 +1787,7 @@ bool DrawTextBlobOp::AreEqual(const PaintOp* base_left, return false; if (!AreEqualEvenIfNaN(left->y, right->y)) return false; - if (left->node_holder != right->node_holder) + if (left->node_id != right->node_id) return false; SkSerialProcs default_procs; @@ -2203,6 +2211,10 @@ bool DrawRecordOp::HasDiscardableImages() const { return record->HasDiscardableImages(); } +bool DrawRecordOp::HasText() const { + return record->HasText(); +} + DrawTextBlobOp::DrawTextBlobOp() : PaintOpWithFlags(kType) {} DrawTextBlobOp::DrawTextBlobOp(sk_sp<SkTextBlob> blob, @@ -2214,13 +2226,13 @@ DrawTextBlobOp::DrawTextBlobOp(sk_sp<SkTextBlob> blob, DrawTextBlobOp::DrawTextBlobOp(sk_sp<SkTextBlob> blob, SkScalar x, SkScalar y, - const PaintFlags& flags, - const NodeHolder& holder) + NodeId node_id, + const PaintFlags& flags) : PaintOpWithFlags(kType, flags), blob(std::move(blob)), x(x), y(y), - node_holder(holder) {} + node_id(node_id) {} DrawTextBlobOp::~DrawTextBlobOp() = default; @@ -2240,7 +2252,9 @@ PaintOpBuffer::CompositeIterator::CompositeIterator(CompositeIterator&& other) = default; PaintOpBuffer::PaintOpBuffer() - : has_non_aa_paint_(false), has_discardable_images_(false) {} + : has_non_aa_paint_(false), + has_discardable_images_(false), + has_text_(false) {} PaintOpBuffer::PaintOpBuffer(PaintOpBuffer&& other) { *this = std::move(other); @@ -2260,6 +2274,7 @@ PaintOpBuffer& PaintOpBuffer::operator=(PaintOpBuffer&& other) { subrecord_op_count_ = other.subrecord_op_count_; has_non_aa_paint_ = other.has_non_aa_paint_; has_discardable_images_ = other.has_discardable_images_; + has_text_ = other.has_text_; // Make sure the other pob can destruct safely. other.used_ = 0; @@ -2281,6 +2296,7 @@ void PaintOpBuffer::Reset() { subrecord_bytes_used_ = 0; subrecord_op_count_ = 0; has_discardable_images_ = false; + has_text_ = false; } // When |op| is a nested PaintOpBuffer, this returns the PaintOp inside @@ -2543,6 +2559,8 @@ bool PaintOpBuffer::operator==(const PaintOpBuffer& other) const { return false; if (has_discardable_images_ != other.has_discardable_images_) return false; + if (has_text_ != other.has_text_) + return false; auto left_iter = Iterator(this); auto right_iter = Iterator(&other); diff --git a/chromium/cc/paint/paint_op_buffer.h b/chromium/cc/paint/paint_op_buffer.h index 943fe9c9dd5..11615ac3697 100644 --- a/chromium/cc/paint/paint_op_buffer.h +++ b/chromium/cc/paint/paint_op_buffer.h @@ -18,7 +18,7 @@ #include "base/memory/aligned_memory.h" #include "base/optional.h" #include "cc/base/math_util.h" -#include "cc/paint/node_holder.h" +#include "cc/paint/node_id.h" #include "cc/paint/paint_canvas.h" #include "cc/paint/paint_export.h" #include "cc/paint/paint_flags.h" @@ -246,6 +246,8 @@ class CC_PAINT_EXPORT PaintOp { bool HasDiscardableImages() const { return false; } bool HasDiscardableImagesFromFlags() const { return false; } + bool HasText() const { return false; } + // Returns the number of bytes used by this op in referenced sub records // and display lists. This doesn't count other objects like paths or blobs. size_t AdditionalBytesUsed() const { return 0; } @@ -662,6 +664,7 @@ class CC_PAINT_EXPORT DrawRecordOp final : public PaintOp { bool HasDiscardableImages() const; int CountSlowPaths() const; bool HasNonAAPaint() const; + bool HasText() const; HAS_SERIALIZATION_FUNCTIONS(); sk_sp<const PaintRecord> record; @@ -741,8 +744,8 @@ class CC_PAINT_EXPORT DrawTextBlobOp final : public PaintOpWithFlags { DrawTextBlobOp(sk_sp<SkTextBlob> blob, SkScalar x, SkScalar y, - const PaintFlags& flags, - const NodeHolder& node_holder); + NodeId node_id, + const PaintFlags& flags); ~DrawTextBlobOp(); static void RasterWithFlags(const DrawTextBlobOp* op, const PaintFlags* flags, @@ -750,13 +753,14 @@ class CC_PAINT_EXPORT DrawTextBlobOp final : public PaintOpWithFlags { const PlaybackParams& params); bool IsValid() const { return flags.IsValid(); } static bool AreEqual(const PaintOp* left, const PaintOp* right); + bool HasText() const { return true; } HAS_SERIALIZATION_FUNCTIONS(); sk_sp<SkTextBlob> blob; SkScalar x; SkScalar y; // This field isn't serialized. - NodeHolder node_holder; + NodeId node_id = kInvalidNodeId; private: DrawTextBlobOp(); @@ -952,6 +956,7 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { int numSlowPaths() const { return num_slow_paths_; } bool HasNonAAPaint() const { return has_non_aa_paint_; } bool HasDiscardableImages() const { return has_discardable_images_; } + bool HasText() const { return has_text_; } bool operator==(const PaintOpBuffer& other) const; bool operator!=(const PaintOpBuffer& other) const { @@ -1012,6 +1017,8 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { has_discardable_images_ |= op->HasDiscardableImages(); has_discardable_images_ |= op->HasDiscardableImagesFromFlags(); + has_text_ |= (op->HasText()); + subrecord_bytes_used_ += op->AdditionalBytesUsed(); subrecord_op_count_ += op->AdditionalOpCount(); } @@ -1239,6 +1246,7 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { bool has_non_aa_paint_ : 1; bool has_discardable_images_ : 1; + bool has_text_ : 1; }; } // namespace cc diff --git a/chromium/cc/paint/paint_op_buffer_unittest.cc b/chromium/cc/paint/paint_op_buffer_unittest.cc index f40f19a4ac6..8ccd0abb75a 100644 --- a/chromium/cc/paint/paint_op_buffer_unittest.cc +++ b/chromium/cc/paint/paint_op_buffer_unittest.cc @@ -2732,15 +2732,10 @@ class MockImageProvider : public ImageProvider { ~MockImageProvider() override = default; - void DoNothing() {} - ImageProvider::ScopedResult GetRasterContent( const DrawImage& draw_image) override { - if (draw_image.paint_image().IsPaintWorklet()) { - auto callback = - base::BindOnce(&MockImageProvider::DoNothing, base::Unretained(this)); - return ScopedResult(record_, std::move(callback)); - } + if (draw_image.paint_image().IsPaintWorklet()) + return ScopedResult(record_); if (fail_all_decodes_) return ImageProvider::ScopedResult(); diff --git a/chromium/cc/paint/paint_op_reader.cc b/chromium/cc/paint/paint_op_reader.cc index 6dd4d8bb6a4..749875dff9b 100644 --- a/chromium/cc/paint/paint_op_reader.cc +++ b/chromium/cc/paint/paint_op_reader.cc @@ -8,6 +8,7 @@ #include <algorithm> #include "base/bits.h" +#include "base/compiler_specific.h" #include "base/debug/dump_without_crashing.h" #include "base/rand_util.h" #include "base/stl_util.h" @@ -236,7 +237,9 @@ void PaintOpReader::Read(SkPath* path) { case PaintCacheEntryState::kInlined: { size_t path_bytes = 0u; ReadSize(&path_bytes); - if (path_bytes > remaining_bytes_ || path_bytes == 0u) + if (path_bytes > remaining_bytes_) + SetInvalid(); + if (path_bytes == 0u) SetInvalid(); if (!valid_) return; @@ -445,7 +448,11 @@ void PaintOpReader::Read(sk_sp<SkTextBlob>* blob) { auto* scratch = CopyScratchSpace(data_bytes); sk_sp<SkTextBlob> deserialized_blob = SkTextBlob::Deserialize(scratch, data_bytes, procs); - if (!deserialized_blob || typeface_ctx.invalid_typeface) { + if (!deserialized_blob) { + SetInvalid(); + return; + } + if (typeface_ctx.invalid_typeface) { SetInvalid(); return; } @@ -611,6 +618,18 @@ void PaintOpReader::Read(SkColorType* color_type) { *color_type = static_cast<SkColorType>(raw_color_type); } +void PaintOpReader::Read(SkYUVColorSpace* yuv_color_space) { + uint32_t raw_yuv_color_space = kIdentity_SkYUVColorSpace; + ReadSimple(&raw_yuv_color_space); + + if (raw_yuv_color_space > kLastEnum_SkYUVColorSpace) { + SetInvalid(); + return; + } + + *yuv_color_space = static_cast<SkYUVColorSpace>(raw_yuv_color_space); +} + void PaintOpReader::AlignMemory(size_t alignment) { // Due to the math below, alignment must be a power of two. DCHECK_GT(alignment, 0u); @@ -629,7 +648,8 @@ void PaintOpReader::AlignMemory(size_t alignment) { remaining_bytes_ -= padding; } -inline void PaintOpReader::SetInvalid() { +// Don't inline this function so that crash reports can show the caller. +NOINLINE void PaintOpReader::SetInvalid() { if (valid_ && options_.crash_dump_on_failure && base::RandInt(1, 10) == 1) { base::debug::DumpWithoutCrashing(); } diff --git a/chromium/cc/paint/paint_op_reader.h b/chromium/cc/paint/paint_op_reader.h index e3ed7b86052..d6a7a821a6f 100644 --- a/chromium/cc/paint/paint_op_reader.h +++ b/chromium/cc/paint/paint_op_reader.h @@ -67,6 +67,7 @@ class CC_PAINT_EXPORT PaintOpReader { void Read(SkColorType* color_type); void Read(SkImageInfo* info); void Read(sk_sp<SkColorSpace>* color_space); + void Read(SkYUVColorSpace* yuv_color_space); void Read(SkClipOp* op) { uint8_t value = 0u; diff --git a/chromium/cc/paint/paint_op_writer.cc b/chromium/cc/paint/paint_op_writer.cc index e94efb08399..d9bf6fc97e2 100644 --- a/chromium/cc/paint/paint_op_writer.cc +++ b/chromium/cc/paint/paint_op_writer.cc @@ -39,7 +39,7 @@ SkIRect MakeSrcRect(const PaintImage& image) { size_t PaintOpWriter::GetFlattenableSize(const SkFlattenable* flattenable) { // The first bit is always written to indicate the serialized size of the // flattenable, or zero if it doesn't exist. - size_t total_size = sizeof(uint64_t) + alignof(uint64_t); + size_t total_size = sizeof(uint64_t) + sizeof(uint64_t) /* alignment */; if (!flattenable) return total_size; @@ -60,7 +60,7 @@ size_t PaintOpWriter::GetImageSize(const PaintImage& image) { image_size += sizeof(info.colorType()); image_size += sizeof(info.width()); image_size += sizeof(info.height()); - image_size += sizeof(uint64_t) + alignof(uint64_t); + image_size += sizeof(uint64_t) + sizeof(uint64_t) /* alignment */; image_size += info.computeMinByteSize(); } return image_size; @@ -466,6 +466,10 @@ void PaintOpWriter::Write(SkColorType color_type) { WriteSimple(static_cast<uint32_t>(color_type)); } +void PaintOpWriter::Write(SkYUVColorSpace yuv_color_space) { + WriteSimple(static_cast<uint32_t>(yuv_color_space)); +} + void PaintOpWriter::WriteData(size_t bytes, const void* input) { EnsureBytes(bytes); if (!valid_) diff --git a/chromium/cc/paint/paint_op_writer.h b/chromium/cc/paint/paint_op_writer.h index 2b7121c181b..af798ea1a03 100644 --- a/chromium/cc/paint/paint_op_writer.h +++ b/chromium/cc/paint/paint_op_writer.h @@ -11,6 +11,7 @@ #include "cc/paint/paint_export.h" #include "cc/paint/paint_filter.h" #include "cc/paint/paint_op_buffer_serializer.h" +#include "third_party/skia/include/core/SkImageInfo.h" struct SkRect; struct SkIRect; @@ -62,6 +63,7 @@ class CC_PAINT_EXPORT PaintOpWriter { void Write(const PaintFilter* filter); void Write(const sk_sp<SkTextBlob>& blob); void Write(SkColorType color_type); + void Write(SkYUVColorSpace yuv_color_space); void Write(SkClipOp op) { Write(static_cast<uint8_t>(op)); } void Write(PaintCanvas::AnnotationType type) { diff --git a/chromium/cc/paint/paint_worklet_input.h b/chromium/cc/paint/paint_worklet_input.h index 94e2cfda4d0..c54238dabb7 100644 --- a/chromium/cc/paint/paint_worklet_input.h +++ b/chromium/cc/paint/paint_worklet_input.h @@ -5,13 +5,18 @@ #ifndef CC_PAINT_PAINT_WORKLET_INPUT_H_ #define CC_PAINT_PAINT_WORKLET_INPUT_H_ +#include "base/containers/flat_map.h" #include "base/memory/ref_counted.h" -#include "cc/cc_export.h" +#include "cc/paint/paint_export.h" +#include "third_party/skia/include/core/SkRefCnt.h" #include "ui/gfx/geometry/size_f.h" namespace cc { -class CC_EXPORT PaintWorkletInput +class PaintOpBuffer; +using PaintRecord = PaintOpBuffer; + +class CC_PAINT_EXPORT PaintWorkletInput : public base::RefCountedThreadSafe<PaintWorkletInput> { public: virtual gfx::SizeF GetSize() const = 0; @@ -22,6 +27,11 @@ class CC_EXPORT PaintWorkletInput virtual ~PaintWorkletInput() = default; }; +// PaintWorkletRecordMap ties the input for a PaintWorklet (PaintWorkletInput) +// to the painted output (a PaintRecord). +using PaintWorkletRecordMap = + base::flat_map<scoped_refptr<PaintWorkletInput>, sk_sp<PaintRecord>>; + } // namespace cc #endif // CC_PAINT_PAINT_WORKLET_INPUT_H_ diff --git a/chromium/cc/paint/paint_worklet_job.cc b/chromium/cc/paint/paint_worklet_job.cc new file mode 100644 index 00000000000..e339853ee76 --- /dev/null +++ b/chromium/cc/paint/paint_worklet_job.cc @@ -0,0 +1,24 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/paint/paint_worklet_job.h" + +#include "cc/paint/paint_worklet_input.h" + +namespace cc { + +PaintWorkletJob::PaintWorkletJob(int layer_id, + scoped_refptr<PaintWorkletInput> input) + : layer_id_(layer_id), input_(std::move(input)) {} + +PaintWorkletJob::PaintWorkletJob(const PaintWorkletJob& other) = default; +PaintWorkletJob::PaintWorkletJob(PaintWorkletJob&& other) = default; +PaintWorkletJob::~PaintWorkletJob() = default; + +void PaintWorkletJob::SetOutput(sk_sp<PaintRecord> output) { + DCHECK(!output_); + output_ = std::move(output); +} + +} // namespace cc diff --git a/chromium/cc/paint/paint_worklet_job.h b/chromium/cc/paint/paint_worklet_job.h new file mode 100644 index 00000000000..e6b0d8a3b07 --- /dev/null +++ b/chromium/cc/paint/paint_worklet_job.h @@ -0,0 +1,59 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_PAINT_PAINT_WORKLET_JOB_H_ +#define CC_PAINT_PAINT_WORKLET_JOB_H_ + +#include "base/containers/flat_map.h" +#include "base/memory/scoped_refptr.h" +#include "cc/paint/paint_export.h" +#include "cc/paint/paint_record.h" +#include "third_party/skia/include/core/SkRefCnt.h" + +namespace cc { + +class PaintWorkletInput; + +// A PaintWorkletJob instance encapsulates the data that needs to be passed +// around in order to dispatch PaintWorklets to the worklet thread, paint them, +// and return the results to cc-impl. +class CC_PAINT_EXPORT PaintWorkletJob { + public: + PaintWorkletJob(int layer_id, scoped_refptr<PaintWorkletInput> input); + PaintWorkletJob(const PaintWorkletJob& other); + PaintWorkletJob(PaintWorkletJob&& other); + ~PaintWorkletJob(); + + int layer_id() const { return layer_id_; } + const scoped_refptr<PaintWorkletInput>& input() const { return input_; } + const sk_sp<PaintRecord>& output() const { return output_; } + + void SetOutput(sk_sp<PaintRecord> output); + + private: + // The id for the layer that the PaintWorkletInput is associated with. + int layer_id_; + + // The input for a PaintWorkletJob is encapsulated in a PaintWorkletInput + // instance; see class-level comments on |PaintWorkletInput| for details. + scoped_refptr<PaintWorkletInput> input_; + + // The output for a PaintWorkletJob is a series of paint ops for the painted + // content, that can be passed to raster. + sk_sp<PaintRecord> output_; +}; + +// The PaintWorklet dispatcher logic passes the PaintWorkletJobVector to the +// worklet thread during painting. To keep the structure alive on both the +// compositor and worklet side (as technically the compositor could be town down +// whilst the worklet is still painting), we use base::RefCountedJob for it. +using PaintWorkletJobVector = + base::RefCountedData<std::vector<PaintWorkletJob>>; +using PaintWorkletId = int; +using PaintWorkletJobMap = + base::flat_map<PaintWorkletId, scoped_refptr<PaintWorkletJobVector>>; + +} // namespace cc + +#endif // CC_PAINT_PAINT_WORKLET_JOB_H_ diff --git a/chromium/cc/paint/paint_worklet_layer_painter.h b/chromium/cc/paint/paint_worklet_layer_painter.h index 6535432bff5..4b56a60a8de 100644 --- a/chromium/cc/paint/paint_worklet_layer_painter.h +++ b/chromium/cc/paint/paint_worklet_layer_painter.h @@ -5,18 +5,32 @@ #ifndef CC_PAINT_PAINT_WORKLET_LAYER_PAINTER_H_ #define CC_PAINT_PAINT_WORKLET_LAYER_PAINTER_H_ -#include "cc/cc_export.h" +#include "base/callback.h" +#include "cc/paint/paint_export.h" #include "cc/paint/paint_record.h" +#include "cc/paint/paint_worklet_job.h" namespace cc { -class PaintWorkletInput; - -class CC_EXPORT PaintWorkletLayerPainter { +// PaintWorkletLayerPainter bridges between the compositor and the PaintWorklet +// thread, providing hooks for the compositor to paint PaintWorklet content that +// Blink has deferred on. +class CC_PAINT_EXPORT PaintWorkletLayerPainter { public: virtual ~PaintWorkletLayerPainter() {} - virtual sk_sp<PaintRecord> Paint(PaintWorkletInput*) = 0; + // Asynchronously paints a set of PaintWorklet instances. The results are + // returned via the provided callback, on the same thread that originally + // called this method. + // + // Only one dispatch is allowed at a time; the calling code should not call + // |DispatchWorklets| again until the passed |DoneCallback| has been called. + using DoneCallback = base::OnceCallback<void(PaintWorkletJobMap)>; + virtual void DispatchWorklets(PaintWorkletJobMap, DoneCallback) = 0; + + // Returns whether or not a dispatched set of PaintWorklet instances is + // currently being painted. + virtual bool HasOngoingDispatch() const = 0; }; } // namespace cc diff --git a/chromium/cc/paint/record_paint_canvas.cc b/chromium/cc/paint/record_paint_canvas.cc index b0277f51a5b..3b1c6108092 100644 --- a/chromium/cc/paint/record_paint_canvas.cc +++ b/chromium/cc/paint/record_paint_canvas.cc @@ -274,9 +274,9 @@ void RecordPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob, void RecordPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob, SkScalar x, SkScalar y, - const PaintFlags& flags, - const NodeHolder& holder) { - list_->push<DrawTextBlobOp>(std::move(blob), x, y, flags, holder); + NodeId node_id, + const PaintFlags& flags) { + list_->push<DrawTextBlobOp>(std::move(blob), x, y, node_id, flags); } void RecordPaintCanvas::drawPicture(sk_sp<const PaintRecord> record) { diff --git a/chromium/cc/paint/record_paint_canvas.h b/chromium/cc/paint/record_paint_canvas.h index c32547f2810..68ad78ab315 100644 --- a/chromium/cc/paint/record_paint_canvas.h +++ b/chromium/cc/paint/record_paint_canvas.h @@ -92,8 +92,8 @@ class CC_PAINT_EXPORT RecordPaintCanvas final : public PaintCanvas { void drawTextBlob(sk_sp<SkTextBlob> blob, SkScalar x, SkScalar y, - const PaintFlags& flags, - const NodeHolder& holder) override; + NodeId node_id, + const PaintFlags& flags) override; void drawPicture(sk_sp<const PaintRecord> record) override; diff --git a/chromium/cc/paint/skia_paint_canvas.cc b/chromium/cc/paint/skia_paint_canvas.cc index 45e6e8fd6a0..3a6dfbbf907 100644 --- a/chromium/cc/paint/skia_paint_canvas.cc +++ b/chromium/cc/paint/skia_paint_canvas.cc @@ -11,7 +11,6 @@ #include "third_party/skia/include/core/SkAnnotation.h" namespace cc { - SkiaPaintCanvas::ContextFlushes::ContextFlushes() : enable(false), max_draws_before_flush(-1) {} @@ -142,9 +141,11 @@ void SkiaPaintCanvas::drawLine(SkScalar x0, 255u); if (!raster_flags.flags()) return; - SkPaint paint = raster_flags.flags()->ToSkPaint(); - SkiaPaintCanvas::canvas_->drawLine(x0, y0, x1, y1, paint); + raster_flags.flags()->DrawToSk( + canvas_, [x0, y0, x1, y1](SkCanvas* c, const SkPaint& p) { + c->drawLine(x0, y0, x1, y1, p); + }); FlushAfterDrawIfNeeded(); } @@ -154,9 +155,9 @@ void SkiaPaintCanvas::drawRect(const SkRect& rect, const PaintFlags& flags) { 255u); if (!raster_flags.flags()) return; - SkPaint paint = raster_flags.flags()->ToSkPaint(); - - canvas_->drawRect(rect, paint); + raster_flags.flags()->DrawToSk( + canvas_, + [&rect](SkCanvas* c, const SkPaint& p) { c->drawRect(rect, p); }); FlushAfterDrawIfNeeded(); } @@ -166,9 +167,9 @@ void SkiaPaintCanvas::drawIRect(const SkIRect& rect, const PaintFlags& flags) { 255u); if (!raster_flags.flags()) return; - SkPaint paint = raster_flags.flags()->ToSkPaint(); - - canvas_->drawIRect(rect, paint); + raster_flags.flags()->DrawToSk( + canvas_, + [&rect](SkCanvas* c, const SkPaint& p) { c->drawIRect(rect, p); }); FlushAfterDrawIfNeeded(); } @@ -178,9 +179,9 @@ void SkiaPaintCanvas::drawOval(const SkRect& oval, const PaintFlags& flags) { 255u); if (!raster_flags.flags()) return; - SkPaint paint = raster_flags.flags()->ToSkPaint(); - - canvas_->drawOval(oval, paint); + raster_flags.flags()->DrawToSk( + canvas_, + [&oval](SkCanvas* c, const SkPaint& p) { c->drawOval(oval, p); }); FlushAfterDrawIfNeeded(); } @@ -190,9 +191,9 @@ void SkiaPaintCanvas::drawRRect(const SkRRect& rrect, const PaintFlags& flags) { 255u); if (!raster_flags.flags()) return; - SkPaint paint = raster_flags.flags()->ToSkPaint(); - - canvas_->drawRRect(rrect, paint); + raster_flags.flags()->DrawToSk( + canvas_, + [&rrect](SkCanvas* c, const SkPaint& p) { c->drawRRect(rrect, p); }); FlushAfterDrawIfNeeded(); } @@ -204,9 +205,10 @@ void SkiaPaintCanvas::drawDRRect(const SkRRect& outer, 255u); if (!raster_flags.flags()) return; - SkPaint paint = raster_flags.flags()->ToSkPaint(); - - canvas_->drawDRRect(outer, inner, paint); + raster_flags.flags()->DrawToSk( + canvas_, [&outer, &inner](SkCanvas* c, const SkPaint& p) { + c->drawDRRect(outer, inner, p); + }); FlushAfterDrawIfNeeded(); } @@ -219,9 +221,10 @@ void SkiaPaintCanvas::drawRoundRect(const SkRect& rect, 255u); if (!raster_flags.flags()) return; - SkPaint paint = raster_flags.flags()->ToSkPaint(); - - canvas_->drawRoundRect(rect, rx, ry, paint); + raster_flags.flags()->DrawToSk( + canvas_, [&rect, rx, ry](SkCanvas* c, const SkPaint& p) { + c->drawRoundRect(rect, rx, ry, p); + }); FlushAfterDrawIfNeeded(); } @@ -231,9 +234,9 @@ void SkiaPaintCanvas::drawPath(const SkPath& path, const PaintFlags& flags) { 255u); if (!raster_flags.flags()) return; - SkPaint paint = raster_flags.flags()->ToSkPaint(); - - canvas_->drawPath(path, paint); + raster_flags.flags()->DrawToSk( + canvas_, + [&path](SkCanvas* c, const SkPaint& p) { c->drawPath(path, p); }); FlushAfterDrawIfNeeded(); } @@ -293,16 +296,18 @@ void SkiaPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob, 255u); if (!raster_flags.flags()) return; - SkPaint paint = raster_flags.flags()->ToSkPaint(); - canvas_->drawTextBlob(blob, x, y, paint); + raster_flags.flags()->DrawToSk(canvas_, + [&blob, x, y](SkCanvas* c, const SkPaint& p) { + c->drawTextBlob(blob, x, y, p); + }); FlushAfterDrawIfNeeded(); } void SkiaPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob, SkScalar x, SkScalar y, - const PaintFlags& flags, - const NodeHolder& holder) { + NodeId node_id, + const PaintFlags& flags) { drawTextBlob(blob, x, y, flags); } diff --git a/chromium/cc/paint/skia_paint_canvas.h b/chromium/cc/paint/skia_paint_canvas.h index cf347d53a6e..6ba29a621d1 100644 --- a/chromium/cc/paint/skia_paint_canvas.h +++ b/chromium/cc/paint/skia_paint_canvas.h @@ -116,8 +116,8 @@ class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas { void drawTextBlob(sk_sp<SkTextBlob> blob, SkScalar x, SkScalar y, - const PaintFlags& flags, - const NodeHolder& holder) override; + NodeId node_id, + const PaintFlags& flags) override; void drawPicture(sk_sp<const PaintRecord> record) override; diff --git a/chromium/cc/paint/text_holder.h b/chromium/cc/paint/text_holder.h deleted file mode 100644 index 0bda890b060..00000000000 --- a/chromium/cc/paint/text_holder.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_PAINT_TEXT_HOLDER_H_ -#define CC_PAINT_TEXT_HOLDER_H_ - -#include <set> -#include <string> - -#include "base/memory/ref_counted.h" -#include "cc/paint/paint_export.h" - -namespace cc { - -// The base class for embedder (blink) to associate DrawTextBlobOp with a text -// node. -// This class has to be RefCountedThreadSafe because the asscociated -// DrawTextBlobOp could be dereferenced in main, compositor or raster worker -// threads. -class CC_PAINT_EXPORT TextHolder - : public base::RefCountedThreadSafe<TextHolder> { - protected: - friend class base::RefCountedThreadSafe<TextHolder>; - virtual ~TextHolder() = default; -}; - -} // namespace cc - -#endif // CC_PAINT_TEXT_HOLDER_H_ diff --git a/chromium/cc/paint/transfer_cache_unittest.cc b/chromium/cc/paint/transfer_cache_unittest.cc index 7eb83e82dd2..19fe578ada0 100644 --- a/chromium/cc/paint/transfer_cache_unittest.cc +++ b/chromium/cc/paint/transfer_cache_unittest.cc @@ -10,8 +10,8 @@ #include "cc/paint/image_transfer_cache_entry.h" #include "cc/paint/raw_memory_transfer_cache_entry.h" #include "cc/paint/transfer_cache_entry.h" -#include "cc/test/test_in_process_context_provider.h" #include "components/viz/test/test_gpu_service_holder.h" +#include "components/viz/test/test_in_process_context_provider.h" #include "gpu/command_buffer/client/client_transfer_cache.h" #include "gpu/command_buffer/client/gles2_cmd_helper.h" #include "gpu/command_buffer/client/gles2_implementation.h" @@ -85,7 +85,7 @@ class TransferCacheTest : public testing::Test { private: viz::TestGpuMemoryBufferManager gpu_memory_buffer_manager_; - TestImageFactory image_factory_; + viz::TestImageFactory image_factory_; std::unique_ptr<gpu::RasterInProcessContext> context_; gl::DisableNullDrawGLBindings enable_pixel_output_; ClientRawMemoryTransferCacheEntry test_client_entry_; diff --git a/chromium/cc/raster/bitmap_raster_buffer_provider.cc b/chromium/cc/raster/bitmap_raster_buffer_provider.cc index 754ff6979a0..db625272e7c 100644 --- a/chromium/cc/raster/bitmap_raster_buffer_provider.cc +++ b/chromium/cc/raster/bitmap_raster_buffer_provider.cc @@ -108,12 +108,11 @@ BitmapRasterBufferProvider::AcquireBufferForRaster( auto backing = std::make_unique<BitmapSoftwareBacking>(); backing->frame_sink = frame_sink_; backing->shared_bitmap_id = viz::SharedBitmap::GenerateId(); - base::MappedReadOnlyRegion mapped_region = + base::MappedReadOnlyRegion shm = viz::bitmap_allocation::AllocateSharedBitmap(size, viz::RGBA_8888); - backing->mapping = std::move(mapped_region.mapping); - frame_sink_->DidAllocateSharedBitmap( - viz::bitmap_allocation::ToMojoHandle(std::move(mapped_region.region)), - backing->shared_bitmap_id); + backing->mapping = std::move(shm.mapping); + frame_sink_->DidAllocateSharedBitmap(std::move(shm.region), + backing->shared_bitmap_id); resource.set_software_backing(std::move(backing)); } diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.cc b/chromium/cc/raster/gpu_raster_buffer_provider.cc index 79c32f61b5d..e8533353bef 100644 --- a/chromium/cc/raster/gpu_raster_buffer_provider.cc +++ b/chromium/cc/raster/gpu_raster_buffer_provider.cc @@ -338,7 +338,7 @@ GpuRasterBufferProvider::GpuRasterBufferProvider( const gfx::Size& max_tile_size, bool unpremultiply_and_dither_low_bit_depth_tiles, bool enable_oop_rasterization, - int raster_metric_frequency) + float raster_metric_probability) : compositor_context_provider_(compositor_context_provider), worker_context_provider_(worker_context_provider), use_gpu_memory_buffer_resources_(use_gpu_memory_buffer_resources), @@ -348,9 +348,8 @@ GpuRasterBufferProvider::GpuRasterBufferProvider( unpremultiply_and_dither_low_bit_depth_tiles_( unpremultiply_and_dither_low_bit_depth_tiles), enable_oop_rasterization_(enable_oop_rasterization), - raster_metric_frequency_(raster_metric_frequency), - random_generator_(base::RandUint64()), - uniform_distribution_(1, raster_metric_frequency) { + random_generator_((uint32_t)base::RandUint64()), + bernoulli_distribution_(raster_metric_probability) { DCHECK(compositor_context_provider); DCHECK(worker_context_provider); } @@ -497,8 +496,7 @@ gpu::SyncToken GpuRasterBufferProvider::PlaybackOnWorkerThreadInternal( gpu::raster::RasterInterface* ri = scoped_context.RasterInterface(); DCHECK(ri); - const bool measure_raster_metric = - uniform_distribution_(random_generator_) == raster_metric_frequency_; + const bool measure_raster_metric = bernoulli_distribution_(random_generator_); gfx::Rect playback_rect = raster_full_rect; if (resource_has_previous_content) { diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.h b/chromium/cc/raster/gpu_raster_buffer_provider.h index e6a435978a7..945b8b821a1 100644 --- a/chromium/cc/raster/gpu_raster_buffer_provider.h +++ b/chromium/cc/raster/gpu_raster_buffer_provider.h @@ -26,16 +26,17 @@ namespace cc { class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider { public: - static constexpr int kRasterMetricFrequency = 100; - GpuRasterBufferProvider(viz::ContextProvider* compositor_context_provider, - viz::RasterContextProvider* worker_context_provider, - bool use_gpu_memory_buffer_resources, - int gpu_rasterization_msaa_sample_count, - viz::ResourceFormat tile_format, - const gfx::Size& max_tile_size, - bool unpremultiply_and_dither_low_bit_depth_tiles, - bool enable_oop_rasterization, - int raster_metric_frequency = kRasterMetricFrequency); + static constexpr float kRasterMetricProbability = 0.01; + GpuRasterBufferProvider( + viz::ContextProvider* compositor_context_provider, + viz::RasterContextProvider* worker_context_provider, + bool use_gpu_memory_buffer_resources, + int gpu_rasterization_msaa_sample_count, + viz::ResourceFormat tile_format, + const gfx::Size& max_tile_size, + bool unpremultiply_and_dither_low_bit_depth_tiles, + bool enable_oop_rasterization, + float raster_metric_probability = kRasterMetricProbability); GpuRasterBufferProvider(const GpuRasterBufferProvider&) = delete; ~GpuRasterBufferProvider() override; @@ -154,7 +155,6 @@ class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider { const gfx::Size max_tile_size_; const bool unpremultiply_and_dither_low_bit_depth_tiles_; const bool enable_oop_rasterization_; - const int raster_metric_frequency_; // Note that this lock should never be acquired while holding the raster // context lock. @@ -164,7 +164,7 @@ class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider { // Accessed with the worker context lock acquired. std::mt19937 random_generator_; - std::uniform_int_distribution<int> uniform_distribution_; + std::bernoulli_distribution bernoulli_distribution_; }; } // namespace cc diff --git a/chromium/cc/raster/one_copy_raster_buffer_provider.cc b/chromium/cc/raster/one_copy_raster_buffer_provider.cc index daf5a89f012..6a40f4e513e 100644 --- a/chromium/cc/raster/one_copy_raster_buffer_provider.cc +++ b/chromium/cc/raster/one_copy_raster_buffer_provider.cc @@ -320,7 +320,8 @@ void OneCopyRasterBufferProvider::PlaybackToStagingBuffer( if (staging_buffer->gpu_memory_buffer) { gfx::GpuMemoryBuffer* buffer = staging_buffer->gpu_memory_buffer.get(); - DCHECK_EQ(1u, gfx::NumberOfPlanesForBufferFormat(buffer->GetFormat())); + DCHECK_EQ(1u, + gfx::NumberOfPlanesForLinearBufferFormat(buffer->GetFormat())); bool rv = buffer->Map(); DCHECK(rv); DCHECK(buffer->memory(0)); diff --git a/chromium/cc/raster/paint_worklet_image_provider.cc b/chromium/cc/raster/paint_worklet_image_provider.cc index 7312ba3dced..a3821403961 100644 --- a/chromium/cc/raster/paint_worklet_image_provider.cc +++ b/chromium/cc/raster/paint_worklet_image_provider.cc @@ -5,15 +5,13 @@ #include "cc/raster/paint_worklet_image_provider.h" #include <utility> -#include "cc/tiles/paint_worklet_image_cache.h" +#include "base/bind_helpers.h" namespace cc { PaintWorkletImageProvider::PaintWorkletImageProvider( - PaintWorkletImageCache* cache) - : cache_(cache) { - DCHECK(cache_); -} + PaintWorkletRecordMap records) + : records_(std::move(records)) {} PaintWorkletImageProvider::~PaintWorkletImageProvider() = default; @@ -24,11 +22,12 @@ PaintWorkletImageProvider& PaintWorkletImageProvider::operator=( PaintWorkletImageProvider&& other) = default; ImageProvider::ScopedResult PaintWorkletImageProvider::GetPaintRecordResult( - PaintWorkletInput* input) { - std::pair<sk_sp<PaintRecord>, base::OnceCallback<void()>> - record_and_callback = cache_->GetPaintRecordAndRef(input); - return ImageProvider::ScopedResult(std::move(record_and_callback.first), - std::move(record_and_callback.second)); + scoped_refptr<PaintWorkletInput> input) { + // The |records_| contains all known PaintWorkletInputs, whether they are + // painted or not, so |input| should always exist in it. + auto it = records_.find(input); + DCHECK(it != records_.end()); + return ImageProvider::ScopedResult(it->second); } } // namespace cc diff --git a/chromium/cc/raster/paint_worklet_image_provider.h b/chromium/cc/raster/paint_worklet_image_provider.h index 866a348efa3..6204c397e57 100644 --- a/chromium/cc/raster/paint_worklet_image_provider.h +++ b/chromium/cc/raster/paint_worklet_image_provider.h @@ -7,16 +7,20 @@ #include "cc/cc_export.h" #include "cc/paint/image_provider.h" +#include "cc/paint/paint_worklet_input.h" namespace cc { -class PaintWorkletImageCache; -class PaintWorkletInput; -// PaintWorkletImageProvider is a bridge between PaintWorkletImageCache and its -// rasterization. +// PaintWorkletImageProvider is a storage class for PaintWorklets and their +// painted content for use during rasterisation. +// +// PaintWorklet-based images are not painted at Blink Paint time; instead a +// placeholder PaintWorkletInput is put in place and the painting is done later +// from the cc-impl thread. By the time raster happens the resultant PaintRecord +// is available, and this class provides the lookup from input to record. class CC_EXPORT PaintWorkletImageProvider { public: - explicit PaintWorkletImageProvider(PaintWorkletImageCache* cache); + explicit PaintWorkletImageProvider(PaintWorkletRecordMap records); PaintWorkletImageProvider(const PaintWorkletImageProvider&) = delete; PaintWorkletImageProvider(PaintWorkletImageProvider&& other); ~PaintWorkletImageProvider(); @@ -25,10 +29,11 @@ class CC_EXPORT PaintWorkletImageProvider { delete; PaintWorkletImageProvider& operator=(PaintWorkletImageProvider&& other); - ImageProvider::ScopedResult GetPaintRecordResult(PaintWorkletInput* input); + ImageProvider::ScopedResult GetPaintRecordResult( + scoped_refptr<PaintWorkletInput> input); private: - PaintWorkletImageCache* cache_; + PaintWorkletRecordMap records_; }; } // namespace cc diff --git a/chromium/cc/raster/raster_buffer_provider.cc b/chromium/cc/raster/raster_buffer_provider.cc index 6252168a3b1..c4fa5a9be41 100644 --- a/chromium/cc/raster/raster_buffer_provider.cc +++ b/chromium/cc/raster/raster_buffer_provider.cc @@ -46,6 +46,7 @@ bool IsSupportedPlaybackToMemoryFormat(viz::ResourceFormat format) { case viz::YVU_420: case viz::YUV_420_BIPLANAR: case viz::UYVY_422: + case viz::P010: return false; } NOTREACHED(); @@ -145,6 +146,7 @@ void RasterBufferProvider::PlaybackToMemory( case viz::YVU_420: case viz::YUV_420_BIPLANAR: case viz::UYVY_422: + case viz::P010: NOTREACHED(); return; } diff --git a/chromium/cc/raster/raster_source.cc b/chromium/cc/raster/raster_source.cc index 8c490e481d0..95235b98fdc 100644 --- a/chromium/cc/raster/raster_source.cc +++ b/chromium/cc/raster/raster_source.cc @@ -245,6 +245,10 @@ gfx::Rect RasterSource::RecordedViewport() const { return recorded_viewport_; } +bool RasterSource::HasText() const { + return display_list_ && display_list_->HasText(); +} + void RasterSource::AsValueInto(base::trace_event::TracedValue* array) const { if (display_list_.get()) viz::TracedValue::AppendIDRef(display_list_.get(), array); @@ -255,6 +259,13 @@ void RasterSource::DidBeginTracing() { display_list_->EmitTraceSnapshot(); } +std::vector<scoped_refptr<PaintWorkletInput>> +RasterSource::GetPaintWorkletInputs() const { + if (!display_list_) + return {}; + return display_list_->discardable_image_map().paint_worklet_inputs(); +} + RasterSource::PlaybackSettings::PlaybackSettings() = default; RasterSource::PlaybackSettings::PlaybackSettings(const PlaybackSettings&) = diff --git a/chromium/cc/raster/raster_source.h b/chromium/cc/raster/raster_source.h index 4866e607fe7..e2af9afa527 100644 --- a/chromium/cc/raster/raster_source.h +++ b/chromium/cc/raster/raster_source.h @@ -103,6 +103,9 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> { // Valid rectangle in which everything is recorded and can be rastered from. virtual gfx::Rect RecordedViewport() const; + // Returns true if this raster source may try and draw text. + bool HasText() const; + // Tracing functionality. virtual void DidBeginTracing(); virtual void AsValueInto(base::trace_event::TracedValue* array) const; @@ -113,6 +116,8 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> { return display_list_; } + std::vector<scoped_refptr<PaintWorkletInput>> GetPaintWorkletInputs() const; + float recording_scale_factor() const { return recording_scale_factor_; } SkColor background_color() const { return background_color_; } diff --git a/chromium/cc/raster/raster_source_unittest.cc b/chromium/cc/raster/raster_source_unittest.cc index 0bd9e4fc367..7c73489c4bb 100644 --- a/chromium/cc/raster/raster_source_unittest.cc +++ b/chromium/cc/raster/raster_source_unittest.cc @@ -182,63 +182,6 @@ TEST(RasterSourceTest, AnalyzeIsSolidScaled) { } } -TEST(RasterSourceTest, MultiPaintWorkletImages) { - gfx::Size layer_bounds(512, 512); - - std::unique_ptr<FakeRecordingSource> recording_source = - FakeRecordingSource::CreateFilledRecordingSource(layer_bounds); - - scoped_refptr<TestPaintWorkletInput> input1 = - base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(32.0f, 32.0f)); - scoped_refptr<TestPaintWorkletInput> input2 = - base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(64.0f, 64.0f)); - scoped_refptr<TestPaintWorkletInput> input3 = - base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100.0f, 100.0f)); - - PaintImage discardable_image[2][2]; - discardable_image[0][0] = CreatePaintWorkletPaintImage(input1); - discardable_image[0][1] = CreatePaintWorkletPaintImage(input2); - discardable_image[1][1] = CreatePaintWorkletPaintImage(input3); - - recording_source->add_draw_image(discardable_image[0][0], gfx::Point(0, 0)); - recording_source->add_draw_image(discardable_image[0][1], gfx::Point(260, 0)); - recording_source->add_draw_image(discardable_image[1][1], - gfx::Point(260, 260)); - recording_source->Rerecord(); - - scoped_refptr<RasterSource> raster = recording_source->CreateRasterSource(); - - // Tile sized iterators. These should find only one image. - { - std::vector<const DrawImage*> images; - raster->GetDiscardableImagesInRect(gfx::Rect(0, 0, 256, 256), &images); - EXPECT_EQ(1u, images.size()); - EXPECT_EQ(discardable_image[0][0], images[0]->paint_image()); - } - // Shifted tile sized iterators. These should find only one image. - { - std::vector<const DrawImage*> images; - raster->GetDiscardableImagesInRect(gfx::Rect(260, 260, 256, 256), &images); - EXPECT_EQ(1u, images.size()); - EXPECT_EQ(discardable_image[1][1], images[0]->paint_image()); - } - // Ensure there's no discardable pixel refs in the empty cell - { - std::vector<const DrawImage*> images; - raster->GetDiscardableImagesInRect(gfx::Rect(0, 256, 256, 256), &images); - EXPECT_EQ(0u, images.size()); - } - // Layer sized iterators. These should find three images. - { - std::vector<const DrawImage*> images; - raster->GetDiscardableImagesInRect(gfx::Rect(0, 0, 512, 512), &images); - EXPECT_EQ(3u, images.size()); - EXPECT_EQ(discardable_image[0][0], images[0]->paint_image()); - EXPECT_EQ(discardable_image[0][1], images[1]->paint_image()); - EXPECT_EQ(discardable_image[1][1], images[2]->paint_image()); - } -} - TEST(RasterSourceTest, PixelRefIteratorDiscardableRefsOneTile) { gfx::Size layer_bounds(512, 512); diff --git a/chromium/cc/raster/single_thread_task_graph_runner.cc b/chromium/cc/raster/single_thread_task_graph_runner.cc index afb47e818a0..67e4ab32d81 100644 --- a/chromium/cc/raster/single_thread_task_graph_runner.cc +++ b/chromium/cc/raster/single_thread_task_graph_runner.cc @@ -9,7 +9,6 @@ #include <string> #include "base/threading/simple_thread.h" -#include "base/threading/thread_restrictions.h" #include "base/trace_event/trace_event.h" namespace cc { @@ -83,8 +82,6 @@ void SingleThreadTaskGraphRunner::WaitForTasksToFinishRunning( { base::AutoLock lock(lock_); - // http://crbug.com/902823 - base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait; auto* task_namespace = work_queue_.GetNamespaceForToken(token); diff --git a/chromium/cc/raster/staging_buffer_pool.cc b/chromium/cc/raster/staging_buffer_pool.cc index 5970012097f..b601add276b 100644 --- a/chromium/cc/raster/staging_buffer_pool.cc +++ b/chromium/cc/raster/staging_buffer_pool.cc @@ -128,8 +128,7 @@ StagingBufferPool::StagingBufferPool( free_staging_buffer_usage_in_bytes_(0), staging_buffer_expiration_delay_( base::TimeDelta::FromMilliseconds(kStagingBufferExpirationDelayMs)), - reduce_memory_usage_pending_(false), - weak_ptr_factory_(this) { + reduce_memory_usage_pending_(false) { DCHECK(worker_context_provider_); base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( this, "cc::StagingBufferPool", base::ThreadTaskRunnerHandle::Get()); diff --git a/chromium/cc/raster/staging_buffer_pool.h b/chromium/cc/raster/staging_buffer_pool.h index 4b2953b01a5..bcea8ab2fa9 100644 --- a/chromium/cc/raster/staging_buffer_pool.h +++ b/chromium/cc/raster/staging_buffer_pool.h @@ -142,7 +142,7 @@ class CC_EXPORT StagingBufferPool std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_; - base::WeakPtrFactory<StagingBufferPool> weak_ptr_factory_; + base::WeakPtrFactory<StagingBufferPool> weak_ptr_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/raster/synchronous_task_graph_runner.cc b/chromium/cc/raster/synchronous_task_graph_runner.cc index 4ee102e943a..b7e0e94b672 100644 --- a/chromium/cc/raster/synchronous_task_graph_runner.cc +++ b/chromium/cc/raster/synchronous_task_graph_runner.cc @@ -10,7 +10,6 @@ #include <utility> #include "base/threading/simple_thread.h" -#include "base/threading/thread_restrictions.h" #include "base/trace_event/trace_event.h" namespace cc { diff --git a/chromium/cc/raster/task_graph_work_queue.cc b/chromium/cc/raster/task_graph_work_queue.cc index 2d04f52e6aa..c268f7eb00b 100644 --- a/chromium/cc/raster/task_graph_work_queue.cc +++ b/chromium/cc/raster/task_graph_work_queue.cc @@ -215,7 +215,7 @@ void TaskGraphWorkQueue::ScheduleTasks(NamespaceToken token, TaskGraph* graph) { })) continue; - DCHECK(!base::ContainsValue(task_namespace.completed_tasks, node.task)); + DCHECK(!base::Contains(task_namespace.completed_tasks, node.task)); node.task->state().DidCancel(); task_namespace.completed_tasks.push_back(node.task); } @@ -326,7 +326,7 @@ void TaskGraphWorkQueue::CompleteTask(PrioritizedTask completed_task) { TaskNamespace::Vector& ready_to_run_namespaces = ready_to_run_namespaces_[dependent_node.category]; - DCHECK(!base::ContainsValue(ready_to_run_namespaces, task_namespace)); + DCHECK(!base::Contains(ready_to_run_namespaces, task_namespace)); ready_to_run_namespaces.push_back(task_namespace); } ready_to_run_namespaces_has_heap_properties = false; diff --git a/chromium/cc/raster/zero_copy_raster_buffer_provider.cc b/chromium/cc/raster/zero_copy_raster_buffer_provider.cc index bce36db08b2..ef6c2c73433 100644 --- a/chromium/cc/raster/zero_copy_raster_buffer_provider.cc +++ b/chromium/cc/raster/zero_copy_raster_buffer_provider.cc @@ -129,7 +129,7 @@ class ZeroCopyRasterBufferImpl : public RasterBuffer { return; } - DCHECK_EQ(1u, gfx::NumberOfPlanesForBufferFormat( + DCHECK_EQ(1u, gfx::NumberOfPlanesForLinearBufferFormat( gpu_memory_buffer_->GetFormat())); bool rv = gpu_memory_buffer_->Map(); DCHECK(rv); diff --git a/chromium/cc/resources/resource_pool.cc b/chromium/cc/resources/resource_pool.cc index 9c73860e220..e169cac0f0f 100644 --- a/chromium/cc/resources/resource_pool.cc +++ b/chromium/cc/resources/resource_pool.cc @@ -99,8 +99,7 @@ ResourcePool::ResourcePool( disallow_non_exact_reuse_(disallow_non_exact_reuse), tracing_id_(g_next_tracing_id.GetNext()), flush_evicted_resources_deadline_(base::TimeTicks::Max()), - clock_(base::DefaultTickClock::GetInstance()), - weak_ptr_factory_(this) { + clock_(base::DefaultTickClock::GetInstance()) { base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( this, "cc::ResourcePool", task_runner_.get()); memory_pressure_listener_.reset( diff --git a/chromium/cc/resources/resource_pool.h b/chromium/cc/resources/resource_pool.h index b7f2f8d9e77..700bba9b88f 100644 --- a/chromium/cc/resources/resource_pool.h +++ b/chromium/cc/resources/resource_pool.h @@ -408,7 +408,7 @@ class CC_EXPORT ResourcePool : public base::trace_event::MemoryDumpProvider { const base::TickClock* clock_; - base::WeakPtrFactory<ResourcePool> weak_ptr_factory_; + base::WeakPtrFactory<ResourcePool> weak_ptr_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/scheduler/compositor_frame_reporter.cc b/chromium/cc/scheduler/compositor_frame_reporter.cc index 9b4c7ac2a5f..1a41c5c6fec 100644 --- a/chromium/cc/scheduler/compositor_frame_reporter.cc +++ b/chromium/cc/scheduler/compositor_frame_reporter.cc @@ -4,36 +4,120 @@ #include "cc/scheduler/compositor_frame_reporter.h" +#include <string> + +#include "base/metrics/histogram_macros.h" +#include "base/strings/strcat.h" #include "base/trace_event/trace_event.h" +#include "cc/base/rolling_time_delta_history.h" namespace cc { +namespace { + +// When considering if a time is abnormal, compare the stage execution +// time to this percentile from the previous times of the same stage. +static constexpr double kAbnormalityPercentile = 95; + +// Use for determining abnormal execution times. If the sample size is less +// than this then don't check for abnormal execution time. +static constexpr size_t kMinimumTimeDeltaSampleSize = 20; + +static constexpr int kMissedFrameReportTypeCount = + static_cast<int>(CompositorFrameReporter::MissedFrameReportTypes:: + kMissedFrameReportTypeCount); +static constexpr int kStageTypeCount = + static_cast<int>(CompositorFrameReporter::StageType::kStageTypeCount); -CompositorFrameReporter::CompositorFrameReporter() { - TRACE_EVENT_ASYNC_BEGIN0("cc,benchmark", "PipelineReporter", this); +// Names for CompositorFrameReporter::StageType, which should be updated in case +// of changes to the enum. +constexpr const char* kStageNames[]{ + "BeginImplFrameToSendBeginMainFrame", + "SendBeginMainFrameToCommit", + "Commit", + "EndCommitToActivation", + "Activation", + "EndActivateToSubmitCompositorFrame", + "SubmitCompositorFrameToPresentationCompositorFrame", + "TotalLatency"}; +static_assert(sizeof(kStageNames) / sizeof(kStageNames[0]) == kStageTypeCount, + "Compositor latency stages has changed."); + +// Names for CompositorFrameReporter::MissedFrameReportTypes, which should be +// updated in case of changes to the enum. +constexpr const char* kReportTypeNames[]{"", "MissedFrame.", + "MissedFrameLatencyIncrease."}; +static_assert(sizeof(kReportTypeNames) / sizeof(kReportTypeNames[0]) == + kMissedFrameReportTypeCount, + "Compositor latency report types has changed."); + +// This value should be recalculate in case of changes to the number of values +// in CompositorFrameReporter::MissedFrameReportTypes or in +// CompositorFrameReporter::StageType +static constexpr int kMaxHistogramIndex = + 2 * kMissedFrameReportTypeCount * kStageTypeCount; +static constexpr int kHistogramMin = 1; +static constexpr int kHistogramMax = 350000; +static constexpr int kHistogramBucketCount = 50; +} // namespace + +CompositorFrameReporter::CompositorFrameReporter(bool is_single_threaded) + : is_single_threaded_(is_single_threaded) { + TRACE_EVENT_ASYNC_BEGIN1("cc,benchmark", "PipelineReporter", this, + "is_single_threaded", is_single_threaded); } CompositorFrameReporter::~CompositorFrameReporter() { - TerminateFrame(); + TerminateReporter(); } -void CompositorFrameReporter::StartStage(const char* stage_name) { - TRACE_EVENT_ASYNC_STEP_INTO0("cc,benchmark", "PipelineReporter", this, - TRACE_STR_COPY(stage_name)); +void CompositorFrameReporter::StartStage( + CompositorFrameReporter::StageType stage_type, + base::TimeTicks start_time, + RollingTimeDeltaHistory* stage_time_delta_history) { + EndCurrentStage(start_time); + current_stage_.stage_type = stage_type; + current_stage_.start_time = start_time; + current_stage_.time_delta_history = stage_time_delta_history; + int stage_type_index = static_cast<int>(current_stage_.stage_type); + CHECK_LT(stage_type_index, static_cast<int>(StageType::kStageTypeCount)); + CHECK_GE(stage_type_index, 0); + TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0( + "cc,benchmark", "PipelineReporter", this, + TRACE_STR_COPY(kStageNames[stage_type_index]), start_time); } -void CompositorFrameReporter::SetFrameTerminationStatus( - FrameTerminationStatus termination_status) { +void CompositorFrameReporter::EndCurrentStage(base::TimeTicks end_time) { + if (current_stage_.start_time == base::TimeTicks()) + return; + current_stage_.end_time = end_time; + stage_history_.emplace_back(current_stage_); + current_stage_.start_time = base::TimeTicks(); + current_stage_.time_delta_history = nullptr; +} + +void CompositorFrameReporter::MissedSubmittedFrame() { + submitted_frame_missed_deadline_ = true; +} + +void CompositorFrameReporter::TerminateFrame( + FrameTerminationStatus termination_status, + base::TimeTicks termination_time) { frame_termination_status_ = termination_status; + frame_termination_time_ = termination_time; + EndCurrentStage(frame_termination_time_); } -void CompositorFrameReporter::TerminateFrame() { - const char* termination_status_str; +void CompositorFrameReporter::TerminateReporter() { + DCHECK_EQ(current_stage_.start_time, base::TimeTicks()); + bool report_latency = false; + const char* termination_status_str = nullptr; switch (frame_termination_status_) { - case FrameTerminationStatus::kSubmittedFrame: - termination_status_str = "submitted_frame"; + case FrameTerminationStatus::kPresentedFrame: + report_latency = true; + termination_status_str = "presented_frame"; break; - case FrameTerminationStatus::kSubmittedFrameMissedDeadline: - termination_status_str = "missed_frame"; + case FrameTerminationStatus::kDidNotPresentFrame: + termination_status_str = "did_not_present_frame"; break; case FrameTerminationStatus::kMainFrameAborted: termination_status_str = "main_frame_aborted"; @@ -48,9 +132,96 @@ void CompositorFrameReporter::TerminateFrame() { NOTREACHED(); break; } - TRACE_EVENT_ASYNC_END1("cc,benchmark", "PipelineReporter", this, - "termination_status", - TRACE_STR_COPY(termination_status_str)); - // TODO(alsan): UMA histogram reporting + + const char* submission_status_str = + submitted_frame_missed_deadline_ ? "missed_frame" : "non_missed_frame"; + + TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP2( + "cc,benchmark", "PipelineReporter", this, frame_termination_time_, + "termination_status", TRACE_STR_COPY(termination_status_str), + "compositor_frame_submission_status", + TRACE_STR_COPY(submission_status_str)); + + // Only report histograms if the frame was presented. + if (report_latency) { + DCHECK(stage_history_.size()); + stage_history_.emplace_back( + StageData{StageType::kTotalLatency, stage_history_.front().start_time, + stage_history_.back().end_time, nullptr}); + ReportStageHistograms(submitted_frame_missed_deadline_); + } +} + +void CompositorFrameReporter::ReportStageHistograms(bool missed_frame) const { + CompositorFrameReporter::MissedFrameReportTypes report_type = + missed_frame + ? CompositorFrameReporter::MissedFrameReportTypes::kMissedFrame + : CompositorFrameReporter::MissedFrameReportTypes::kNonMissedFrame; + + for (const StageData& stage : stage_history_) { + base::TimeDelta stage_delta = stage.end_time - stage.start_time; + ReportHistogram(report_type, stage.stage_type, stage_delta); + + if (!stage.time_delta_history) + continue; + + if (!missed_frame) { + stage.time_delta_history->InsertSample(stage_delta); + } else { + // If enough sample data is recorded compare the stage duration with the + // known normal stage duration and if it's higher than normal, report the + // difference. + if (stage.time_delta_history->sample_count() >= + kMinimumTimeDeltaSampleSize) { + base::TimeDelta time_upper_limit = GetStateNormalUpperLimit(stage); + if (stage_delta > time_upper_limit) { + ReportHistogram(CompositorFrameReporter::MissedFrameReportTypes:: + kMissedFrameLatencyIncrease, + stage.stage_type, stage_delta - time_upper_limit); + } + } + + // In case of a missing frame, remove a sample from the recorded normal + // stages. This invalidates the recorded normal durations if at a point + // all frames start missing for a while. + stage.time_delta_history->RemoveOldestSample(); + } + } +} + +void CompositorFrameReporter::ReportHistogram( + CompositorFrameReporter::MissedFrameReportTypes report_type, + CompositorFrameReporter::StageType stage_type, + base::TimeDelta time_delta) const { + const int report_type_index = static_cast<int>(report_type); + const int stage_type_index = static_cast<int>(stage_type); + const int histogram_index = + (stage_type_index * kMissedFrameReportTypeCount + report_type_index) * 2 + + (is_single_threaded_ ? 1 : 0); + + CHECK_LT(stage_type_index, kStageTypeCount); + CHECK_GE(stage_type_index, 0); + CHECK_LT(report_type_index, kMissedFrameReportTypeCount); + CHECK_GE(report_type_index, 0); + CHECK_LT(histogram_index, kMaxHistogramIndex); + CHECK_GE(histogram_index, 0); + + const char* compositor_type = is_single_threaded_ ? "SingleThreaded" : ""; + const std::string name = + base::StrCat({compositor_type, "CompositorLatency.", + kReportTypeNames[static_cast<int>(report_type)], + kStageNames[static_cast<int>(stage_type)]}); + + STATIC_HISTOGRAM_POINTER_GROUP( + name, histogram_index, kMaxHistogramIndex, + AddTimeMicrosecondsGranularity(time_delta), + base::Histogram::FactoryGet( + name, kHistogramMin, kHistogramMax, kHistogramBucketCount, + base::HistogramBase::kUmaTargetedHistogramFlag)); +} + +base::TimeDelta CompositorFrameReporter::GetStateNormalUpperLimit( + const StageData& stage) const { + return stage.time_delta_history->Percentile(kAbnormalityPercentile); } } // namespace cc diff --git a/chromium/cc/scheduler/compositor_frame_reporter.h b/chromium/cc/scheduler/compositor_frame_reporter.h index b122693b1d2..ae7d8bba562 100644 --- a/chromium/cc/scheduler/compositor_frame_reporter.h +++ b/chromium/cc/scheduler/compositor_frame_reporter.h @@ -5,30 +5,36 @@ #ifndef CC_SCHEDULER_COMPOSITOR_FRAME_REPORTER_H_ #define CC_SCHEDULER_COMPOSITOR_FRAME_REPORTER_H_ +#include <vector> + +#include "base/time/time.h" #include "cc/base/base_export.h" #include "cc/cc_export.h" namespace cc { -// This is used for tracing the pipeline stages of a single frame. +class RollingTimeDeltaHistory; + +// This is used for tracing and reporting the duration of pipeline stages within +// a single frame. // // For each stage in the frame pipeline, calling StartStage will start tracing // that stage (and end any currently running stages). -// TODO(alsan): Report stage durations to UMA. +// +// If the tracked frame is submitted (i.e. the frame termination status is +// kSubmittedFrame or kSubmittedFrameMissedDeadline), then the duration of each +// stage along with the total latency will be reported to UMA. These reported +// durations will be differentiated by whether the compositor is single threaded +// and whether the submitted frame missed the deadline. The format of each stage +// reported to UMA is "[SingleThreaded]Compositor.[MissedFrame.].<StageName>". class CC_EXPORT CompositorFrameReporter { public: - enum FrameTerminationStatus { - // Compositor frame (with main thread updates) is submitted before a new - // BeginImplFrame is issued (i.e. BF -> BMF -> Commit -> Activate -> - // Submit). - kSubmittedFrame, - - // Same as SubmittedFrame, but with the condition that there is another - // frame being processed in the pipeline at an earlier stage. - // This would imply that a new BeginImplFrame was issued during the lifetime - // of this reporter, and therefore it missed its deadline - // (e.g. BF1 -> BMF1 -> Submit -> BF2 -> Commit1 -> Activate1 -> BMF2 -> - // Submit). - kSubmittedFrameMissedDeadline, + enum class FrameTerminationStatus { + // The tracked compositor frame was presented. + kPresentedFrame, + + // The tracked compositor frame was submitted to the display compositor but + // was not presented. + kDidNotPresentFrame, // Main frame was aborted; the reporter will not continue reporting. kMainFrameAborted, @@ -47,20 +53,76 @@ class CC_EXPORT CompositorFrameReporter { kUnknown }; - CompositorFrameReporter(); - CompositorFrameReporter(const CompositorFrameReporter& reporter) = delete; + enum class MissedFrameReportTypes { + kNonMissedFrame, + kMissedFrame, + kMissedFrameLatencyIncrease, + kMissedFrameReportTypeCount + }; + + enum class StageType { + kBeginImplFrameToSendBeginMainFrame, + kSendBeginMainFrameToCommit, + kCommit, + kEndCommitToActivation, + kActivation, + kEndActivateToSubmitCompositorFrame, + kSubmitCompositorFrameToPresentationCompositorFrame, + kTotalLatency, + kStageTypeCount + }; + + explicit CompositorFrameReporter(bool is_single_threaded = false); ~CompositorFrameReporter(); + CompositorFrameReporter(const CompositorFrameReporter& reporter) = delete; CompositorFrameReporter& operator=(const CompositorFrameReporter& reporter) = delete; - void StartStage(const char* stage_name); - void SetFrameTerminationStatus(FrameTerminationStatus termination_status); + void MissedSubmittedFrame(); + + // Note that the started stage may be reported to UMA. If the histogram is + // intended to be reported then the histograms.xml file must be updated too. + void StartStage(StageType stage_type, + base::TimeTicks start_time, + RollingTimeDeltaHistory* stage_time_delta_history); + void TerminateFrame(FrameTerminationStatus termination_status, + base::TimeTicks termination_time); + + int StageHistorySizeForTesting() { return stage_history_.size(); } + + protected: + struct StageData { + StageType stage_type; + base::TimeTicks start_time; + base::TimeTicks end_time; + RollingTimeDeltaHistory* time_delta_history; + }; + + StageData current_stage_; + + // Stage data is recorded here. On destruction these stages will be reported + // to UMA if the termination status is |kPresentedFrame|. Reported data will + // be divided based on the frame submission status. + std::vector<StageData> stage_history_; private: + void TerminateReporter(); + void EndCurrentStage(base::TimeTicks end_time); + void ReportStageHistograms(bool missed_frame) const; + void ReportHistogram( + CompositorFrameReporter::MissedFrameReportTypes report_type, + StageType stage_type, + base::TimeDelta time_delta) const; + // Returns true if the stage duration is greater than |kAbnormalityPercentile| + // of its RollingTimeDeltaHistory. + base::TimeDelta GetStateNormalUpperLimit(const StageData& stage) const; + + const bool is_single_threaded_; + bool submitted_frame_missed_deadline_ = false; + base::TimeTicks frame_termination_time_; FrameTerminationStatus frame_termination_status_ = FrameTerminationStatus::kUnknown; - void TerminateFrame(); }; } // namespace cc diff --git a/chromium/cc/scheduler/compositor_frame_reporter_unittest.cc b/chromium/cc/scheduler/compositor_frame_reporter_unittest.cc new file mode 100644 index 00000000000..d23a51bebc0 --- /dev/null +++ b/chromium/cc/scheduler/compositor_frame_reporter_unittest.cc @@ -0,0 +1,298 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/scheduler/compositor_frame_reporter.h" + +#include "base/test/metrics/histogram_tester.h" +#include "cc/scheduler/compositor_frame_reporting_controller.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +class CompositorFrameReporterTest; + +class CompositorFrameReporterTest : public testing::Test { + public: + CompositorFrameReporterTest() + : pipeline_reporter_(std::make_unique<CompositorFrameReporter>()) { + AdvanceNowByMs(1); + } + + void AdvanceNowByMs(int advance_ms) { + now_ += base::TimeDelta::FromMicroseconds(advance_ms); + } + + base::TimeTicks Now() { return now_; } + + protected: + std::unique_ptr<CompositorFrameReporter> pipeline_reporter_; + base::TimeTicks now_; +}; + +TEST_F(CompositorFrameReporterTest, MainFrameAbortedReportingTest) { + base::HistogramTester histogram_tester; + + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + Now(), nullptr); + EXPECT_EQ(0, pipeline_reporter_->StageHistorySizeForTesting()); + + AdvanceNowByMs(3); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now(), + nullptr); + EXPECT_EQ(1, pipeline_reporter_->StageHistorySizeForTesting()); + + AdvanceNowByMs(2); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kMainFrameAborted, + Now()); + EXPECT_EQ(2, pipeline_reporter_->StageHistorySizeForTesting()); + + pipeline_reporter_ = nullptr; + histogram_tester.ExpectTotalCount( + "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 0); + histogram_tester.ExpectTotalCount( + "CompositorLatency.SendBeginMainFrameToCommit", 0); +} + +TEST_F(CompositorFrameReporterTest, ReplacedByNewReporterReportingTest) { + base::HistogramTester histogram_tester; + + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now(), nullptr); + EXPECT_EQ(0, pipeline_reporter_->StageHistorySizeForTesting()); + + AdvanceNowByMs(3); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, Now(), + nullptr); + EXPECT_EQ(1, pipeline_reporter_->StageHistorySizeForTesting()); + + AdvanceNowByMs(2); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kReplacedByNewReporter, + Now()); + EXPECT_EQ(2, pipeline_reporter_->StageHistorySizeForTesting()); + + pipeline_reporter_ = nullptr; + histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 0); + histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation", + 0); +} + +TEST_F(CompositorFrameReporterTest, SubmittedFrameReportingTest) { + base::HistogramTester histogram_tester; + + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kActivation, Now(), nullptr); + EXPECT_EQ(0, pipeline_reporter_->StageHistorySizeForTesting()); + + AdvanceNowByMs(3); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now(), nullptr); + EXPECT_EQ(1, pipeline_reporter_->StageHistorySizeForTesting()); + + AdvanceNowByMs(2); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + EXPECT_EQ(2, pipeline_reporter_->StageHistorySizeForTesting()); + + pipeline_reporter_ = nullptr; + histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.EndActivateToSubmitCompositorFrame", 1); + histogram_tester.ExpectTotalCount("CompositorLatency.TotalLatency", 1); + histogram_tester.ExpectTotalCount("CompositorLatency.MissedFrame.Activation", + 0); + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.EndActivateToSubmitCompositorFrame", 0); + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.TotalLatency", 0); + + histogram_tester.ExpectBucketCount("CompositorLatency.Activation", 3, 1); + histogram_tester.ExpectBucketCount( + "CompositorLatency.EndActivateToSubmitCompositorFrame", 2, 1); + histogram_tester.ExpectBucketCount("CompositorLatency.TotalLatency", 5, 1); +} + +TEST_F(CompositorFrameReporterTest, SubmittedMissedFrameReportingTest) { + base::HistogramTester histogram_tester; + + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now(), + nullptr); + EXPECT_EQ(0, pipeline_reporter_->StageHistorySizeForTesting()); + + AdvanceNowByMs(3); + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now(), nullptr); + EXPECT_EQ(1, pipeline_reporter_->StageHistorySizeForTesting()); + + AdvanceNowByMs(2); + pipeline_reporter_->MissedSubmittedFrame(); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + EXPECT_EQ(2, pipeline_reporter_->StageHistorySizeForTesting()); + + pipeline_reporter_ = nullptr; + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.SendBeginMainFrameToCommit", 1); + histogram_tester.ExpectTotalCount("CompositorLatency.MissedFrame.Commit", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.TotalLatency", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.SendBeginMainFrameToCommit", 0); + histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 0); + histogram_tester.ExpectTotalCount("CompositorLatency.TotalLatency", 0); + + histogram_tester.ExpectBucketCount( + "CompositorLatency.MissedFrame.SendBeginMainFrameToCommit", 3, 1); + histogram_tester.ExpectBucketCount("CompositorLatency.MissedFrame.Commit", 2, + 1); + histogram_tester.ExpectBucketCount( + "CompositorLatency.MissedFrame.TotalLatency", 5, 1); +} + +TEST_F(CompositorFrameReporterTest, MissedFrameLatencyIncreaseReportingTest) { + base::HistogramTester histogram_tester; + RollingTimeDeltaHistory time_delta_history(50); + RollingTimeDeltaHistory time_delta_history2(50); + + // Terminate this frame since it will get destroyed in the for loop. + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kDidNotProduceFrame, + Now()); + + // Submit 19 non-missed frames. + for (int i = 0; i < 19; ++i) { + pipeline_reporter_ = std::make_unique<CompositorFrameReporter>(); + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now(), &time_delta_history); + AdvanceNowByMs(1); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, Now(), + &time_delta_history2); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, + Now()); + } + pipeline_reporter_ = nullptr; + EXPECT_EQ((size_t)19, time_delta_history.sample_count()); + EXPECT_EQ((size_t)19, time_delta_history2.sample_count()); + histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 19); + histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation", + 19); + + // Submit 3 frames missed frames. This will remove 3 sample from the front of + // time delta history. And 16 sample will be in the time delta history. + for (int i = 0; i < 3; ++i) { + pipeline_reporter_ = std::make_unique<CompositorFrameReporter>(); + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now(), &time_delta_history); + AdvanceNowByMs(100); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, Now(), + &time_delta_history2); + pipeline_reporter_->MissedSubmittedFrame(); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, + Now()); + } + pipeline_reporter_ = nullptr; + EXPECT_EQ((size_t)16, time_delta_history.sample_count()); + EXPECT_EQ((size_t)16, time_delta_history2.sample_count()); + DCHECK_EQ(time_delta_history.sample_count(), (size_t)16); + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrameLatencyIncrease.Commit", 0); + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrameLatencyIncrease.EndCommitToActivation", 0); + histogram_tester.ExpectTotalCount("CompositorLatency.MissedFrame.Commit", 3); + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.EndCommitToActivation", 3); + + // Submit 5 frame so that missed frame duration increases would be reported. + for (int i = 0; i < 5; ++i) { + pipeline_reporter_ = std::make_unique<CompositorFrameReporter>(); + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now(), &time_delta_history); + AdvanceNowByMs(1); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, Now(), + &time_delta_history2); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, + Now()); + } + pipeline_reporter_ = nullptr; + EXPECT_EQ((size_t)21, time_delta_history.sample_count()); + EXPECT_EQ((size_t)21, time_delta_history2.sample_count()); + + histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 24); + histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation", + 24); + + // Submit missed frame that is not abnormal (more than 95 percentile of the + // frame history). This brings down the time delta history count to 20. + pipeline_reporter_ = std::make_unique<CompositorFrameReporter>(); + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now(), &time_delta_history); + AdvanceNowByMs(1); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, Now(), + &time_delta_history2); + pipeline_reporter_->MissedSubmittedFrame(); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + pipeline_reporter_ = nullptr; + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrameLatencyIncrease.Commit", 0); + histogram_tester.ExpectTotalCount("CompositorLatency.MissedFrame.Commit", 4); + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.EndCommitToActivation", 4); + + EXPECT_EQ((size_t)20, time_delta_history.sample_count()); + EXPECT_EQ((size_t)20, time_delta_history2.sample_count()); + + // Submit missed frame that is abnormal (more than 95 percentile of the + // frame history). + pipeline_reporter_ = std::make_unique<CompositorFrameReporter>(); + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now(), &time_delta_history); + AdvanceNowByMs(3); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, Now(), + &time_delta_history2); + pipeline_reporter_->MissedSubmittedFrame(); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + pipeline_reporter_ = nullptr; + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrameLatencyIncrease.Commit", 1); + histogram_tester.ExpectTotalCount("CompositorLatency.MissedFrame.Commit", 5); + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.EndCommitToActivation", 5); + + // Submit not-missed frame with abnormal times. + pipeline_reporter_ = std::make_unique<CompositorFrameReporter>(); + pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, + Now(), &time_delta_history); + AdvanceNowByMs(3); + pipeline_reporter_->StartStage( + CompositorFrameReporter::StageType::kEndCommitToActivation, Now(), + &time_delta_history2); + pipeline_reporter_->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); + pipeline_reporter_ = nullptr; + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrameLatencyIncrease.Commit", 1); + histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 25); + histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation", + 25); +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/scheduler/compositor_frame_reporting_controller.cc b/chromium/cc/scheduler/compositor_frame_reporting_controller.cc index 1b17021fd22..dd74268707a 100644 --- a/chromium/cc/scheduler/compositor_frame_reporting_controller.cc +++ b/chromium/cc/scheduler/compositor_frame_reporting_controller.cc @@ -5,36 +5,91 @@ #include "cc/scheduler/compositor_frame_reporting_controller.h" #include "cc/scheduler/compositor_frame_reporter.h" +#include "components/viz/common/quads/compositor_frame_metadata.h" namespace cc { -CompositorFrameReportingController::CompositorFrameReportingController() {} +namespace { +using StageType = CompositorFrameReporter::StageType; +RollingTimeDeltaHistory* GetStageHistory( + std::unique_ptr<RollingTimeDeltaHistory> stage_history_[], + StageType stage_type) { + return stage_history_[static_cast<int>(stage_type)].get(); +} + +static constexpr size_t kMaxHistorySize = 50; +} // namespace + +CompositorFrameReportingController::CompositorFrameReportingController( + bool is_single_threaded) + : is_single_threaded_(is_single_threaded) { + for (int i = 0; i < static_cast<int>( + CompositorFrameReporter::StageType::kStageTypeCount); + ++i) { + stage_history_[i] = + std::make_unique<RollingTimeDeltaHistory>(kMaxHistorySize); + } +} CompositorFrameReportingController::~CompositorFrameReportingController() { + base::TimeTicks now = Now(); for (int i = 0; i < PipelineStage::kNumPipelineStages; ++i) { if (reporters_[i]) { - reporters_[i]->SetFrameTerminationStatus( - CompositorFrameReporter::FrameTerminationStatus::kDidNotProduceFrame); + reporters_[i]->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kDidNotProduceFrame, + now); } } + for (auto& pair : submitted_compositor_frames_) { + pair.reporter->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame, + Now()); + } +} + +CompositorFrameReportingController::SubmittedCompositorFrame:: + SubmittedCompositorFrame() = default; +CompositorFrameReportingController::SubmittedCompositorFrame:: + SubmittedCompositorFrame(uint32_t frame_token, + std::unique_ptr<CompositorFrameReporter> reporter) + : frame_token(frame_token), reporter(std::move(reporter)) {} +CompositorFrameReportingController::SubmittedCompositorFrame:: + ~SubmittedCompositorFrame() = default; + +CompositorFrameReportingController::SubmittedCompositorFrame:: + SubmittedCompositorFrame(SubmittedCompositorFrame&& other) = default; + +base::TimeTicks CompositorFrameReportingController::Now() const { + return base::TimeTicks::Now(); } void CompositorFrameReportingController::WillBeginImplFrame() { + base::TimeTicks begin_time = Now(); + if (reporters_[PipelineStage::kBeginImplFrame]) { + reporters_[PipelineStage::kBeginImplFrame]->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kReplacedByNewReporter, + begin_time); + } std::unique_ptr<CompositorFrameReporter> reporter = - std::make_unique<CompositorFrameReporter>(); - reporter->StartStage("BeginImplFrameToSendBeginMainFrame"); - if (reporters_[PipelineStage::kBeginImplFrame]) - reporters_[PipelineStage::kBeginImplFrame]->SetFrameTerminationStatus( - CompositorFrameReporter::FrameTerminationStatus:: - kReplacedByNewReporter); + std::make_unique<CompositorFrameReporter>(is_single_threaded_); + reporter->StartStage( + CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, + begin_time, + GetStageHistory(stage_history_, CompositorFrameReporter::StageType:: + kBeginImplFrameToSendBeginMainFrame)); reporters_[PipelineStage::kBeginImplFrame] = std::move(reporter); } void CompositorFrameReportingController::WillBeginMainFrame() { DCHECK(reporters_[PipelineStage::kBeginImplFrame]); - DCHECK(reporters_[PipelineStage::kBeginMainFrame] != - reporters_[PipelineStage::kBeginImplFrame]); + // We need to use .get() below because operator<< in std::unique_ptr is a + // C++20 feature. + DCHECK_NE(reporters_[PipelineStage::kBeginMainFrame].get(), + reporters_[PipelineStage::kBeginImplFrame].get()); reporters_[PipelineStage::kBeginImplFrame]->StartStage( - "SendBeginMainFrameToCommit"); + CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now(), + GetStageHistory( + stage_history_, + CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit)); AdvanceReporterStage(PipelineStage::kBeginImplFrame, PipelineStage::kBeginMainFrame); } @@ -43,20 +98,26 @@ void CompositorFrameReportingController::BeginMainFrameAborted() { DCHECK(reporters_[PipelineStage::kBeginMainFrame]); std::unique_ptr<CompositorFrameReporter> aborted_frame_reporter = std::move(reporters_[PipelineStage::kBeginMainFrame]); - aborted_frame_reporter->StartStage("BeginMainFrameAborted"); - aborted_frame_reporter->SetFrameTerminationStatus( - CompositorFrameReporter::FrameTerminationStatus::kMainFrameAborted); + aborted_frame_reporter->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kMainFrameAborted, + Now()); } void CompositorFrameReportingController::WillCommit() { DCHECK(reporters_[PipelineStage::kBeginMainFrame]); - reporters_[PipelineStage::kBeginMainFrame]->StartStage("Commit"); + reporters_[PipelineStage::kBeginMainFrame]->StartStage( + CompositorFrameReporter::StageType::kCommit, Now(), + GetStageHistory(stage_history_, + CompositorFrameReporter::StageType::kCommit)); } void CompositorFrameReportingController::DidCommit() { DCHECK(reporters_[PipelineStage::kBeginMainFrame]); reporters_[PipelineStage::kBeginMainFrame]->StartStage( - "EndCommitToActivation"); + CompositorFrameReporter::StageType::kEndCommitToActivation, Now(), + GetStageHistory( + stage_history_, + CompositorFrameReporter::StageType::kEndCommitToActivation)); AdvanceReporterStage(PipelineStage::kBeginMainFrame, PipelineStage::kCommit); } @@ -65,11 +126,15 @@ void CompositorFrameReportingController::WillInvalidateOnImplSide() { // TODO(alsan): Report latency of impl side invalidations. next_activate_has_invalidation_ = true; } + void CompositorFrameReportingController::WillActivate() { DCHECK(reporters_[PipelineStage::kCommit] || next_activate_has_invalidation_); if (!reporters_[PipelineStage::kCommit]) return; - reporters_[PipelineStage::kCommit]->StartStage("Activation"); + reporters_[PipelineStage::kCommit]->StartStage( + CompositorFrameReporter::StageType::kActivation, Now(), + GetStageHistory(stage_history_, + CompositorFrameReporter::StageType::kActivation)); } void CompositorFrameReportingController::DidActivate() { @@ -78,45 +143,74 @@ void CompositorFrameReportingController::DidActivate() { if (!reporters_[PipelineStage::kCommit]) return; reporters_[PipelineStage::kCommit]->StartStage( - "EndActivateToSubmitCompositorFrame"); + CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, + Now(), + GetStageHistory(stage_history_, CompositorFrameReporter::StageType:: + kEndActivateToSubmitCompositorFrame)); AdvanceReporterStage(PipelineStage::kCommit, PipelineStage::kActivate); } -void CompositorFrameReportingController::DidSubmitCompositorFrame() { +void CompositorFrameReportingController::DidSubmitCompositorFrame( + uint32_t frame_token) { if (!reporters_[PipelineStage::kActivate]) return; std::unique_ptr<CompositorFrameReporter> submitted_reporter = std::move(reporters_[PipelineStage::kActivate]); - submitted_reporter->StartStage("SubmitCompositorFrame"); // If there are any other reporters active on the other stages of the // pipeline then that means a new frame was started during the duration of // this reporter and therefore the frame being tracked missed the deadline. if (reporters_[PipelineStage::kBeginImplFrame] || reporters_[PipelineStage::kBeginMainFrame] || reporters_[PipelineStage::kCommit]) { - submitted_reporter->SetFrameTerminationStatus( - CompositorFrameReporter::FrameTerminationStatus:: - kSubmittedFrameMissedDeadline); - } else { - submitted_reporter->SetFrameTerminationStatus( - CompositorFrameReporter::FrameTerminationStatus::kSubmittedFrame); + submitted_reporter->MissedSubmittedFrame(); } + submitted_reporter->StartStage( + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame, + Now(), + GetStageHistory(stage_history_, + CompositorFrameReporter::StageType:: + kSubmitCompositorFrameToPresentationCompositorFrame)); + submitted_compositor_frames_.emplace_back(frame_token, + std::move(submitted_reporter)); } void CompositorFrameReportingController::DidNotProduceFrame() { - if (reporters_[PipelineStage::kActivate]) - reporters_[PipelineStage::kActivate]->SetFrameTerminationStatus( - CompositorFrameReporter::FrameTerminationStatus::kDidNotProduceFrame); + if (!reporters_[PipelineStage::kActivate]) + return; + reporters_[PipelineStage::kActivate]->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kDidNotProduceFrame, + Now()); reporters_[PipelineStage::kActivate] = nullptr; } +void CompositorFrameReportingController::DidPresentCompositorFrame( + uint32_t frame_token, + base::TimeTicks presentation_time) { + while (!submitted_compositor_frames_.empty()) { + auto submitted_frame = submitted_compositor_frames_.begin(); + if (viz::FrameTokenGT(submitted_frame->frame_token, frame_token)) + break; + + auto termination_status = + CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame; + if (submitted_frame->frame_token != frame_token) + termination_status = + CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame; + + submitted_frame->reporter->TerminateFrame(termination_status, + presentation_time); + submitted_compositor_frames_.erase(submitted_frame); + } +} + void CompositorFrameReportingController::AdvanceReporterStage( PipelineStage start, PipelineStage target) { if (reporters_[target]) { - reporters_[target]->SetFrameTerminationStatus( - CompositorFrameReporter::FrameTerminationStatus:: - kReplacedByNewReporter); + reporters_[target]->TerminateFrame( + CompositorFrameReporter::FrameTerminationStatus::kReplacedByNewReporter, + Now()); } reporters_[target] = std::move(reporters_[start]); } diff --git a/chromium/cc/scheduler/compositor_frame_reporting_controller.h b/chromium/cc/scheduler/compositor_frame_reporting_controller.h index 9b72cffc28d..a481593c592 100644 --- a/chromium/cc/scheduler/compositor_frame_reporting_controller.h +++ b/chromium/cc/scheduler/compositor_frame_reporting_controller.h @@ -6,13 +6,16 @@ #define CC_SCHEDULER_COMPOSITOR_FRAME_REPORTING_CONTROLLER_H_ #include <memory> +#include <vector> #include "base/time/time.h" #include "cc/base/base_export.h" +#include "cc/base/rolling_time_delta_history.h" #include "cc/cc_export.h" +#include "cc/scheduler/compositor_frame_reporter.h" namespace cc { -class CompositorFrameReporter; +class RollingTimeDeltaHistory; // This is used for managing simultaneous CompositorFrameReporter instances // in the case that the compositor has high latency. Calling one of the @@ -31,7 +34,7 @@ class CC_EXPORT CompositorFrameReportingController { kNumPipelineStages }; - CompositorFrameReportingController(); + explicit CompositorFrameReportingController(bool is_single_threaded = false); virtual ~CompositorFrameReportingController(); CompositorFrameReportingController( @@ -48,17 +51,42 @@ class CC_EXPORT CompositorFrameReportingController { virtual void DidCommit(); virtual void WillActivate(); virtual void DidActivate(); - virtual void DidSubmitCompositorFrame(); + virtual void DidSubmitCompositorFrame(uint32_t frame_token); virtual void DidNotProduceFrame(); + virtual void DidPresentCompositorFrame(uint32_t frame_token, + base::TimeTicks presentation_time); protected: + struct SubmittedCompositorFrame { + uint32_t frame_token; + std::unique_ptr<CompositorFrameReporter> reporter; + SubmittedCompositorFrame(); + SubmittedCompositorFrame(uint32_t frame_token, + std::unique_ptr<CompositorFrameReporter> reporter); + SubmittedCompositorFrame(SubmittedCompositorFrame&& other); + ~SubmittedCompositorFrame(); + }; + base::TimeTicks Now() const; std::unique_ptr<CompositorFrameReporter> reporters_[PipelineStage::kNumPipelineStages]; private: void AdvanceReporterStage(PipelineStage start, PipelineStage target); + // Used by the managed reporters to differentiate the histogram names when + // reporting to UMA. + const bool is_single_threaded_; bool next_activate_has_invalidation_ = false; + + // Mapping of frame token to pipeline reporter for submitted compositor + // frames. + base::circular_deque<SubmittedCompositorFrame> submitted_compositor_frames_; + + // These keep track of stage durations for when a frame did not miss a + // deadline. The history is used by reporter instances to determine if a + // missed frame had a stage duration that was abnormally large. + std::unique_ptr<RollingTimeDeltaHistory> stage_history_[static_cast<size_t>( + CompositorFrameReporter::StageType::kStageTypeCount)]; }; } // namespace cc diff --git a/chromium/cc/scheduler/compositor_frame_reporting_controller_unittest.cc b/chromium/cc/scheduler/compositor_frame_reporting_controller_unittest.cc index 94f6ffdfa66..8210d247bcc 100644 --- a/chromium/cc/scheduler/compositor_frame_reporting_controller_unittest.cc +++ b/chromium/cc/scheduler/compositor_frame_reporting_controller_unittest.cc @@ -5,6 +5,8 @@ #include "cc/scheduler/compositor_frame_reporting_controller.h" #include "base/macros.h" +#include "base/test/metrics/histogram_tester.h" +#include "components/viz/common/quads/compositor_frame_metadata.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { @@ -79,17 +81,27 @@ class CompositorFrameReportingControllerTest : public testing::Test { reporting_controller_.DidActivate(); } - void SimulateSubmitCompositorFrame() { + void SimulateSubmitCompositorFrame(uint32_t frame_token) { if (!reporting_controller_.reporters() [CompositorFrameReportingController::PipelineStage::kActivate]) SimulateActivate(); CHECK(reporting_controller_.reporters() [CompositorFrameReportingController::PipelineStage::kActivate]); - reporting_controller_.DidSubmitCompositorFrame(); + reporting_controller_.DidSubmitCompositorFrame(frame_token); + } + + void SimulatePresentCompositorFrame() { + ++next_token_; + SimulateSubmitCompositorFrame(*next_token_); + reporting_controller_.DidPresentCompositorFrame(*next_token_, + base::TimeTicks::Now()); } protected: TestCompositorFrameReportingController reporting_controller_; + + private: + viz::FrameTokenGenerator next_token_; }; TEST_F(CompositorFrameReportingControllerTest, ActiveReporterCounts) { @@ -108,24 +120,24 @@ TEST_F(CompositorFrameReportingControllerTest, ActiveReporterCounts) { EXPECT_EQ(1, reporting_controller_.ActiveReporters()); // BF -> BF - // Should replace previous reporter + // Should replace previous reporter. reporting_controller_.WillBeginImplFrame(); EXPECT_EQ(1, reporting_controller_.ActiveReporters()); // BF -> BMF -> BF - // Should add new reporter + // Should add new reporter. reporting_controller_.WillBeginMainFrame(); reporting_controller_.WillBeginImplFrame(); EXPECT_EQ(2, reporting_controller_.ActiveReporters()); // BF -> BMF -> BF -> Commit - // Should stay same + // Should stay same. reporting_controller_.WillCommit(); reporting_controller_.DidCommit(); EXPECT_EQ(2, reporting_controller_.ActiveReporters()); // BF -> BMF -> BF -> Commit -> BMF -> Activate -> Commit -> Activation - // Having two reporters at Activate phase should delete the older one + // Having two reporters at Activate phase should delete the older one. reporting_controller_.WillBeginMainFrame(); reporting_controller_.WillActivate(); reporting_controller_.DidActivate(); @@ -135,10 +147,10 @@ TEST_F(CompositorFrameReportingControllerTest, ActiveReporterCounts) { reporting_controller_.DidActivate(); EXPECT_EQ(1, reporting_controller_.ActiveReporters()); - reporting_controller_.DidSubmitCompositorFrame(); + reporting_controller_.DidSubmitCompositorFrame(0); EXPECT_EQ(0, reporting_controller_.ActiveReporters()); - // 4 simultaneous reporters + // 4 simultaneous reporters active. SimulateActivate(); SimulateCommit(); @@ -148,9 +160,75 @@ TEST_F(CompositorFrameReportingControllerTest, ActiveReporterCounts) { SimulateBeginImplFrame(); EXPECT_EQ(4, reporting_controller_.ActiveReporters()); - // Any additional BeginImplFrame's would be ignored + // Any additional BeginImplFrame's would be ignored. SimulateBeginImplFrame(); EXPECT_EQ(4, reporting_controller_.ActiveReporters()); } + +TEST_F(CompositorFrameReportingControllerTest, + SubmittedFrameHistogramReporting) { + base::HistogramTester histogram_tester; + + // 2 reporters active. + SimulateActivate(); + SimulateBeginImplFrame(); + + // Submitting and Presenting the next reporter should be a missed. + SimulatePresentCompositorFrame(); + + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.BeginImplFrameToSendBeginMainFrame", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.SendBeginMainFrameToCommit", 1); + histogram_tester.ExpectTotalCount("CompositorLatency.MissedFrame.Commit", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.EndCommitToActivation", 1); + histogram_tester.ExpectTotalCount("CompositorLatency.MissedFrame.Activation", + 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.EndActivateToSubmitCompositorFrame", 1); + + // Other histograms should not be reported. + histogram_tester.ExpectTotalCount( + "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 0); + histogram_tester.ExpectTotalCount( + "CompositorLatency.SendBeginMainFrameToCommit", 0); + histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 0); + histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation", + 0); + histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 0); + histogram_tester.ExpectTotalCount( + "CompositorLatency.EndActivateToSubmitCompositorFrame", 0); + + // Submitting the next reporter will not be counted as missed. + // In practice this submitted frame should be considered as missed because a + // new BeginFrame would have been issued, which is the cause for this frame + // submission. + SimulatePresentCompositorFrame(); + // Other histograms should not be reported. + histogram_tester.ExpectTotalCount( + "CompositorLatency.BeginImplFrameToSendBeginMainFrame", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.SendBeginMainFrameToCommit", 1); + histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 1); + histogram_tester.ExpectTotalCount("CompositorLatency.EndCommitToActivation", + 1); + histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.EndActivateToSubmitCompositorFrame", 1); + + // Missed frame histogram counts should not change. + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.BeginImplFrameToSendBeginMainFrame", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.SendBeginMainFrameToCommit", 1); + histogram_tester.ExpectTotalCount("CompositorLatency.MissedFrame.Commit", 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.EndCommitToActivation", 1); + histogram_tester.ExpectTotalCount("CompositorLatency.MissedFrame.Activation", + 1); + histogram_tester.ExpectTotalCount( + "CompositorLatency.MissedFrame.EndActivateToSubmitCompositorFrame", 1); +} } // namespace } // namespace cc diff --git a/chromium/cc/scheduler/compositor_timing_history.cc b/chromium/cc/scheduler/compositor_timing_history.cc index 6e1b3966f4f..0b509c097da 100644 --- a/chromium/cc/scheduler/compositor_timing_history.cc +++ b/chromium/cc/scheduler/compositor_timing_history.cc @@ -77,9 +77,6 @@ const double kPrepareTilesEstimationPercentile = 90.0; const double kActivateEstimationPercentile = 90.0; const double kDrawEstimationPercentile = 90.0; -constexpr base::TimeDelta kSubmitAckWatchdogTimeout = - base::TimeDelta::FromSeconds(8); - // This macro is deprecated since its bucket count uses too much bandwidth. // It also has sub-optimal range and bucket distribution. // TODO(brianderson): Delete this macro and associated UMAs once there is @@ -423,7 +420,6 @@ CompositorTimingHistory::CompositorTimingHistory( activate_duration_history_(kDurationHistorySize), draw_duration_history_(kDurationHistorySize), begin_main_frame_on_critical_path_(false), - submit_ack_watchdog_enabled_(false), uma_reporter_(CreateUMAReporter(uma_category)), rendering_stats_instrumentation_(rendering_stats_instrumentation), compositor_frame_reporting_controller_( @@ -559,7 +555,6 @@ void CompositorTimingHistory::DidCreateAndInitializeLayerTreeFrameSink() { // After we get a new output surface, we won't get a spurious // CompositorFrameAck from the old output surface. submit_start_time_ = base::TimeTicks(); - submit_ack_watchdog_enabled_ = false; } void CompositorTimingHistory::WillBeginImplFrame( @@ -582,14 +577,6 @@ void CompositorTimingHistory::WillBeginImplFrame( SetBeginMainFrameCommittingContinuously(false); } - if (submit_ack_watchdog_enabled_) { - base::TimeDelta submit_not_acked_time_ = now - submit_start_time_; - if (submit_not_acked_time_ >= kSubmitAckWatchdogTimeout) { - // Only record this UMA once per submitted CompositorFrame. - submit_ack_watchdog_enabled_ = false; - } - } - if (frame_type == viz::BeginFrameArgs::NORMAL) uma_reporter_->AddBeginImplFrameLatency(now - frame_time); @@ -922,11 +909,10 @@ void CompositorTimingHistory::DidDraw( draw_start_time_ = base::TimeTicks(); } -void CompositorTimingHistory::DidSubmitCompositorFrame() { +void CompositorTimingHistory::DidSubmitCompositorFrame(uint32_t frame_token) { DCHECK_EQ(base::TimeTicks(), submit_start_time_); - compositor_frame_reporting_controller_->DidSubmitCompositorFrame(); + compositor_frame_reporting_controller_->DidSubmitCompositorFrame(frame_token); submit_start_time_ = Now(); - submit_ack_watchdog_enabled_ = true; } void CompositorTimingHistory::DidNotProduceFrame() { @@ -937,11 +923,16 @@ void CompositorTimingHistory::DidReceiveCompositorFrameAck() { DCHECK_NE(base::TimeTicks(), submit_start_time_); base::TimeDelta submit_to_ack_duration = Now() - submit_start_time_; uma_reporter_->AddSubmitToAckLatency(submit_to_ack_duration); - if (submit_ack_watchdog_enabled_) - submit_ack_watchdog_enabled_ = false; submit_start_time_ = base::TimeTicks(); } +void CompositorTimingHistory::DidPresentCompositorFrame( + uint32_t frame_token, + base::TimeTicks presentation_time) { + compositor_frame_reporting_controller_->DidPresentCompositorFrame( + frame_token, presentation_time); +} + void CompositorTimingHistory::SetTreePriority(TreePriority priority) { tree_priority_ = priority; } diff --git a/chromium/cc/scheduler/compositor_timing_history.h b/chromium/cc/scheduler/compositor_timing_history.h index b5ae6cee06e..7dff0c9335c 100644 --- a/chromium/cc/scheduler/compositor_timing_history.h +++ b/chromium/cc/scheduler/compositor_timing_history.h @@ -88,9 +88,11 @@ class CC_EXPORT CompositorTimingHistory { size_t main_thread_animations_count, bool current_frame_had_raf, bool next_frame_has_pending_raf); - void DidSubmitCompositorFrame(); + void DidSubmitCompositorFrame(uint32_t frame_token); void DidNotProduceFrame(); void DidReceiveCompositorFrameAck(); + void DidPresentCompositorFrame(uint32_t frame_token, + base::TimeTicks presentation_time); void WillInvalidateOnImplSide(); void SetTreePriority(TreePriority priority); @@ -159,9 +161,6 @@ class CC_EXPORT CompositorTimingHistory { bool pending_tree_is_impl_side_; - // Watchdog timers. - bool submit_ack_watchdog_enabled_; - std::unique_ptr<UMAReporter> uma_reporter_; // Owned by LayerTreeHost and is destroyed when LayerTreeHost is destroyed. diff --git a/chromium/cc/scheduler/scheduler.cc b/chromium/cc/scheduler/scheduler.cc index f9fed94544d..61073cebb06 100644 --- a/chromium/cc/scheduler/scheduler.cc +++ b/chromium/cc/scheduler/scheduler.cc @@ -115,6 +115,11 @@ void Scheduler::NotifyAnimationWorkletStateChange(AnimationWorkletState state, ProcessScheduledActions(); } +void Scheduler::NotifyPaintWorkletStateChange(PaintWorkletState state) { + state_machine_.NotifyPaintWorkletStateChange(state); + ProcessScheduledActions(); +} + void Scheduler::SetNeedsBeginMainFrame() { state_machine_.SetNeedsBeginMainFrame(); ProcessScheduledActions(); @@ -136,8 +141,8 @@ void Scheduler::SetNeedsPrepareTiles() { ProcessScheduledActions(); } -void Scheduler::DidSubmitCompositorFrame() { - compositor_timing_history_->DidSubmitCompositorFrame(); +void Scheduler::DidSubmitCompositorFrame(uint32_t frame_token) { + compositor_timing_history_->DidSubmitCompositorFrame(frame_token); state_machine_.DidSubmitCompositorFrame(); // There is no need to call ProcessScheduledActions here because @@ -191,6 +196,12 @@ void Scheduler::DidPrepareTiles() { state_machine_.DidPrepareTiles(); } +void Scheduler::DidPresentCompositorFrame(uint32_t frame_token, + base::TimeTicks presentation_time) { + compositor_timing_history_->DidPresentCompositorFrame(frame_token, + presentation_time); +} + void Scheduler::DidLoseLayerTreeFrameSink() { TRACE_EVENT0("cc", "Scheduler::DidLoseLayerTreeFrameSink"); state_machine_.DidLoseLayerTreeFrameSink(); @@ -211,7 +222,6 @@ void Scheduler::DidCreateAndInitializeLayerTreeFrameSink() { void Scheduler::NotifyBeginMainFrameStarted( base::TimeTicks main_thread_start_time) { TRACE_EVENT0("cc", "Scheduler::NotifyBeginMainFrameStarted"); - state_machine_.NotifyBeginMainFrameStarted(); compositor_timing_history_->BeginMainFrameStarted(main_thread_start_time); } @@ -319,7 +329,8 @@ bool Scheduler::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) { client_->FrameIntervalUpdated(last_frame_interval_); } - if (ShouldDropBeginFrame(args)) { + // Drop the BeginFrame if we don't need one. + if (!state_machine_.BeginFrameNeeded()) { TRACE_EVENT_INSTANT0("cc", "Scheduler::BeginFrameDropped", TRACE_EVENT_SCOPE_THREAD); // Since we don't use the BeginFrame, we may later receive the same @@ -782,8 +793,8 @@ void Scheduler::ProcessScheduledActions() { begin_main_frame_args_.on_critical_path, begin_main_frame_args_.frame_time); state_machine_.WillSendBeginMainFrame(); - // TODO(brianderson): Pass begin_main_frame_args_ directly to client. client_->ScheduledActionSendBeginMainFrame(begin_main_frame_args_); + last_dispatched_begin_main_frame_args_ = begin_main_frame_args_; break; case SchedulerStateMachine::Action:: NOTIFY_BEGIN_MAIN_FRAME_NOT_EXPECTED_UNTIL: @@ -801,6 +812,7 @@ void Scheduler::ProcessScheduledActions() { state_machine_.WillCommit(commit_has_no_updates); compositor_timing_history_->WillCommit(); client_->ScheduledActionCommit(); + last_commit_origin_frame_args_ = last_dispatched_begin_main_frame_args_; break; } case SchedulerStateMachine::Action::ACTIVATE_SYNC_TREE: @@ -808,6 +820,7 @@ void Scheduler::ProcessScheduledActions() { state_machine_.WillActivate(); client_->ScheduledActionActivateSyncTree(); compositor_timing_history_->DidActivate(); + last_activate_origin_frame_args_ = last_commit_origin_frame_args_; break; case SchedulerStateMachine::Action::PERFORM_IMPL_SIDE_INVALIDATION: state_machine_.WillPerformImplSideInvalidation(); @@ -914,28 +927,6 @@ void Scheduler::UpdateCompositorTimingHistoryRecordingEnabled() { state_machine_.visible()); } -bool Scheduler::ShouldDropBeginFrame(const viz::BeginFrameArgs& args) const { - // Drop the BeginFrame if we don't need one. - if (!state_machine_.BeginFrameNeeded()) - return true; - - // Also ignore MISSED args in full-pipe mode, because a missed BeginFrame may - // have already been completed by the DisplayScheduler. In such a case, - // handling it now would be likely to mess up future full-pipe BeginFrames. - // The only situation in which we can reasonably receive MISSED args is when - // our frame sink hierarchy changes, since we always request BeginFrames in - // full-pipe mode. If surface synchronization is also enabled, we can and - // should use the MISSED args safely because the parent's latest - // CompositorFrame will block its activation until we submit a new frame. - if (args.type == viz::BeginFrameArgs::MISSED && - settings_.wait_for_all_pipeline_stages_before_draw && - !settings_.enable_surface_synchronization) { - return true; - } - - return false; -} - bool Scheduler::ShouldRecoverMainLatency( const viz::BeginFrameArgs& args, bool can_activate_before_deadline) const { @@ -1019,11 +1010,9 @@ bool Scheduler::CanBeginMainFrameAndActivateBeforeDeadline( return estimated_draw_time < args.deadline; } -bool Scheduler::IsBeginMainFrameSentOrStarted() const { - return (state_machine_.begin_main_frame_state() == - SchedulerStateMachine::BeginMainFrameState::SENT || - state_machine_.begin_main_frame_state() == - SchedulerStateMachine::BeginMainFrameState::STARTED); +bool Scheduler::IsBeginMainFrameSent() const { + return state_machine_.begin_main_frame_state() == + SchedulerStateMachine::BeginMainFrameState::SENT; } viz::BeginFrameAck Scheduler::CurrentBeginFrameAckForActiveTree() const { diff --git a/chromium/cc/scheduler/scheduler.h b/chromium/cc/scheduler/scheduler.h index a84943e7ee8..6168d88768c 100644 --- a/chromium/cc/scheduler/scheduler.h +++ b/chromium/cc/scheduler/scheduler.h @@ -108,6 +108,7 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { void SetBeginFrameSource(viz::BeginFrameSource* source); using AnimationWorkletState = SchedulerStateMachine::AnimationWorkletState; + using PaintWorkletState = SchedulerStateMachine::PaintWorkletState; using TreeType = SchedulerStateMachine::TreeType; // Sets whether asynchronous animation worklet mutations are running. @@ -116,11 +117,20 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { void NotifyAnimationWorkletStateChange(AnimationWorkletState state, TreeType tree); + // Sets whether asynchronous paint worklets are running. Paint worklets + // running should block activation of the pending tree, as it isn't fully + // painted until they are done. + void NotifyPaintWorkletStateChange(PaintWorkletState state); + // Set |needs_begin_main_frame_| to true, which will cause the BeginFrame // source to be told to send BeginFrames to this client so that this client // can send a CompositorFrame to the display compositor with appropriate // timing. void SetNeedsBeginMainFrame(); + bool needs_begin_main_frame() const { + return state_machine_.needs_begin_main_frame(); + } + // Requests a single impl frame (after the current frame if there is one // active). void SetNeedsOneBeginImplFrame(); @@ -143,7 +153,7 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { // Drawing should result in submitting a CompositorFrame to the // LayerTreeFrameSink and then calling this. - void DidSubmitCompositorFrame(); + void DidSubmitCompositorFrame(uint32_t frame_token); // 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(); @@ -169,6 +179,11 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { // track how long PrepareTiles takes. void DidPrepareTiles(); + // |DidPresentCompositorFrame| is called when the renderer receives + // presentation feedback. + void DidPresentCompositorFrame(uint32_t frame_token, + base::TimeTicks presentation_time); + void DidLoseLayerTreeFrameSink(); void DidCreateAndInitializeLayerTreeFrameSink(); @@ -216,8 +231,17 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { viz::BeginFrameAck CurrentBeginFrameAckForActiveTree() const; + const viz::BeginFrameArgs& last_dispatched_begin_main_frame_args() const { + return last_dispatched_begin_main_frame_args_; + } + const viz::BeginFrameArgs& last_activate_origin_frame_args() const { + return last_activate_origin_frame_args_; + } + void ClearHistory(); + bool IsBeginMainFrameSent() const; + protected: // Virtual for testing. virtual base::TimeTicks Now() const; @@ -244,6 +268,17 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { viz::BeginFrameAck last_begin_frame_ack_; viz::BeginFrameArgs begin_main_frame_args_; + // For keeping track of the original BeginFrameArgs from the Main Thread + // that led to the corresponding action, i.e.: + // BeginMainFrame => Commit => Activate => Submit + // So, |last_commit_origin_frame_args_| is the BeginFrameArgs that was + // dispatched to the main-thread, and lead to the commit to happen. + // |last_activate_origin_frame_args_| is then set to that BeginFrameArgs when + // the committed change is activated. + viz::BeginFrameArgs last_dispatched_begin_main_frame_args_; + viz::BeginFrameArgs last_commit_origin_frame_args_; + viz::BeginFrameArgs last_activate_origin_frame_args_; + // Task posted for the deadline or drawing phase of the scheduler. This task // can be rescheduled e.g. when the condition for the deadline is met, it is // scheduled to run immediately. @@ -300,7 +335,6 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { void DrawForced(); void ProcessScheduledActions(); void UpdateCompositorTimingHistoryRecordingEnabled(); - bool ShouldDropBeginFrame(const viz::BeginFrameArgs& args) const; bool ShouldRecoverMainLatency(const viz::BeginFrameArgs& args, bool can_activate_before_deadline) const; bool ShouldRecoverImplLatency(const viz::BeginFrameArgs& args, @@ -310,7 +344,6 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase { base::TimeDelta bmf_to_activate_estimate, base::TimeTicks now) const; void AdvanceCommitStateIfPossible(); - bool IsBeginMainFrameSentOrStarted() const; void BeginImplFrameWithDeadline(const viz::BeginFrameArgs& args); void BeginImplFrameSynchronous(const viz::BeginFrameArgs& args); diff --git a/chromium/cc/scheduler/scheduler_settings.cc b/chromium/cc/scheduler/scheduler_settings.cc index 5fb52494ae3..11e3664df39 100644 --- a/chromium/cc/scheduler/scheduler_settings.cc +++ b/chromium/cc/scheduler/scheduler_settings.cc @@ -18,26 +18,16 @@ std::unique_ptr<base::trace_event::ConvertableToTraceFormat> SchedulerSettings::AsValue() const { std::unique_ptr<base::trace_event::TracedValue> state( new base::trace_event::TracedValue()); - state->SetBoolean("main_frame_while_submit_frame_throttled_enabled", - main_frame_while_submit_frame_throttled_enabled); state->SetBoolean("main_frame_before_activation_enabled", main_frame_before_activation_enabled); state->SetBoolean("commit_to_active_tree", commit_to_active_tree); - state->SetBoolean("timeout_and_draw_when_animation_checkerboards", - timeout_and_draw_when_animation_checkerboards); state->SetInteger("maximum_number_of_failed_draws_before_draw_is_forced", maximum_number_of_failed_draws_before_draw_is_forced); state->SetBoolean("using_synchronous_renderer_compositor", using_synchronous_renderer_compositor); - state->SetInteger("background_frame_interval", - background_frame_interval.InMicroseconds()); state->SetBoolean("enable_latency_recovery", enable_latency_recovery); state->SetBoolean("wait_for_all_pipeline_stages_before_draw", wait_for_all_pipeline_stages_before_draw); - state->SetBoolean("enable_surface_synchronization", - enable_surface_synchronization); - state->SetBoolean("compositor_threaded_scrollbar_scrolling", - compositor_threaded_scrollbar_scrolling); return std::move(state); } diff --git a/chromium/cc/scheduler/scheduler_settings.h b/chromium/cc/scheduler/scheduler_settings.h index 318820844fa..86230194b63 100644 --- a/chromium/cc/scheduler/scheduler_settings.h +++ b/chromium/cc/scheduler/scheduler_settings.h @@ -26,18 +26,34 @@ class CC_EXPORT SchedulerSettings { SchedulerSettings(const SchedulerSettings& other); ~SchedulerSettings(); - bool main_frame_while_submit_frame_throttled_enabled = false; + // Whether a BeginMainFrame should be issued while there is a pending-tree + // still waiting to be activated. This is disabled by default for the UI + // compositor, and enabled for renderers (unless there are too few cores). bool main_frame_before_activation_enabled = false; + + // Whether commits should happen directly to the active tree, skipping the + // pending tree. This is turned on only for the UI compositor (and in some + // tests). bool commit_to_active_tree = false; - bool timeout_and_draw_when_animation_checkerboards = true; + + // This is enabled for android-webview. bool using_synchronous_renderer_compositor = false; + + // This is used to determine whether some begin-frames should be skipped (both + // in the main-thread and the compositor-thread) if previous frames have had + // high latency. + // It is enabled by default on desktop platforms, and has been recently + // disabled by default on android. It may be disabled on all platforms. See + // more in https://crbug.com/933846 bool enable_latency_recovery = true; + + // Turning this on effectively disables pipelining of compositor frame + // production stages by waiting for each stage to complete before producing + // the frame. Turning this on also disables latency-recovery. This is enabled + // for headless-mode and some tests, and disabled elsewhere by default. bool wait_for_all_pipeline_stages_before_draw = false; - bool enable_surface_synchronization = false; - bool compositor_threaded_scrollbar_scrolling = false; int maximum_number_of_failed_draws_before_draw_is_forced = 3; - base::TimeDelta background_frame_interval = base::TimeDelta::FromSeconds(1); std::unique_ptr<base::trace_event::ConvertableToTraceFormat> AsValue() const; }; diff --git a/chromium/cc/scheduler/scheduler_state_machine.cc b/chromium/cc/scheduler/scheduler_state_machine.cc index 29479c7dd3c..7ec7152365a 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.cc +++ b/chromium/cc/scheduler/scheduler_state_machine.cc @@ -79,8 +79,6 @@ const char* SchedulerStateMachine::BeginMainFrameStateToString( return "BeginMainFrameState::IDLE"; case BeginMainFrameState::SENT: return "BeginMainFrameState::SENT"; - case BeginMainFrameState::STARTED: - return "BeginMainFrameState::STARTED"; case BeginMainFrameState::READY_TO_COMMIT: return "BeginMainFrameState::READY_TO_COMMIT"; } @@ -241,6 +239,8 @@ void SchedulerStateMachine::AsValueInto( processing_animation_worklets_for_active_tree_); state->SetBoolean("processing_animation_worklets_for_pending_tree", processing_animation_worklets_for_pending_tree_); + state->SetBoolean("processing_paint_worklets_for_pending_tree", + processing_paint_worklets_for_pending_tree_); state->EndDictionary(); } @@ -379,6 +379,15 @@ bool SchedulerStateMachine::ShouldActivateSyncTree() const { if (active_tree_needs_first_draw_) return false; + // Delay pending tree activation until paint worklets have completed painting + // the pending tree. This must occur before the |ShouldAbortCurrentFrame| + // check as we cannot have an unpainted active tree. + // + // Note that paint worklets continue to paint when the page is not visible, so + // any abort will eventually happen when they complete. + if (processing_paint_worklets_for_pending_tree_) + return false; + if (ShouldAbortCurrentFrame()) return true; @@ -537,18 +546,16 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const { if (!HasInitializedLayerTreeFrameSink()) return false; - if (!settings_.main_frame_while_submit_frame_throttled_enabled) { - // Throttle the BeginMainFrames on CompositorFrameAck unless we just - // submitted a frame to potentially improve impl-thread latency over - // main-thread throughput. - // TODO(brianderson): Remove this restriction to improve throughput or - // make it conditional on ImplLatencyTakesPriority. - bool just_submitted_in_deadline = - begin_impl_frame_state_ == BeginImplFrameState::INSIDE_DEADLINE && - did_submit_in_last_frame_; - if (IsDrawThrottled() && !just_submitted_in_deadline) - return false; - } + // Throttle the BeginMainFrames on CompositorFrameAck unless we just + // submitted a frame to potentially improve impl-thread latency over + // main-thread throughput. + // TODO(brianderson): Remove this restriction to improve throughput or + // make it conditional on ImplLatencyTakesPriority. + bool just_submitted_in_deadline = + begin_impl_frame_state_ == BeginImplFrameState::INSIDE_DEADLINE && + did_submit_in_last_frame_; + if (IsDrawThrottled() && !just_submitted_in_deadline) + return false; if (skip_next_begin_main_frame_to_reduce_latency_) return false; @@ -710,8 +717,7 @@ bool SchedulerStateMachine::ShouldDeferInvalidatingForMainFrame() const { return true; // If the main frame was already sent, wait for the main thread to respond. - if (begin_main_frame_state_ == BeginMainFrameState::SENT || - begin_main_frame_state_ == BeginMainFrameState::STARTED) + if (begin_main_frame_state_ == BeginMainFrameState::SENT) return true; // If the main thread committed during the last frame, i.e. it was not @@ -847,6 +853,11 @@ void SchedulerStateMachine::WillCommit(bool commit_has_no_updates) { } void SchedulerStateMachine::WillActivate() { + // We cannot activate the pending tree while paint worklets are still being + // processed; the pending tree *must* be fully painted before it can ever be + // activated because we cannot paint the active tree. + DCHECK(!processing_paint_worklets_for_pending_tree_); + if (layer_tree_frame_sink_state_ == LayerTreeFrameSinkState::WAITING_FOR_FIRST_ACTIVATION) layer_tree_frame_sink_state_ = LayerTreeFrameSinkState::ACTIVE; @@ -906,8 +917,7 @@ void SchedulerStateMachine::DidDrawInternal(DrawResult draw_result) { if (consecutive_checkerboard_animations_ >= settings_.maximum_number_of_failed_draws_before_draw_is_forced && - forced_redraw_state_ == ForcedRedrawOnTimeoutState::IDLE && - settings_.timeout_and_draw_when_animation_checkerboards) { + forced_redraw_state_ == ForcedRedrawOnTimeoutState::IDLE) { // We need to force a draw, but it doesn't make sense to do this until // we've committed and have new textures. forced_redraw_state_ = ForcedRedrawOnTimeoutState::WAITING_FOR_COMMIT; @@ -1134,8 +1144,7 @@ void SchedulerStateMachine::OnBeginImplFrameIdle() { main_thread_missed_last_deadline_ = CommitPending() || has_pending_tree_ || active_tree_needs_first_draw_; main_thread_failed_to_respond_last_deadline_ = - begin_main_frame_state_ == BeginMainFrameState::SENT || - begin_main_frame_state_ == BeginMainFrameState::STARTED; + begin_main_frame_state_ == BeginMainFrameState::SENT; // If we're entering a state where we won't get BeginFrames set all the // funnels so that we don't perform any actions that we shouldn't. @@ -1364,7 +1373,7 @@ void SchedulerStateMachine::SetNeedsOneBeginImplFrame() { } void SchedulerStateMachine::NotifyReadyToCommit() { - DCHECK_EQ(begin_main_frame_state_, BeginMainFrameState::STARTED) + DCHECK_EQ(begin_main_frame_state_, BeginMainFrameState::SENT) << AsValue()->ToString(); begin_main_frame_state_ = BeginMainFrameState::READY_TO_COMMIT; // In commit_to_active_tree mode, commit should happen right after BeginFrame, @@ -1374,7 +1383,7 @@ void SchedulerStateMachine::NotifyReadyToCommit() { } void SchedulerStateMachine::BeginMainFrameAborted(CommitEarlyOutReason reason) { - DCHECK_EQ(begin_main_frame_state_, BeginMainFrameState::STARTED); + DCHECK_EQ(begin_main_frame_state_, BeginMainFrameState::SENT); // If the main thread aborted, it doesn't matter if the main thread missed // the last deadline since it didn't have an update anyway. @@ -1415,6 +1424,12 @@ void SchedulerStateMachine::DidLoseLayerTreeFrameSink() { } bool SchedulerStateMachine::NotifyReadyToActivate() { + // It is not valid for clients to try and activate the pending tree whilst + // paint worklets are still being processed; the pending tree *must* be fully + // painted before it can ever be activated (even if e.g. it is not visible), + // because we cannot paint the active tree. + DCHECK(!processing_paint_worklets_for_pending_tree_); + if (!has_pending_tree_ || pending_tree_is_ready_for_activation_) return false; @@ -1448,6 +1463,16 @@ void SchedulerStateMachine::NotifyAnimationWorkletStateChange( } } +void SchedulerStateMachine::NotifyPaintWorkletStateChange( + PaintWorkletState state) { + bool processing_paint_worklets_for_pending_tree = + (state == PaintWorkletState::PROCESSING); + DCHECK_NE(processing_paint_worklets_for_pending_tree, + processing_paint_worklets_for_pending_tree_); + processing_paint_worklets_for_pending_tree_ = + processing_paint_worklets_for_pending_tree; +} + void SchedulerStateMachine::DidCreateAndInitializeLayerTreeFrameSink() { DCHECK_EQ(layer_tree_frame_sink_state_, LayerTreeFrameSinkState::CREATING); layer_tree_frame_sink_state_ = @@ -1464,11 +1489,6 @@ void SchedulerStateMachine::DidCreateAndInitializeLayerTreeFrameSink() { main_thread_missed_last_deadline_ = false; } -void SchedulerStateMachine::NotifyBeginMainFrameStarted() { - DCHECK_EQ(begin_main_frame_state_, BeginMainFrameState::SENT); - begin_main_frame_state_ = BeginMainFrameState::STARTED; -} - bool SchedulerStateMachine::HasInitializedLayerTreeFrameSink() const { switch (layer_tree_frame_sink_state_) { case LayerTreeFrameSinkState::NONE: diff --git a/chromium/cc/scheduler/scheduler_state_machine.h b/chromium/cc/scheduler/scheduler_state_machine.h index 091f1af3b7c..198e2ff1749 100644 --- a/chromium/cc/scheduler/scheduler_state_machine.h +++ b/chromium/cc/scheduler/scheduler_state_machine.h @@ -88,13 +88,15 @@ class CC_EXPORT SchedulerStateMachine { BeginImplFrameDeadlineMode mode); enum class BeginMainFrameState { - IDLE, - SENT, - STARTED, - READY_TO_COMMIT, + IDLE, // A new BeginMainFrame can start. + SENT, // A BeginMainFrame has already been issued. + READY_TO_COMMIT, // A previously issued BeginMainFrame has been processed, + // and is ready to commit. }; static const char* BeginMainFrameStateToString(BeginMainFrameState state); + // When a redraw is forced, it goes through a complete commit -> activation -> + // draw cycle. Until a redraw has been forced, it remains in IDLE state. enum class ForcedRedrawOnTimeoutState { IDLE, WAITING_FOR_COMMIT, @@ -109,9 +111,7 @@ class CC_EXPORT SchedulerStateMachine { } bool CommitPending() const { - return begin_main_frame_state_ == BeginMainFrameState::SENT || - begin_main_frame_state_ == BeginMainFrameState::STARTED || - begin_main_frame_state_ == BeginMainFrameState::READY_TO_COMMIT; + return begin_main_frame_state_ != BeginMainFrameState::IDLE; } bool NewActiveTreeLikely() const { @@ -271,9 +271,6 @@ class CC_EXPORT SchedulerStateMachine { // frame sink is not ready to receive frames. void SetSkipDraw(bool skip); - // Indicates that scheduled BeginMainFrame is started. - void NotifyBeginMainFrameStarted(); - // Indicates that the pending tree is ready for activation. Returns whether // the notification received updated the state for the current pending tree, // if any. @@ -283,6 +280,7 @@ class CC_EXPORT SchedulerStateMachine { void NotifyReadyToDraw(); enum class AnimationWorkletState { PROCESSING, IDLE }; + enum class PaintWorkletState { PROCESSING, IDLE }; enum class TreeType { ACTIVE, PENDING }; // Indicates if currently processing animation worklets for the active or @@ -291,6 +289,11 @@ class CC_EXPORT SchedulerStateMachine { void NotifyAnimationWorkletStateChange(AnimationWorkletState state, TreeType tree); + // Sets whether asynchronous paint worklets are running. Paint worklets + // running should block activation of the pending tree, as it isn't fully + // painted until they are done. + void NotifyPaintWorkletStateChange(PaintWorkletState state); + void SetNeedsImplSideInvalidation(bool needs_first_draw_on_activation); bool has_pending_tree() const { return has_pending_tree_; } @@ -377,6 +380,9 @@ class CC_EXPORT SchedulerStateMachine { LayerTreeFrameSinkState::NONE; BeginImplFrameState begin_impl_frame_state_ = BeginImplFrameState::IDLE; BeginMainFrameState begin_main_frame_state_ = BeginMainFrameState::IDLE; + + // A redraw is forced when too many checkerboarded-frames are produced during + // an animation. ForcedRedrawOnTimeoutState forced_redraw_state_ = ForcedRedrawOnTimeoutState::IDLE; @@ -451,6 +457,9 @@ class CC_EXPORT SchedulerStateMachine { // Indicates if an aysnc mutation cycle is in-flight or queued for the pending // tree. Only one can be running or queued at any time. bool processing_animation_worklets_for_pending_tree_ = false; + // Indicates if asychronous paint worklet painting is ongoing for the pending + // tree. During this time we should not activate the pending tree. + bool processing_paint_worklets_for_pending_tree_ = false; // Set to true if the main thread fails to respond with a commit or abort the // main frame before the draw deadline on the previous impl frame. diff --git a/chromium/cc/scheduler/scheduler_state_machine_unittest.cc b/chromium/cc/scheduler/scheduler_state_machine_unittest.cc index 676b83ee4ae..9e1adff48c4 100644 --- a/chromium/cc/scheduler/scheduler_state_machine_unittest.cc +++ b/chromium/cc/scheduler/scheduler_state_machine_unittest.cc @@ -6,6 +6,7 @@ #include <stddef.h> +#include "base/test/gtest_util.h" #include "base/trace_event/trace_event.h" #include "cc/scheduler/scheduler.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" @@ -74,7 +75,6 @@ const SchedulerStateMachine::BeginImplFrameState all_begin_impl_frame_states[] = const SchedulerStateMachine::BeginMainFrameState begin_main_frame_states[] = { SchedulerStateMachine::BeginMainFrameState::IDLE, SchedulerStateMachine::BeginMainFrameState::SENT, - SchedulerStateMachine::BeginMainFrameState::STARTED, SchedulerStateMachine::BeginMainFrameState::READY_TO_COMMIT}; // Exposes the protected state fields of the SchedulerStateMachine for testing @@ -450,7 +450,6 @@ TEST(SchedulerStateMachineTest, MainFrameBeforeActivationEnabled) { SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); @@ -468,7 +467,6 @@ TEST(SchedulerStateMachineTest, MainFrameBeforeActivationEnabled) { // Verify the pending commit doesn't overwrite the pending // tree until the pending tree has been activated. - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); @@ -561,7 +559,6 @@ TEST(SchedulerStateMachineTest, FailedDrawForMissingHighResNeedsCommit) { EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); // Finish the commit and activation. - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); @@ -610,7 +607,6 @@ TEST(SchedulerStateMachineTest, // Finish the commit. Note, we should not yet be forcing a draw, but should // continue the commit as usual. - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); @@ -924,7 +920,6 @@ TEST(SchedulerStateMachineTest, EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); state.NotifyReadyToActivate(); @@ -953,7 +948,6 @@ TEST(SchedulerStateMachineTest, TestSetNeedsBeginMainFrameIsNotLost) { EXPECT_TRUE(state.NeedsCommit()); // Let the frame finish. - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_MAIN_FRAME_STATE( SchedulerStateMachine::BeginMainFrameState::READY_TO_COMMIT); @@ -1012,7 +1006,6 @@ TEST(SchedulerStateMachineTest, TestFullCycle) { EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); // Tell the scheduler the frame finished. - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_MAIN_FRAME_STATE( SchedulerStateMachine::BeginMainFrameState::READY_TO_COMMIT); @@ -1053,7 +1046,6 @@ TEST(SchedulerStateMachineTest, CommitWithoutDrawWithPendingTree) { state.IssueNextBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); state.NotifyReadyToActivate(); @@ -1066,7 +1058,6 @@ TEST(SchedulerStateMachineTest, CommitWithoutDrawWithPendingTree) { state.IssueNextBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); } @@ -1085,7 +1076,6 @@ TEST(SchedulerStateMachineTest, DontCommitWithoutDrawWithoutPendingTree) { state.OnBeginImplFrame(0, 10, kAnimateOnly); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); state.NotifyReadyToActivate(); @@ -1110,7 +1100,6 @@ TEST(SchedulerStateMachineTest, AbortedMainFrameDoesNotResetPendingTree) { EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); @@ -1126,7 +1115,6 @@ TEST(SchedulerStateMachineTest, AbortedMainFrameDoesNotResetPendingTree) { SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); EXPECT_TRUE(state.has_pending_tree()); - state.NotifyBeginMainFrameStarted(); state.BeginMainFrameAborted(CommitEarlyOutReason::FINISHED_NO_UPDATES); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); EXPECT_TRUE(state.has_pending_tree()); @@ -1139,7 +1127,6 @@ TEST(SchedulerStateMachineTest, AbortedMainFrameDoesNotResetPendingTree) { EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); EXPECT_TRUE(state.has_pending_tree()); @@ -1172,7 +1159,6 @@ TEST(SchedulerStateMachineTest, TestFullCycleWithCommitToActive) { EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); // Tell the scheduler the frame finished. - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_MAIN_FRAME_STATE( SchedulerStateMachine::BeginMainFrameState::READY_TO_COMMIT); @@ -1211,7 +1197,6 @@ TEST(SchedulerStateMachineTest, TestFullCycleWithCommitToActive) { state.DidReceiveCompositorFrameAck(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); state.NotifyReadyToActivate(); @@ -1252,7 +1237,6 @@ TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) { EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); // Tell the scheduler the frame finished. - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_MAIN_FRAME_STATE( SchedulerStateMachine::BeginMainFrameState::READY_TO_COMMIT); @@ -1351,7 +1335,6 @@ TEST(SchedulerStateMachineTest, TestAbortBeginMainFrameBecauseInvisible) { // Become invisible and abort BeginMainFrame. state.SetVisible(false); - state.NotifyBeginMainFrameStarted(); state.BeginMainFrameAborted(CommitEarlyOutReason::ABORTED_NOT_VISIBLE); // NeedsCommit should now be true again because we never actually did a @@ -1407,7 +1390,6 @@ TEST(SchedulerStateMachineTest, TestAbortBeginMainFrameBecauseCommitNotNeeded) { // Abort the commit, true means that the BeginMainFrame was sent but there // was no work to do on the main thread. - state.NotifyBeginMainFrameStarted(); state.BeginMainFrameAborted(CommitEarlyOutReason::FINISHED_NO_UPDATES); // NeedsCommit should now be false because the commit was actually handled. @@ -1535,7 +1517,6 @@ TEST(SchedulerStateMachineTest, EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); // Finish the commit, which should make the surface active. - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); EXPECT_EQ(state.layer_tree_frame_sink_state(), @@ -1585,7 +1566,6 @@ TEST(SchedulerStateMachineTest, SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); // Activate so we need the first draw - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); state.NotifyReadyToActivate(); @@ -1631,7 +1611,6 @@ TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) { EXPECT_ACTION(SchedulerStateMachine::Action::NONE); // Finish the frame, commit and activate. - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); state.NotifyReadyToActivate(); @@ -1689,7 +1668,6 @@ TEST(SchedulerStateMachineTest, EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); // Finish the frame, and commit and activate. - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); state.NotifyReadyToActivate(); @@ -1724,7 +1702,6 @@ TEST(SchedulerStateMachineTest, EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); @@ -1770,7 +1747,6 @@ TEST(SchedulerStateMachineTest, // Cause a lost context. state.DidLoseLayerTreeFrameSink(); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); @@ -1837,7 +1813,6 @@ TEST(SchedulerStateMachineTest, TestFinishCommitWhenCommitInProgress) { // After the commit completes, activation and draw happen immediately // because we are not visible. - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::ACTIVATE_SYNC_TREE); @@ -1862,7 +1837,6 @@ TEST(SchedulerStateMachineTest, // After the commit completes, activation and draw happen immediately // because we are not visible. - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_TRUE(state.ShouldAbortCurrentFrame()); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); @@ -1995,7 +1969,6 @@ TEST(SchedulerStateMachineTest, // Since only the scroll offset changed, the main thread will abort the // commit. - state.NotifyBeginMainFrameStarted(); state.BeginMainFrameAborted(CommitEarlyOutReason::FINISHED_NO_UPDATES); // Since the commit was aborted, we should draw right away instead of waiting @@ -2008,7 +1981,6 @@ void FinishPreviousCommitAndDrawWithoutExitingDeadline( // Gross, but allows us to use macros below. StateMachine& state = *state_ptr; - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); @@ -2201,7 +2173,6 @@ TEST(SchedulerStateMachineTest, // Abort the commit, since that is what we expect the main thread to do if the // LayerTreeFrameSink was lost due to a synchronous call from the main thread // to release the LayerTreeFrameSink. - state.NotifyBeginMainFrameStarted(); state.BeginMainFrameAborted( CommitEarlyOutReason::ABORTED_LAYER_TREE_FRAME_SINK_LOST); @@ -2283,7 +2254,6 @@ TEST(SchedulerStateMachineTest, state.IssueNextBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); state.NotifyReadyToActivate(); @@ -2311,7 +2281,6 @@ TEST(SchedulerStateMachineTest, state.IssueNextBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); - state.NotifyBeginMainFrameStarted(); state.BeginMainFrameAborted(CommitEarlyOutReason::FINISHED_NO_UPDATES); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); state.OnBeginImplFrameDeadline(); @@ -2373,7 +2342,6 @@ TEST(SchedulerStateMachineTest, NoImplSideInvalidationUntilFrameSinkActive) { // No impl side invalidation because we're still waiting for first commit. EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); @@ -2416,7 +2384,6 @@ TEST(SchedulerStateMachineTest, ImplSideInvalidationWhenPendingTreeExists) { state.IssueNextBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); @@ -2456,7 +2423,6 @@ TEST(SchedulerStateMachineTest, ImplSideInvalidationWhileReadyToCommit) { EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); // Request an impl-side invalidation after we are ready to commit. The @@ -2513,7 +2479,6 @@ TEST(SchedulerStateMachineTest, ImplSideInvalidationsThrottledOnDraw) { state.IssueNextBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); state.NotifyReadyToActivate(); @@ -2595,7 +2560,6 @@ TEST(SchedulerStateMachineTest, TestFullPipelineMode) { state.CurrentBeginImplFrameDeadlineMode()); // Tell the scheduler the frame finished. - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_MAIN_FRAME_STATE( SchedulerStateMachine::BeginMainFrameState::READY_TO_COMMIT); @@ -2686,7 +2650,6 @@ TEST(SchedulerStateMachineTest, TestFullPipelineMode) { state.CurrentBeginImplFrameDeadlineMode()); // Abort commit and ensure that we don't block anymore. - state.NotifyBeginMainFrameStarted(); state.BeginMainFrameAborted(CommitEarlyOutReason::FINISHED_NO_UPDATES); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); EXPECT_MAIN_FRAME_STATE(SchedulerStateMachine::BeginMainFrameState::IDLE); @@ -2716,7 +2679,6 @@ TEST(SchedulerStateMachineTest, AllowSkippingActiveTreeFirstDraws) { state.OnBeginImplFrame(0, 2, kAnimateOnly); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); @@ -2736,7 +2698,6 @@ TEST(SchedulerStateMachineTest, DelayDrawIfAnimationWorkletsPending) { state.OnBeginImplFrame(0, 10, kAnimateOnly); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); state.NotifyReadyToActivate(); @@ -2801,7 +2762,6 @@ TEST(SchedulerStateMachineTest, BlockActivationIfAnimationWorkletsPending) { state.OnBeginImplFrame(0, 10, kAnimateOnly); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); - state.NotifyBeginMainFrameStarted(); state.NotifyReadyToCommit(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); state.NotifyAnimationWorkletStateChange( @@ -2815,5 +2775,57 @@ TEST(SchedulerStateMachineTest, BlockActivationIfAnimationWorkletsPending) { EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::ACTIVATE_SYNC_TREE); } +TEST(SchedulerStateMachineTest, BlockActivationIfPaintWorkletsPending) { + SchedulerSettings settings; + StateMachine state(settings); + SET_UP_STATE(state); + + state.SetNeedsBeginMainFrame(); + state.OnBeginImplFrame(0, 10, kAnimateOnly); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); + state.NotifyReadyToCommit(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); + + // Post-commit, we start working on PaintWorklets. It is not valid to activate + // until they are done. + state.NotifyPaintWorkletStateChange( + SchedulerStateMachine::PaintWorkletState::PROCESSING); + // We (correctly) cannot call state.NotifyReadyToActivate() here as it hits a + // DCHECK because PaintWorklets are ongoing. + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); + state.NotifyPaintWorkletStateChange( + SchedulerStateMachine::PaintWorkletState::IDLE); + state.NotifyReadyToActivate(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::ACTIVATE_SYNC_TREE); +} + +TEST(SchedulerStateMachineTest, + BlockActivationIfPaintWorkletsPendingEvenWhenAbortingFrame) { + SchedulerSettings settings; + StateMachine state(settings); + SET_UP_STATE(state); + + state.SetNeedsBeginMainFrame(); + state.OnBeginImplFrame(0, 10, kAnimateOnly); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME); + state.NotifyReadyToCommit(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); + + // Even if we are aborting the frame, we must paint the pending tree before we + // activate it (because we cannot have an unpainted active tree). + state.NotifyPaintWorkletStateChange( + SchedulerStateMachine::PaintWorkletState::PROCESSING); + state.SetVisible(false); + ASSERT_TRUE(state.ShouldAbortCurrentFrame()); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE); + state.NotifyPaintWorkletStateChange( + SchedulerStateMachine::PaintWorkletState::IDLE); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::ACTIVATE_SYNC_TREE); +} + } // namespace } // namespace cc diff --git a/chromium/cc/scheduler/scheduler_unittest.cc b/chromium/cc/scheduler/scheduler_unittest.cc index 2f5deee74de..46882a54cc4 100644 --- a/chromium/cc/scheduler/scheduler_unittest.cc +++ b/chromium/cc/scheduler/scheduler_unittest.cc @@ -163,7 +163,7 @@ class FakeSchedulerClient : public SchedulerClient, draw_will_happen_ && swap_will_happen_if_draw_happens_; if (swap_will_happen) { last_begin_frame_ack_ = scheduler_->CurrentBeginFrameAckForActiveTree(); - scheduler_->DidSubmitCompositorFrame(); + scheduler_->DidSubmitCompositorFrame(0); if (automatic_ack_) scheduler_->DidReceiveCompositorFrameAck(); @@ -2153,126 +2153,6 @@ TEST_F(SchedulerTest, MainFrameThenImplFrameSkippedAfterLateCommitAndLateAck) { "ScheduledActionDrawIfPossible"); } -TEST_F( - SchedulerTest, - Deadlock_CommitMakesProgressWhileSwapTrottledAndActiveTreeNeedsFirstDraw) { - // NPAPI plugins on Windows block the Browser UI thread on the Renderer main - // thread. This prevents the scheduler from receiving any pending swap acks. - - scheduler_settings_.main_frame_while_submit_frame_throttled_enabled = true; - SetUpScheduler(EXTERNAL_BFS); - - // Disables automatic swap acks so this test can force swap ack throttling - // to simulate a blocked Browser ui thread. - client_->SetAutomaticSubmitCompositorFrameAck(false); - - // Get a new active tree in main-thread high latency mode and put us - // in a swap throttled state. - client_->Reset(); - EXPECT_FALSE(scheduler_->CommitPending()); - scheduler_->SetNeedsBeginMainFrame(); - scheduler_->SetNeedsRedraw(); - EXPECT_SCOPED(AdvanceFrame()); - EXPECT_TRUE(scheduler_->CommitPending()); - EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - EXPECT_FALSE(client_->IsInsideBeginImplFrame()); - scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); - scheduler_->NotifyReadyToCommit(); - scheduler_->NotifyReadyToActivate(); - EXPECT_FALSE(scheduler_->CommitPending()); - EXPECT_ACTIONS("AddObserver(this)", "WillBeginImplFrame", - "ScheduledActionSendBeginMainFrame", - "ScheduledActionDrawIfPossible", "ScheduledActionCommit", - "ScheduledActionActivateSyncTree"); - - // Make sure that we can finish the next commit even while swap throttled. - client_->Reset(); - EXPECT_FALSE(scheduler_->CommitPending()); - scheduler_->SetNeedsBeginMainFrame(); - EXPECT_SCOPED(AdvanceFrame()); - scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); - scheduler_->NotifyReadyToCommit(); - scheduler_->NotifyReadyToActivate(); - EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - EXPECT_FALSE(client_->IsInsideBeginImplFrame()); - EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame", - "ScheduledActionCommit"); - - // Make sure we do not send a BeginMainFrame while swap throttled and - // we have both a pending tree and an active tree. - client_->Reset(); - EXPECT_FALSE(scheduler_->CommitPending()); - scheduler_->SetNeedsBeginMainFrame(); - EXPECT_SCOPED(AdvanceFrame()); - EXPECT_FALSE(scheduler_->CommitPending()); - task_runner_->RunPendingTasks(); // Run posted deadline. - EXPECT_ACTIONS("WillBeginImplFrame"); -} - -TEST_F( - SchedulerTest, - CommitMakesProgressWhenIdleAndHasPendingTreeAndActiveTreeNeedsFirstDraw) { - // This verifies we don't block commits longer than we need to - // for performance reasons - not deadlock reasons. - - // Since we are simulating a long commit, set up a client with draw duration - // estimates that prevent skipping main frames to get to low latency mode. - scheduler_settings_.main_frame_while_submit_frame_throttled_enabled = true; - scheduler_settings_.main_frame_before_activation_enabled = true; - SetUpScheduler(EXTERNAL_BFS); - - // Disables automatic swap acks so this test can force swap ack throttling - // to simulate a blocked Browser ui thread. - client_->SetAutomaticSubmitCompositorFrameAck(false); - - // Start a new commit in main-thread high latency mode and hold off on - // activation. - client_->Reset(); - EXPECT_FALSE(scheduler_->CommitPending()); - scheduler_->SetNeedsBeginMainFrame(); - scheduler_->SetNeedsRedraw(); - EXPECT_SCOPED(AdvanceFrame()); - EXPECT_TRUE(scheduler_->CommitPending()); - EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - EXPECT_FALSE(client_->IsInsideBeginImplFrame()); - scheduler_->DidReceiveCompositorFrameAck(); - scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); - scheduler_->NotifyReadyToCommit(); - EXPECT_FALSE(scheduler_->CommitPending()); - EXPECT_ACTIONS("AddObserver(this)", "WillBeginImplFrame", - "ScheduledActionSendBeginMainFrame", - "ScheduledActionDrawIfPossible", "ScheduledActionCommit"); - - // Start another commit while we still have an active tree. - client_->Reset(); - EXPECT_FALSE(scheduler_->CommitPending()); - scheduler_->SetNeedsBeginMainFrame(); - scheduler_->SetNeedsRedraw(); - EXPECT_SCOPED(AdvanceFrame()); - EXPECT_TRUE(scheduler_->CommitPending()); - EXPECT_TRUE(client_->IsInsideBeginImplFrame()); - task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true)); - EXPECT_FALSE(client_->IsInsideBeginImplFrame()); - scheduler_->DidReceiveCompositorFrameAck(); - scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks()); - EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame", - "ScheduledActionDrawIfPossible"); - - // Can't commit yet because there's still a pending tree. - client_->Reset(); - scheduler_->NotifyReadyToCommit(); - EXPECT_NO_ACTION(); - - // Activate the pending tree, which also unblocks the commit immediately - // while we are in an idle state. - client_->Reset(); - scheduler_->NotifyReadyToActivate(); - EXPECT_ACTIONS("ScheduledActionActivateSyncTree", "ScheduledActionCommit"); -} - void SchedulerTest::BeginFramesNotFromClient(BeginFrameSourceType bfs_type) { SetUpScheduler(bfs_type); @@ -4112,38 +3992,8 @@ TEST_F(SchedulerTest, CriticalBeginMainFrameToActivateIsFast) { EXPECT_TRUE(scheduler_->ImplLatencyTakesPriority()); } -TEST_F(SchedulerTest, WaitForAllPipelineStagesSkipsMissedBeginFrames) { - scheduler_settings_.wait_for_all_pipeline_stages_before_draw = true; - client_ = std::make_unique<FakeSchedulerClient>(); - CreateScheduler(EXTERNAL_BFS); - - // Initialize frame sink so that state machine Scheduler and state machine - // need BeginFrames. - scheduler_->SetVisible(true); - scheduler_->SetCanDraw(true); - EXPECT_ACTIONS("ScheduledActionBeginLayerTreeFrameSinkCreation"); - client_->Reset(); - scheduler_->DidCreateAndInitializeLayerTreeFrameSink(); - scheduler_->SetNeedsBeginMainFrame(); - EXPECT_TRUE(scheduler_->begin_frames_expected()); - EXPECT_FALSE(client_->IsInsideBeginImplFrame()); - client_->Reset(); - - base::TimeDelta interval = base::TimeDelta::FromMilliseconds(16); - task_runner_->AdvanceMockTickClock(interval); - viz::BeginFrameArgs args = viz::BeginFrameArgs::Create( - BEGINFRAME_FROM_HERE, 0u, 1u, task_runner_->NowTicks(), - task_runner_->NowTicks() + interval, interval, - viz::BeginFrameArgs::MISSED); - fake_external_begin_frame_source_->TestOnBeginFrame(args); - EXPECT_FALSE(client_->IsInsideBeginImplFrame()); -} - -TEST_F( - SchedulerTest, - WaitForAllPipelineStagesUsesMissedBeginFramesWithSurfaceSynchronization) { +TEST_F(SchedulerTest, WaitForAllPipelineStagesUsesMissedBeginFrames) { scheduler_settings_.wait_for_all_pipeline_stages_before_draw = true; - scheduler_settings_.enable_surface_synchronization = true; client_ = std::make_unique<FakeSchedulerClient>(); CreateScheduler(EXTERNAL_BFS); @@ -4173,7 +4023,6 @@ TEST_F( TEST_F(SchedulerTest, WaitForAllPipelineStagesAlwaysObservesBeginFrames) { scheduler_settings_.wait_for_all_pipeline_stages_before_draw = true; - scheduler_settings_.enable_surface_synchronization = true; client_ = std::make_unique<FakeSchedulerClient>(); CreateScheduler(EXTERNAL_BFS); diff --git a/chromium/cc/tiles/checker_image_tracker.cc b/chromium/cc/tiles/checker_image_tracker.cc index 6eda401d7a5..c31c7b5e3e9 100644 --- a/chromium/cc/tiles/checker_image_tracker.cc +++ b/chromium/cc/tiles/checker_image_tracker.cc @@ -135,8 +135,7 @@ CheckerImageTracker::CheckerImageTracker(ImageController* image_controller, : image_controller_(image_controller), client_(client), enable_checker_imaging_(enable_checker_imaging), - min_image_bytes_to_checker_(min_image_bytes_to_checker), - weak_factory_(this) {} + min_image_bytes_to_checker_(min_image_bytes_to_checker) {} CheckerImageTracker::~CheckerImageTracker() = default; diff --git a/chromium/cc/tiles/checker_image_tracker.h b/chromium/cc/tiles/checker_image_tracker.h index ee5578f8fef..7628689915b 100644 --- a/chromium/cc/tiles/checker_image_tracker.h +++ b/chromium/cc/tiles/checker_image_tracker.h @@ -208,7 +208,7 @@ class CC_EXPORT CheckerImageTracker { base::flat_map<PaintImage::Id, PaintImage::DecodingMode> decoding_mode_map_; - base::WeakPtrFactory<CheckerImageTracker> weak_factory_; + base::WeakPtrFactory<CheckerImageTracker> weak_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/tiles/decoded_image_tracker.cc b/chromium/cc/tiles/decoded_image_tracker.cc index 5db3f198b6d..a7aa6b497ad 100644 --- a/chromium/cc/tiles/decoded_image_tracker.cc +++ b/chromium/cc/tiles/decoded_image_tracker.cc @@ -28,8 +28,7 @@ DecodedImageTracker::DecodedImageTracker( scoped_refptr<base::SequencedTaskRunner> task_runner) : image_controller_(controller), task_runner_(std::move(task_runner)), - tick_clock_(base::DefaultTickClock::GetInstance()), - weak_ptr_factory_(this) { + tick_clock_(base::DefaultTickClock::GetInstance()) { DCHECK(image_controller_); } diff --git a/chromium/cc/tiles/decoded_image_tracker.h b/chromium/cc/tiles/decoded_image_tracker.h index 4803dfa3a2e..c3bf49793bc 100644 --- a/chromium/cc/tiles/decoded_image_tracker.h +++ b/chromium/cc/tiles/decoded_image_tracker.h @@ -93,7 +93,7 @@ class CC_EXPORT DecodedImageTracker { // Defaults to base::TimeTicks::Now(), but overrideable for testing. const base::TickClock* tick_clock_; - base::WeakPtrFactory<DecodedImageTracker> weak_ptr_factory_; + base::WeakPtrFactory<DecodedImageTracker> weak_ptr_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/tiles/gpu_image_decode_cache.cc b/chromium/cc/tiles/gpu_image_decode_cache.cc index 30ce3e45e4d..78dd9c6b720 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache.cc @@ -8,7 +8,9 @@ #include "base/auto_reset.h" #include "base/bind.h" +#include "base/containers/span.h" #include "base/debug/alias.h" +#include "base/feature_list.h" #include "base/hash/hash.h" #include "base/memory/discardable_memory_allocator.h" #include "base/metrics/histogram_macros.h" @@ -18,7 +20,6 @@ #include "base/trace_event/memory_dump_manager.h" #include "cc/base/devtools_instrumentation.h" #include "cc/base/histograms.h" -#include "cc/paint/image_transfer_cache_entry.h" #include "cc/raster/scoped_grcontext_access.h" #include "cc/raster/tile_task.h" #include "cc/tiles/mipmap_util.h" @@ -26,6 +27,8 @@ #include "gpu/command_buffer/client/context_support.h" #include "gpu/command_buffer/client/gles2_interface.h" #include "gpu/command_buffer/client/raster_interface.h" +#include "gpu/command_buffer/common/sync_token.h" +#include "gpu/config/gpu_finch_features.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkSurface.h" @@ -33,6 +36,8 @@ #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/GrContext.h" #include "third_party/skia/include/gpu/GrTexture.h" +#include "ui/gfx/color_space.h" +#include "ui/gfx/geometry/size.h" #include "ui/gfx/skia_util.h" #include "ui/gl/trace_util.h" @@ -160,6 +165,16 @@ bool ShouldGenerateMips(const DrawImage& draw_image, return false; } +// This helper method takes in +// - |yuva_size_info| corresponding to the plane dimensions of a YUV image +// - |info| indicating SkAlphaType and SkColorSpace per plane (though the final +// image color space is currently indicated through other means at creation +// of the YUV SkImage) +// - |memory_ptr| pointing to sufficient contiguous memory for the planes +// +// It then sets each SkPixmap to have the dimensions specified by its respective +// SkYUVAIndex within |yuva_size_info| and to point to bytes in memory at +// |planes[index]|. void SetYuvPixmapsFromSizeInfo(SkPixmap* pixmap_y, SkPixmap* pixmap_u, SkPixmap* pixmap_v, @@ -189,6 +204,41 @@ void SetYuvPixmapsFromSizeInfo(SkPixmap* pixmap_y, v_decode_info.minRowBytes()); } +// Helper method to fill in |scaled_u_size| and |scaled_v_size| by computing +// the mip level for each plane given the final raster dimensions and the +// unscaled U and V plane sizes. Also takes in |draw_image| to compute the Y +// plane mip level and DCHECK that the computed mip levels for U and V are +// reasonable. +// +// TODO(crbug.com/915972): Assumes 420 subsampling and DCHECKs the size ratios. +void ComputeMippedUVPlaneSizes(const gfx::Size& target_raster_size, + const gfx::Size& unscaled_u_size, + const gfx::Size& unscaled_v_size, + const DrawImage& draw_image, + gfx::Size* scaled_u_size, + gfx::Size* scaled_v_size) { + DCHECK(scaled_u_size); + DCHECK(scaled_v_size); + DCHECK_EQ(unscaled_u_size, unscaled_v_size); + DCHECK_EQ((draw_image.paint_image().width() + 1) / 2, + unscaled_u_size.width()); + const int uv_mip_level = + MipMapUtil::GetLevelForSize(unscaled_u_size, target_raster_size); + // Check that the chroma planes do not shrink *more* than the luma. + // At least for YUV420, they will shrink at most one mip level below luma, + // which avoids blurriness. + DCHECK_GE(uv_mip_level, 0); + if (CalculateUploadScaleMipLevel(draw_image) == 0) { + // If Y is not scaled, then U and V shouldn't be either. + DCHECK_EQ(uv_mip_level, 0); + } else { + DCHECK_EQ(CalculateUploadScaleMipLevel(draw_image) - 1, uv_mip_level); + } + + *scaled_u_size = MipMapUtil::GetSizeForLevel(unscaled_u_size, uv_mip_level); + *scaled_v_size = *scaled_u_size; +} + // Draws and scales the provided |draw_image| into the |target_pixmap|. If the // draw/scale can be done directly, calls directly into PaintImage::Decode. // if not, decodes to a compatible temporary pixmap and then converts that into @@ -316,23 +366,34 @@ bool DrawAndScaleImage(const DrawImage& draw_image, &unscaled_pixmap_v, yuva_size_info, planes, decode_info, decode_pixmap.writable_addr()); - // Assumes YUV420 and splits decode_pixmap into pixmaps for each plane. - // TODO(crbug.com/915972): Fix this assumption. const SkImageInfo y_info_scaled = info.makeColorType(kGray_8_SkColorType); - const size_t uv_width_scaled = (y_info_scaled.width() + 1) / 2; - const size_t uv_height_scaled = (y_info_scaled.height() + 1) / 2; - const SkImageInfo uv_info_scaled = - y_info_scaled.makeWH(uv_width_scaled, uv_height_scaled); + // The target raster dimensions get passed through: + // |target_pixmap|.info() -> |pixmap|->info() -> |info| -> |y_info_scaled| + const gfx::Size target_raster_size(y_info_scaled.width(), + y_info_scaled.height()); + gfx::Size unscaled_u_size(unscaled_pixmap_u.width(), + unscaled_pixmap_u.height()); + gfx::Size unscaled_v_size(unscaled_pixmap_v.width(), + unscaled_pixmap_v.height()); + gfx::Size scaled_u_size; + gfx::Size scaled_v_size; + ComputeMippedUVPlaneSizes(target_raster_size, unscaled_u_size, + unscaled_v_size, draw_image, &scaled_u_size, + &scaled_v_size); + const SkImageInfo u_info_scaled = + y_info_scaled.makeWH(scaled_u_size.width(), scaled_u_size.height()); + const SkImageInfo v_info_scaled = + y_info_scaled.makeWH(scaled_v_size.width(), scaled_v_size.height()); const size_t y_plane_bytes = y_info_scaled.computeMinByteSize(); - const size_t u_plane_bytes = uv_info_scaled.computeMinByteSize(); + const size_t u_plane_bytes = u_info_scaled.computeMinByteSize(); DCHECK(!SkImageInfo::ByteSizeOverflowed(y_plane_bytes)); DCHECK(!SkImageInfo::ByteSizeOverflowed(u_plane_bytes)); pixmap_y->reset(y_info_scaled, data_ptr, y_info_scaled.minRowBytes()); - pixmap_u->reset(uv_info_scaled, data_ptr + y_plane_bytes, - uv_info_scaled.minRowBytes()); - pixmap_v->reset(uv_info_scaled, data_ptr + y_plane_bytes + u_plane_bytes, - uv_info_scaled.minRowBytes()); + pixmap_u->reset(u_info_scaled, data_ptr + y_plane_bytes, + u_info_scaled.minRowBytes()); + pixmap_v->reset(v_info_scaled, data_ptr + y_plane_bytes + u_plane_bytes, + v_info_scaled.minRowBytes()); const bool all_planes_scaled_successfully = unscaled_pixmap_y.scalePixels(*pixmap_y, filter_quality) && @@ -520,10 +581,12 @@ class ImageUploadTaskImpl : public TileTask { ImageUploadTaskImpl(GpuImageDecodeCache* cache, const DrawImage& draw_image, scoped_refptr<TileTask> decode_dependency, + sk_sp<SkData> encoded_data, const ImageDecodeCache::TracingInfo& tracing_info) : TileTask(false), cache_(cache), image_(draw_image), + encoded_data_(std::move(encoded_data)), tracing_info_(tracing_info) { DCHECK(!SkipImage(draw_image)); // If an image is already decoded and locked, we will not generate a @@ -539,7 +602,7 @@ class ImageUploadTaskImpl : public TileTask { void RunOnWorkerThread() override { TRACE_EVENT2("cc", "ImageUploadTaskImpl::RunOnWorkerThread", "mode", "gpu", "source_prepare_tiles_id", tracing_info_.prepare_tiles_id); - cache_->UploadImageInTask(image_); + cache_->UploadImageInTask(image_, std::move(encoded_data_)); } // Overridden from TileTask: @@ -553,6 +616,7 @@ class ImageUploadTaskImpl : public TileTask { private: GpuImageDecodeCache* cache_; DrawImage image_; + sk_sp<SkData> encoded_data_; const ImageDecodeCache::TracingInfo tracing_info_; }; @@ -601,8 +665,11 @@ int GpuImageDecodeCache::ImageDataBase::UsageState() const { return state; } -GpuImageDecodeCache::DecodedImageData::DecodedImageData(bool is_bitmap_backed) - : is_bitmap_backed_(is_bitmap_backed) {} +GpuImageDecodeCache::DecodedImageData::DecodedImageData( + bool is_bitmap_backed, + bool do_hardware_accelerated_decode) + : is_bitmap_backed_(is_bitmap_backed), + do_hardware_accelerated_decode_(do_hardware_accelerated_decode) {} GpuImageDecodeCache::DecodedImageData::~DecodedImageData() { ResetData(); } @@ -684,6 +751,14 @@ void GpuImageDecodeCache::DecodedImageData::ResetData() { } void GpuImageDecodeCache::DecodedImageData::ReportUsageStats() const { + if (do_hardware_accelerated_decode_) { + // When doing hardware decode acceleration, we don't want to record usage + // stats for the decode data. The reason is that the decode is done in the + // GPU process and the decoded result stays there. On the renderer side, we + // don't use or lock the decoded data, so reporting this status would + // incorrectly distort the software decoding statistics. + return; + } UMA_HISTOGRAM_ENUMERATION("Renderer4.GpuImageDecodeState", static_cast<ImageUsageState>(UsageState()), IMAGE_USAGE_STATE_COUNT); @@ -786,6 +861,7 @@ GpuImageDecodeCache::ImageData::ImageData( int upload_scale_mip_level, bool needs_mips, bool is_bitmap_backed, + bool do_hardware_accelerated_decode, bool is_yuv_format) : paint_image_id(paint_image_id), mode(mode), @@ -796,7 +872,7 @@ GpuImageDecodeCache::ImageData::ImageData( needs_mips(needs_mips), is_bitmap_backed(is_bitmap_backed), is_yuv(is_yuv_format), - decode(is_bitmap_backed) {} + decode(is_bitmap_backed, do_hardware_accelerated_decode) {} GpuImageDecodeCache::ImageData::~ImageData() { // We should never delete ImageData while it is in use or before it has been @@ -923,9 +999,17 @@ ImageDecodeCache::TaskResult GpuImageDecodeCache::GetTaskForImageAndRefInternal( const InUseCacheKey cache_key = InUseCacheKey::FromDrawImage(draw_image); ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key); scoped_refptr<ImageData> new_data; + sk_sp<SkData> encoded_data; if (!image_data) { - // We need an ImageData, create one now. - new_data = CreateImageData(draw_image); + // We need an ImageData, create one now. Note that hardware decode + // acceleration is allowed only in the DecodeTaskType::kPartOfUploadTask + // case. This prevents the img.decode() and checkerboard images paths from + // going through hardware decode acceleration. + new_data = CreateImageData( + draw_image, + task_type == + DecodeTaskType::kPartOfUploadTask /* allow_hardware_decode */, + &encoded_data); image_data = new_data.get(); } else if (image_data->decode.decode_failure) { // We have already tried and failed to decode this image, so just return. @@ -976,7 +1060,7 @@ ImageDecodeCache::TaskResult GpuImageDecodeCache::GetTaskForImageAndRefInternal( task = base::MakeRefCounted<ImageUploadTaskImpl>( this, draw_image, GetImageDecodeTaskAndRef(draw_image, tracing_info, task_type), - tracing_info); + std::move(encoded_data), tracing_info); image_data->upload.task = task; } else { task = GetImageDecodeTaskAndRef(draw_image, tracing_info, task_type); @@ -1015,9 +1099,11 @@ DecodedDrawImage GpuImageDecodeCache::GetDecodedImageForDraw( base::AutoLock lock(lock_); const InUseCacheKey cache_key = InUseCacheKey::FromDrawImage(draw_image); ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key); + sk_sp<SkData> encoded_data; if (!image_data) { // We didn't find the image, create a new entry. - auto data = CreateImageData(draw_image); + auto data = CreateImageData(draw_image, true /* allow_hardware_decode */, + &encoded_data); image_data = data.get(); AddToPersistentCache(draw_image, std::move(data)); } @@ -1032,7 +1118,7 @@ DecodedDrawImage GpuImageDecodeCache::GetDecodedImageForDraw( // We may or may not need to decode and upload the image we've found, the // following functions early-out to if we already decoded. DecodeImageIfNecessary(draw_image, image_data, TaskType::kInRaster); - UploadImageIfNecessary(draw_image, image_data); + UploadImageIfNecessary(draw_image, image_data, std::move(encoded_data)); // Unref the image decode, but not the image. The image ref will be released // in DrawWithImageFinished. UnrefImageDecode(draw_image, cache_key); @@ -1299,7 +1385,8 @@ void GpuImageDecodeCache::DecodeImageInTask(const DrawImage& draw_image, DecodeImageIfNecessary(draw_image, image_data, task_type); } -void GpuImageDecodeCache::UploadImageInTask(const DrawImage& draw_image) { +void GpuImageDecodeCache::UploadImageInTask(const DrawImage& draw_image, + sk_sp<SkData> encoded_data) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "GpuImageDecodeCache::UploadImage"); base::Optional<viz::RasterContextProvider::ScopedRasterContextLock> @@ -1319,7 +1406,7 @@ void GpuImageDecodeCache::UploadImageInTask(const DrawImage& draw_image) { if (image_data->is_bitmap_backed) DecodeImageIfNecessary(draw_image, image_data, TaskType::kInRaster); - UploadImageIfNecessary(draw_image, image_data); + UploadImageIfNecessary(draw_image, image_data, std::move(encoded_data)); } void GpuImageDecodeCache::OnImageDecodeTaskCompleted( @@ -1365,8 +1452,7 @@ void GpuImageDecodeCache::OnImageUploadTaskCompleted( UnrefImageInternal(draw_image, cache_key); } -// Checks if an existing image decode exists. If not, returns a task to produce -// the requested decode. +// Checks if an image decode needs a decode task and returns it. scoped_refptr<TileTask> GpuImageDecodeCache::GetImageDecodeTaskAndRef( const DrawImage& draw_image, const TracingInfo& tracing_info, @@ -1384,6 +1470,9 @@ scoped_refptr<TileTask> GpuImageDecodeCache::GetImageDecodeTaskAndRef( ImageData* image_data = GetImageDataForDrawImage(draw_image, cache_key); DCHECK(image_data); + if (image_data->decode.do_hardware_accelerated_decode()) + return nullptr; + // No decode is necessary for bitmap backed images. if (image_data->decode.is_locked() || image_data->is_bitmap_backed) { // We should never be creating a decode task for a not budgeted image. @@ -1625,6 +1714,28 @@ bool GpuImageDecodeCache::ExceedsPreferredCount() const { return persistent_cache_.size() > items_limit; } +void GpuImageDecodeCache::InsertTransferCacheEntry( + const ClientImageTransferCacheEntry& image_entry, + ImageData* image_data) { + DCHECK(image_data); + uint32_t size = image_entry.SerializedSize(); + void* data = context_->ContextSupport()->MapTransferCacheEntry(size); + if (data) { + bool succeeded = image_entry.Serialize( + base::make_span(reinterpret_cast<uint8_t*>(data), size)); + DCHECK(succeeded); + context_->ContextSupport()->UnmapAndCreateTransferCacheEntry( + image_entry.UnsafeType(), image_entry.Id()); + image_data->upload.SetTransferCacheId(image_entry.Id()); + } else { + // Transfer cache entry can fail due to a lost gpu context or failure + // to allocate shared memory. Handle this gracefully. Mark this + // image as "decode failed" so that we do not try to handle it again. + // If this was a lost context, we'll recreate this image decode cache. + image_data->decode.decode_failure = true; + } +} + void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image, ImageData* image_data, TaskType task_type) { @@ -1632,6 +1743,11 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image, DCHECK_GT(image_data->decode.ref_count, 0u); + if (image_data->decode.do_hardware_accelerated_decode()) { + // We get here in the case of an at-raster decode. + return; + } + if (image_data->decode.decode_failure) { // We have already tried and failed to decode this image. Don't try again. return; @@ -1661,7 +1777,6 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image, } TRACE_EVENT0("cc,benchmark", "GpuImageDecodeCache::DecodeImage"); - RecordImageMipLevelUMA(image_data->upload_scale_mip_level); image_data->decode.ResetData(); std::unique_ptr<base::DiscardableMemory> backing_memory; @@ -1747,7 +1862,8 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image, } void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image, - ImageData* image_data) { + ImageData* image_data, + sk_sp<SkData> encoded_data) { CheckContextLockAcquiredIfNecessary(); lock_.AssertAcquired(); @@ -1776,11 +1892,15 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image, return; TRACE_EVENT0("cc", "GpuImageDecodeCache::UploadImage"); - DCHECK(image_data->decode.is_locked()); + if (!image_data->decode.do_hardware_accelerated_decode()) { + // These are not needed for accelerated decodes because there was no decode + // task. + DCHECK(image_data->decode.is_locked()); + image_data->decode.mark_used(); + } DCHECK_GT(image_data->decode.ref_count, 0u); DCHECK_GT(image_data->upload.ref_count, 0u); - image_data->decode.mark_used(); sk_sp<SkColorSpace> color_space = SupportsColorSpaceConversion() && draw_image.target_color_space().IsValid() @@ -1799,27 +1919,76 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image, if (image_data->mode == DecodedDataMode::kTransferCache) { DCHECK(use_transfer_cache_); - SkPixmap pixmap; - if (!image_data->decode.image()->peekPixels(&pixmap)) + if (image_data->decode.do_hardware_accelerated_decode()) { + // The assumption is that scaling is not currently supported for + // hardware-accelerated decodes. + DCHECK_EQ(0, image_data->upload_scale_mip_level); + const gfx::Size output_size(draw_image.paint_image().width(), + draw_image.paint_image().height()); + // Try to get the encoded data if we don't have it already: this can + // happen, e.g., if we create an upload task using a pre-existing + // ImageData. In that case, we previously decided to do hardware decode + // acceleration but we didn't cache the encoded data. + if (!encoded_data) { + encoded_data = draw_image.paint_image().GetSkImage()->refEncodedData(); + DCHECK(encoded_data); + } + const uint32_t transfer_cache_id = + ClientImageTransferCacheEntry::GetNextId(); + const gpu::SyncToken decode_sync_token = + context_->RasterInterface()->ScheduleImageDecode( + base::make_span<const uint8_t>(encoded_data->bytes(), + encoded_data->size()), + output_size, transfer_cache_id, + color_space ? gfx::ColorSpace(*color_space) : gfx::ColorSpace(), + image_data->needs_mips); + + if (!decode_sync_token.HasData()) { + image_data->decode.decode_failure = true; + return; + } + + image_data->upload.SetTransferCacheId(transfer_cache_id); + + // Note that we wait for the decode sync token here for two reasons: + // + // 1) To make sure that raster work that depends on the image decode + // happens after the decode completes. + // + // 2) To protect the transfer cache entry from being unlocked on the + // service side before the decode is completed. + context_->RasterInterface()->WaitSyncTokenCHROMIUM( + decode_sync_token.GetConstData()); + return; + } - ClientImageTransferCacheEntry image_entry(&pixmap, color_space.get(), - image_data->needs_mips); - uint32_t size = image_entry.SerializedSize(); - void* data = context_->ContextSupport()->MapTransferCacheEntry(size); - if (data) { - bool succeeded = image_entry.Serialize( - base::make_span(reinterpret_cast<uint8_t*>(data), size)); - DCHECK(succeeded); - context_->ContextSupport()->UnmapAndCreateTransferCacheEntry( - image_entry.UnsafeType(), image_entry.Id()); - image_data->upload.SetTransferCacheId(image_entry.Id()); + // Non-hardware-accelerated path. + if (image_data->is_yuv) { + SkPixmap y_pixmap; + SkPixmap u_pixmap; + SkPixmap v_pixmap; + if (!image_data->decode.y_image()->peekPixels(&y_pixmap) || + !image_data->decode.u_image()->peekPixels(&u_pixmap) || + !image_data->decode.v_image()->peekPixels(&v_pixmap)) { + return; + } + // WebP documentation says to use Rec 601 for converting to RGB. + // TODO(crbug.com/915707): Change QueryYUVA8 to set the colorspace based + // on image type. + SkYUVColorSpace yuva_color_space = + SkYUVColorSpace::kRec601_SkYUVColorSpace; + ClientImageTransferCacheEntry image_entry( + &y_pixmap, &u_pixmap, &v_pixmap, decoded_target_colorspace.get(), + yuva_color_space, image_data->needs_mips); + InsertTransferCacheEntry(image_entry, image_data); } else { - // Transfer cache entry can fail due to a lost gpu context or failure - // to allocate shared memory. Handle this gracefully. Mark this - // image as "decode failed" so that we do not try to handle it again. - // If this was a lost context, we'll recreate this image decode cache. - image_data->decode.decode_failure = true; + SkPixmap pixmap; + if (!image_data->decode.image()->peekPixels(&pixmap)) + return; + ClientImageTransferCacheEntry image_entry(&pixmap, color_space.get(), + image_data->needs_mips); + InsertTransferCacheEntry(image_entry, image_data); } return; @@ -1968,7 +2137,9 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image, } scoped_refptr<GpuImageDecodeCache::ImageData> -GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image) { +GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image, + bool allow_hardware_decode, + sk_sp<SkData>* encoded_data) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "GpuImageDecodeCache::CreateImageData"); lock_.AssertAcquired(); @@ -1977,12 +2148,13 @@ GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image) { bool needs_mips = ShouldGenerateMips(draw_image, upload_scale_mip_level); SkImageInfo image_info = CreateImageInfoForDrawImage(draw_image, upload_scale_mip_level); - + const bool image_larger_than_max_texture = + image_info.width() > max_texture_size_ || + image_info.height() > max_texture_size_; DecodedDataMode mode; if (use_transfer_cache_) { mode = DecodedDataMode::kTransferCache; - } else if (image_info.width() > max_texture_size_ || - image_info.height() > max_texture_size_) { + } else if (image_larger_than_max_texture) { // Image too large to upload. Try to use SW fallback. mode = DecodedDataMode::kCpu; } else { @@ -2009,18 +2181,84 @@ GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image) { const bool is_bitmap_backed = !draw_image.paint_image().IsLazyGenerated() && upload_scale_mip_level == 0 && !cache_color_conversion_on_cpu; - const bool is_yuv = - draw_image.paint_image().IsYuv() && mode == DecodedDataMode::kGpu; + + // Figure out if we will do hardware accelerated decoding. The criteria is as + // follows: + // + // - The kVaapiJpegImageDecodeAcceleration feature is enabled. + // - The caller allows hardware decodes. + // - We are using the transfer cache (OOP-R). + // - The image does not require downscaling for uploading (see TODO below). + // - All the encoded data was received prior to any decoding work. Otherwise, + // it means that the software decoder has already started decoding the + // image, so we just let it finish. + // - The image's color space is sRGB. This is because we don't currently + // support detecting embedded color profiles. + // - The image is supported according to the profiles advertised by the GPU + // service. Checking this involves obtaining the contiguous encoded data + // which may require a copy if the data is not already contiguous. Because + // of this, we return a pointer to the contiguous data (as |encoded_data|) + // so that we can re-use it later (when requesting the image decode). + // + // TODO(crbug.com/953363): ideally, we can make the hardware decoder support + // decision without requiring contiguous data. + // + // TODO(crbug.com/953367): currently, we don't support scaling with hardware + // decode acceleration. Note that it's still okay for the image to be + // downscaled by Skia using the GPU. + // + // TODO(crbug.com/981208): |data_size| needs to be set to the size of the + // decoded data, but for accelerated decodes we won't know until the driver + // gives us the result in the GPU process. Figure out what to do. + bool do_hardware_accelerated_decode = false; + if (base::FeatureList::IsEnabled( + features::kVaapiJpegImageDecodeAcceleration) && + allow_hardware_decode && mode == DecodedDataMode::kTransferCache && + upload_scale_mip_level == 0 && + draw_image.paint_image().IsEligibleForAcceleratedDecoding() && + draw_image.paint_image().color_space() && + draw_image.paint_image().color_space()->isSRGB()) { + sk_sp<SkData> tmp_encoded_data = + draw_image.paint_image().GetSkImage() + ? draw_image.paint_image().GetSkImage()->refEncodedData() + : nullptr; + if (tmp_encoded_data && + context_->ContextSupport()->CanDecodeWithHardwareAcceleration( + base::make_span<const uint8_t>(tmp_encoded_data->bytes(), + tmp_encoded_data->size()))) { + do_hardware_accelerated_decode = true; + DCHECK(encoded_data); + *encoded_data = std::move(tmp_encoded_data); + } + } + + // If draw_image.paint_image().IsEligibleForAcceleratedDecoding() returns + // true, the image should not be backed by a bitmap. + DCHECK(!do_hardware_accelerated_decode || !is_bitmap_backed); + + SkYUVASizeInfo target_yuva_size_info; + const bool is_yuv = !do_hardware_accelerated_decode && + draw_image.paint_image().IsYuv(&target_yuva_size_info) && + mode != DecodedDataMode::kCpu && + !image_larger_than_max_texture; // TODO(crbug.com/910276): Change after alpha support. - // TODO(crbug.com/915972): Remove YUV420 assumption. if (is_yuv) { - // We can't use |temp_yuva_size_info| because it doesn't know about - // any scaling based on mip levels that |image_info| does incorporate. size_t y_size_bytes = image_info.width() * image_info.height(); - size_t u_size_bytes = - ((image_info.width() + 1) / 2) * ((image_info.height() + 1) / 2); - size_t v_size_bytes = u_size_bytes; + const gfx::Size target_raster_size(image_info.width(), image_info.height()); + gfx::Size unscaled_u_size( + target_yuva_size_info.fSizes[SkYUVAIndex::kU_Index].width(), + target_yuva_size_info.fSizes[SkYUVAIndex::kU_Index].height()); + gfx::Size unscaled_v_size( + target_yuva_size_info.fSizes[SkYUVAIndex::kV_Index].width(), + target_yuva_size_info.fSizes[SkYUVAIndex::kV_Index].height()); + gfx::Size scaled_u_size; + gfx::Size scaled_v_size; + ComputeMippedUVPlaneSizes(target_raster_size, unscaled_u_size, + unscaled_v_size, draw_image, &scaled_u_size, + &scaled_v_size); + size_t u_size_bytes = base::checked_cast<size_t>(scaled_u_size.GetArea()); + size_t v_size_bytes = base::checked_cast<size_t>(scaled_v_size.GetArea()); data_size = y_size_bytes + u_size_bytes + v_size_bytes; } @@ -2028,7 +2266,7 @@ GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image) { draw_image.paint_image().stable_id(), mode, data_size, draw_image.target_color_space(), CalculateDesiredFilterQuality(draw_image), upload_scale_mip_level, - needs_mips, is_bitmap_backed, is_yuv)); + needs_mips, is_bitmap_backed, do_hardware_accelerated_decode, is_yuv)); } void GpuImageDecodeCache::WillAddCacheEntry(const DrawImage& draw_image) { @@ -2350,7 +2588,8 @@ bool GpuImageDecodeCache::IsCompatible(const ImageData* image_data, size_t GpuImageDecodeCache::GetDrawImageSizeForTesting(const DrawImage& image) { base::AutoLock lock(lock_); - scoped_refptr<ImageData> data = CreateImageData(image); + scoped_refptr<ImageData> data = CreateImageData( + image, false /* allow_hardware_decode */, nullptr /* encoded_data */); return data->size; } @@ -2394,6 +2633,9 @@ sk_sp<SkImage> GpuImageDecodeCache::GetSWImageDecodeForTesting( return image_data->decode.ImageForTesting(); } +// Used for in-process-raster YUV decoding tests, where we often need the +// SkImages for each underlying plane because asserting or requesting fields for +// the YUV SkImage may flatten it to RGB or not be possible to request. sk_sp<SkImage> GpuImageDecodeCache::GetUploadedPlaneForTesting( const DrawImage& draw_image, size_t index) { diff --git a/chromium/cc/tiles/gpu_image_decode_cache.h b/chromium/cc/tiles/gpu_image_decode_cache.h index 81ca6286703..638a77f2388 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.h +++ b/chromium/cc/tiles/gpu_image_decode_cache.h @@ -16,7 +16,9 @@ #include "base/synchronization/lock.h" #include "base/trace_event/memory_dump_provider.h" #include "cc/cc_export.h" +#include "cc/paint/image_transfer_cache_entry.h" #include "cc/tiles/image_decode_cache.h" +#include "third_party/skia/include/core/SkData.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkYUVAIndex.h" #include "third_party/skia/include/gpu/gl/GrGLTypes.h" @@ -36,9 +38,9 @@ namespace cc { // Generally, when an image is required for raster, GpuImageDecodeCache // creates two tasks, one to decode the image, and one to upload the image to // the GPU. These tasks are completed before the raster task which depends on -// the image. We need to seperate decode and upload tasks, as decode can occur +// the image. We need to separate decode and upload tasks, as decode can occur // simultaneously on multiple threads, while upload requires the GL context -// lock must happen on our non-concurrent raster thread. +// lock so it must happen on our non-concurrent raster thread. // // Decoded and Uploaded image data share a single cache entry. Depending on how // far we've progressed, this cache entry may contain CPU-side decoded data, @@ -97,6 +99,35 @@ namespace cc { // keeps an ImageData alive while it is present in either the // |persistent_cache_| or |in_use_cache_|. // +// HARDWARE ACCELERATED DECODES: +// +// In Chrome OS, we have the ability to use specialized hardware to decode +// certain images. Because this requires interacting with drivers, it must be +// done in the GPU process. Therefore, we follow a different path than the usual +// decode -> upload tasks: +// 1) We decide whether to do hardware decode acceleration for an image before +// we create the decode/upload tasks. Under the hood, this involves parsing +// the image and checking if it's supported by the hardware decoder +// according to information advertised by the GPU process. Also, we only +// allow hardware decoding in OOP-R mode. +// 2) If we do decide to do hardware decoding, we don't create a decode task. +// Instead, we create only an upload task and store enough state to +// indicate that the image will go through this hardware accelerated path. +// The reason that we use the upload task is that we need to hold the +// context lock in order to schedule the image decode. +// 3) When the upload task runs, we send a request to the GPU process to start +// the image decode. This is an IPC message that does not require us to +// wait for the response. Instead, we get a sync token that is signalled +// when the decode completes. We insert a wait for this sync token right +// after sending the decode request. +// +// We also handle the more unusual case where images are decoded at raster time. +// The process is similar: we skip the software decode and then request the +// hardware decode in the same way as step (3) above. +// +// Note that the decoded data never makes it back to the renderer. It stays in +// the GPU process. The sync token ensures that any raster work that needs the +// image happens after the decode completes. class CC_EXPORT GpuImageDecodeCache : public ImageDecodeCache, public base::trace_event::MemoryDumpProvider { @@ -144,7 +175,7 @@ class CC_EXPORT GpuImageDecodeCache // Called by Decode / Upload tasks. void DecodeImageInTask(const DrawImage& image, TaskType task_type); - void UploadImageInTask(const DrawImage& image); + void UploadImageInTask(const DrawImage& image, sk_sp<SkData> encoded_data); // Called by Decode / Upload tasks when tasks are finished. void OnImageDecodeTaskCompleted(const DrawImage& image, @@ -216,7 +247,8 @@ class CC_EXPORT GpuImageDecodeCache // Stores the CPU-side decoded bits of an image and supporting fields. struct DecodedImageData : public ImageDataBase { - explicit DecodedImageData(bool is_bitmap_backed); + explicit DecodedImageData(bool is_bitmap_backed, + bool do_hardware_accelerated_decode); ~DecodedImageData(); bool Lock(); @@ -255,6 +287,10 @@ class CC_EXPORT GpuImageDecodeCache bool is_yuv() const { return image_yuv_planes_.has_value(); } + bool do_hardware_accelerated_decode() const { + return do_hardware_accelerated_decode_; + } + // Test-only functions. sk_sp<SkImage> ImageForTesting() const { return image_; } @@ -278,6 +314,12 @@ class CC_EXPORT GpuImageDecodeCache std::unique_ptr<base::DiscardableMemory> data_; sk_sp<SkImage> image_; // RGBX (or null in YUV decode path) base::Optional<YUVSkImages> image_yuv_planes_; + + // |do_hardware_accelerated_decode_| keeps track of images that should go + // through hardware decode acceleration. Currently, this path is intended + // only for Chrome OS and only for some JPEG images (see + // https://crbug.com/868400). + bool do_hardware_accelerated_decode_; }; // Stores the GPU-side image and supporting fields. @@ -447,6 +489,7 @@ class CC_EXPORT GpuImageDecodeCache int upload_scale_mip_level, bool needs_mips, bool is_bitmap_backed, + bool do_hardware_accelerated_decode, bool is_yuv_format); bool IsGpuOrTransferCache() const; @@ -543,6 +586,9 @@ class CC_EXPORT GpuImageDecodeCache bool CanFitInWorkingSet(size_t size) const; bool ExceedsPreferredCount() const; + void InsertTransferCacheEntry( + const ClientImageTransferCacheEntry& image_entry, + ImageData* image_data); void DecodeImageIfNecessary(const DrawImage& draw_image, ImageData* image_data, TaskType task_type); @@ -557,7 +603,9 @@ class CC_EXPORT GpuImageDecodeCache sk_sp<SkColorSpace> decoded_color_space) const; scoped_refptr<GpuImageDecodeCache::ImageData> CreateImageData( - const DrawImage& image); + const DrawImage& image, + bool allow_hardware_decode, + sk_sp<SkData>* encoded_data); void WillAddCacheEntry(const DrawImage& draw_image); SkImageInfo CreateImageInfoForDrawImage(const DrawImage& draw_image, int upload_scale_mip_level) const; @@ -591,7 +639,8 @@ class CC_EXPORT GpuImageDecodeCache // Requires that the |context_| lock be held when calling. void UploadImageIfNecessary(const DrawImage& draw_image, - ImageData* image_data); + ImageData* image_data, + sk_sp<SkData> encoded_data); // Flush pending operations on context_->GrContext() for each element of // |yuv_images| and then clear the vector. diff --git a/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc b/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc index a3b7e86666e..70807864351 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc @@ -8,8 +8,8 @@ #include "cc/paint/draw_image.h" #include "cc/paint/paint_image_builder.h" #include "cc/raster/tile_task.h" -#include "cc/test/test_in_process_context_provider.h" #include "cc/tiles/gpu_image_decode_cache.h" +#include "components/viz/test/test_in_process_context_provider.h" #include "gpu/command_buffer/client/raster_interface.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/perf/perf_test.h" @@ -45,9 +45,10 @@ class GpuImageDecodeCachePerfTest : timer_(kWarmupRuns, base::TimeDelta::FromMilliseconds(kTimeLimitMillis), kTimeCheckInterval), - context_provider_(base::MakeRefCounted<TestInProcessContextProvider>( - UseTransferCache(), - false /* support_locking */)), + context_provider_( + base::MakeRefCounted<viz::TestInProcessContextProvider>( + UseTransferCache(), + false /* support_locking */)), cache_(context_provider_.get(), UseTransferCache(), kRGBA_8888_SkColorType, @@ -87,7 +88,7 @@ class GpuImageDecodeCachePerfTest } base::LapTimer timer_; - scoped_refptr<TestInProcessContextProvider> context_provider_; + scoped_refptr<viz::TestInProcessContextProvider> context_provider_; GpuImageDecodeCache cache_; }; diff --git a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc index 77f3210c518..c30c1b1c101 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc @@ -6,6 +6,7 @@ #include <memory> +#include "base/test/scoped_feature_list.h" #include "cc/paint/draw_image.h" #include "cc/paint/image_transfer_cache_entry.h" #include "cc/paint/paint_image_builder.h" @@ -15,12 +16,20 @@ #include "cc/test/transfer_cache_test_helper.h" #include "components/viz/test/test_context_provider.h" #include "components/viz/test/test_gles2_interface.h" +#include "gpu/command_buffer/client/raster_implementation_gles.h" +#include "gpu/command_buffer/common/command_buffer_id.h" +#include "gpu/command_buffer/common/constants.h" +#include "gpu/config/gpu_finch_features.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkImageGenerator.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/GrContext.h" +using testing::_; +using testing::StrictMock; + namespace cc { namespace { @@ -101,13 +110,15 @@ class FakeGPUImageDecodeTestGLES2Interface : public viz::TestGLES2Interface, public: explicit FakeGPUImageDecodeTestGLES2Interface( FakeDiscardableManager* discardable_manager, - TransferCacheTestHelper* transfer_cache_helper) + TransferCacheTestHelper* transfer_cache_helper, + bool advertise_accelerated_decoding) : extension_string_( "GL_EXT_texture_format_BGRA8888 GL_OES_rgb8_rgba8 " "GL_OES_texture_npot GL_EXT_texture_rg " "GL_OES_texture_half_float GL_OES_texture_half_float_linear"), discardable_manager_(discardable_manager), - transfer_cache_helper_(transfer_cache_helper) {} + transfer_cache_helper_(transfer_cache_helper), + advertise_accelerated_decoding_(advertise_accelerated_decoding) {} ~FakeGPUImageDecodeTestGLES2Interface() override { // All textures / framebuffers / renderbuffers should be cleaned up. @@ -161,6 +172,11 @@ class FakeGPUImageDecodeTestGLES2Interface : public viz::TestGLES2Interface, transfer_cache_helper_->DeleteEntryDirect(MakeEntryKey(type, id)); } + bool CanDecodeWithHardwareAcceleration( + base::span<const uint8_t> encoded_data) const override { + return advertise_accelerated_decoding_; + } + std::pair<TransferCacheEntryType, uint32_t> MakeEntryKey(uint32_t type, uint32_t id) { DCHECK_LE(type, static_cast<uint32_t>(TransferCacheEntryType::kLast)); @@ -213,39 +229,78 @@ class FakeGPUImageDecodeTestGLES2Interface : public viz::TestGLES2Interface, const std::string extension_string_; FakeDiscardableManager* discardable_manager_; TransferCacheTestHelper* transfer_cache_helper_; + bool advertise_accelerated_decoding_ = false; size_t mapped_entry_size_ = 0; std::unique_ptr<uint8_t[]> mapped_entry_; }; +class MockRasterImplementation : public gpu::raster::RasterImplementationGLES { + public: + explicit MockRasterImplementation(gpu::gles2::GLES2Interface* gl) + : RasterImplementationGLES(gl) {} + ~MockRasterImplementation() override = default; + + gpu::SyncToken ScheduleImageDecode(base::span<const uint8_t> encoded_data, + const gfx::Size& output_size, + uint32_t transfer_cache_entry_id, + const gfx::ColorSpace& target_color_space, + bool needs_mips) override { + DoScheduleImageDecode(output_size, transfer_cache_entry_id, + target_color_space, needs_mips); + if (!next_accelerated_decode_fails_) { + return gpu::SyncToken(gpu::CommandBufferNamespace::GPU_IO, + gpu::CommandBufferId::FromUnsafeValue(1u), + next_release_count_++); + } + return gpu::SyncToken(); + } + + void SetAcceleratedDecodingFailed() { next_accelerated_decode_fails_ = true; } + + MOCK_METHOD4(DoScheduleImageDecode, + void(const gfx::Size& /* output_size */, + uint32_t /* transfer_cache_entry_id */, + const gfx::ColorSpace& /* target_color_space */, + bool /* needs_mips */)); + + private: + bool next_accelerated_decode_fails_ = false; + uint64_t next_release_count_ = 1u; +}; + class GPUImageDecodeTestMockContextProvider : public viz::TestContextProvider { public: static scoped_refptr<GPUImageDecodeTestMockContextProvider> Create( FakeDiscardableManager* discardable_manager, - TransferCacheTestHelper* transfer_cache_helper) { + TransferCacheTestHelper* transfer_cache_helper, + bool advertise_accelerated_decoding) { + auto support = std::make_unique<FakeGPUImageDecodeTestGLES2Interface>( + discardable_manager, transfer_cache_helper, + advertise_accelerated_decoding); + auto gl = std::make_unique<FakeGPUImageDecodeTestGLES2Interface>( + discardable_manager, transfer_cache_helper, + false /* advertise_accelerated_decoding */); + auto raster = + std::make_unique<StrictMock<MockRasterImplementation>>(gl.get()); return new GPUImageDecodeTestMockContextProvider( - std::make_unique<FakeGPUImageDecodeTestGLES2Interface>( - discardable_manager, transfer_cache_helper), - std::make_unique<FakeGPUImageDecodeTestGLES2Interface>( - discardable_manager, transfer_cache_helper)); + std::move(support), std::move(gl), std::move(raster)); } private: ~GPUImageDecodeTestMockContextProvider() override = default; GPUImageDecodeTestMockContextProvider( std::unique_ptr<viz::TestContextSupport> support, - std::unique_ptr<viz::TestGLES2Interface> gl) - : TestContextProvider(std::move(support), std::move(gl), true) {} + std::unique_ptr<viz::TestGLES2Interface> gl, + std::unique_ptr<gpu::raster::RasterInterface> raster) + : TestContextProvider(std::move(support), + std::move(gl), + std::move(raster), + true) {} }; -SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) { +SkMatrix CreateMatrix(const SkSize& scale) { SkMatrix matrix; matrix.setScale(scale.width(), scale.height()); - - if (!is_decomposable) { - // Perspective is not decomposable, add it. - matrix[SkMatrix::kMPersp0] = 0.1f; - } - return matrix; } @@ -260,13 +315,21 @@ SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) { size_t kGpuMemoryLimitBytes = 96 * 1024 * 1024; class GpuImageDecodeCacheTest - : public ::testing::TestWithParam<std::tuple<SkColorType, - bool /* use_transfer_cache */, - bool /* do_yuv_decode */>> { + : public ::testing::TestWithParam< + std::tuple<SkColorType, + bool /* use_transfer_cache */, + bool /* do_yuv_decode */, + bool /* advertise_accelerated_decoding */>> { public: void SetUp() override { + advertise_accelerated_decoding_ = std::get<3>(GetParam()); + if (advertise_accelerated_decoding_) { + feature_list_.InitAndEnableFeature( + features::kVaapiJpegImageDecodeAcceleration); + } context_provider_ = GPUImageDecodeTestMockContextProvider::Create( - &discardable_manager_, &transfer_cache_helper_); + &discardable_manager_, &transfer_cache_helper_, + advertise_accelerated_decoding_); discardable_manager_.SetGLES2Interface( context_provider_->UnboundTestContextGL()); context_provider_->BindToCurrentThread(); @@ -351,6 +414,28 @@ class GpuImageDecodeCacheTest gfx::ColorSpace::TransferID::LINEAR); } + DrawImage CreateDrawImageInternal( + const PaintImage& paint_image, + const SkMatrix& matrix = SkMatrix::I(), + gfx::ColorSpace* color_space = nullptr, + SkFilterQuality filter_quality = kMedium_SkFilterQuality, + SkIRect* src_rect = nullptr, + size_t frame_index = PaintImage::kDefaultFrameIndex) { + SkIRect src_rectangle; + gfx::ColorSpace cs; + if (!src_rect) { + src_rectangle = + SkIRect::MakeWH(paint_image.width(), paint_image.height()); + src_rect = &src_rectangle; + } + if (!color_space) { + cs = DefaultColorSpace(); + color_space = &cs; + } + return DrawImage(paint_image, *src_rect, filter_quality, matrix, + frame_index, *color_space); + } + GPUImageDecodeTestMockContextProvider* context_provider() { return context_provider_.get(); } @@ -404,14 +489,27 @@ class GpuImageDecodeCacheTest return static_cast<ServiceImageTransferCacheEntry*>(entry)->image(); } - void CompareAllPlanesToMippedVersions(GpuImageDecodeCache* cache, - const DrawImage& draw_image, - bool should_have_mips) { + void CompareAllPlanesToMippedVersions( + GpuImageDecodeCache* cache, + const DrawImage& draw_image, + const base::Optional<uint32_t> transfer_cache_id, + bool should_have_mips) { for (size_t i = 0; i < SkYUVASizeInfo::kMaxCount; ++i) { // TODO(crbug.com/910276): Skip alpha plane until supported in cache. if (i != SkYUVAIndex::kA_Index) { - auto original_uploaded_plane = - cache->GetUploadedPlaneForTesting(draw_image, i); + sk_sp<SkImage> original_uploaded_plane; + if (use_transfer_cache_) { + DCHECK(transfer_cache_id.has_value()); + const uint32_t id = transfer_cache_id.value(); + auto* image_entry = + transfer_cache_helper_.GetEntryAs<ServiceImageTransferCacheEntry>( + id); + original_uploaded_plane = image_entry->GetPlaneImage(i); + } else { + original_uploaded_plane = + cache->GetUploadedPlaneForTesting(draw_image, i); + } + ASSERT_TRUE(original_uploaded_plane); auto plane_with_mips = original_uploaded_plane->makeTextureImage( context_provider()->GrContext(), nullptr /* color space */, @@ -422,35 +520,60 @@ class GpuImageDecodeCacheTest } } + void VerifyUploadedPlaneSizes( + GpuImageDecodeCache* cache, + const DrawImage& draw_image, + const base::Optional<uint32_t> transfer_cache_id, + const SkISize plane_sizes[SkYUVASizeInfo::kMaxCount]) { + for (size_t i = 0; i < SkYUVASizeInfo::kMaxCount; ++i) { + // TODO(crbug.com/910276): Skip alpha plane until supported in cache. + if (i != SkYUVAIndex::kA_Index) { + sk_sp<SkImage> uploaded_plane; + if (use_transfer_cache_) { + DCHECK(transfer_cache_id.has_value()); + const uint32_t id = transfer_cache_id.value(); + auto* image_entry = + transfer_cache_helper_.GetEntryAs<ServiceImageTransferCacheEntry>( + id); + uploaded_plane = image_entry->GetPlaneImage(i); + } else { + uploaded_plane = cache->GetUploadedPlaneForTesting(draw_image, i); + } + ASSERT_TRUE(uploaded_plane); + EXPECT_EQ(plane_sizes[i], uploaded_plane->dimensions()); + } + } + } + protected: + base::test::ScopedFeatureList feature_list_; + + // The order of these members is important because |context_provider_| depends + // on |discardable_manager_| and |transfer_cache_helper_|. FakeDiscardableManager discardable_manager_; - scoped_refptr<GPUImageDecodeTestMockContextProvider> context_provider_; TransferCacheTestHelper transfer_cache_helper_; + scoped_refptr<GPUImageDecodeTestMockContextProvider> context_provider_; + bool use_transfer_cache_; SkColorType color_type_; bool do_yuv_decode_; + bool advertise_accelerated_decoding_; int max_texture_size_ = 0; }; TEST_P(GpuImageDecodeCacheTest, GetTaskForImageSameImage) { auto cache = CreateCache(); PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(1.5f, 1.5f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); EXPECT_TRUE(result.task); - DrawImage another_draw_image( - image, SkIRect::MakeWH(image.width(), image.height()), quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage another_draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(1.5f, 1.5f))); ImageDecodeCache::TaskResult another_result = cache->GetTaskForImageAndRef( another_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(another_result.need_unref); @@ -466,13 +589,8 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageSameImage) { TEST_P(GpuImageDecodeCacheTest, GetTaskForImageSmallerScale) { auto cache = CreateCache(); PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(1.5f, 1.5f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -482,10 +600,8 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageSmallerScale) { EXPECT_EQ(result.task->dependencies().size(), 1u); EXPECT_TRUE(result.task->dependencies()[0]); - DrawImage another_draw_image( - image, SkIRect::MakeWH(image.width(), image.height()), quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage another_draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult another_result = cache->GetTaskForImageAndRef( another_draw_image, ImageDecodeCache::TracingInfo()); @@ -508,21 +624,15 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageSmallerScale) { TEST_P(GpuImageDecodeCacheTest, GetTaskForImageLowerQuality) { auto cache = CreateCache(); PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - bool is_decomposable = true; - SkMatrix matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f), is_decomposable); - - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - kHigh_SkFilterQuality, matrix, - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + SkMatrix matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f)); + DrawImage draw_image = CreateDrawImageInternal(image, matrix); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); EXPECT_TRUE(result.task); - DrawImage another_draw_image( - image, SkIRect::MakeWH(image.width(), image.height()), - kLow_SkFilterQuality, matrix, PaintImage::kDefaultFrameIndex, - DefaultColorSpace()); + DrawImage another_draw_image = CreateDrawImageInternal( + image, matrix, nullptr /* color_space */, kLow_SkFilterQuality); ImageDecodeCache::TaskResult another_result = cache->GetTaskForImageAndRef( another_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(another_result.need_unref); @@ -537,25 +647,18 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageLowerQuality) { TEST_P(GpuImageDecodeCacheTest, GetTaskForImageDifferentImage) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; PaintImage first_image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage first_draw_image = CreateDrawImageInternal( + first_image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult first_result = cache->GetTaskForImageAndRef( first_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(first_result.need_unref); EXPECT_TRUE(first_result.task); PaintImage second_image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage second_draw_image( - second_image, - SkIRect::MakeWH(second_image.width(), second_image.height()), quality, - CreateMatrix(SkSize::Make(0.25f, 0.25f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage second_draw_image = CreateDrawImageInternal( + second_image, CreateMatrix(SkSize::Make(0.25f, 0.25f))); ImageDecodeCache::TaskResult second_result = cache->GetTaskForImageAndRef( second_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(second_result.need_unref); @@ -573,14 +676,10 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageDifferentImage) { TEST_P(GpuImageDecodeCacheTest, GetTaskForImageLargerScale) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; PaintImage first_image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage first_draw_image = CreateDrawImageInternal( + first_image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult first_result = cache->GetTaskForImageAndRef( first_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(first_result.need_unref); @@ -591,20 +690,15 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageLargerScale) { cache->UnrefImage(first_draw_image); - DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage second_draw_image = CreateDrawImageInternal(first_image); ImageDecodeCache::TaskResult second_result = cache->GetTaskForImageAndRef( second_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(second_result.need_unref); EXPECT_TRUE(second_result.task); EXPECT_TRUE(first_result.task.get() != second_result.task.get()); - DrawImage third_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage third_draw_image = CreateDrawImageInternal( + first_image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult third_result = cache->GetTaskForImageAndRef( third_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(third_result.need_unref); @@ -619,33 +713,23 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageLargerScale) { TEST_P(GpuImageDecodeCacheTest, GetTaskForImageLargerScaleNoReuse) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage first_image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage first_draw_image = CreateDrawImageInternal( + first_image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult first_result = cache->GetTaskForImageAndRef( first_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(first_result.need_unref); EXPECT_TRUE(first_result.task); - DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage second_draw_image = CreateDrawImageInternal(first_image); ImageDecodeCache::TaskResult second_result = cache->GetTaskForImageAndRef( second_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(second_result.need_unref); EXPECT_TRUE(second_result.task); EXPECT_TRUE(first_result.task.get() != second_result.task.get()); - DrawImage third_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage third_draw_image = CreateDrawImageInternal( + first_image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult third_result = cache->GetTaskForImageAndRef( third_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(third_result.need_unref); @@ -663,14 +747,10 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageLargerScaleNoReuse) { TEST_P(GpuImageDecodeCacheTest, GetTaskForImageHigherQuality) { auto cache = CreateCache(); - bool is_decomposable = true; - SkMatrix matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f), is_decomposable); - + SkMatrix matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f)); PaintImage first_image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - kLow_SkFilterQuality, matrix, PaintImage::kDefaultFrameIndex, - DefaultColorSpace()); + DrawImage first_draw_image = CreateDrawImageInternal( + first_image, matrix, nullptr /* color_space */, kLow_SkFilterQuality); ImageDecodeCache::TaskResult first_result = cache->GetTaskForImageAndRef( first_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(first_result.need_unref); @@ -681,10 +761,8 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageHigherQuality) { cache->UnrefImage(first_draw_image); - DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - kHigh_SkFilterQuality, matrix, PaintImage::kDefaultFrameIndex, - DefaultColorSpace()); + DrawImage second_draw_image = CreateDrawImageInternal( + first_image, matrix, nullptr /* color_space */, kMedium_SkFilterQuality); ImageDecodeCache::TaskResult second_result = cache->GetTaskForImageAndRef( second_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(second_result.need_unref); @@ -698,14 +776,9 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageHigherQuality) { TEST_P(GpuImageDecodeCacheTest, GetTaskForImageAlreadyDecodedAndLocked) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -740,14 +813,9 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageAlreadyDecodedAndLocked) { TEST_P(GpuImageDecodeCacheTest, GetTaskForImageAlreadyDecodedNotLocked) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -782,14 +850,9 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageAlreadyDecodedNotLocked) { TEST_P(GpuImageDecodeCacheTest, GetTaskForImageAlreadyUploaded) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -813,14 +876,9 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageAlreadyUploaded) { TEST_P(GpuImageDecodeCacheTest, GetTaskForImageCanceledGetsNewTask) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -856,14 +914,9 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageCanceledGetsNewTask) { TEST_P(GpuImageDecodeCacheTest, GetTaskForImageCanceledWhileReffedGetsNewTask) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -903,14 +956,9 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageCanceledWhileReffedGetsNewTask) { TEST_P(GpuImageDecodeCacheTest, GetTaskForImageUploadCanceledButDecodeRun) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -932,14 +980,9 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageUploadCanceledButDecodeRun) { TEST_P(GpuImageDecodeCacheTest, NoTaskForImageAlreadyFailedDecoding) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -962,14 +1005,9 @@ TEST_P(GpuImageDecodeCacheTest, NoTaskForImageAlreadyFailedDecoding) { TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDraw) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -996,14 +1034,9 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDraw) { TEST_P(GpuImageDecodeCacheTest, GetLargeDecodedImageForDraw) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreateLargePaintImageForSoftwareFallback(); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(1.0f, 1.0f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -1030,16 +1063,11 @@ TEST_P(GpuImageDecodeCacheTest, GetLargeDecodedImageForDraw) { TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecode) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - cache->SetWorkingSetLimitsForTesting(0 /* max_bytes */, 0 /* max_items */); PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(1.0f, 1.0f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); @@ -1066,14 +1094,10 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecode) { TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawLargerScale) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - kLow_SkFilterQuality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f)), + nullptr /* color_space */, kLow_SkFilterQuality); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -1082,10 +1106,8 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawLargerScale) { TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get()); TestTileTaskRunner::ProcessTask(result.task.get()); - DrawImage larger_draw_image( - image, SkIRect::MakeWH(image.width(), image.height()), quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage larger_draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(1.5f, 1.5f))); ImageDecodeCache::TaskResult larger_result = cache->GetTaskForImageAndRef( larger_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(larger_result.need_unref); @@ -1121,13 +1143,10 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawLargerScale) { TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawHigherQuality) { auto cache = CreateCache(); - bool is_decomposable = true; - SkMatrix matrix = CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable); - + SkMatrix matrix = CreateMatrix(SkSize::Make(0.5f, 0.5f)); PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - kLow_SkFilterQuality, matrix, - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal( + image, matrix, nullptr /* color_space */, kLow_SkFilterQuality); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -1136,10 +1155,7 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawHigherQuality) { TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get()); TestTileTaskRunner::ProcessTask(result.task.get()); - DrawImage higher_quality_draw_image( - image, SkIRect::MakeWH(image.width(), image.height()), - kHigh_SkFilterQuality, matrix, PaintImage::kDefaultFrameIndex, - DefaultColorSpace()); + DrawImage higher_quality_draw_image = CreateDrawImageInternal(image, matrix); ImageDecodeCache::TaskResult hq_result = cache->GetTaskForImageAndRef( higher_quality_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(hq_result.need_unref); @@ -1174,14 +1190,9 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawHigherQuality) { TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawNegative) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(-0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(-0.5f, 0.5f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -1212,15 +1223,11 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawNegative) { TEST_P(GpuImageDecodeCacheTest, GetLargeScaledDecodedImageForDraw) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreateLargePaintImageForSoftwareFallback( gfx::Size(GetLargeImageSize().width(), GetLargeImageSize().height() * 2)); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f)), + nullptr /* color_space */, kHigh_SkFilterQuality); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -1253,17 +1260,11 @@ TEST_P(GpuImageDecodeCacheTest, GetLargeScaledDecodedImageForDraw) { TEST_P(GpuImageDecodeCacheTest, AtRasterUsedDirectlyIfSpaceAllows) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; const gfx::Size test_image_size = GetNormalImageSize(); - cache->SetWorkingSetLimitsForTesting(0 /* max_bytes */, 0 /* max_items */); PaintImage image = CreatePaintImageInternal(test_image_size); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); @@ -1297,16 +1298,11 @@ TEST_P(GpuImageDecodeCacheTest, AtRasterUsedDirectlyIfSpaceAllows) { TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecodeMultipleTimes) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - cache->SetWorkingSetLimitsForTesting(0 /* max_bytes */, 0 /* max_items */); PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); // Must hold context lock before calling GetDecodedImageForDraw / // DrawWithImageFinished. @@ -1335,16 +1331,10 @@ TEST_P(GpuImageDecodeCacheTest, TEST_P(GpuImageDecodeCacheTest, GetLargeDecodedImageForDrawAtRasterDecodeMultipleTimes) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - cache->SetWorkingSetLimitsForTesting(0 /* max_bytes */, 0 /* max_items */); PaintImage image = CreateLargePaintImageForSoftwareFallback(); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); // Must hold context lock before calling GetDecodedImageForDraw / // DrawWithImageFinished. @@ -1374,14 +1364,9 @@ TEST_P(GpuImageDecodeCacheTest, TEST_P(GpuImageDecodeCacheTest, ZeroSizedImagesAreSkipped) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(0.f, 0.f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.f, 0.f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); @@ -1400,15 +1385,12 @@ TEST_P(GpuImageDecodeCacheTest, ZeroSizedImagesAreSkipped) { TEST_P(GpuImageDecodeCacheTest, NonOverlappingSrcRectImagesAreSkipped) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); DrawImage draw_image(image, SkIRect::MakeXYWH(image.width() + 1, image.height() + 1, image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + kMedium_SkFilterQuality, + CreateMatrix(SkSize::Make(1.f, 1.f)), PaintImage::kDefaultFrameIndex, DefaultColorSpace()); ImageDecodeCache::TaskResult result = @@ -1428,14 +1410,11 @@ TEST_P(GpuImageDecodeCacheTest, NonOverlappingSrcRectImagesAreSkipped) { TEST_P(GpuImageDecodeCacheTest, CanceledTasksDoNotCountAgainstBudget) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image( - image, SkIRect::MakeXYWH(0, 0, image.width(), image.height()), quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + SkIRect src_rect = SkIRect::MakeXYWH(0, 0, image.width(), image.height()); + DrawImage draw_image = CreateDrawImageInternal( + image, CreateMatrix(SkSize::Make(1.f, 1.f)), nullptr /* color_space */, + kMedium_SkFilterQuality, &src_rect); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); @@ -1454,14 +1433,9 @@ TEST_P(GpuImageDecodeCacheTest, CanceledTasksDoNotCountAgainstBudget) { TEST_P(GpuImageDecodeCacheTest, ShouldAggressivelyFreeResources) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); { ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo()); @@ -1515,15 +1489,10 @@ TEST_P(GpuImageDecodeCacheTest, ShouldAggressivelyFreeResources) { TEST_P(GpuImageDecodeCacheTest, OrphanedImagesFreeOnReachingZeroRefs) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - // Create a downscaled image. PaintImage first_image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage first_draw_image = CreateDrawImageInternal( + first_image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult first_result = cache->GetTaskForImageAndRef( first_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(first_result.need_unref); @@ -1535,10 +1504,8 @@ TEST_P(GpuImageDecodeCacheTest, OrphanedImagesFreeOnReachingZeroRefs) { // Create a larger version of |first_image|, this should immediately free the // memory used by |first_image| for the smaller scale. - DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage second_draw_image = CreateDrawImageInternal( + first_image, CreateMatrix(SkSize::Make(1.0f, 1.0f))); ImageDecodeCache::TaskResult second_result = cache->GetTaskForImageAndRef( second_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(second_result.need_unref); @@ -1563,15 +1530,10 @@ TEST_P(GpuImageDecodeCacheTest, OrphanedImagesFreeOnReachingZeroRefs) { TEST_P(GpuImageDecodeCacheTest, OrphanedZeroRefImagesImmediatelyDeleted) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - // Create a downscaled image. PaintImage first_image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage first_draw_image = CreateDrawImageInternal( + first_image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult first_result = cache->GetTaskForImageAndRef( first_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(first_result.need_unref); @@ -1587,10 +1549,7 @@ TEST_P(GpuImageDecodeCacheTest, OrphanedZeroRefImagesImmediatelyDeleted) { // Create a larger version of |first_image|, this should immediately free the // memory used by |first_image| for the smaller scale. - DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage second_draw_image = CreateDrawImageInternal(first_image); ImageDecodeCache::TaskResult second_result = cache->GetTaskForImageAndRef( second_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(second_result.need_unref); @@ -1610,14 +1569,11 @@ TEST_P(GpuImageDecodeCacheTest, OrphanedZeroRefImagesImmediatelyDeleted) { TEST_P(GpuImageDecodeCacheTest, QualityCappedAtMedium) { auto cache = CreateCache(); PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - bool is_decomposable = true; - SkMatrix matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f), is_decomposable); + SkMatrix matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f)); // Create an image with kLow_FilterQuality. - DrawImage low_draw_image(image, - SkIRect::MakeWH(image.width(), image.height()), - kLow_SkFilterQuality, matrix, - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage low_draw_image = CreateDrawImageInternal( + image, matrix, nullptr /* color_space */, kLow_SkFilterQuality); ImageDecodeCache::TaskResult low_result = cache->GetTaskForImageAndRef( low_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(low_result.need_unref); @@ -1625,21 +1581,16 @@ TEST_P(GpuImageDecodeCacheTest, QualityCappedAtMedium) { // Get the same image at kMedium_SkFilterQuality. We can't re-use low, so we // should get a new task/ref. - DrawImage medium_draw_image( - image, SkIRect::MakeWH(image.width(), image.height()), - kMedium_SkFilterQuality, matrix, PaintImage::kDefaultFrameIndex, - DefaultColorSpace()); + DrawImage medium_draw_image = CreateDrawImageInternal(image); ImageDecodeCache::TaskResult medium_result = cache->GetTaskForImageAndRef( medium_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(medium_result.need_unref); EXPECT_TRUE(medium_result.task.get()); EXPECT_FALSE(low_result.task.get() == medium_result.task.get()); - // Get the same image at kHigh_FilterQuality. We should re-use medium. - DrawImage high_quality_draw_image( - image, SkIRect::MakeWH(image.width(), image.height()), - kHigh_SkFilterQuality, matrix, PaintImage::kDefaultFrameIndex, - DefaultColorSpace()); + // Get the same image at kHigh_SkFilterQuality. We should re-use medium. + DrawImage high_quality_draw_image = CreateDrawImageInternal( + image, matrix, nullptr /* color_space */, kHigh_SkFilterQuality); ImageDecodeCache::TaskResult high_quality_result = cache->GetTaskForImageAndRef(high_quality_draw_image, ImageDecodeCache::TracingInfo()); @@ -1660,15 +1611,9 @@ TEST_P(GpuImageDecodeCacheTest, QualityCappedAtMedium) { // cache entry creation doesn't cause a buffer overflow/crash. TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawMipUsageChange) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - // Create an image decode task and cache entry that does not need mips. PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -1687,10 +1632,8 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawMipUsageChange) { viz::ContextProvider::ScopedContextLock context_lock(context_provider()); // Do an at-raster decode of the above image that *does* require mips. - DrawImage draw_image_mips( - image, SkIRect::MakeWH(image.width(), image.height()), quality, - CreateMatrix(SkSize::Make(0.6f, 0.6f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image_mips = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.6f, 0.6f))); DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image_mips)); cache->DrawWithImageFinished(draw_image_mips, decoded_draw_image); @@ -1698,13 +1641,10 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawMipUsageChange) { TEST_P(GpuImageDecodeCacheTest, OutOfRasterDecodeTask) { auto cache = CreateCache(); - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - bool is_decomposable = true; - SkMatrix matrix = CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - kLow_SkFilterQuality, matrix, - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + SkMatrix matrix = CreateMatrix(SkSize::Make(1.0f, 1.0f)); + DrawImage draw_image = CreateDrawImageInternal( + image, matrix, nullptr /* color_space */, kLow_SkFilterQuality); ImageDecodeCache::TaskResult result = cache->GetOutOfRasterDecodeTaskForImageAndRef(draw_image); @@ -1723,16 +1663,10 @@ TEST_P(GpuImageDecodeCacheTest, OutOfRasterDecodeTask) { TEST_P(GpuImageDecodeCacheTest, ZeroCacheNormalWorkingSet) { SetCachedTexturesLimit(0); auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - // Add an image to the cache-> Due to normal working set, this should produce // a task and a ref. PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -1776,19 +1710,13 @@ TEST_P(GpuImageDecodeCacheTest, SmallCacheNormalWorkingSet) { // Cache will fit one image. SetCachedTexturesLimit(1); auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); PaintImage image2 = CreatePaintImageInternal(GetNormalImageSize()); DrawImage draw_image2( - image2, SkIRect::MakeWH(image2.width(), image2.height()), quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + image2, SkIRect::MakeWH(image2.width(), image2.height()), + kMedium_SkFilterQuality, CreateMatrix(SkSize::Make(1.0f, 1.0f)), PaintImage::kDefaultFrameIndex, DefaultColorSpace()); // Add an image to the cache and un-ref it. @@ -1858,15 +1786,9 @@ TEST_P(GpuImageDecodeCacheTest, SmallCacheNormalWorkingSet) { TEST_P(GpuImageDecodeCacheTest, ClearCache) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - for (int i = 0; i < 10; ++i) { PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image( - image, SkIRect::MakeWH(image.width(), image.height()), quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -1888,15 +1810,9 @@ TEST_P(GpuImageDecodeCacheTest, ClearCache) { TEST_P(GpuImageDecodeCacheTest, ClearCacheInUse) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - // Create an image but keep it reffed so it can't be immediately freed. PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -1923,36 +1839,27 @@ TEST_P(GpuImageDecodeCacheTest, ClearCacheInUse) { TEST_P(GpuImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - gfx::ColorSpace color_space_a = gfx::ColorSpace::CreateSRGB(); gfx::ColorSpace color_space_b = gfx::ColorSpace::CreateXYZD50(); PaintImage first_image = CreatePaintImageInternal(gfx::Size(100, 100)); - DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, color_space_a); + DrawImage first_draw_image = CreateDrawImageInternal( + first_image, CreateMatrix(SkSize::Make(1.0f, 1.0f)), &color_space_a); ImageDecodeCache::TaskResult first_result = cache->GetTaskForImageAndRef( first_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(first_result.need_unref); EXPECT_TRUE(first_result.task); - DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, color_space_b); + DrawImage second_draw_image = CreateDrawImageInternal( + first_image, CreateMatrix(SkSize::Make(1.0f, 1.0f)), &color_space_b); ImageDecodeCache::TaskResult second_result = cache->GetTaskForImageAndRef( second_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(second_result.need_unref); EXPECT_TRUE(second_result.task); EXPECT_TRUE(first_result.task.get() != second_result.task.get()); - DrawImage third_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, color_space_a); + DrawImage third_draw_image = CreateDrawImageInternal( + first_image, CreateMatrix(SkSize::Make(1.0f, 1.0f)), &color_space_a); ImageDecodeCache::TaskResult third_result = cache->GetTaskForImageAndRef( third_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(third_result.need_unref); @@ -1970,15 +1877,10 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { TEST_P(GpuImageDecodeCacheTest, GetTaskForLargeImageNonSRGBColorSpace) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; gfx::ColorSpace color_space = gfx::ColorSpace::CreateXYZD50(); - PaintImage image = CreateLargePaintImageForSoftwareFallback(); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, color_space); + DrawImage draw_image = CreateDrawImageInternal( + image, CreateMatrix(SkSize::Make(1.0f, 1.0f)), &color_space); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -1992,7 +1894,6 @@ TEST_P(GpuImageDecodeCacheTest, GetTaskForLargeImageNonSRGBColorSpace) { TEST_P(GpuImageDecodeCacheTest, CacheDecodesExpectedFrames) { auto cache = CreateCache(); - std::vector<FrameMetadata> frames = { FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)), FrameMetadata(true, base::TimeDelta::FromMilliseconds(3)), @@ -2014,12 +1915,10 @@ TEST_P(GpuImageDecodeCacheTest, CacheDecodesExpectedFrames) { viz::ContextProvider::ScopedContextLock context_lock(context_provider()); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; + SkFilterQuality quality = kMedium_SkFilterQuality; DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - 1u, DefaultColorSpace()); + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f)), 1u, + DefaultColorSpace()); auto decoded_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); ASSERT_TRUE(decoded_image.image()); @@ -2046,8 +1945,7 @@ TEST_P(GpuImageDecodeCacheTest, CacheDecodesExpectedFrames) { ASSERT_LT(subset_height, test_image_size.height()); DrawImage subset_draw_image( image, SkIRect::MakeWH(subset_width, subset_height), quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), 3u, - DefaultColorSpace()); + CreateMatrix(SkSize::Make(1.0f, 1.0f)), 3u, DefaultColorSpace()); decoded_image = EnsureImageBacked(cache->GetDecodedImageForDraw(subset_draw_image)); ASSERT_TRUE(decoded_image.image()); @@ -2059,15 +1957,10 @@ TEST_P(GpuImageDecodeCacheTest, CacheDecodesExpectedFrames) { TEST_P(GpuImageDecodeCacheTest, OrphanedDataCancelledWhileReplaced) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - // Create a downscaled image. PaintImage first_image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage first_draw_image = CreateDrawImageInternal( + first_image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); ImageDecodeCache::TaskResult first_result = cache->GetTaskForImageAndRef( first_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(first_result.need_unref); @@ -2078,10 +1971,7 @@ TEST_P(GpuImageDecodeCacheTest, OrphanedDataCancelledWhileReplaced) { // Create a larger version of |first_image|, this should immediately free // the memory used by |first_image| for the smaller scale. - DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image.width(), first_image.height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage second_draw_image = CreateDrawImageInternal(first_image); ImageDecodeCache::TaskResult second_result = cache->GetTaskForImageAndRef( second_draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(second_result.need_unref); @@ -2114,15 +2004,10 @@ TEST_P(GpuImageDecodeCacheTest, OrphanedDataCancelledWhileReplaced) { TEST_P(GpuImageDecodeCacheTest, AlreadyBudgetedImagesAreNotAtRaster) { auto cache = CreateCache(); - bool is_decomposable = true; - const SkFilterQuality quality = kHigh_SkFilterQuality; const gfx::Size test_image_size = GetNormalImageSize(); PaintImage image = CreatePaintImageInternal(test_image_size); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); const size_t bytes_for_test_image = GetBytesNeededForSingleImage(test_image_size); @@ -2155,8 +2040,6 @@ TEST_P(GpuImageDecodeCacheTest, AlreadyBudgetedImagesAreNotAtRaster) { TEST_P(GpuImageDecodeCacheTest, ImageBudgetingByCount) { auto cache = CreateCache(); - bool is_decomposable = true; - const SkFilterQuality quality = kHigh_SkFilterQuality; const gfx::Size test_image_size = GetNormalImageSize(); // Allow a single image by count. Use a high byte limit as we want to test the @@ -2166,10 +2049,7 @@ TEST_P(GpuImageDecodeCacheTest, ImageBudgetingByCount) { cache->SetWorkingSetLimitsForTesting( bytes_for_test_image * 100 /* max_bytes */, 1u /* max_items */); PaintImage image = CreatePaintImageInternal(test_image_size); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); // The image counts against our budget. viz::ContextProvider::ScopedContextLock context_lock(context_provider()); @@ -2179,11 +2059,9 @@ TEST_P(GpuImageDecodeCacheTest, ImageBudgetingByCount) { EXPECT_TRUE(decoded_draw_image.is_budgeted()); // Try another image, it shouldn't be budgeted and should be at-raster. - DrawImage second_draw_image( - CreatePaintImageInternal(GetNormalImageSize()), - SkIRect::MakeWH(image.width(), image.height()), quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + PaintImage second_paint_image = + CreatePaintImageInternal(GetNormalImageSize()); + DrawImage second_draw_image = CreateDrawImageInternal(second_paint_image); // Should be at raster. ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef( @@ -2202,15 +2080,10 @@ TEST_P(GpuImageDecodeCacheTest, ImageBudgetingByCount) { TEST_P(GpuImageDecodeCacheTest, ImageBudgetingBySize) { auto cache = CreateCache(); - bool is_decomposable = true; - const SkFilterQuality quality = kHigh_SkFilterQuality; const gfx::Size test_image_size = GetNormalImageSize(); PaintImage image = CreatePaintImageInternal(test_image_size); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); const size_t bytes_for_test_image = GetBytesNeededForSingleImage(test_image_size); @@ -2228,11 +2101,8 @@ TEST_P(GpuImageDecodeCacheTest, ImageBudgetingBySize) { EXPECT_TRUE(decoded_draw_image.is_budgeted()); // Try another image, it shouldn't be budgeted and should be at-raster. - DrawImage second_draw_image( - CreatePaintImageInternal(test_image_size), - SkIRect::MakeWH(image.width(), image.height()), quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + PaintImage test_paint_image = CreatePaintImageInternal(test_image_size); + DrawImage second_draw_image = CreateDrawImageInternal(test_paint_image); // Should be at raster. ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef( @@ -2252,15 +2122,11 @@ TEST_P(GpuImageDecodeCacheTest, ImageBudgetingBySize) { TEST_P(GpuImageDecodeCacheTest, ColorConversionDuringDecodeForLargeImageNonSRGBColorSpace) { auto cache = CreateCache(); - bool is_decomposable = true; - const SkFilterQuality quality = kHigh_SkFilterQuality; gfx::ColorSpace color_space = gfx::ColorSpace::CreateXYZD50(); PaintImage image = CreateLargePaintImageForSoftwareFallback(); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, color_space); + DrawImage draw_image = CreateDrawImageInternal( + image, CreateMatrix(SkSize::Make(1.0f, 1.0f)), &color_space); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -2307,15 +2173,11 @@ TEST_P(GpuImageDecodeCacheTest, TEST_P(GpuImageDecodeCacheTest, ColorConversionDuringUploadForSmallImageNonSRGBColorSpace) { auto cache = CreateCache(); - bool is_decomposable = true; - const SkFilterQuality quality = kHigh_SkFilterQuality; gfx::ColorSpace color_space = gfx::ColorSpace::CreateDisplayP3D65(); PaintImage image = CreatePaintImageInternal(gfx::Size(11, 12)); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, color_space); + DrawImage draw_image = CreateDrawImageInternal( + image, CreateMatrix(SkSize::Make(1.0f, 1.0f)), &color_space); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -2341,9 +2203,11 @@ TEST_P(GpuImageDecodeCacheTest, EXPECT_EQ(image.width(), service_image->width()); EXPECT_EQ(image.height(), service_image->height()); - // Color space should be logically equal to the original color space. - EXPECT_TRUE(SkColorSpace::Equals(service_image->colorSpace(), - target_color_space.get())); + if (!do_yuv_decode_) { + // Color space should be logically equal to the original color space. + EXPECT_TRUE(SkColorSpace::Equals(service_image->colorSpace(), + target_color_space.get())); + } } else { // Ensure that the HW uploaded image had color space conversion applied. EXPECT_TRUE(SkColorSpace::Equals(decoded_draw_image.image()->colorSpace(), @@ -2360,14 +2224,9 @@ TEST_P(GpuImageDecodeCacheTest, NonLazyImageUploadNoScale) { return; } auto cache = CreateCache(); - bool is_decomposable = true; - const SkFilterQuality quality = kHigh_SkFilterQuality; PaintImage image = CreateBitmapImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); viz::ContextProvider::ScopedContextLock context_lock(context_provider()); DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); @@ -2385,14 +2244,9 @@ TEST_P(GpuImageDecodeCacheTest, NonLazyImageUploadTaskHasNoDeps) { return; } auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; PaintImage image = CreateBitmapImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); auto result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -2409,14 +2263,9 @@ TEST_P(GpuImageDecodeCacheTest, NonLazyImageUploadTaskCancelled) { return; } auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; PaintImage image = CreateBitmapImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); auto result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -2437,14 +2286,10 @@ TEST_P(GpuImageDecodeCacheTest, NonLazyImageLargeImageColorConverted) { const bool should_cache_sw_image = cache->SupportsColorSpaceConversion() && !use_transfer_cache_; - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; - PaintImage image = CreateBitmapImageInternal(GetLargeImageSize()); - DrawImage draw_image( - image, SkIRect::MakeWH(image.width(), image.height()), quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, gfx::ColorSpace::CreateDisplayP3D65()); + gfx::ColorSpace color_space = gfx::ColorSpace::CreateDisplayP3D65(); + DrawImage draw_image = CreateDrawImageInternal( + image, CreateMatrix(SkSize::Make(1.0f, 1.0f)), &color_space); viz::ContextProvider::ScopedContextLock context_lock(context_provider()); DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); @@ -2456,9 +2301,8 @@ TEST_P(GpuImageDecodeCacheTest, NonLazyImageLargeImageColorConverted) { auto sw_image = cache->GetSWImageDecodeForTesting(draw_image); ASSERT_EQ(!!sw_image, should_cache_sw_image); if (should_cache_sw_image) { - EXPECT_TRUE(SkColorSpace::Equals( - sw_image->colorSpace(), - gfx::ColorSpace::CreateDisplayP3D65().ToSkColorSpace().get())); + EXPECT_TRUE(SkColorSpace::Equals(sw_image->colorSpace(), + color_space.ToSkColorSpace().get())); } } @@ -2468,14 +2312,10 @@ TEST_P(GpuImageDecodeCacheTest, NonLazyImageUploadDownscaled) { return; } auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; PaintImage image = CreateBitmapImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); viz::ContextProvider::ScopedContextLock context_lock(context_provider()); DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); @@ -2486,8 +2326,6 @@ TEST_P(GpuImageDecodeCacheTest, NonLazyImageUploadDownscaled) { TEST_P(GpuImageDecodeCacheTest, KeepOnlyLast2ContentIds) { auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; viz::ContextProvider::ScopedContextLock context_lock(context_provider()); const PaintImage::Id paint_image_id = PaintImage::GetNextId(); @@ -2497,10 +2335,8 @@ TEST_P(GpuImageDecodeCacheTest, KeepOnlyLast2ContentIds) { for (int i = 0; i < 10; ++i) { PaintImage image = CreatePaintImageInternal( GetNormalImageSize(), SkColorSpace::MakeSRGB(), paint_image_id); - DrawImage draw_image( - image, SkIRect::MakeWH(image.width(), image.height()), quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f))); DecodedDrawImage decoded_draw_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); @@ -2536,8 +2372,6 @@ TEST_P(GpuImageDecodeCacheTest, DecodeToScale) { return; } auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; viz::ContextProvider::ScopedContextLock context_lock(context_provider()); const SkISize full_size = SkISize::Make(100, 100); @@ -2554,10 +2388,8 @@ TEST_P(GpuImageDecodeCacheTest, DecodeToScale) { .set_paint_image_generator(generator) .TakePaintImage(); - DrawImage draw_image( - paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - quality, CreateMatrix(SkSize::Make(0.5, 0.5), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal( + paint_image, CreateMatrix(SkSize::Make(0.5, 0.5))); DecodedDrawImage decoded_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); const int expected_width = @@ -2583,8 +2415,6 @@ TEST_P(GpuImageDecodeCacheTest, DecodeToScaleNoneQuality) { return; } auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kNone_SkFilterQuality; viz::ContextProvider::ScopedContextLock context_lock(context_provider()); SkISize full_size = SkISize::Make(100, 100); @@ -2601,10 +2431,9 @@ TEST_P(GpuImageDecodeCacheTest, DecodeToScaleNoneQuality) { .set_paint_image_generator(generator) .TakePaintImage(); - DrawImage draw_image( - paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - quality, CreateMatrix(SkSize::Make(0.5, 0.5), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(paint_image, CreateMatrix(SkSize::Make(0.5, 0.5)), + nullptr /* color_space */, kNone_SkFilterQuality); DecodedDrawImage decoded_image = EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); ASSERT_TRUE(decoded_image.image()); @@ -2627,12 +2456,10 @@ TEST_P(GpuImageDecodeCacheTest, BasicMips) { SkSize scale, gfx::ColorSpace color_space, bool should_have_mips) { auto cache = CreateCache(); - bool is_decomposable = true; PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); - DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), - filter_quality, CreateMatrix(scale, is_decomposable), - PaintImage::kDefaultFrameIndex, color_space); + DrawImage draw_image = CreateDrawImageInternal( + image, CreateMatrix(scale), &color_space, filter_quality); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -2644,23 +2471,23 @@ TEST_P(GpuImageDecodeCacheTest, BasicMips) { // Must hold context lock before calling GetDecodedImageForDraw / // DrawWithImageFinished. viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + // Pull out transfer cache ID from the DecodedDrawImage while it still has + // it attached. + DecodedDrawImage serialized_decoded_draw_image = + cache->GetDecodedImageForDraw(draw_image); + const base::Optional<uint32_t> transfer_cache_entry_id = + serialized_decoded_draw_image.transfer_cache_entry_id(); DecodedDrawImage decoded_draw_image = - EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); + EnsureImageBacked(std::move(serialized_decoded_draw_image)); EXPECT_TRUE(decoded_draw_image.image()); EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); if (do_yuv_decode_) { - // As of M74, Skia will flatten a YUV SkImage upon calling - // makeTextureImage. Thus, we must separately request mips for each - // plane and compare to the original uploaded planes. - CompareAllPlanesToMippedVersions(cache.get(), draw_image, - should_have_mips); - EXPECT_TRUE( - SkColorSpace::Equals(cache->SupportsColorSpaceConversion() - ? color_space.ToSkColorSpace().get() - : nullptr, - decoded_draw_image.image()->colorSpace())); - + // Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus, + // we must separately request mips for each plane and compare to the + // original uploaded planes. + CompareAllPlanesToMippedVersions( + cache.get(), draw_image, transfer_cache_entry_id, should_have_mips); } else { sk_sp<SkImage> image_with_mips = decoded_draw_image.image()->makeTextureImage( @@ -2688,9 +2515,6 @@ TEST_P(GpuImageDecodeCacheTest, BasicMips) { // Medium filter quality == mips decode_and_check_mips(kMedium_SkFilterQuality, SkSize::Make(0.6f, 0.6f), DefaultColorSpace(), true); - // High filter quality == mips - decode_and_check_mips(kHigh_SkFilterQuality, SkSize::Make(0.6f, 0.6f), - DefaultColorSpace(), true); // Color conversion preserves mips decode_and_check_mips(kMedium_SkFilterQuality, SkSize::Make(0.6f, 0.6f), gfx::ColorSpace::CreateXYZD50(), true); @@ -2698,17 +2522,12 @@ TEST_P(GpuImageDecodeCacheTest, BasicMips) { TEST_P(GpuImageDecodeCacheTest, MipsAddedSubsequentDraw) { auto cache = CreateCache(); - bool is_decomposable = true; - auto filter_quality = kMedium_SkFilterQuality; PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); // Create an image with no scaling. It will not have mips. { - DrawImage draw_image( - image, SkIRect::MakeWH(image.width(), image.height()), filter_quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -2720,17 +2539,24 @@ TEST_P(GpuImageDecodeCacheTest, MipsAddedSubsequentDraw) { // Must hold context lock before calling GetDecodedImageForDraw / // DrawWithImageFinished. viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + // Pull out transfer cache ID from the DecodedDrawImage while it still has + // it attached. + DecodedDrawImage serialized_decoded_draw_image = + cache->GetDecodedImageForDraw(draw_image); + const base::Optional<uint32_t> transfer_cache_entry_id = + serialized_decoded_draw_image.transfer_cache_entry_id(); DecodedDrawImage decoded_draw_image = - EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); + EnsureImageBacked(std::move(serialized_decoded_draw_image)); EXPECT_TRUE(decoded_draw_image.image()); EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); // No mips should be generated. if (do_yuv_decode_) { - // As of M74, Skia will flatten a YUV SkImage upon calling - // makeTextureImage. Thus, we must separately request mips for each - // plane and compare to the original uploaded planes. + // Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus, + // we must separately request mips for each plane and compare to the + // original uploaded planes. CompareAllPlanesToMippedVersions(cache.get(), draw_image, + transfer_cache_entry_id, false /* should_have_mips */); } else { sk_sp<SkImage> image_with_mips = @@ -2750,10 +2576,8 @@ TEST_P(GpuImageDecodeCacheTest, MipsAddedSubsequentDraw) { // no new task (re-uses the existing image), but mips should have been // added. { - DrawImage draw_image( - image, SkIRect::MakeWH(image.width(), image.height()), filter_quality, - CreateMatrix(SkSize::Make(0.6f, 0.6f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.6f, 0.6f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -2762,21 +2586,26 @@ TEST_P(GpuImageDecodeCacheTest, MipsAddedSubsequentDraw) { // Must hold context lock before calling GetDecodedImageForDraw / // DrawWithImageFinished. viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + // Pull out transfer cache ID from the DecodedDrawImage while it still has + // it attached. + DecodedDrawImage serialized_decoded_draw_image = + cache->GetDecodedImageForDraw(draw_image); + const base::Optional<uint32_t> transfer_cache_entry_id = + serialized_decoded_draw_image.transfer_cache_entry_id(); DecodedDrawImage decoded_draw_image = - EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); + EnsureImageBacked(std::move(serialized_decoded_draw_image)); EXPECT_TRUE(decoded_draw_image.image()); EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); // Mips should be generated if (do_yuv_decode_) { + // Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus, + // we must separately request mips for each plane and compare to the + // original uploaded planes. CompareAllPlanesToMippedVersions(cache.get(), draw_image, + transfer_cache_entry_id, true /* should_have_mips */); - EXPECT_TRUE( - SkColorSpace::Equals(cache->SupportsColorSpaceConversion() - ? DefaultColorSpace().ToSkColorSpace().get() - : nullptr, - decoded_draw_image.image()->colorSpace())); } else { sk_sp<SkImage> image_with_mips = decoded_draw_image.image()->makeTextureImage( @@ -2790,9 +2619,6 @@ TEST_P(GpuImageDecodeCacheTest, MipsAddedSubsequentDraw) { TEST_P(GpuImageDecodeCacheTest, MipsAddedWhileOriginalInUse) { auto cache = CreateCache(); - bool is_decomposable = true; - auto filter_quality = kMedium_SkFilterQuality; - PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); struct Decode { @@ -2803,10 +2629,7 @@ TEST_P(GpuImageDecodeCacheTest, MipsAddedWhileOriginalInUse) { // Create an image with no scaling. It will not have mips. { - DrawImage draw_image( - image, SkIRect::MakeWH(image.width(), image.height()), filter_quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(image); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -2818,17 +2641,24 @@ TEST_P(GpuImageDecodeCacheTest, MipsAddedWhileOriginalInUse) { // Must hold context lock before calling GetDecodedImageForDraw / // DrawWithImageFinished. viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + // Pull out transfer cache ID from the DecodedDrawImage while it still has + // it attached. + DecodedDrawImage serialized_decoded_draw_image = + cache->GetDecodedImageForDraw(draw_image); + const base::Optional<uint32_t> transfer_cache_entry_id = + serialized_decoded_draw_image.transfer_cache_entry_id(); DecodedDrawImage decoded_draw_image = - EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); + EnsureImageBacked(std::move(serialized_decoded_draw_image)); ASSERT_TRUE(decoded_draw_image.image()); ASSERT_TRUE(decoded_draw_image.image()->isTextureBacked()); // No mips should be generated. if (do_yuv_decode_) { - // As of M74, Skia will flatten a YUV SkImage upon calling - // makeTextureImage. Thus, we must separately request mips for each - // plane and compare to the original uploaded planes. + // Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus, + // we must separately request mips for each plane and compare to the + // original uploaded planes. CompareAllPlanesToMippedVersions(cache.get(), draw_image, + transfer_cache_entry_id, false /* should_have_mips */); } else { sk_sp<SkImage> image_with_mips = @@ -2841,10 +2671,8 @@ TEST_P(GpuImageDecodeCacheTest, MipsAddedWhileOriginalInUse) { // Second decode with mips. { - DrawImage draw_image( - image, SkIRect::MakeWH(image.width(), image.height()), filter_quality, - CreateMatrix(SkSize::Make(0.6f, 0.6f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = + CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.6f, 0.6f))); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo()); EXPECT_TRUE(result.need_unref); @@ -2853,21 +2681,26 @@ TEST_P(GpuImageDecodeCacheTest, MipsAddedWhileOriginalInUse) { // Must hold context lock before calling GetDecodedImageForDraw / // DrawWithImageFinished. viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + // Pull out transfer cache ID from the DecodedDrawImage while it still has + // it attached. + DecodedDrawImage serialized_decoded_draw_image = + cache->GetDecodedImageForDraw(draw_image); + const base::Optional<uint32_t> transfer_cache_entry_id = + serialized_decoded_draw_image.transfer_cache_entry_id(); DecodedDrawImage decoded_draw_image = - EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image)); + EnsureImageBacked(std::move(serialized_decoded_draw_image)); ASSERT_TRUE(decoded_draw_image.image()); ASSERT_TRUE(decoded_draw_image.image()->isTextureBacked()); // Mips should be generated. if (do_yuv_decode_) { + // Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus, + // we must separately request mips for each plane and compare to the + // original uploaded planes. CompareAllPlanesToMippedVersions(cache.get(), draw_image, + transfer_cache_entry_id, true /* should_have_mips */); - EXPECT_TRUE( - SkColorSpace::Equals(cache->SupportsColorSpaceConversion() - ? DefaultColorSpace().ToSkColorSpace().get() - : nullptr, - decoded_draw_image.image()->colorSpace())); } else { sk_sp<SkImage> image_with_mips = decoded_draw_image.image()->makeTextureImage( @@ -2911,21 +2744,139 @@ TEST_P(GpuImageDecodeCacheTest, MipsAddedWhileOriginalInUse) { } } +TEST_P(GpuImageDecodeCacheTest, + OriginalYUVDecodeScaledDrawCorrectlyMipsPlanes) { + // This test creates an image that will be YUV decoded and drawn at 80% scale. + // Because the final size is between mip levels, we expect the image to be + // decoded and uploaded at original size (mip level 0 for all planes) but to + // have mips attached since kMedium_SkFilterQuality uses bilinear filtering + // between mip levels. + if (!do_yuv_decode_) { + // The YUV case may choose different mip levels between chroma and luma + // planes. + return; + } + auto cache = CreateCache(); + SkFilterQuality filter_quality = kMedium_SkFilterQuality; + SkSize requires_decode_at_original_scale = SkSize::Make(0.8f, 0.8f); + + PaintImage image = CreatePaintImageInternal(GetNormalImageSize()); + DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), + filter_quality, + CreateMatrix(requires_decode_at_original_scale), + PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + ImageDecodeCache::TaskResult result = + cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(result.need_unref); + EXPECT_TRUE(result.task); + + TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get()); + TestTileTaskRunner::ProcessTask(result.task.get()); + + // Must hold context lock before calling GetDecodedImageForDraw / + // DrawWithImageFinished. + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + // Pull out transfer cache ID from the DecodedDrawImage while it still has + // it attached. + DecodedDrawImage serialized_decoded_draw_image = + cache->GetDecodedImageForDraw(draw_image); + const base::Optional<uint32_t> transfer_cache_entry_id = + serialized_decoded_draw_image.transfer_cache_entry_id(); + DecodedDrawImage decoded_draw_image = + EnsureImageBacked(std::move(serialized_decoded_draw_image)); + EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); + + // Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus, we + // must separately request mips for each plane and compare to the original + // uploaded planes. + CompareAllPlanesToMippedVersions(cache.get(), draw_image, + transfer_cache_entry_id, + true /* should_have_mips */); + SkYUVASizeInfo yuv_size_info = GetYUV420SizeInfo(GetNormalImageSize()); + VerifyUploadedPlaneSizes(cache.get(), draw_image, transfer_cache_entry_id, + yuv_size_info.fSizes); + + cache->DrawWithImageFinished(draw_image, decoded_draw_image); + cache->UnrefImage(draw_image); +} + +TEST_P(GpuImageDecodeCacheTest, ScaledYUVDecodeScaledDrawCorrectlyMipsPlanes) { + // This test creates an image that will be YUV decoded and drawn at 45% scale. + // Because the final size is between mip levels, we expect the image to be + // decoded and uploaded at half its original size (mip level 1 for Y plane but + // level 0 for chroma planes) and to have mips attached since + // kMedium_SkFilterQuality uses bilinear filtering between mip levels. + if (!do_yuv_decode_) { + // The YUV case may choose different mip levels between chroma and luma + // planes. + return; + } + auto cache = CreateCache(); + SkFilterQuality filter_quality = kMedium_SkFilterQuality; + SkSize less_than_half_scale = SkSize::Make(0.45f, 0.45f); + + gfx::Size image_size = GetNormalImageSize(); + PaintImage image = CreatePaintImageInternal(image_size); + DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), + filter_quality, CreateMatrix(less_than_half_scale), + PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + ImageDecodeCache::TaskResult result = + cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(result.need_unref); + EXPECT_TRUE(result.task); + + TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get()); + TestTileTaskRunner::ProcessTask(result.task.get()); + + // Must hold context lock before calling GetDecodedImageForDraw / + // DrawWithImageFinished. + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + // Pull out transfer cache ID from the DecodedDrawImage while it still has + // it attached. + DecodedDrawImage serialized_decoded_draw_image = + cache->GetDecodedImageForDraw(draw_image); + const base::Optional<uint32_t> transfer_cache_entry_id = + serialized_decoded_draw_image.transfer_cache_entry_id(); + DecodedDrawImage decoded_draw_image = + EnsureImageBacked(std::move(serialized_decoded_draw_image)); + EXPECT_TRUE(decoded_draw_image.image()); + EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked()); + + // Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus, we + // must separately request mips for each plane and compare to the original + // uploaded planes. + CompareAllPlanesToMippedVersions(cache.get(), draw_image, + transfer_cache_entry_id, + true /* should_have_mips */); + + // Because we intend to draw this image at 0.45 x 0.45 scale, we will upload + // the Y plane at mip level 1 (corresponding to half the original size). The + // chroma planes (U and V) should be uploaded at the same size as the Y plane, + // corresponding to mip level 0, because the largest dimensions greater than + // or equal to target dimensions for them is their original size. + SkISize mipped_plane_sizes[SkYUVASizeInfo::kMaxCount]; + mipped_plane_sizes[SkYUVAIndex::kY_Index] = SkISize::Make( + (image_size.width() + 1) / 2, (image_size.height() + 1) / 2); + mipped_plane_sizes[SkYUVAIndex::kU_Index] = + mipped_plane_sizes[SkYUVAIndex::kY_Index]; + mipped_plane_sizes[SkYUVAIndex::kV_Index] = + mipped_plane_sizes[SkYUVAIndex::kY_Index]; + VerifyUploadedPlaneSizes(cache.get(), draw_image, transfer_cache_entry_id, + mipped_plane_sizes); + + cache->DrawWithImageFinished(draw_image, decoded_draw_image); + cache->UnrefImage(draw_image); +} + TEST_P(GpuImageDecodeCacheTest, GetBorderlineLargeDecodedImageForDraw) { // We will create a texture that's at the maximum size the GPU says it can // support for uploads. auto cache = CreateCache(); - bool is_decomposable = true; - SkFilterQuality quality = kHigh_SkFilterQuality; PaintImage almost_too_large_image = CreatePaintImageInternal(gfx::Size(max_texture_size_, max_texture_size_)); - DrawImage draw_image(almost_too_large_image, - SkIRect::MakeWH(almost_too_large_image.width(), - almost_too_large_image.height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - PaintImage::kDefaultFrameIndex, DefaultColorSpace()); + DrawImage draw_image = CreateDrawImageInternal(almost_too_large_image); ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); @@ -2959,16 +2910,326 @@ bool true_array[] = {true}; INSTANTIATE_TEST_SUITE_P( GpuImageDecodeCacheTestsInProcessRaster, GpuImageDecodeCacheTest, - testing::Combine(testing::ValuesIn(test_color_types), - testing::ValuesIn(false_array) /* use_transfer_cache */, - testing::Bool() /* do_yuv_decode */)); + testing::Combine( + testing::ValuesIn(test_color_types), + testing::ValuesIn(false_array) /* use_transfer_cache */, + testing::Bool() /* do_yuv_decode */, + testing::ValuesIn(false_array) /* advertise_accelerated_decoding */)); INSTANTIATE_TEST_SUITE_P( GpuImageDecodeCacheTestsOOPR, GpuImageDecodeCacheTest, - testing::Combine(testing::ValuesIn(test_color_types), - testing::ValuesIn(true_array) /* use_transfer_cache */, - testing::ValuesIn(false_array) /* do_yuv_decode */)); + testing::Combine( + testing::ValuesIn(test_color_types), + testing::ValuesIn(true_array) /* use_transfer_cache */, + testing::Bool() /* do_yuv_decode */, + testing::ValuesIn(false_array) /* advertise_accelerated_decoding */)); + +class GpuImageDecodeCacheWithAcceleratedDecodesTest + : public GpuImageDecodeCacheTest { + public: + PaintImage CreatePaintImageForDecodeAcceleration( + const gfx::Size& size, + sk_sp<SkColorSpace> color_space = nullptr, + bool is_eligible_for_accelerated_decoding = true) { + SkImageInfo info = + SkImageInfo::Make(size.width(), size.height(), color_type_, + kPremul_SkAlphaType, color_space); + sk_sp<FakePaintImageGenerator> generator; + if (do_yuv_decode_) { + generator = + sk_make_sp<FakePaintImageGenerator>(info, GetYUV420SizeInfo(size)); + } else { + generator = sk_make_sp<FakePaintImageGenerator>(info); + } + if (is_eligible_for_accelerated_decoding) + generator->SetEligibleForAcceleratedDecoding(); + PaintImage image = PaintImageBuilder::WithDefault() + .set_id(PaintImage::GetNextId()) + .set_paint_image_generator(generator) + .TakePaintImage(); + return image; + } + + StrictMock<MockRasterImplementation>* raster_implementation() const { + return static_cast<StrictMock<MockRasterImplementation>*>( + context_provider_->RasterInterface()); + } +}; + +TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, + RequestAcceleratedDecodeSuccessfully) { + auto cache = CreateCache(); + const gfx::Size image_size = GetNormalImageSize(); + const sk_sp<SkColorSpace> image_color_space = SkColorSpace::MakeSRGB(); + const gfx::ColorSpace target_color_space(*image_color_space); + ASSERT_TRUE(target_color_space.IsValid()); + const PaintImage image = + CreatePaintImageForDecodeAcceleration(image_size, image_color_space); + const SkFilterQuality quality = kHigh_SkFilterQuality; + DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), + quality, CreateMatrix(SkSize::Make(0.75f, 0.75f)), + PaintImage::kDefaultFrameIndex, target_color_space); + ImageDecodeCache::TaskResult result = + cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(result.need_unref); + ASSERT_TRUE(result.task); + + // Accelerated decodes should not produce decode tasks. + ASSERT_TRUE(result.task->dependencies().empty()); + EXPECT_CALL(*raster_implementation(), + DoScheduleImageDecode(image_size, _, gfx::ColorSpace(), _)) + .Times(1); + TestTileTaskRunner::ProcessTask(result.task.get()); + + // Must hold context lock before calling GetDecodedImageForDraw / + // DrawWithImageFinished. + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + const DecodedDrawImage decoded_draw_image = + cache->GetDecodedImageForDraw(draw_image); + EXPECT_TRUE(decoded_draw_image.transfer_cache_entry_id().has_value()); + cache->DrawWithImageFinished(draw_image, decoded_draw_image); + cache->UnrefImage(draw_image); +} + +TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, + RequestAcceleratedDecodeSuccessfullyWithColorSpaceConversion) { + auto cache = CreateCache(); + const gfx::Size image_size = GetNormalImageSize(); + const sk_sp<SkColorSpace> image_color_space = SkColorSpace::MakeSRGB(); + const gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateXYZD50(); + ASSERT_TRUE(target_color_space.IsValid()); + const PaintImage image = + CreatePaintImageForDecodeAcceleration(image_size, image_color_space); + const SkFilterQuality quality = kHigh_SkFilterQuality; + DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), + quality, CreateMatrix(SkSize::Make(0.75f, 0.75f)), + PaintImage::kDefaultFrameIndex, target_color_space); + ImageDecodeCache::TaskResult result = + cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(result.need_unref); + ASSERT_TRUE(result.task); + + // Accelerated decodes should not produce decode tasks. + ASSERT_TRUE(result.task->dependencies().empty()); + EXPECT_CALL(*raster_implementation(), + DoScheduleImageDecode(image_size, _, + cache->SupportsColorSpaceConversion() + ? target_color_space + : gfx::ColorSpace(), + _)) + .Times(1); + TestTileTaskRunner::ProcessTask(result.task.get()); + + // Must hold context lock before calling GetDecodedImageForDraw / + // DrawWithImageFinished. + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + const DecodedDrawImage decoded_draw_image = + cache->GetDecodedImageForDraw(draw_image); + EXPECT_TRUE(decoded_draw_image.transfer_cache_entry_id().has_value()); + cache->DrawWithImageFinished(draw_image, decoded_draw_image); + cache->UnrefImage(draw_image); +} + +TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, + AcceleratedDecodeRequestFails) { + auto cache = CreateCache(); + const gfx::Size image_size = GetNormalImageSize(); + const sk_sp<SkColorSpace> image_color_space = SkColorSpace::MakeSRGB(); + const gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateXYZD50(); + ASSERT_TRUE(target_color_space.IsValid()); + const PaintImage image = + CreatePaintImageForDecodeAcceleration(image_size, image_color_space); + const SkFilterQuality quality = kHigh_SkFilterQuality; + DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), + quality, CreateMatrix(SkSize::Make(0.75f, 0.75f)), + PaintImage::kDefaultFrameIndex, target_color_space); + ImageDecodeCache::TaskResult result = + cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(result.need_unref); + ASSERT_TRUE(result.task); + + // Accelerated decodes should not produce decode tasks. + ASSERT_TRUE(result.task->dependencies().empty()); + raster_implementation()->SetAcceleratedDecodingFailed(); + EXPECT_CALL(*raster_implementation(), + DoScheduleImageDecode(image_size, _, + cache->SupportsColorSpaceConversion() + ? target_color_space + : gfx::ColorSpace(), + _)) + .Times(1); + TestTileTaskRunner::ProcessTask(result.task.get()); + + // Must hold context lock before calling GetDecodedImageForDraw / + // DrawWithImageFinished. + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + const DecodedDrawImage decoded_draw_image = + cache->GetDecodedImageForDraw(draw_image); + EXPECT_FALSE(decoded_draw_image.transfer_cache_entry_id().has_value()); + cache->DrawWithImageFinished(draw_image, decoded_draw_image); + cache->UnrefImage(draw_image); +} + +TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, + CannotRequestAcceleratedDecodeBecauseOfStandAloneDecode) { + auto cache = CreateCache(); + const gfx::Size image_size = GetNormalImageSize(); + const sk_sp<SkColorSpace> image_color_space = SkColorSpace::MakeSRGB(); + const gfx::ColorSpace target_color_space(*image_color_space); + ASSERT_TRUE(target_color_space.IsValid()); + const PaintImage image = + CreatePaintImageForDecodeAcceleration(image_size, image_color_space); + const SkFilterQuality quality = kHigh_SkFilterQuality; + DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f)), + PaintImage::kDefaultFrameIndex, target_color_space); + ImageDecodeCache::TaskResult result = + cache->GetOutOfRasterDecodeTaskForImageAndRef(draw_image); + EXPECT_TRUE(result.need_unref); + ASSERT_TRUE(result.task); + + // A non-accelerated standalone decode should produce only a decode task. + ASSERT_TRUE(result.task->dependencies().empty()); + TestTileTaskRunner::ProcessTask(result.task.get()); + cache->UnrefImage(draw_image); +} + +TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, + CannotRequestAcceleratedDecodeBecauseOfNonZeroUploadMipLevel) { + auto cache = CreateCache(); + const gfx::Size image_size = GetNormalImageSize(); + const sk_sp<SkColorSpace> image_color_space = SkColorSpace::MakeSRGB(); + const gfx::ColorSpace target_color_space(*image_color_space); + ASSERT_TRUE(target_color_space.IsValid()); + const PaintImage image = + CreatePaintImageForDecodeAcceleration(image_size, image_color_space); + const SkFilterQuality quality = kHigh_SkFilterQuality; + DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f)), + PaintImage::kDefaultFrameIndex, target_color_space); + ImageDecodeCache::TaskResult result = + cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(result.need_unref); + ASSERT_TRUE(result.task); + + // A non-accelerated normal decode should produce a decode dependency. + ASSERT_EQ(result.task->dependencies().size(), 1u); + ASSERT_TRUE(result.task->dependencies()[0]); + TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get()); + TestTileTaskRunner::ProcessTask(result.task.get()); + cache->UnrefImage(draw_image); +} + +TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, + CannotRequestAcceleratedDecodeBecauseOfIneligiblePaintImage) { + auto cache = CreateCache(); + const gfx::Size image_size = GetNormalImageSize(); + const sk_sp<SkColorSpace> image_color_space = SkColorSpace::MakeSRGB(); + const gfx::ColorSpace target_color_space(*image_color_space); + ASSERT_TRUE(target_color_space.IsValid()); + const PaintImage image = CreatePaintImageForDecodeAcceleration( + image_size, image_color_space, + false /* is_eligible_for_accelerated_decoding */); + const SkFilterQuality quality = kHigh_SkFilterQuality; + DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f)), + PaintImage::kDefaultFrameIndex, target_color_space); + ImageDecodeCache::TaskResult result = + cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(result.need_unref); + ASSERT_TRUE(result.task); + + // A non-accelerated normal decode should produce a decode dependency. + ASSERT_EQ(result.task->dependencies().size(), 1u); + ASSERT_TRUE(result.task->dependencies()[0]); + TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get()); + TestTileTaskRunner::ProcessTask(result.task.get()); + cache->UnrefImage(draw_image); +} + +TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, + CannotRequestAcceleratedDecodeBecauseOfNonSRGBColorSpace) { + auto cache = CreateCache(); + const gfx::Size image_size = GetNormalImageSize(); + const sk_sp<SkColorSpace> image_color_space = + SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kAdobeRGB); + const gfx::ColorSpace target_color_space(*image_color_space); + ASSERT_TRUE(target_color_space.IsValid()); + const PaintImage image = + CreatePaintImageForDecodeAcceleration(image_size, image_color_space); + const SkFilterQuality quality = kHigh_SkFilterQuality; + DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f)), + PaintImage::kDefaultFrameIndex, target_color_space); + ImageDecodeCache::TaskResult result = + cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(result.need_unref); + ASSERT_TRUE(result.task); + + // A non-accelerated normal decode should produce a decode dependency. + ASSERT_EQ(result.task->dependencies().size(), 1u); + ASSERT_TRUE(result.task->dependencies()[0]); + TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get()); + TestTileTaskRunner::ProcessTask(result.task.get()); + cache->UnrefImage(draw_image); +} + +TEST_P(GpuImageDecodeCacheWithAcceleratedDecodesTest, + RequestAcceleratedDecodeSuccessfullyAfterCancellation) { + auto cache = CreateCache(); + const gfx::Size image_size = GetNormalImageSize(); + const sk_sp<SkColorSpace> image_color_space = SkColorSpace::MakeSRGB(); + const gfx::ColorSpace target_color_space(*image_color_space); + ASSERT_TRUE(target_color_space.IsValid()); + const PaintImage image = + CreatePaintImageForDecodeAcceleration(image_size, image_color_space); + const SkFilterQuality quality = kHigh_SkFilterQuality; + DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()), + quality, CreateMatrix(SkSize::Make(0.75f, 0.75f)), + PaintImage::kDefaultFrameIndex, target_color_space); + ImageDecodeCache::TaskResult result = + cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(result.need_unref); + ASSERT_TRUE(result.task); + + // Accelerated decodes should not produce decode tasks. + ASSERT_TRUE(result.task->dependencies().empty()); + + // Cancel the upload. + TestTileTaskRunner::CancelTask(result.task.get()); + TestTileTaskRunner::CompleteTask(result.task.get()); + + // Get the image again - we should have an upload task. + ImageDecodeCache::TaskResult another_result = + cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo()); + EXPECT_TRUE(another_result.need_unref); + ASSERT_TRUE(another_result.task); + EXPECT_EQ(another_result.task->dependencies().size(), 0u); + EXPECT_CALL(*raster_implementation(), + DoScheduleImageDecode(image_size, _, gfx::ColorSpace(), _)) + .Times(1); + TestTileTaskRunner::ProcessTask(another_result.task.get()); + + // Must hold context lock before calling GetDecodedImageForDraw / + // DrawWithImageFinished. + viz::ContextProvider::ScopedContextLock context_lock(context_provider()); + const DecodedDrawImage decoded_draw_image = + cache->GetDecodedImageForDraw(draw_image); + EXPECT_TRUE(decoded_draw_image.transfer_cache_entry_id().has_value()); + cache->DrawWithImageFinished(draw_image, decoded_draw_image); + cache->UnrefImage(draw_image); + cache->UnrefImage(draw_image); +} + +INSTANTIATE_TEST_SUITE_P( + GpuImageDecodeCacheTestsOOPR, + GpuImageDecodeCacheWithAcceleratedDecodesTest, + testing::Combine( + testing::ValuesIn(test_color_types), + testing::ValuesIn(true_array) /* use_transfer_cache */, + testing::Bool() /* do_yuv_decode */, + testing::ValuesIn(true_array) /* advertise_accelerated_decoding */)); #undef EXPECT_TRUE_IF_NOT_USING_TRANSFER_CACHE #undef EXPECT_FALSE_IF_NOT_USING_TRANSFER_CACHE diff --git a/chromium/cc/tiles/image_controller.cc b/chromium/cc/tiles/image_controller.cc index 919be166e5f..9f600462eb8 100644 --- a/chromium/cc/tiles/image_controller.cc +++ b/chromium/cc/tiles/image_controller.cc @@ -21,8 +21,7 @@ ImageController::ImageController( base::SequencedTaskRunner* origin_task_runner, scoped_refptr<base::SequencedTaskRunner> worker_task_runner) : worker_task_runner_(std::move(worker_task_runner)), - origin_task_runner_(origin_task_runner), - weak_ptr_factory_(this) { + origin_task_runner_(origin_task_runner) { weak_ptr_ = weak_ptr_factory_.GetWeakPtr(); } @@ -129,11 +128,6 @@ void ImageController::StopWorkerTasks() { image_decode_queue_.clear(); } -void ImageController::SetPaintWorkletLayerPainter( - std::unique_ptr<PaintWorkletLayerPainter> painter) { - paint_worklet_image_cache_.SetPaintWorkletLayerPainter(std::move(painter)); -} - void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) { DCHECK(!cache_ || !cache); @@ -152,27 +146,7 @@ void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) { } } -void ImageController::ConvertPaintWorkletImagesToTask( - std::vector<DrawImage>* sync_decoded_images, - std::vector<scoped_refptr<TileTask>>* tasks) { - for (auto it = sync_decoded_images->begin(); - it != sync_decoded_images->end();) { - if (!it->paint_image().IsPaintWorklet()) { - ++it; - continue; - } - scoped_refptr<TileTask> result = - paint_worklet_image_cache_.GetTaskForPaintWorkletImage(*it); - if (result) - tasks->push_back(std::move(result)); - // Remove it so that there is no need to check whether an image is - // PaintWorklet generated or not in TileManager's - // work_to_schedule->extra_prepaint_images.insert. - it = sync_decoded_images->erase(it); - } -} - -void ImageController::ConvertDataImagesToTasks( +void ImageController::ConvertImagesToTasks( std::vector<DrawImage>* sync_decoded_images, std::vector<scoped_refptr<TileTask>>* tasks, bool* has_at_raster_images, @@ -181,10 +155,10 @@ void ImageController::ConvertDataImagesToTasks( *has_at_raster_images = false; for (auto it = sync_decoded_images->begin(); it != sync_decoded_images->end();) { - if (it->paint_image().IsPaintWorklet()) { - ++it; - continue; - } + // PaintWorklet images should not be included in this set; they have already + // been painted before raster and so do not need raster-time work. + DCHECK(!it->paint_image().IsPaintWorklet()); + ImageDecodeCache::TaskResult result = cache_->GetTaskForImageAndRef(*it, tracing_info); *has_at_raster_images |= result.IsAtRaster(); @@ -212,8 +186,8 @@ std::vector<scoped_refptr<TileTask>> ImageController::SetPredecodeImages( const ImageDecodeCache::TracingInfo& tracing_info) { std::vector<scoped_refptr<TileTask>> new_tasks; bool has_at_raster_images = false; - ConvertDataImagesToTasks(&images, &new_tasks, &has_at_raster_images, - tracing_info); + ConvertImagesToTasks(&images, &new_tasks, &has_at_raster_images, + tracing_info); UnrefImages(predecode_locked_images_); predecode_locked_images_ = std::move(images); return new_tasks; diff --git a/chromium/cc/tiles/image_controller.h b/chromium/cc/tiles/image_controller.h index 2533b6e50f2..1ec832d13c1 100644 --- a/chromium/cc/tiles/image_controller.h +++ b/chromium/cc/tiles/image_controller.h @@ -19,7 +19,6 @@ #include "cc/paint/draw_image.h" #include "cc/raster/tile_task.h" #include "cc/tiles/image_decode_cache.h" -#include "cc/tiles/paint_worklet_image_cache.h" namespace cc { @@ -39,32 +38,17 @@ class CC_EXPORT ImageController { ImageController& operator=(const ImageController&) = delete; void SetImageDecodeCache(ImageDecodeCache* cache); - void SetPaintWorkletLayerPainter( - std::unique_ptr<PaintWorkletLayerPainter> painter); - // The name "Data images" are the images that are not generated by - // PaintWorklet. - // Build tile tasks for synchronously decoded images that are not generated by - // PaintWorklet. + // Build tile tasks for synchronously decoded images. // |sync_decoded_images| is the input. These are the images from a particular // tile, retrieved by the DiscardableImageMap. Images can be removed from the // vector under certain conditions. // |tasks| is an output, which are the built tile tasks. // |has_at_raster_images| is an output parameter. // |tracing_info| is used in tracing or UMA only. - void ConvertDataImagesToTasks( - std::vector<DrawImage>* sync_decoded_images, - std::vector<scoped_refptr<TileTask>>* tasks, - bool* has_at_raster_images, - const ImageDecodeCache::TracingInfo& tracing_info); - // TODO(crbug.com/915566): bundle all tasks into a big TaskBag. - // Build tile tasks for images that are generated by PaintWorklet. - // |sync_decoded_images| is the input, which are the images from a particular - // tile, retrieved by DiscardableImageMap. Images are removed from the vector - // once the tile task is built. - // |tasks| is an output, which are the built tile tasks. - void ConvertPaintWorkletImagesToTask( - std::vector<DrawImage>* sync_decoded_images, - std::vector<scoped_refptr<TileTask>>* tasks); + void ConvertImagesToTasks(std::vector<DrawImage>* sync_decoded_images, + std::vector<scoped_refptr<TileTask>>* tasks, + bool* has_at_raster_images, + const ImageDecodeCache::TracingInfo& tracing_info); void UnrefImages(const std::vector<DrawImage>& images); void ReduceMemoryUsage(); std::vector<scoped_refptr<TileTask>> SetPredecodeImages( @@ -90,9 +74,6 @@ class CC_EXPORT ImageController { } ImageDecodeCache* cache() const { return cache_; } - PaintWorkletImageCache* paint_worklet_image_cache() { - return &paint_worklet_image_cache_; - } protected: scoped_refptr<base::SequencedTaskRunner> worker_task_runner_; @@ -128,7 +109,6 @@ class CC_EXPORT ImageController { base::WeakPtr<ImageController> weak_ptr_; ImageDecodeCache* cache_ = nullptr; - PaintWorkletImageCache paint_worklet_image_cache_; std::vector<DrawImage> predecode_locked_images_; static ImageDecodeRequestId s_next_image_decode_queue_id_; @@ -151,7 +131,7 @@ class CC_EXPORT ImageController { // from generating new tasks, this vector should be empty. std::vector<ImageDecodeRequest> orphaned_decode_requests_; - base::WeakPtrFactory<ImageController> weak_ptr_factory_; + base::WeakPtrFactory<ImageController> weak_ptr_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/tiles/image_controller_unittest.cc b/chromium/cc/tiles/image_controller_unittest.cc index 1c3e0d3b215..17b20018fb2 100644 --- a/chromium/cc/tiles/image_controller_unittest.cc +++ b/chromium/cc/tiles/image_controller_unittest.cc @@ -244,9 +244,7 @@ DrawImage CreateBitmapDrawImage(gfx::Size size) { class ImageControllerTest : public testing::Test { public: - ImageControllerTest() - : task_runner_(base::SequencedTaskRunnerHandle::Get()), - weak_ptr_factory_(this) { + ImageControllerTest() : task_runner_(base::SequencedTaskRunnerHandle::Get()) { image_ = CreateDiscardableDrawImage(gfx::Size(1, 1)); } ~ImageControllerTest() override = default; @@ -314,31 +312,9 @@ class ImageControllerTest : public testing::Test { std::unique_ptr<ImageController> controller_; DrawImage image_; - base::WeakPtrFactory<ImageControllerTest> weak_ptr_factory_; + base::WeakPtrFactory<ImageControllerTest> weak_ptr_factory_{this}; }; -// Test that GetTasksForImagesAndRef does not generate task for PaintWorklet -// images. -TEST_F(ImageControllerTest, GetTasksForImagesAndRefForPaintWorkletImages) { - std::vector<DrawImage> images(1); - ImageDecodeCache::TracingInfo tracing_info; - - PaintImage paint_image = CreatePaintImage(100, 100); - DrawImage draw_image( - paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - kNone_SkFilterQuality, CreateMatrix(SkSize::Make(1.f, 1.f), true), - PaintImage::kDefaultFrameIndex); - images[0] = draw_image; - - ASSERT_EQ(1u, images.size()); - - std::vector<scoped_refptr<TileTask>> tasks; - bool has_at_raster_images = false; - controller()->ConvertDataImagesToTasks(&images, &tasks, &has_at_raster_images, - tracing_info); - EXPECT_EQ(tasks.size(), 0u); -} - TEST_F(ImageControllerTest, NullControllerUnrefsImages) { std::vector<DrawImage> images(10); ImageDecodeCache::TracingInfo tracing_info; diff --git a/chromium/cc/tiles/image_decode_cache.cc b/chromium/cc/tiles/image_decode_cache.cc index 7c83ee45a15..9c68e3cc26c 100644 --- a/chromium/cc/tiles/image_decode_cache.cc +++ b/chromium/cc/tiles/image_decode_cache.cc @@ -4,7 +4,6 @@ #include "cc/tiles/image_decode_cache.h" -#include "base/metrics/histogram_macros.h" #include "cc/raster/tile_task.h" namespace cc { @@ -19,11 +18,4 @@ ImageDecodeCache::TaskResult::TaskResult(const TaskResult& result) = default; ImageDecodeCache::TaskResult::~TaskResult() = default; -void ImageDecodeCache::RecordImageMipLevelUMA(int mip_level) { - DCHECK_GE(mip_level, 0); - DCHECK_LT(mip_level, 32); - UMA_HISTOGRAM_EXACT_LINEAR("Renderer4.ImageDecodeMipLevel", mip_level + 1, - 33); -} - } // namespace cc diff --git a/chromium/cc/tiles/image_decode_cache.h b/chromium/cc/tiles/image_decode_cache.h index 6d7d339ee28..241f5772b02 100644 --- a/chromium/cc/tiles/image_decode_cache.h +++ b/chromium/cc/tiles/image_decode_cache.h @@ -143,9 +143,6 @@ class CC_EXPORT ImageDecodeCache { // image can directly be used for raster (for instance bitmaps in a software // draw). virtual bool UseCacheForDrawImage(const DrawImage& image) const = 0; - - protected: - void RecordImageMipLevelUMA(int mip_level); }; } // namespace cc diff --git a/chromium/cc/tiles/paint_worklet_image_cache.cc b/chromium/cc/tiles/paint_worklet_image_cache.cc deleted file mode 100644 index d0bd702cae7..00000000000 --- a/chromium/cc/tiles/paint_worklet_image_cache.cc +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/tiles/paint_worklet_image_cache.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "cc/paint/paint_worklet_layer_painter.h" - -namespace cc { - -class PaintWorkletTaskImpl : public TileTask { - public: - PaintWorkletTaskImpl(PaintWorkletImageCache* cache, - const PaintImage& paint_image) - : TileTask(true), cache_(cache), paint_image_(paint_image) {} - PaintWorkletTaskImpl(const PaintWorkletTaskImpl&) = delete; - - PaintWorkletTaskImpl& operator=(const PaintWorkletTaskImpl&) = delete; - - // Overridden from Task: - void RunOnWorkerThread() override { cache_->PaintImageInTask(paint_image_); } - - // Overridden from TileTask: - void OnTaskCompleted() override {} - - protected: - ~PaintWorkletTaskImpl() override = default; - - private: - PaintWorkletImageCache* cache_; - PaintImage paint_image_; -}; - -PaintWorkletImageCache::PaintWorkletImageCache() {} - -PaintWorkletImageCache::~PaintWorkletImageCache() { - for (const auto& pair : records_) - DCHECK_EQ(pair.second.used_ref_count, 0u); -} - -void PaintWorkletImageCache::SetPaintWorkletLayerPainter( - std::unique_ptr<PaintWorkletLayerPainter> painter) { - DCHECK(!painter_); - painter_ = std::move(painter); -} - -scoped_refptr<TileTask> PaintWorkletImageCache::GetTaskForPaintWorkletImage( - const DrawImage& image) { - DCHECK(painter_); - DCHECK(image.paint_image().IsPaintWorklet()); - return base::MakeRefCounted<PaintWorkletTaskImpl>(this, image.paint_image()); -} - -// TODO(xidachen): we might need to consider the animated property value and the -// PaintWorkletInput to decide whether we need to call Paint() function or not. -void PaintWorkletImageCache::PaintImageInTask(const PaintImage& paint_image) { - // TODO(crbug.com/939009): When creating a TileTask for a given PaintImage at - // GetTaskForPaintWorkletImage, we should not create a new TileTask if there - // is already a TileTask for this PaintImage. - { - base::AutoLock hold(records_lock_); - if (records_.find(paint_image.paint_worklet_input()) != records_.end()) - return; - } - // Because the compositor could be waiting on the lock in NotifyPrepareTiles, - // we unlock here such that the compositor won't be blocked on potentially - // slow Paint function. - // TODO(xidachen): ensure that the canvas operations in the PaintRecord - // matches the PaintGeneratedImage::Draw. - sk_sp<PaintRecord> record = - painter_->Paint(paint_image.paint_worklet_input()); - if (!record) - return; - { - base::AutoLock hold(records_lock_); - // It is possible for two or more threads to both pass through the first - // lock and arrive here. To avoid ref-count issues caused by potential - // racing among threads, we use insert such that if an entry already exists - // for a particular key, the value won't be overridden. - records_.insert( - std::make_pair(paint_image.paint_worklet_input(), - PaintWorkletImageCacheValue(std::move(record), 0))); - } -} - -std::pair<sk_sp<PaintRecord>, base::OnceCallback<void()>> -PaintWorkletImageCache::GetPaintRecordAndRef(PaintWorkletInput* input) { - base::AutoLock hold(records_lock_); - DCHECK(records_.find(input) != records_.end()); - records_[input].used_ref_count++; - records_[input].num_of_frames_not_accessed = 0u; - // The PaintWorkletImageCache object lives as long as the LayerTreeHostImpl, - // and that ensures that this pointer and the input will be alive when this - // callback is executed. - auto callback = - base::BindOnce(&PaintWorkletImageCache::DecrementCacheRefCount, - base::Unretained(this), base::Unretained(input)); - return std::make_pair(records_[input].record, std::move(callback)); -} - -void PaintWorkletImageCache::SetNumOfFramesToPurgeCacheEntryForTest( - size_t num) { - num_of_frames_to_purge_cache_entry_ = num; -} - -void PaintWorkletImageCache::DecrementCacheRefCount(PaintWorkletInput* input) { - base::AutoLock hold(records_lock_); - auto it = records_.find(input); - DCHECK(it != records_.end()); - - auto& pair = it->second; - DCHECK_GT(pair.used_ref_count, 0u); - pair.used_ref_count--; -} - -void PaintWorkletImageCache::NotifyDidPrepareTiles() { - base::AutoLock hold(records_lock_); - base::EraseIf( - records_, - [this]( - const std::pair<PaintWorkletInput*, PaintWorkletImageCacheValue>& t) { - return t.second.num_of_frames_not_accessed >= - num_of_frames_to_purge_cache_entry_ && - t.second.used_ref_count == 0; - }); - for (auto& pair : records_) - pair.second.num_of_frames_not_accessed++; -} - -PaintWorkletImageCache::PaintWorkletImageCacheValue:: - PaintWorkletImageCacheValue() = default; - -PaintWorkletImageCache::PaintWorkletImageCacheValue:: - PaintWorkletImageCacheValue(sk_sp<PaintRecord> record, size_t ref_count) - : record(std::move(record)), used_ref_count(ref_count) {} - -PaintWorkletImageCache::PaintWorkletImageCacheValue:: - PaintWorkletImageCacheValue(const PaintWorkletImageCacheValue& other) - : record(other.record), used_ref_count(other.used_ref_count) {} - -PaintWorkletImageCache::PaintWorkletImageCacheValue:: - ~PaintWorkletImageCacheValue() = default; - -} // namespace cc diff --git a/chromium/cc/tiles/paint_worklet_image_cache.h b/chromium/cc/tiles/paint_worklet_image_cache.h deleted file mode 100644 index f9b976f357b..00000000000 --- a/chromium/cc/tiles/paint_worklet_image_cache.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2018 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_TILES_PAINT_WORKLET_IMAGE_CACHE_H_ -#define CC_TILES_PAINT_WORKLET_IMAGE_CACHE_H_ - -#include <utility> - -#include "base/containers/flat_map.h" -#include "base/synchronization/lock.h" -#include "cc/cc_export.h" -#include "cc/paint/draw_image.h" -#include "cc/paint/paint_record.h" -#include "cc/paint/paint_worklet_layer_painter.h" -#include "cc/raster/tile_task.h" -#include "cc/tiles/image_decode_cache.h" - -namespace cc { - -// PaintWorkletImageCache is responsible for generating tasks of executing -// PaintWorklet JS paint callbacks, and being able to return the generated -// results when requested. -class CC_EXPORT PaintWorkletImageCache { - public: - struct CC_EXPORT PaintWorkletImageCacheValue { - PaintWorkletImageCacheValue(); - PaintWorkletImageCacheValue(sk_sp<PaintRecord> record, size_t ref_count); - PaintWorkletImageCacheValue(const PaintWorkletImageCacheValue&); - ~PaintWorkletImageCacheValue(); - - sk_sp<PaintRecord> record; - size_t used_ref_count; - // Indicates how many continuous frames that this cache is never accessed or - // updated. A cache entry should be purged if this number is larger than - // |num_of_frames_to_purge_cache_entry_|. - size_t num_of_frames_not_accessed = 0u; - }; - - PaintWorkletImageCache(); - - ~PaintWorkletImageCache(); - - void SetPaintWorkletLayerPainter( - std::unique_ptr<PaintWorkletLayerPainter> painter); - - scoped_refptr<TileTask> GetTaskForPaintWorkletImage(const DrawImage& image); - - void PaintImageInTask(const PaintImage& paint_image); - - void NotifyDidPrepareTiles(); - - // Returns a callback to decrement the ref count for the corresponding entry. - std::pair<sk_sp<PaintRecord>, base::OnceCallback<void()>> - GetPaintRecordAndRef(PaintWorkletInput* input); - - const base::flat_map<PaintWorkletInput*, PaintWorkletImageCacheValue>& - GetRecordsForTest() { - return records_; - } - - void SetNumOfFramesToPurgeCacheEntryForTest(size_t); - - private: - void DecrementCacheRefCount(PaintWorkletInput* input); - - // This is a map of paint worklet inputs to a pair of paint record and a - // reference count. The paint record is the representation of the worklet - // output based on the input, and the reference count is the number of times - // that it is used for tile rasterization. - base::flat_map<PaintWorkletInput*, PaintWorkletImageCacheValue> records_; - - // The |records_| can be accessed from compositor and raster worker threads at - // the same time. To prevent race, we need to lock on it. - base::Lock records_lock_; - - // The PaintWorkletImageCache is owned by ImageController, which has the same - // life time as the LayerTreeHostImpl, that guarantees that the painter will - // live as long as the LayerTreeHostImpl. - std::unique_ptr<PaintWorkletLayerPainter> painter_; - - size_t num_of_frames_to_purge_cache_entry_ = 5u; -}; - -} // namespace cc - -#endif // CC_TILES_PAINT_WORKLET_IMAGE_CACHE_H_ diff --git a/chromium/cc/tiles/paint_worklet_image_cache_unittest.cc b/chromium/cc/tiles/paint_worklet_image_cache_unittest.cc deleted file mode 100644 index 7af4b9340c6..00000000000 --- a/chromium/cc/tiles/paint_worklet_image_cache_unittest.cc +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <memory> -#include <utility> - -#include "cc/tiles/paint_worklet_image_cache.h" - -#include "cc/paint/draw_image.h" -#include "cc/raster/paint_worklet_image_provider.h" -#include "cc/test/skia_common.h" -#include "cc/test/test_paint_worklet_input.h" -#include "cc/test/test_paint_worklet_layer_painter.h" -#include "cc/test/test_tile_task_runner.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cc { -namespace { - -class TestPaintWorkletImageCache : public PaintWorkletImageCache { - public: - TestPaintWorkletImageCache() {} -}; - -SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) { - SkMatrix matrix; - matrix.setScale(scale.width(), scale.height()); - - if (!is_decomposable) { - // Perspective is not decomposable, add it. - matrix[SkMatrix::kMPersp0] = 0.1f; - } - - return matrix; -} - -PaintImage CreatePaintImage(int width, int height) { - scoped_refptr<TestPaintWorkletInput> input = - base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(width, height)); - return CreatePaintWorkletPaintImage(input); -} - -scoped_refptr<TileTask> GetTaskForPaintWorkletImage( - const PaintImage& paint_image, - TestPaintWorkletImageCache* cache) { - DrawImage draw_image( - paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()), - kNone_SkFilterQuality, CreateMatrix(SkSize::Make(1.f, 1.f), true), - PaintImage::kDefaultFrameIndex); - return cache->GetTaskForPaintWorkletImage(draw_image); -} - -void TestPaintRecord(const PaintRecord* record) { - EXPECT_EQ(record->total_op_count(), 1u); - - // GetOpAtForTesting check whether the type is the same as DrawImageOp or not. - // If not, it returns a nullptr. - auto* paint_op = record->GetOpAtForTesting<DrawImageOp>(0); - EXPECT_TRUE(paint_op); -} - -TEST(PaintWorkletImageCacheTest, GetTaskForImage) { - TestPaintWorkletImageCache cache; - std::unique_ptr<TestPaintWorkletLayerPainter> painter = - std::make_unique<TestPaintWorkletLayerPainter>(); - cache.SetPaintWorkletLayerPainter(std::move(painter)); - PaintImage paint_image = CreatePaintImage(100, 100); - scoped_refptr<TileTask> task = - GetTaskForPaintWorkletImage(paint_image, &cache); - EXPECT_TRUE(task); - PaintWorkletImageProvider provider(&cache); - - TestTileTaskRunner::ProcessTask(task.get()); - - { - ImageProvider::ScopedResult result = - provider.GetPaintRecordResult(paint_image.paint_worklet_input()); - EXPECT_TRUE(result.paint_record()); - TestPaintRecord(result.paint_record()); - - base::flat_map<PaintWorkletInput*, - PaintWorkletImageCache::PaintWorkletImageCacheValue> - records = cache.GetRecordsForTest(); - // Test the ref count. - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); - } - base::flat_map<PaintWorkletInput*, - PaintWorkletImageCache::PaintWorkletImageCacheValue> - records = cache.GetRecordsForTest(); - // Test the ref count, which should have been decremented when the result - // goes out of the scope. - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 0u); - - { - ImageProvider::ScopedResult result = - provider.GetPaintRecordResult(paint_image.paint_worklet_input()); - - base::flat_map<PaintWorkletInput*, - PaintWorkletImageCache::PaintWorkletImageCacheValue> - records = cache.GetRecordsForTest(); - // Test the ref count. - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); - - ImageProvider::ScopedResult moved_result = std::move(result); - - EXPECT_FALSE(result); - - EXPECT_TRUE(moved_result.paint_record()); - TestPaintRecord(moved_result.paint_record()); - - // Once moved, the ref count from |result| should have been transferred to - // |moved_result|, so there should be only one un-ref when they both go out - // of scope. - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); - } - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 0u); -} - -TEST(PaintWorkletImageCacheTest, EntryWithNonZeroRefCountNotPurged) { - TestPaintWorkletImageCache cache; - std::unique_ptr<TestPaintWorkletLayerPainter> painter = - std::make_unique<TestPaintWorkletLayerPainter>(); - cache.SetPaintWorkletLayerPainter(std::move(painter)); - PaintImage paint_image = CreatePaintImage(100, 100); - scoped_refptr<TileTask> task = - GetTaskForPaintWorkletImage(paint_image, &cache); - EXPECT_TRUE(task); - - TestTileTaskRunner::ProcessTask(task.get()); - - PaintWorkletImageProvider provider(&cache); - ImageProvider::ScopedResult result = - provider.GetPaintRecordResult(paint_image.paint_worklet_input()); - base::flat_map<PaintWorkletInput*, - PaintWorkletImageCache::PaintWorkletImageCacheValue> - records = cache.GetRecordsForTest(); - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); - - cache.NotifyDidPrepareTiles(); - cache.NotifyDidPrepareTiles(); - cache.NotifyDidPrepareTiles(); - - records = cache.GetRecordsForTest(); - EXPECT_EQ(records.size(), 1u); -} - -TEST(PaintWorkletImageCacheTest, MultipleRecordsInCache) { - TestPaintWorkletImageCache cache; - std::unique_ptr<TestPaintWorkletLayerPainter> painter = - std::make_unique<TestPaintWorkletLayerPainter>(); - cache.SetPaintWorkletLayerPainter(std::move(painter)); - PaintImage paint_image1 = CreatePaintImage(100, 100); - scoped_refptr<TileTask> task1 = - GetTaskForPaintWorkletImage(paint_image1, &cache); - EXPECT_TRUE(task1); - PaintImage paint_image2 = CreatePaintImage(200, 200); - scoped_refptr<TileTask> task2 = - GetTaskForPaintWorkletImage(paint_image2, &cache); - EXPECT_TRUE(task2); - - TestTileTaskRunner::ProcessTask(task1.get()); - TestTileTaskRunner::ProcessTask(task2.get()); - - base::flat_map<PaintWorkletInput*, - PaintWorkletImageCache::PaintWorkletImageCacheValue> - records = cache.GetRecordsForTest(); - EXPECT_EQ(records.size(), 2u); - - cache.SetNumOfFramesToPurgeCacheEntryForTest(2u); - PaintRecord* record1 = - records[paint_image1.paint_worklet_input()].record.get(); - EXPECT_TRUE(record1); - // Test the |num_of_frames_not_accessed| for this cache entry. - EXPECT_EQ( - records[paint_image1.paint_worklet_input()].num_of_frames_not_accessed, - 0u); - TestPaintRecord(record1); - - PaintRecord* record2 = - records[paint_image2.paint_worklet_input()].record.get(); - EXPECT_TRUE(record2); - // Test the |num_of_frames_not_accessed| for this cache entry. - EXPECT_EQ( - records[paint_image2.paint_worklet_input()].num_of_frames_not_accessed, - 0u); - TestPaintRecord(record2); - - // NotifyDidPrepareTiles is called by TileManager::PrepareTiles() which is - // called at each new impl frame. Here we test that a paint record with - // |num_of_frames_not_accessed| >= 2 is purged from the cache. - cache.NotifyDidPrepareTiles(); - records = cache.GetRecordsForTest(); - EXPECT_EQ( - records[paint_image1.paint_worklet_input()].num_of_frames_not_accessed, - 1u); - EXPECT_EQ( - records[paint_image2.paint_worklet_input()].num_of_frames_not_accessed, - 1u); - - std::pair<sk_sp<PaintRecord>, base::OnceCallback<void()>> pair = - cache.GetPaintRecordAndRef(paint_image1.paint_worklet_input()); - // Run the callback to decrement the ref count. - std::move(pair.second).Run(); - records = cache.GetRecordsForTest(); - EXPECT_EQ( - records[paint_image1.paint_worklet_input()].num_of_frames_not_accessed, - 0u); - - cache.NotifyDidPrepareTiles(); - cache.NotifyDidPrepareTiles(); - records = cache.GetRecordsForTest(); - // The cache entry for paint_image2 should have been purged because it was - // never accessed/updated in the last 2 frames. - EXPECT_EQ(records.size(), 1u); - EXPECT_EQ( - records[paint_image1.paint_worklet_input()].num_of_frames_not_accessed, - 2u); -} - -// This test ensures that if an entry already exist, then the PaintImageInTask -// will not replace it with a new entry and reset its ref count. -TEST(PaintWorkletImageCacheTest, CacheEntryLookup) { - TestPaintWorkletImageCache cache; - std::unique_ptr<TestPaintWorkletLayerPainter> painter = - std::make_unique<TestPaintWorkletLayerPainter>(); - cache.SetPaintWorkletLayerPainter(std::move(painter)); - PaintImage paint_image = CreatePaintImage(100, 100); - scoped_refptr<TileTask> task = - GetTaskForPaintWorkletImage(paint_image, &cache); - EXPECT_TRUE(task); - PaintWorkletImageProvider provider(&cache); - - TestTileTaskRunner::ProcessTask(task.get()); - - { - ImageProvider::ScopedResult result = - provider.GetPaintRecordResult(paint_image.paint_worklet_input()); - EXPECT_TRUE(result.paint_record()); - TestPaintRecord(result.paint_record()); - - base::flat_map<PaintWorkletInput*, - PaintWorkletImageCache::PaintWorkletImageCacheValue> - records = cache.GetRecordsForTest(); - // Test the ref count. - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); - - // Create a new task with the same PaintWorkletInput as the previous task. - // Then ProcessTask will invoke PaintWorkletImageCache::PaintImageInTask, - // and it should early exit, without replacing the existing PaintRecord and - // resetting the ref count. - scoped_refptr<TileTask> task_with_the_same_input = - GetTaskForPaintWorkletImage(paint_image, &cache); - EXPECT_TRUE(task); - TestTileTaskRunner::ProcessTask(task_with_the_same_input.get()); - EXPECT_EQ(records[paint_image.paint_worklet_input()].used_ref_count, 1u); - } -} - -} // namespace -} // namespace cc diff --git a/chromium/cc/tiles/picture_layer_tiling.h b/chromium/cc/tiles/picture_layer_tiling.h index 4f66d992b35..253fdea09ac 100644 --- a/chromium/cc/tiles/picture_layer_tiling.h +++ b/chromium/cc/tiles/picture_layer_tiling.h @@ -17,6 +17,7 @@ #include "cc/base/region.h" #include "cc/base/tiling_data.h" #include "cc/cc_export.h" +#include "cc/paint/paint_worklet_input.h" #include "cc/tiles/tile.h" #include "cc/tiles/tile_priority.h" #include "cc/trees/occlusion.h" @@ -40,8 +41,7 @@ class CC_EXPORT PictureLayerTilingClient { // Create a tile at the given content_rect (in the contents scale of the // tiling) This might return null if the client cannot create such a tile. virtual std::unique_ptr<Tile> CreateTile(const Tile::CreateInfo& info) = 0; - virtual gfx::Size CalculateTileSize( - const gfx::Size& content_bounds) const = 0; + virtual gfx::Size CalculateTileSize(const gfx::Size& content_bounds) = 0; // This invalidation region defines the area (if any, it can by null) that // tiles can not be shared between pending and active trees. virtual const Region* GetPendingInvalidation() = 0; @@ -49,6 +49,7 @@ class CC_EXPORT PictureLayerTilingClient { const PictureLayerTiling* tiling) const = 0; virtual bool HasValidTilePriorities() const = 0; virtual bool RequiresHighResToDraw() const = 0; + virtual const PaintWorkletRecordMap& GetPaintWorkletRecords() const = 0; protected: virtual ~PictureLayerTilingClient() {} @@ -135,6 +136,9 @@ class CC_EXPORT PictureLayerTiling { const scoped_refptr<RasterSource>& raster_source() const { return raster_source_; } + const PaintWorkletRecordMap& GetPaintWorkletRecords() const { + return client_->GetPaintWorkletRecords(); + } gfx::Size tiling_size() const { return tiling_data_.tiling_size(); } gfx::Rect live_tiles_rect() const { return live_tiles_rect_; } gfx::Size tile_size() const { return tiling_data_.max_texture_size(); } diff --git a/chromium/cc/tiles/picture_layer_tiling_set.cc b/chromium/cc/tiles/picture_layer_tiling_set.cc index 38d7ad0e9fa..5a2bcb740a0 100644 --- a/chromium/cc/tiles/picture_layer_tiling_set.cc +++ b/chromium/cc/tiles/picture_layer_tiling_set.cc @@ -242,7 +242,7 @@ void PictureLayerTilingSet::CleanUpTilings( continue; // Don't remove tilings that are required. - if (base::ContainsValue(needed_tilings, tiling.get())) { + if (base::Contains(needed_tilings, tiling.get())) { continue; } diff --git a/chromium/cc/tiles/prioritized_tile.h b/chromium/cc/tiles/prioritized_tile.h index a2996602c7f..54538898271 100644 --- a/chromium/cc/tiles/prioritized_tile.h +++ b/chromium/cc/tiles/prioritized_tile.h @@ -6,6 +6,7 @@ #define CC_TILES_PRIORITIZED_TILE_H_ #include "cc/cc_export.h" +#include "cc/paint/paint_worklet_input.h" #include "cc/raster/raster_source.h" #include "cc/tiles/picture_layer_tiling.h" #include "cc/tiles/tile.h" @@ -32,6 +33,9 @@ class CC_EXPORT PrioritizedTile { const scoped_refptr<RasterSource>& raster_source() const { return source_tiling_->raster_source(); } + const PaintWorkletRecordMap& GetPaintWorkletRecords() const { + return source_tiling_->GetPaintWorkletRecords(); + } const TilePriority& priority() const { return priority_; } bool is_occluded() const { return is_occluded_; } bool is_process_for_images_only() const { diff --git a/chromium/cc/tiles/software_image_decode_cache.cc b/chromium/cc/tiles/software_image_decode_cache.cc index f858a180756..6aa420ce0b5 100644 --- a/chromium/cc/tiles/software_image_decode_cache.cc +++ b/chromium/cc/tiles/software_image_decode_cache.cc @@ -76,7 +76,12 @@ class SoftwareImageDecodeTaskImpl : public TileTask { paint_image_.GetSkImage().get(), devtools_instrumentation::ScopedImageDecodeTask::kSoftware, ImageDecodeCache::ToScopedTaskType(tracing_info_.task_type)); - cache_->DecodeImageInTask(image_key_, paint_image_, task_type_); + SoftwareImageDecodeCache::TaskProcessingResult result = + cache_->DecodeImageInTask(image_key_, paint_image_, task_type_); + + // Do not log timing UMAs if we did not perform a full decode. + if (result != SoftwareImageDecodeCache::TaskProcessingResult::kFullDecode) + image_decode_task.SuppressMetrics(); } // Overridden from TileTask: @@ -300,9 +305,10 @@ void SoftwareImageDecodeCache::UnrefImage(const CacheKey& key) { } } -void SoftwareImageDecodeCache::DecodeImageInTask(const CacheKey& key, - const PaintImage& paint_image, - DecodeTaskType task_type) { +SoftwareImageDecodeCache::TaskProcessingResult +SoftwareImageDecodeCache::DecodeImageInTask(const CacheKey& key, + const PaintImage& paint_image, + DecodeTaskType task_type) { TRACE_EVENT1("cc,benchmark", "SoftwareImageDecodeCache::DecodeImageInTask", "key", key.ToString()); base::AutoLock lock(lock_); @@ -316,16 +322,16 @@ void SoftwareImageDecodeCache::DecodeImageInTask(const CacheKey& key, DCHECK_GT(cache_entry->ref_count, 0); DCHECK(cache_entry->is_budgeted); - DecodeImageIfNecessary(key, paint_image, cache_entry); + TaskProcessingResult result = + DecodeImageIfNecessary(key, paint_image, cache_entry); DCHECK(cache_entry->decode_failed || cache_entry->is_locked); - RecordImageMipLevelUMA( - MipMapUtil::GetLevelForSize(key.src_rect().size(), key.target_size())); + return result; } -void SoftwareImageDecodeCache::DecodeImageIfNecessary( - const CacheKey& key, - const PaintImage& paint_image, - CacheEntry* entry) { +SoftwareImageDecodeCache::TaskProcessingResult +SoftwareImageDecodeCache::DecodeImageIfNecessary(const CacheKey& key, + const PaintImage& paint_image, + CacheEntry* entry) { TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "SoftwareImageDecodeCache::DecodeImageIfNecessary", "key", key.ToString()); @@ -336,18 +342,18 @@ void SoftwareImageDecodeCache::DecodeImageIfNecessary( entry->decode_failed = true; if (entry->decode_failed) - return; + return TaskProcessingResult::kCancelled; if (entry->memory) { if (entry->is_locked) - return; + return TaskProcessingResult::kLockOnly; bool lock_succeeded = entry->Lock(); // TODO(vmpstr): Deprecate the prepaint split, since it doesn't matter. RecordLockExistingCachedImageHistogram(TilePriority::NOW, lock_succeeded); if (lock_succeeded) - return; + return TaskProcessingResult::kLockOnly; } std::unique_ptr<CacheEntry> local_cache_entry; @@ -439,7 +445,7 @@ void SoftwareImageDecodeCache::DecodeImageIfNecessary( if (!local_cache_entry) { entry->decode_failed = true; - return; + return TaskProcessingResult::kCancelled; } // Just in case someone else did this already, just unlock our work. @@ -457,6 +463,8 @@ void SoftwareImageDecodeCache::DecodeImageIfNecessary( local_cache_entry->MoveImageMemoryTo(entry); DCHECK(entry->is_locked); } + + return TaskProcessingResult::kFullDecode; } base::Optional<SoftwareImageDecodeCache::CacheKey> diff --git a/chromium/cc/tiles/software_image_decode_cache.h b/chromium/cc/tiles/software_image_decode_cache.h index c3b7c03303d..176adeec8ed 100644 --- a/chromium/cc/tiles/software_image_decode_cache.h +++ b/chromium/cc/tiles/software_image_decode_cache.h @@ -32,6 +32,10 @@ class CC_EXPORT SoftwareImageDecodeCache enum class DecodeTaskType { USE_IN_RASTER_TASKS, USE_OUT_OF_RASTER_TASKS }; + // Identifies whether a decode task performed decode work, or was fulfilled / + // failed trivially. + enum class TaskProcessingResult { kFullDecode, kLockOnly, kCancelled }; + SoftwareImageDecodeCache(SkColorType color_type, size_t locked_memory_limit_bytes, PaintImage::GeneratorClientId generator_client_id); @@ -56,9 +60,9 @@ class CC_EXPORT SoftwareImageDecodeCache // Decode the given image and store it in the cache. This is only called by an // image decode task from a worker thread. - void DecodeImageInTask(const CacheKey& key, - const PaintImage& paint_image, - DecodeTaskType task_type); + TaskProcessingResult DecodeImageInTask(const CacheKey& key, + const PaintImage& paint_image, + DecodeTaskType task_type); void OnImageDecodeTaskCompleted(const CacheKey& key, DecodeTaskType task_type); @@ -123,9 +127,9 @@ class CC_EXPORT SoftwareImageDecodeCache CacheEntry* AddCacheEntry(const CacheKey& key); - void DecodeImageIfNecessary(const CacheKey& key, - const PaintImage& paint_image, - CacheEntry* cache_entry); + TaskProcessingResult DecodeImageIfNecessary(const CacheKey& key, + const PaintImage& paint_image, + CacheEntry* cache_entry); void AddBudgetForImage(const CacheKey& key, CacheEntry* entry); void RemoveBudgetForImage(const CacheKey& key, CacheEntry* entry); base::Optional<CacheKey> FindCachedCandidate(const CacheKey& key); diff --git a/chromium/cc/tiles/software_image_decode_cache_utils.h b/chromium/cc/tiles/software_image_decode_cache_utils.h index 835d68c8492..3293d8b4cc9 100644 --- a/chromium/cc/tiles/software_image_decode_cache_utils.h +++ b/chromium/cc/tiles/software_image_decode_cache_utils.h @@ -50,12 +50,11 @@ class SoftwareImageDecodeCacheUtils { bool operator==(const CacheKey& other) const { // The frame_key always has to be the same. However, after that all // original decodes are the same, so if we can use the original decode, - // return true. If not, then we have to compare every field. Note we don't - // compare |nearest_neighbor_| because we would only use kOriginal type in - // that case (dchecked below), which implies no scale. The returned scale - // to Skia would respect the nearest neighbor value of the requested - // image. - DCHECK(!is_nearest_neighbor_ || type_ == kOriginal); + // return true. If not, then we have to compare every field. + // |nearest_neighbor_| is not compared below since it is not used for + // scaled decodes and does not affect the contents of the cache entry + // (just passed to skia for the filtering to be done at raster time). + DCHECK(!is_nearest_neighbor_ || type_ != kSubrectAndScale); return frame_key_ == other.frame_key_ && type_ == other.type_ && target_color_space_ == other.target_color_space_ && (type_ == kOriginal || (src_rect_ == other.src_rect_ && diff --git a/chromium/cc/tiles/tile_manager.cc b/chromium/cc/tiles/tile_manager.cc index 165bb015145..032b487ef5e 100644 --- a/chromium/cc/tiles/tile_manager.cc +++ b/chromium/cc/tiles/tile_manager.cc @@ -402,9 +402,7 @@ TileManager::TileManager( base::Unretained(this))), has_scheduled_tile_tasks_(false), prepare_tiles_count_(0u), - next_tile_id_(0u), - task_set_finished_weak_ptr_factory_(this), - ready_to_draw_callback_weak_ptr_factory_(this) {} + next_tile_id_(0u) {} TileManager::~TileManager() { FinishTasksAndCleanUp(); @@ -550,8 +548,6 @@ bool TileManager::PrepareTiles( // Schedule tile tasks. ScheduleTasks(std::move(prioritized_work)); - image_controller_.paint_worklet_image_cache()->NotifyDidPrepareTiles(); - TRACE_EVENT_INSTANT1("cc", "DidPrepareTiles", TRACE_EVENT_SCOPE_THREAD, "state", BasicStateAsValue()); return true; @@ -779,8 +775,12 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() { // If we couldn't fit the tile into our current memory limit, then we're // done. if (!memory_usage_is_within_limit) { - if (tile_is_needed_now) + if (tile_is_needed_now) { + LOG(ERROR) << "WARNING: tile memory limits exceeded, some content may " + "not draw"; + had_enough_memory_to_schedule_tiles_needed_now = false; + } all_tiles_that_need_to_be_rasterized_are_scheduled_ = false; break; } @@ -1190,10 +1190,8 @@ scoped_refptr<TileTask> TileManager::CreateRasterTask( prepare_tiles_count_, prioritized_tile.priority().priority_bin, ImageDecodeCache::TaskType::kInRaster); bool has_at_raster_images = false; - image_controller_.ConvertDataImagesToTasks( - &sync_decoded_images, &decode_tasks, &has_at_raster_images, tracing_info); - image_controller_.ConvertPaintWorkletImagesToTask(&sync_decoded_images, - &decode_tasks); + image_controller_.ConvertImagesToTasks(&sync_decoded_images, &decode_tasks, + &has_at_raster_images, tracing_info); // Notify |decoded_image_tracker_| after |image_controller_| to ensure we've // taken new refs on the images before releasing the predecode API refs. decoded_image_tracker_.OnImagesUsedInDraw(sync_decoded_images); @@ -1246,8 +1244,13 @@ scoped_refptr<TileTask> TileManager::CreateRasterTask( PlaybackImageProvider image_provider(image_controller_.cache(), raster_color_space, std::move(settings)); + // We make a deliberate copy of the PaintWorklet map here, as the + // PictureLayerImpl's map could be mutated or destroyed whilst raster from an + // earlier snapshot is still ongoing on the raster worker threads. + PaintWorkletRecordMap paint_worklet_records = + prioritized_tile.GetPaintWorkletRecords(); PaintWorkletImageProvider paint_worklet_image_provider( - image_controller_.paint_worklet_image_cache()); + std::move(paint_worklet_records)); DispatchingImageProvider dispatching_image_provider( std::move(image_provider), std::move(paint_worklet_image_provider)); @@ -1711,11 +1714,6 @@ TileManager::ActivationStateAsValue() { return std::move(state); } -void TileManager::SetPaintWorkletLayerPainter( - std::unique_ptr<PaintWorkletLayerPainter> painter) { - image_controller_.SetPaintWorkletLayerPainter(std::move(painter)); -} - void TileManager::ActivationStateAsValueInto( base::trace_event::TracedValue* state) { state->SetString("tree_priority", diff --git a/chromium/cc/tiles/tile_manager.h b/chromium/cc/tiles/tile_manager.h index 015ce53e837..afa7d42ef37 100644 --- a/chromium/cc/tiles/tile_manager.h +++ b/chromium/cc/tiles/tile_manager.h @@ -294,9 +294,6 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { void set_active_url(const GURL& url) { active_url_ = url; } - void SetPaintWorkletLayerPainter( - std::unique_ptr<PaintWorkletLayerPainter> painter); - protected: friend class Tile; // Must be called by tile during destruction. @@ -473,9 +470,10 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { // different. The |task_set_finished_weak_ptr_factory_| is invalidated any // time new tasks are scheduled, preventing a race when the callback has // been scheduled but not yet executed. - base::WeakPtrFactory<TileManager> task_set_finished_weak_ptr_factory_; + base::WeakPtrFactory<TileManager> task_set_finished_weak_ptr_factory_{this}; // The |ready_to_draw_callback_weak_ptr_factory_| is never invalidated. - base::WeakPtrFactory<TileManager> ready_to_draw_callback_weak_ptr_factory_; + base::WeakPtrFactory<TileManager> ready_to_draw_callback_weak_ptr_factory_{ + this}; }; } // namespace cc diff --git a/chromium/cc/tiles/tile_manager_unittest.cc b/chromium/cc/tiles/tile_manager_unittest.cc index 9c6d1e0516b..7b4e57f2e2c 100644 --- a/chromium/cc/tiles/tile_manager_unittest.cc +++ b/chromium/cc/tiles/tile_manager_unittest.cc @@ -2583,7 +2583,7 @@ TEST_F(TileManagerReadyToDrawTest, TilePrioritiesUpdated) { final_num_prepaint++; } else { final_num_required++; - if (base::ContainsValue(prepaint_tiles, tile)) { + if (base::Contains(prepaint_tiles, tile)) { found_one_prepaint_to_required_transition = true; } } diff --git a/chromium/cc/tiles/tile_task_manager.cc b/chromium/cc/tiles/tile_task_manager.cc index 2bfdd9e88ef..476a7d7a965 100644 --- a/chromium/cc/tiles/tile_task_manager.cc +++ b/chromium/cc/tiles/tile_task_manager.cc @@ -5,6 +5,7 @@ #include "cc/tiles/tile_task_manager.h" #include "base/memory/ptr_util.h" +#include "base/threading/thread_restrictions.h" #include "base/trace_event/trace_event.h" namespace cc { @@ -50,7 +51,10 @@ void TileTaskManagerImpl::Shutdown() { // Cancel non-scheduled tasks and wait for running tasks to finish. TaskGraph empty; task_graph_runner_->ScheduleTasks(namespace_token_, &empty); - task_graph_runner_->WaitForTasksToFinishRunning(namespace_token_); + { + base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait; + task_graph_runner_->WaitForTasksToFinishRunning(namespace_token_); + } } } // namespace cc diff --git a/chromium/cc/trees/damage_tracker.cc b/chromium/cc/trees/damage_tracker.cc index f3ddee21062..743b681dc6a 100644 --- a/chromium/cc/trees/damage_tracker.cc +++ b/chromium/cc/trees/damage_tracker.cc @@ -386,7 +386,7 @@ void DamageTracker::AccumulateDamageFromLayer(LayerImpl* layer) { // If the layer properties haven't changed, then the the target surface is // only affected by the layer's damaged area, which could be empty. gfx::Rect damage_rect = - gfx::UnionRects(layer->update_rect(), layer->damage_rect()); + gfx::UnionRects(layer->update_rect(), layer->GetDamageRect()); damage_rect.Intersect(gfx::Rect(layer->bounds())); if (!damage_rect.IsEmpty()) { @@ -416,7 +416,7 @@ void DamageTracker::AccumulateDamageFromLayer(LayerImpl* layer) { if (layer_is_new || !layer->update_rect().IsEmpty() || layer->LayerPropertyChangedNotFromPropertyTrees() || - !layer->damage_rect().IsEmpty() || property_change_on_non_target_node) { + !layer->GetDamageRect().IsEmpty() || property_change_on_non_target_node) { has_damage_from_contributing_content_ |= !damage_for_this_update_.IsEmpty(); } } diff --git a/chromium/cc/trees/damage_tracker_unittest.cc b/chromium/cc/trees/damage_tracker_unittest.cc index fcd75ca4f75..b9046c6644b 100644 --- a/chromium/cc/trees/damage_tracker_unittest.cc +++ b/chromium/cc/trees/damage_tracker_unittest.cc @@ -26,6 +26,36 @@ namespace cc { namespace { +class TestLayerImpl : public LayerImpl { + public: + TestLayerImpl(LayerTreeImpl* tree_impl, int id); + + void AddDamageRect(const gfx::Rect& damage_rect); + + // LayerImpl overrides. + gfx::Rect GetDamageRect() const override; + void ResetChangeTracking() override; + + private: + gfx::Rect damage_rect_; +}; + +TestLayerImpl::TestLayerImpl(LayerTreeImpl* tree_impl, int id) + : LayerImpl(tree_impl, id) {} + +void TestLayerImpl::AddDamageRect(const gfx::Rect& damage_rect) { + damage_rect_.Union(damage_rect); +} + +gfx::Rect TestLayerImpl::GetDamageRect() const { + return damage_rect_; +} + +void TestLayerImpl::ResetChangeTracking() { + LayerImpl::ResetChangeTracking(); + damage_rect_.SetRect(0, 0, 0, 0); +} + void ExecuteCalculateDrawProperties(LayerImpl* root, float device_scale_factor, RenderSurfaceList* render_surface_list) { @@ -71,16 +101,15 @@ class DamageTrackerTest : public testing::Test { LayerImpl* CreateTestTreeWithOneSurface(int number_of_children) { host_impl_.active_tree()->DetachLayers(); - std::unique_ptr<LayerImpl> root = - LayerImpl::Create(host_impl_.active_tree(), 1); + auto root = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 1); root->SetBounds(gfx::Size(500, 500)); root->SetDrawsContent(true); root->test_properties()->force_render_surface = true; for (int i = 0; i < number_of_children; ++i) { - std::unique_ptr<LayerImpl> child = - LayerImpl::Create(host_impl_.active_tree(), 2 + i); + auto child = + std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 2 + i); child->test_properties()->position = gfx::PointF(100.f, 100.f); child->SetBounds(gfx::Size(30, 30)); child->SetDrawsContent(true); @@ -98,16 +127,13 @@ class DamageTrackerTest : public testing::Test { // two children of its own. host_impl_.active_tree()->DetachLayers(); - std::unique_ptr<LayerImpl> root = - LayerImpl::Create(host_impl_.active_tree(), 1); - std::unique_ptr<LayerImpl> child1 = - LayerImpl::Create(host_impl_.active_tree(), 2); - std::unique_ptr<LayerImpl> child2 = - LayerImpl::Create(host_impl_.active_tree(), 3); - std::unique_ptr<LayerImpl> grand_child1 = - LayerImpl::Create(host_impl_.active_tree(), 4); - std::unique_ptr<LayerImpl> grand_child2 = - LayerImpl::Create(host_impl_.active_tree(), 5); + auto root = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 1); + auto child1 = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 2); + auto child2 = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 3); + auto grand_child1 = + std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 4); + auto grand_child2 = + std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 5); root->SetBounds(gfx::Size(500, 500)); root->SetDrawsContent(true); @@ -276,7 +302,8 @@ TEST_F(DamageTrackerTest, VerifyDamageForUpdateRects) { TEST_F(DamageTrackerTest, VerifyDamageForLayerDamageRects) { LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface(); - LayerImpl* child = root->test_properties()->children[0]; + auto* child = + static_cast<TestLayerImpl*>(root->test_properties()->children[0]); // CASE 1: Adding the layer damage rect should cause the corresponding damage // to the surface. @@ -336,7 +363,8 @@ TEST_F(DamageTrackerTest, VerifyDamageForLayerDamageRects) { TEST_F(DamageTrackerTest, VerifyDamageForLayerUpdateAndDamageRects) { LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface(); - LayerImpl* child = root->test_properties()->children[0]; + auto* child = + static_cast<TestLayerImpl*>(root->test_properties()->children[0]); // CASE 1: Adding the layer damage rect and update rect should cause the // corresponding damage to the surface. @@ -547,9 +575,12 @@ TEST_F(DamageTrackerTest, TransformPropertyChangeNoSurface) { TEST_F(DamageTrackerTest, VerifyDamageForUpdateAndDamageRectsFromContributingContents) { LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces(); - LayerImpl* child1 = root->test_properties()->children[0]; - LayerImpl* child2 = root->test_properties()->children[1]; - LayerImpl* grandchild1 = child1->test_properties()->children[0]; + auto* child1 = + static_cast<TestLayerImpl*>(root->test_properties()->children[0]); + auto* child2 = + static_cast<TestLayerImpl*>(root->test_properties()->children[1]); + auto* grandchild1 = + static_cast<TestLayerImpl*>(child1->test_properties()->children[0]); // CASE 1: Adding the layer1's damage rect and update rect should cause the // corresponding damage to the surface. @@ -1912,8 +1943,10 @@ TEST_F(DamageTrackerTest, DamageRectTooBigWithFilter) { TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) { LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces(); LayerImpl* child1 = root->test_properties()->children[0]; - LayerImpl* grandchild1 = child1->test_properties()->children[0]; - LayerImpl* grandchild2 = child1->test_properties()->children[1]; + auto* grandchild1 = + static_cast<TestLayerImpl*>(child1->test_properties()->children[0]); + auto* grandchild2 = + static_cast<TestLayerImpl*>(child1->test_properties()->children[1]); // Really far left. grandchild1->test_properties()->position = @@ -2002,8 +2035,10 @@ TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) { TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurfaceWithFilter) { LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces(); LayerImpl* child1 = root->test_properties()->children[0]; - LayerImpl* grandchild1 = child1->test_properties()->children[0]; - LayerImpl* grandchild2 = child1->test_properties()->children[1]; + auto* grandchild1 = + static_cast<TestLayerImpl*>(child1->test_properties()->children[0]); + auto* grandchild2 = + static_cast<TestLayerImpl*>(child1->test_properties()->children[1]); // Set up a moving pixels filter on the child. FilterOperations filters; diff --git a/chromium/cc/trees/draw_property_utils.cc b/chromium/cc/trees/draw_property_utils.cc index 9c1bec65c92..cf566cb9b6e 100644 --- a/chromium/cc/trees/draw_property_utils.cc +++ b/chromium/cc/trees/draw_property_utils.cc @@ -838,8 +838,17 @@ void FindLayersThatNeedUpdates(LayerTreeImpl* layer_tree_impl, } void ComputeTransforms(TransformTree* transform_tree) { - if (!transform_tree->needs_update()) + if (!transform_tree->needs_update()) { +#if DCHECK_IS_ON() + // If the transform tree does not need an update, no TransformNode should + // need a local transform update. + for (int i = TransformTree::kContentsRootNodeId; + i < static_cast<int>(transform_tree->size()); ++i) { + DCHECK(!transform_tree->Node(i)->needs_local_transform_update); + } +#endif return; + } for (int i = TransformTree::kContentsRootNodeId; i < static_cast<int>(transform_tree->size()); ++i) transform_tree->UpdateTransforms(i); @@ -872,22 +881,11 @@ void UpdatePropertyTrees(LayerTreeHost* layer_tree_host, } void UpdatePropertyTreesAndRenderSurfaces(LayerImpl* root_layer, - PropertyTrees* property_trees, - bool can_adjust_raster_scales) { - bool render_surfaces_need_update = false; - if (property_trees->can_adjust_raster_scales != can_adjust_raster_scales) { - property_trees->can_adjust_raster_scales = can_adjust_raster_scales; - property_trees->transform_tree.set_needs_update(true); - render_surfaces_need_update = true; - } + PropertyTrees* property_trees) { if (property_trees->transform_tree.needs_update()) { property_trees->clip_tree.set_needs_update(true); property_trees->effect_tree.set_needs_update(true); } - if (render_surfaces_need_update) { - property_trees->effect_tree.UpdateRenderSurfaces( - root_layer->layer_tree_impl()); - } UpdateRenderTarget(&property_trees->effect_tree); ComputeTransforms(&property_trees->transform_tree); diff --git a/chromium/cc/trees/draw_property_utils.h b/chromium/cc/trees/draw_property_utils.h index c37eaa0c066..6ca984986bd 100644 --- a/chromium/cc/trees/draw_property_utils.h +++ b/chromium/cc/trees/draw_property_utils.h @@ -44,8 +44,7 @@ void CC_EXPORT UpdatePropertyTrees(LayerTreeHost* layer_tree_host, void CC_EXPORT UpdatePropertyTreesAndRenderSurfaces(LayerImpl* root_layer, - PropertyTrees* property_trees, - bool can_adjust_raster_scales); + PropertyTrees* property_trees); void CC_EXPORT FindLayersThatNeedUpdates(LayerTreeHost* layer_tree_host, const PropertyTrees* property_trees, diff --git a/chromium/cc/trees/effect_node.cc b/chromium/cc/trees/effect_node.cc index 8c7a016eee4..847432950b3 100644 --- a/chromium/cc/trees/effect_node.cc +++ b/chromium/cc/trees/effect_node.cc @@ -57,6 +57,7 @@ bool EffectNode::operator==(const EffectNode& other) const { filters == other.filters && backdrop_filters == other.backdrop_filters && backdrop_filter_bounds == other.backdrop_filter_bounds && + backdrop_mask_element_id == other.backdrop_mask_element_id && filters_origin == other.filters_origin && rounded_corner_bounds == other.rounded_corner_bounds && is_fast_rounded_corner == other.is_fast_rounded_corner && @@ -142,10 +143,15 @@ const char* RenderSurfaceReasonToString(RenderSurfaceReason reason) { } void EffectNode::AsValueInto(base::trace_event::TracedValue* value) const { + value->SetInteger("backdrop_mask_element_id", + backdrop_mask_element_id.GetInternalValue()); value->SetInteger("id", id); value->SetInteger("parent_id", parent_id); value->SetInteger("stable_id", stable_id); value->SetDouble("opacity", opacity); + if (!backdrop_filters.IsEmpty()) { + value->SetString("backdrop_filters", backdrop_filters.ToString()); + } value->SetDouble("backdrop_filter_quality", backdrop_filter_quality); value->SetBoolean("is_fast_rounded_corner", is_fast_rounded_corner); if (!rounded_corner_bounds.IsEmpty()) { diff --git a/chromium/cc/trees/effect_node.h b/chromium/cc/trees/effect_node.h index 340305e9355..eaddb83fb4e 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/paint/element_id.h" #include "cc/paint/filter_operations.h" #include "third_party/skia/include/core/SkBlendMode.h" #include "ui/gfx/geometry/point_f.h" @@ -40,6 +41,7 @@ enum class RenderSurfaceReason : uint8_t { kTrilinearFiltering, kCache, kCopyRequest, + kMirrored, // This must be the last value because it's used in tracing code to know the // number of reasons. kTest, @@ -73,6 +75,11 @@ struct CC_EXPORT EffectNode { float backdrop_filter_quality; gfx::PointF filters_origin; + // The element id corresponding to the mask to apply to the filtered backdrop + // image. Note that this is separate from mask_layer_id, which is a layer id, + // and is used for masking the "normal" (non-backdrop-filter) content. + ElementId backdrop_mask_element_id; + // Bounds of rounded corner rrect in the space of the transform node // associated with this effect node. gfx::RRectF rounded_corner_bounds; diff --git a/chromium/cc/trees/frame_sequence_tracker.cc b/chromium/cc/trees/frame_sequence_tracker.cc new file mode 100644 index 00000000000..a4020409e7a --- /dev/null +++ b/chromium/cc/trees/frame_sequence_tracker.cc @@ -0,0 +1,390 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/trees/frame_sequence_tracker.h" + +#include "base/metrics/histogram.h" +#include "base/metrics/histogram_macros.h" +#include "base/strings/strcat.h" +#include "base/trace_event/trace_event.h" +#include "base/trace_event/traced_value.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" +#include "components/viz/common/quads/compositor_frame_metadata.h" +#include "ui/gfx/presentation_feedback.h" + +namespace cc { + +namespace { + +enum class ThreadType { + kMain, + kCompositor, +}; + +constexpr const char* const kBuiltinSequences[] = { + [FrameSequenceTrackerType::kCompositorAnimation] = "CompositorAnimation", + [FrameSequenceTrackerType::kMainThreadAnimation] = "MainThreadAnimation", + [FrameSequenceTrackerType::kPinchZoom] = "PinchZoom", + [FrameSequenceTrackerType::kRAF] = "RAF", + [FrameSequenceTrackerType::kTouchScroll] = "TouchScroll", + [FrameSequenceTrackerType::kWheelScroll] = "WheelScroll", +}; + +constexpr int kBuiltinSequenceNum = base::size(kBuiltinSequences); +constexpr int kMaximumHistogramIndex = 2 * kBuiltinSequenceNum; + +int GetIndexForMetric(ThreadType thread_type, FrameSequenceTrackerType type) { + return thread_type == ThreadType::kMain + ? static_cast<int>(type) + : static_cast<int>(type + kBuiltinSequenceNum); +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// FrameSequenceTrackerCollection + +FrameSequenceTrackerCollection::FrameSequenceTrackerCollection() {} +FrameSequenceTrackerCollection::~FrameSequenceTrackerCollection() { + removal_trackers_.clear(); + DCHECK(frame_trackers_.empty()); +} + +std::unique_ptr<FrameSequenceTracker> +FrameSequenceTrackerCollection::CreateTracker(FrameSequenceTrackerType type) { + // The collection always outlives the trackers. So using Unretained() here is + // safe. + auto tracker = base::WrapUnique(new FrameSequenceTracker( + type, base::BindOnce(&FrameSequenceTrackerCollection::RemoveFrameTracker, + base::Unretained(this)))); + AddFrameTracker(tracker.get()); + return tracker; +} + +void FrameSequenceTrackerCollection::ScheduleRemoval( + std::unique_ptr<FrameSequenceTracker> tracker) { + if (!tracker) + return; + tracker->ScheduleTerminate(); + removal_trackers_.push_back(std::move(tracker)); +} + +void FrameSequenceTrackerCollection::ClearAll() { + removal_trackers_.clear(); + DCHECK(frame_trackers_.empty()); +} + +void FrameSequenceTrackerCollection::NotifyBeginImplFrame( + const viz::BeginFrameArgs& args) { + for (auto* tracker : frame_trackers_) { + tracker->ReportBeginImplFrame(args); + } +} + +void FrameSequenceTrackerCollection::NotifyBeginMainFrame( + const viz::BeginFrameArgs& args) { + for (auto* tracker : frame_trackers_) { + tracker->ReportBeginMainFrame(args); + } +} + +void FrameSequenceTrackerCollection::NotifyImplFrameCausedNoDamage( + const viz::BeginFrameAck& ack) { + for (auto* tracker : frame_trackers_) { + tracker->ReportImplFrameCausedNoDamage(ack); + } +} + +void FrameSequenceTrackerCollection::NotifyMainFrameCausedNoDamage( + const viz::BeginFrameArgs& args) { + for (auto* tracker : frame_trackers_) { + tracker->ReportMainFrameCausedNoDamage(args); + } +} + +void FrameSequenceTrackerCollection::NotifyPauseFrameProduction() { + for (auto* tracker : frame_trackers_) + tracker->PauseFrameProduction(); +} + +void FrameSequenceTrackerCollection::NotifySubmitFrame( + uint32_t frame_token, + const viz::BeginFrameAck& ack, + const viz::BeginFrameArgs& origin_args) { + for (auto* tracker : frame_trackers_) { + tracker->ReportSubmitFrame(frame_token, ack, origin_args); + } +} + +void FrameSequenceTrackerCollection::NotifyFramePresented( + uint32_t frame_token, + const gfx::PresentationFeedback& feedback) { + for (auto* tracker : frame_trackers_) + tracker->ReportFramePresented(frame_token, feedback); + + // Destroy the trackers that are ready to be terminated. + base::EraseIf( + removal_trackers_, + [](const std::unique_ptr<FrameSequenceTracker>& tracker) { + return tracker->termination_status() == + FrameSequenceTracker::TerminationStatus::kReadyForTermination; + }); +} + +void FrameSequenceTrackerCollection::AddFrameTracker( + FrameSequenceTracker* tracker) { + frame_trackers_.push_back(tracker); +} + +void FrameSequenceTrackerCollection::RemoveFrameTracker( + FrameSequenceTracker* tracker) { + base::Erase(frame_trackers_, tracker); +} + +//////////////////////////////////////////////////////////////////////////////// +// FrameSequenceTracker + +FrameSequenceTracker::FrameSequenceTracker( + FrameSequenceTrackerType type, + base::OnceCallback<void(FrameSequenceTracker*)> destroy_callback) + : type_(type), destroy_callback_(std::move(destroy_callback)) { + DCHECK_LT(type_, FrameSequenceTrackerType::kMaxType); + TRACE_EVENT_ASYNC_BEGIN1("cc,benchmark", "FrameSequenceTracker", this, "name", + TRACE_STR_COPY(kBuiltinSequences[type_])); +} + +FrameSequenceTracker::~FrameSequenceTracker() { + DCHECK_LE(impl_throughput_.frames_produced, impl_throughput_.frames_expected); + DCHECK_LE(main_throughput_.frames_produced, main_throughput_.frames_expected); + DCHECK_LE(main_throughput_.frames_produced, impl_throughput_.frames_produced); + TRACE_EVENT_ASYNC_END1( + "cc,benchmark", "FrameSequenceTracker", this, "args", + ThroughputData::ToTracedValue(impl_throughput_, main_throughput_)); + ThroughputData::ReportHistogram( + type_, "CompositorThread", + GetIndexForMetric(ThreadType::kCompositor, type_), impl_throughput_); + ThroughputData::ReportHistogram(type_, "MainThread", + GetIndexForMetric(ThreadType::kMain, type_), + main_throughput_); + std::move(destroy_callback_).Run(this); +} + +void FrameSequenceTracker::ReportBeginImplFrame( + const viz::BeginFrameArgs& args) { + if (termination_status_ != TerminationStatus::kActive) + return; + + if (ShouldIgnoreBeginFrameSource(args.source_id)) + return; + + UpdateTrackedFrameData(&begin_impl_frame_data_, args.source_id, + args.sequence_number); + impl_throughput_.frames_expected += + begin_impl_frame_data_.previous_sequence_delta; +} + +void FrameSequenceTracker::ReportBeginMainFrame( + const viz::BeginFrameArgs& args) { + if (termination_status_ != TerminationStatus::kActive) + return; + + if (ShouldIgnoreBeginFrameSource(args.source_id)) + return; + + UpdateTrackedFrameData(&begin_main_frame_data_, args.source_id, + args.sequence_number); + if (first_received_main_sequence_ == 0) + first_received_main_sequence_ = args.sequence_number; + main_throughput_.frames_expected += + begin_main_frame_data_.previous_sequence_delta; +} + +void FrameSequenceTracker::ReportSubmitFrame( + uint32_t frame_token, + const viz::BeginFrameAck& ack, + const viz::BeginFrameArgs& origin_args) { + if (termination_status_ != TerminationStatus::kActive) + return; + + if (ShouldIgnoreBeginFrameSource(ack.source_id)) + return; + + if (begin_impl_frame_data_.previous_sequence == 0 || + ack.sequence_number < begin_impl_frame_data_.previous_sequence) { + return; + } + + if (first_submitted_frame_ == 0) + first_submitted_frame_ = frame_token; + last_submitted_frame_ = frame_token; + + if (!ShouldIgnoreBeginFrameSource(origin_args.source_id) && + first_received_main_sequence_ && + origin_args.sequence_number >= first_received_main_sequence_) { + if (last_submitted_main_sequence_ == 0 || + origin_args.sequence_number > last_submitted_main_sequence_) { + last_submitted_main_sequence_ = origin_args.sequence_number; + main_frames_.push_back(frame_token); + DCHECK_GE(main_throughput_.frames_expected, main_frames_.size()); + } + } +} + +void FrameSequenceTracker::ReportFramePresented( + uint32_t frame_token, + const gfx::PresentationFeedback& feedback) { + const bool frame_token_acks_last_frame = + frame_token == last_submitted_frame_ || + viz::FrameTokenGT(frame_token, last_submitted_frame_); + + // Update termination status if this is scheduled for termination, and it is + // not waiting for any frames, or it has received the presentation-feedback + // for the latest frame it is tracking. + if (termination_status_ == TerminationStatus::kScheduledForTermination && + (last_submitted_frame_ == 0 || frame_token_acks_last_frame)) { + termination_status_ = TerminationStatus::kReadyForTermination; + } + + if (first_submitted_frame_ == 0 || + viz::FrameTokenGT(first_submitted_frame_, frame_token)) { + // We are getting presentation feedback for frames that were submitted + // before this sequence started. So ignore these. + return; + } + + const bool was_presented = !feedback.timestamp.is_null(); + if (was_presented && last_submitted_frame_) { + DCHECK_LT(impl_throughput_.frames_produced, + impl_throughput_.frames_expected); + ++impl_throughput_.frames_produced; + + if (frame_token_acks_last_frame) + last_submitted_frame_ = 0; + } + + while (!main_frames_.empty() && + !viz::FrameTokenGT(main_frames_.front(), frame_token)) { + if (was_presented && main_frames_.front() == frame_token) { + DCHECK_LT(main_throughput_.frames_produced, + main_throughput_.frames_expected); + ++main_throughput_.frames_produced; + } + main_frames_.pop_front(); + } +} + +void FrameSequenceTracker::ReportImplFrameCausedNoDamage( + const viz::BeginFrameAck& ack) { + if (termination_status_ != TerminationStatus::kActive) + return; + + if (ShouldIgnoreBeginFrameSource(ack.source_id)) + return; + + // It is possible that this is called before a begin-impl-frame has been + // dispatched for this frame-sequence. In such cases, ignore this call. + if (begin_impl_frame_data_.previous_sequence == 0 || + ack.sequence_number < begin_impl_frame_data_.previous_sequence) { + return; + } + DCHECK_GT(impl_throughput_.frames_expected, 0u); + DCHECK_GT(impl_throughput_.frames_expected, impl_throughput_.frames_produced); + --impl_throughput_.frames_expected; + + if (begin_impl_frame_data_.previous_sequence == ack.sequence_number) + begin_impl_frame_data_.previous_sequence = 0; +} + +void FrameSequenceTracker::ReportMainFrameCausedNoDamage( + const viz::BeginFrameArgs& args) { + if (termination_status_ != TerminationStatus::kActive) + return; + + if (ShouldIgnoreBeginFrameSource(args.source_id)) + return; + + // It is possible that this is called before a begin-main-frame has been + // dispatched for this frame-sequence. In such cases, ignore this call. + if (begin_main_frame_data_.previous_sequence == 0 || + args.sequence_number < begin_main_frame_data_.previous_sequence) { + return; + } + + DCHECK_GT(main_throughput_.frames_expected, 0u); + DCHECK_GT(main_throughput_.frames_expected, main_throughput_.frames_produced); + --main_throughput_.frames_expected; + DCHECK_GE(main_throughput_.frames_expected, main_frames_.size()); + + if (begin_main_frame_data_.previous_sequence == args.sequence_number) + begin_main_frame_data_.previous_sequence = 0; +} + +void FrameSequenceTracker::PauseFrameProduction() { + // Reset the states, so that the tracker ignores the vsyncs until the next + // received begin-frame. + begin_impl_frame_data_ = {0, 0, 0}; + begin_main_frame_data_ = {0, 0, 0}; +} + +void FrameSequenceTracker::UpdateTrackedFrameData(TrackedFrameData* frame_data, + uint64_t source_id, + uint64_t sequence_number) { + if (frame_data->previous_sequence && + frame_data->previous_source == source_id) { + uint8_t current_latency = sequence_number - frame_data->previous_sequence; + frame_data->previous_sequence_delta = current_latency; + } else { + frame_data->previous_sequence_delta = 1; + } + frame_data->previous_source = source_id; + frame_data->previous_sequence = sequence_number; +} + +bool FrameSequenceTracker::ShouldIgnoreBeginFrameSource( + uint64_t source_id) const { + if (begin_impl_frame_data_.previous_source == 0) + return false; + return source_id != begin_impl_frame_data_.previous_source; +} + +std::unique_ptr<base::trace_event::TracedValue> +FrameSequenceTracker::ThroughputData::ToTracedValue( + const ThroughputData& impl, + const ThroughputData& main) { + auto dict = std::make_unique<base::trace_event::TracedValue>(); + dict->SetInteger("impl-frames-produced", impl.frames_produced); + dict->SetInteger("impl-frames-expected", impl.frames_expected); + dict->SetInteger("main-frames-produced", main.frames_produced); + dict->SetInteger("main-frames-expected", main.frames_expected); + return dict; +} + +void FrameSequenceTracker::ThroughputData::ReportHistogram( + FrameSequenceTrackerType sequence_type, + const char* thread_name, + int metric_index, + const ThroughputData& data) { + DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType); + + UMA_HISTOGRAM_COUNTS_1000("Graphics.Smoothness.FrameSequenceLength", + data.frames_expected); + + // Avoid reporting any throughput metric for sequences that had a small amount + // of frames. + constexpr int kMinFramesForThroughputMetric = 4; + if (data.frames_expected < kMinFramesForThroughputMetric) + return; + + const std::string name = + base::StrCat({"Graphics.Smoothness.Throughput.", thread_name, ".", + kBuiltinSequences[sequence_type]}); + const int percent = + static_cast<int>(100 * data.frames_produced / data.frames_expected); + STATIC_HISTOGRAM_POINTER_GROUP( + name, metric_index, kMaximumHistogramIndex, Add(percent), + base::LinearHistogram::FactoryGet( + name, 1, 100, 101, base::HistogramBase::kUmaTargetedHistogramFlag)); +} + +} // namespace cc diff --git a/chromium/cc/trees/frame_sequence_tracker.h b/chromium/cc/trees/frame_sequence_tracker.h new file mode 100644 index 00000000000..57b370eb316 --- /dev/null +++ b/chromium/cc/trees/frame_sequence_tracker.h @@ -0,0 +1,238 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_TREES_FRAME_SEQUENCE_TRACKER_H_ +#define CC_TREES_FRAME_SEQUENCE_TRACKER_H_ + +#include <stdint.h> +#include <memory> +#include <set> +#include <utility> +#include <vector> + +#include "base/callback_helpers.h" +#include "base/containers/circular_deque.h" +#include "base/macros.h" +#include "base/trace_event/traced_value.h" +#include "cc/cc_export.h" + +namespace gfx { +struct PresentationFeedback; +} + +namespace viz { +struct BeginFrameAck; +struct BeginFrameArgs; +} // namespace viz + +namespace cc { +class FrameSequenceTracker; + +enum FrameSequenceTrackerType { + kCompositorAnimation, + kMainThreadAnimation, + kPinchZoom, + kRAF, + kTouchScroll, + kWheelScroll, + kMaxType +}; + +// Used for notifying attached FrameSequenceTracker's of begin-frames and +// submitted frames. +class CC_EXPORT FrameSequenceTrackerCollection { + public: + FrameSequenceTrackerCollection(); + ~FrameSequenceTrackerCollection(); + + FrameSequenceTrackerCollection(const FrameSequenceTrackerCollection&) = + delete; + FrameSequenceTrackerCollection& operator=( + const FrameSequenceTrackerCollection&) = delete; + + // Creates a tracker for the specified sequence-type. + std::unique_ptr<FrameSequenceTracker> CreateTracker( + FrameSequenceTrackerType type); + + // Schedules |tracker| for destruction. This is preferred instead of outright + // desrtruction of the tracker, since this ensures that the actual tracker + // instance is destroyed *after* the presentation-feedbacks have been received + // for all submitted frames. + void ScheduleRemoval(std::unique_ptr<FrameSequenceTracker> tracker); + + // Removes all trackers. This also immediately destroys all trackers that had + // been scheduled for destruction, even if there are pending + // presentation-feedbacks. This is typically used if the client no longer + // expects to receive presentation-feedbacks for the previously submitted + // frames (e.g. when the gpu process dies). + void ClearAll(); + + // Notifies all trackers of various events. + void NotifyBeginImplFrame(const viz::BeginFrameArgs& args); + void NotifyBeginMainFrame(const viz::BeginFrameArgs& args); + void NotifyImplFrameCausedNoDamage(const viz::BeginFrameAck& ack); + void NotifyMainFrameCausedNoDamage(const viz::BeginFrameArgs& args); + void NotifyPauseFrameProduction(); + void NotifySubmitFrame(uint32_t frame_token, + const viz::BeginFrameAck& ack, + const viz::BeginFrameArgs& origin_args); + + // Note that this notifies the trackers of the presentation-feedbacks, and + // destroys any tracker that had been scheduled for destruction (using + // |ScheduleRemoval()|) if it has no more pending frames. + void NotifyFramePresented(uint32_t frame_token, + const gfx::PresentationFeedback& feedback); + + private: + void AddFrameTracker(FrameSequenceTracker* tracker); + void RemoveFrameTracker(FrameSequenceTracker* tracker); + + std::vector<FrameSequenceTracker*> frame_trackers_; + std::vector<std::unique_ptr<FrameSequenceTracker>> removal_trackers_; +}; + +// Tracks a sequence of frames to determine the throughput. It tracks this by +// tracking the vsync sequence-numbers (from |BeginFrameArgs::sequence_number|), +// and the presentation-timestamps (from |gfx::PresentationFeedback|). It also +// tracks which frames were expected to include update from the main-thread, and +// which presented frames did include updates from the main-thread. +// This object should be created through +// FrameSequenceTrackerCollection::CreateTracker() API. +class CC_EXPORT FrameSequenceTracker { + public: + enum class TerminationStatus { + kActive, + kScheduledForTermination, + kReadyForTermination, + }; + + ~FrameSequenceTracker(); + + FrameSequenceTracker(const FrameSequenceTracker&) = delete; + FrameSequenceTracker& operator=(const FrameSequenceTracker&) = delete; + + // Notifies the tracker when the compositor thread starts to process a + // BeginFrameArgs. + void ReportBeginImplFrame(const viz::BeginFrameArgs& args); + + // Notifies the tracker when a BeginFrameArgs is dispatched to the main + // thread. + void ReportBeginMainFrame(const viz::BeginFrameArgs& args); + + // Notifies the tracker when the compositor submits a CompositorFrame. + // |origin_args| represents the BeginFrameArgs that triggered the update from + // the main-thread. + void ReportSubmitFrame(uint32_t frame_token, + const viz::BeginFrameAck& ack, + const viz::BeginFrameArgs& origin_args); + + // Notifies the tracker of the presentation-feedback of a previously submitted + // CompositorFrame with |frame_token|. + void ReportFramePresented(uint32_t frame_token, + const gfx::PresentationFeedback& feedback); + + // Notifies the tracker that a CompositorFrame is not going to be submitted + // for a particular BeginFrameArgs because it did not cause any damage (visual + // change). Note that if a begin-main-frame was dispatched, then a separate + // call to |ReportMainFrameCausedNoDamage()| is made to notify that the + // main-thread did not cause any damage/updates. + void ReportImplFrameCausedNoDamage(const viz::BeginFrameAck& ack); + + // Notifies the tracker that a |BeginFrameArgs| either was not dispatched to + // the main-thread (because it did not ask for it), or that a |BeginFrameArgs| + // that was dispatched to the main-thread did not cause any updates/damage. + void ReportMainFrameCausedNoDamage(const viz::BeginFrameArgs& args); + + // Notifies that frame production has currently paused. This is typically used + // for interactive frame-sequences, e.g. during touch-scroll. + void PauseFrameProduction(); + + TerminationStatus termination_status() const { return termination_status_; } + + private: + friend class FrameSequenceTrackerCollection; + + FrameSequenceTracker( + FrameSequenceTrackerType type, + base::OnceCallback<void(FrameSequenceTracker*)> destroy_callback); + + void ScheduleTerminate() { + termination_status_ = TerminationStatus::kScheduledForTermination; + } + + struct TrackedFrameData { + // Represents the |BeginFrameArgs::source_id| and + // |BeginFrameArgs::sequence_number| fields of the last processed + // BeginFrameArgs. + uint64_t previous_source = 0; + uint64_t previous_sequence = 0; + + // The difference in |BeginFrameArgs::sequence_number| fields of the last + // two processed BeginFrameArgs. + uint8_t previous_sequence_delta = 0; + }; + + struct ThroughputData { + static std::unique_ptr<base::trace_event::TracedValue> ToTracedValue( + const ThroughputData& impl, + const ThroughputData& main); + static void ReportHistogram(FrameSequenceTrackerType sequence_type, + const char* thread_name, + int metric_index, + const ThroughputData& data); + // Tracks the number of frames that were expected to be shown during this + // frame-sequence. + uint32_t frames_expected = 0; + + // Tracks the number of frames that were actually presented to the user + // during this frame-sequence. + uint32_t frames_produced = 0; + }; + + void UpdateTrackedFrameData(TrackedFrameData* frame_data, + uint64_t source_id, + uint64_t sequence_number); + + bool ShouldIgnoreBeginFrameSource(uint64_t source_id) const; + + const FrameSequenceTrackerType type_; + base::OnceCallback<void(FrameSequenceTracker*)> destroy_callback_; + + TerminationStatus termination_status_ = TerminationStatus::kActive; + + TrackedFrameData begin_impl_frame_data_; + TrackedFrameData begin_main_frame_data_; + + ThroughputData impl_throughput_; + ThroughputData main_throughput_; + + // Tracks the list of frame-tokens for compositor-frames that included new + // updates from the main-thread, whose presentation-feedback have not been + // received yet. When the presentation-feedback for a frame is received, the + // corresponding frame-token is removed from this collection. + base::circular_deque<uint32_t> main_frames_; + + // Keeps track of the sequence-number of the first received begin-main-frame. + // This is used to ignore submitted frames that include updates from earlier + // begin-main-frames. + uint64_t first_received_main_sequence_ = 0; + + // Keeps track of the first submitted compositor-frame. This is used to ignore + // reports from frames that were submitted before this tracker had been + // created. + uint32_t first_submitted_frame_ = 0; + + // Keeps track of the latest submitted compositor-frame, so that it can + // determine when it has received presentation-feedback for submitted frames. + // This is used to decide when to terminate this FrameSequenceTracker object. + uint32_t last_submitted_frame_ = 0; + + // Keeps track of the last sequence-number that produced a frame from the + // main-thread. + uint64_t last_submitted_main_sequence_ = 0; +}; + +} // namespace cc + +#endif // CC_TREES_FRAME_SEQUENCE_TRACKER_H_ diff --git a/chromium/cc/trees/frame_sequence_tracker_unittest.cc b/chromium/cc/trees/frame_sequence_tracker_unittest.cc new file mode 100644 index 00000000000..1de4c9c38a9 --- /dev/null +++ b/chromium/cc/trees/frame_sequence_tracker_unittest.cc @@ -0,0 +1,109 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/trees/frame_sequence_tracker.h" + +#include "base/macros.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +class FrameSequenceTrackerTest; + +class FrameSequenceTrackerTest : public testing::Test { + public: + const uint32_t kImplDamage = 0x1; + const uint32_t kMainDamage = 0x2; + + FrameSequenceTrackerTest() + : tracker_( + collection_.CreateTracker(FrameSequenceTrackerType::kTouchScroll)) { + } + ~FrameSequenceTrackerTest() override = default; + + std::unique_ptr<FrameSequenceTracker> CreateNewTracker() { + return collection_.CreateTracker(FrameSequenceTrackerType::kTouchScroll); + } + + viz::BeginFrameArgs CreateBeginFrameArgs(uint64_t source_id, + uint64_t sequence_number) { + auto now = base::TimeTicks::Now(); + auto interval = base::TimeDelta::FromMilliseconds(16); + auto deadline = now + interval; + return viz::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, source_id, + sequence_number, now, deadline, interval, + viz::BeginFrameArgs::NORMAL); + } + + void StartImplAndMainFrames(const viz::BeginFrameArgs& args) { + collection_.NotifyBeginImplFrame(args); + collection_.NotifyBeginMainFrame(args); + } + + uint32_t DispatchCompleteFrame(const viz::BeginFrameArgs& args, + uint32_t damage_type) { + StartImplAndMainFrames(args); + + if (damage_type & kImplDamage) { + if (!(damage_type & kMainDamage)) { + collection_.NotifyMainFrameCausedNoDamage(args); + } + uint32_t frame_token = NextFrameToken(); + collection_.NotifySubmitFrame(frame_token, viz::BeginFrameAck(args, true), + args); + return frame_token; + } else { + collection_.NotifyImplFrameCausedNoDamage( + viz::BeginFrameAck(args, false)); + collection_.NotifyMainFrameCausedNoDamage(args); + } + return 0; + } + + uint32_t NextFrameToken() { + static uint32_t frame_token = 0; + return ++frame_token; + } + + protected: + FrameSequenceTrackerCollection collection_; + + std::unique_ptr<FrameSequenceTracker> tracker_; +}; + +// Tests that the tracker works correctly when the source-id for the +// begin-frames change. +TEST_F(FrameSequenceTrackerTest, SourceIdChangeDuringSequence) { + const uint64_t source_1 = 1; + uint64_t sequence_1 = 0; + + // Dispatch some frames, both causing damage to impl/main, and both impl and + // main providing damage to the frame. + auto args_1 = CreateBeginFrameArgs(source_1, ++sequence_1); + DispatchCompleteFrame(args_1, kImplDamage | kMainDamage); + args_1 = CreateBeginFrameArgs(source_1, ++sequence_1); + DispatchCompleteFrame(args_1, kImplDamage | kMainDamage); + + // Start a new tracker. + auto tracker = CreateNewTracker(); + + // Change the source-id, and start an impl frame. This time, the main-frame + // does not provide any damage. + const uint64_t source_2 = 2; + uint64_t sequence_2 = 0; + auto args_2 = CreateBeginFrameArgs(source_2, ++sequence_2); + collection_.NotifyBeginImplFrame(args_2); + collection_.NotifyBeginMainFrame(args_2); + collection_.NotifyMainFrameCausedNoDamage(args_2); + // Since the main-frame did not have any new damage from the latest + // BeginFrameArgs, the submit-frame will carry the previous BeginFrameArgs + // (from source_1); + collection_.NotifySubmitFrame(NextFrameToken(), + viz::BeginFrameAck(args_2, true), args_1); +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/trees/image_animation_controller.cc b/chromium/cc/trees/image_animation_controller.cc index eb92e3b4f5f..a7e1ebf6de6 100644 --- a/chromium/cc/trees/image_animation_controller.cc +++ b/chromium/cc/trees/image_animation_controller.cc @@ -454,7 +454,7 @@ size_t ImageAnimationController::AnimationState::NextFrameIndex() const { ImageAnimationController::InvalidationScheduler::InvalidationScheduler( base::SingleThreadTaskRunner* task_runner, Client* client) - : task_runner_(task_runner), client_(client), weak_factory_(this) { + : task_runner_(task_runner), client_(client) { DCHECK(task_runner_->BelongsToCurrentThread()); } diff --git a/chromium/cc/trees/image_animation_controller.h b/chromium/cc/trees/image_animation_controller.h index a95ec12448e..1f118704810 100644 --- a/chromium/cc/trees/image_animation_controller.h +++ b/chromium/cc/trees/image_animation_controller.h @@ -267,7 +267,7 @@ class CC_EXPORT ImageAnimationController { // The time at which the next animation is expected to run. base::TimeTicks next_animation_time_; - base::WeakPtrFactory<InvalidationScheduler> weak_factory_; + base::WeakPtrFactory<InvalidationScheduler> weak_factory_{this}; }; // The AnimationState for images is persisted until they are cleared on diff --git a/chromium/cc/trees/layer_tree_frame_sink.cc b/chromium/cc/trees/layer_tree_frame_sink.cc index 71ed2774860..34ac5b7dfa5 100644 --- a/chromium/cc/trees/layer_tree_frame_sink.cc +++ b/chromium/cc/trees/layer_tree_frame_sink.cc @@ -48,8 +48,7 @@ LayerTreeFrameSink::LayerTreeFrameSink( : context_provider_(std::move(context_provider)), worker_context_provider_(std::move(worker_context_provider)), compositor_task_runner_(std::move(compositor_task_runner)), - gpu_memory_buffer_manager_(gpu_memory_buffer_manager), - weak_ptr_factory_(this) { + gpu_memory_buffer_manager_(gpu_memory_buffer_manager) { DETACH_FROM_THREAD(thread_checker_); } diff --git a/chromium/cc/trees/layer_tree_frame_sink.h b/chromium/cc/trees/layer_tree_frame_sink.h index 76634a79771..68b9734737a 100644 --- a/chromium/cc/trees/layer_tree_frame_sink.h +++ b/chromium/cc/trees/layer_tree_frame_sink.h @@ -8,6 +8,7 @@ #include <deque> #include <memory> +#include "base/memory/read_only_shared_memory_region.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" @@ -127,7 +128,7 @@ class CC_EXPORT LayerTreeFrameSink : public viz::SharedBitmapReporter, virtual void DidNotProduceFrame(const viz::BeginFrameAck& ack) = 0; // viz::SharedBitmapReporter implementation. - void DidAllocateSharedBitmap(mojo::ScopedSharedBufferHandle buffer, + void DidAllocateSharedBitmap(base::ReadOnlySharedMemoryRegion region, const viz::SharedBitmapId& id) override = 0; void DidDeleteSharedBitmap(const viz::SharedBitmapId& id) override = 0; @@ -154,7 +155,7 @@ class CC_EXPORT LayerTreeFrameSink : public viz::SharedBitmapReporter, private: THREAD_CHECKER(thread_checker_); - base::WeakPtrFactory<LayerTreeFrameSink> weak_ptr_factory_; + base::WeakPtrFactory<LayerTreeFrameSink> weak_ptr_factory_{this}; }; } // 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 fc298c8fef5..50848593309 100644 --- a/chromium/cc/trees/layer_tree_frame_sink_client.h +++ b/chromium/cc/trees/layer_tree_frame_sink_client.h @@ -68,9 +68,6 @@ class CC_EXPORT LayerTreeFrameSinkClient { // viz::ContextProviders) must be recreated. virtual void DidLoseLayerTreeFrameSink() = 0; - // Notification that the client does not need a new BeginFrame. - virtual void DidNotNeedBeginFrame() = 0; - // For SynchronousCompositor (WebView) to ask the layer compositor to submit // a new CompositorFrame synchronously. virtual void OnDraw(const gfx::Transform& transform, diff --git a/chromium/cc/trees/layer_tree_frame_sink_unittest.cc b/chromium/cc/trees/layer_tree_frame_sink_unittest.cc index 58334b8322f..5eabeb0a5dc 100644 --- a/chromium/cc/trees/layer_tree_frame_sink_unittest.cc +++ b/chromium/cc/trees/layer_tree_frame_sink_unittest.cc @@ -4,6 +4,7 @@ #include "cc/trees/layer_tree_frame_sink.h" +#include "base/memory/read_only_shared_memory_region.h" #include "base/single_thread_task_runner.h" #include "base/test/test_simple_task_runner.h" #include "cc/test/fake_layer_tree_frame_sink_client.h" @@ -34,7 +35,7 @@ class StubLayerTreeFrameSink : public LayerTreeFrameSink { client_->DidReceiveCompositorFrameAck(); } void DidNotProduceFrame(const viz::BeginFrameAck& ack) override {} - void DidAllocateSharedBitmap(mojo::ScopedSharedBufferHandle buffer, + void DidAllocateSharedBitmap(base::ReadOnlySharedMemoryRegion region, const viz::SharedBitmapId& id) override {} void DidDeleteSharedBitmap(const viz::SharedBitmapId& id) override {} }; diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc index 4e9a9393289..5270f43d422 100644 --- a/chromium/cc/trees/layer_tree_host.cc +++ b/chromium/cc/trees/layer_tree_host.cc @@ -131,10 +131,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), - content_source_id_(0), event_listener_properties_(), - mutator_host_(params.mutator_host), - defer_main_frame_update_weak_ptr_factory_(this) { + mutator_host_(params.mutator_host) { DCHECK(task_graph_runner_); DCHECK(!settings_.enable_checker_imaging || image_worker_task_runner_); @@ -392,7 +390,11 @@ void LayerTreeHost::FinishCommitOnImplThread( // Dump property trees and layers if run with: // --vmodule=layer_tree_host=3 if (VLOG_IS_ON(3)) { - VLOG(3) << "After finishing commit on impl, the sync tree:" + const char* client_name = GetClientNameForMetrics(); + if (!client_name) + client_name = "<unknown client>"; + VLOG(3) << "After finishing (" << client_name + << ") commit on impl, the sync tree:" << "\nproperty_trees:\n" << sync_tree->property_trees()->ToString() << "\n" << "cc::LayerImpls:\n" @@ -769,7 +771,7 @@ std::string LayerTreeHost::LayersAsString() const { return layers; } -bool LayerTreeHost::CaptureContent(std::vector<NodeHolder>* content) { +bool LayerTreeHost::CaptureContent(std::vector<NodeId>* content) { if (viewport_visible_rect_.IsEmpty()) return false; @@ -914,15 +916,9 @@ void LayerTreeHost::ApplyViewportChanges(const ScrollAndScaleSet& info) { SetNeedsUpdateLayers(); } -void LayerTreeHost::RecordWheelAndTouchScrollingCount( - const ScrollAndScaleSet& info) { - bool has_scrolled_by_wheel = info.has_scrolled_by_wheel; - bool has_scrolled_by_touch = info.has_scrolled_by_touch; - - if (has_scrolled_by_wheel || has_scrolled_by_touch) { - client_->RecordWheelAndTouchScrollingCount(has_scrolled_by_wheel, - has_scrolled_by_touch); - } +void LayerTreeHost::RecordManipulationTypeCounts( + const ScrollAndScaleSet& scroll_info) { + client_->RecordManipulationTypeCounts(scroll_info.manipulation_info); } void LayerTreeHost::SendOverscrollAndScrollEndEventsFromImplSide( @@ -972,7 +968,7 @@ void LayerTreeHost::ApplyScrollAndScale(ScrollAndScaleSet* info) { // on the main thread. ApplyViewportChanges(*info); - RecordWheelAndTouchScrollingCount(*info); + RecordManipulationTypeCounts(*info); } void LayerTreeHost::RecordStartOfFrameMetrics() { @@ -1343,10 +1339,6 @@ void LayerTreeHost::SetExternalPageScaleFactor( SetNeedsCommit(); } -void LayerTreeHost::SetContentSourceId(uint32_t id) { - content_source_id_ = id; -} - void LayerTreeHost::ClearCachesOnNextCommit() { clear_caches_on_next_commit_ = true; } @@ -1606,8 +1598,6 @@ void LayerTreeHost::PushLayerTreePropertiesTo(LayerTreeImpl* tree_impl) { tree_impl->SetRasterColorSpace(raster_color_space_id_, raster_color_space_); tree_impl->SetExternalPageScaleFactor(external_page_scale_factor_); - tree_impl->set_content_source_id(content_source_id_); - tree_impl->set_painted_device_scale_factor(painted_device_scale_factor_); tree_impl->SetDeviceScaleFactor(device_scale_factor_); tree_impl->SetDeviceViewportSize(device_viewport_size_); diff --git a/chromium/cc/trees/layer_tree_host.h b/chromium/cc/trees/layer_tree_host.h index b7cf9d9af7d..54000cfece0 100644 --- a/chromium/cc/trees/layer_tree_host.h +++ b/chromium/cc/trees/layer_tree_host.h @@ -33,7 +33,7 @@ #include "cc/input/scrollbar.h" #include "cc/layers/layer_collections.h" #include "cc/layers/layer_list_iterator.h" -#include "cc/paint/node_holder.h" +#include "cc/paint/node_id.h" #include "cc/trees/compositor_mode.h" #include "cc/trees/layer_tree_frame_sink.h" #include "cc/trees/layer_tree_host_client.h" @@ -432,9 +432,6 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { return painted_device_scale_factor_; } - void SetContentSourceId(uint32_t); - uint32_t content_source_id() const { return content_source_id_; } - // Clears image caches and resets the scheduling history for the content // produced by this host so far. void ClearCachesOnNextCommit(); @@ -678,8 +675,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { std::string LayersAsString() const; // Captures the on-screen text content, if success, fills the associated - // NodeHolder in |content| and return true, otherwise return false. - bool CaptureContent(std::vector<NodeHolder>* content); + // NodeId in |content| and return true, otherwise return false. + bool CaptureContent(std::vector<NodeId>* content); protected: LayerTreeHost(InitParams params, CompositorMode mode); @@ -722,7 +719,7 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { enum { kNumFramesToConsiderBeforeRemovingSlowPathFlag = 60 }; void ApplyViewportChanges(const ScrollAndScaleSet& info); - void RecordWheelAndTouchScrollingCount(const ScrollAndScaleSet& info); + void RecordManipulationTypeCounts(const ScrollAndScaleSet& scroll_info); void SendOverscrollAndScrollEndEventsFromImplSide( const ScrollAndScaleSet& info); void ApplyPageScaleDeltaFromImplSide(float page_scale_delta); @@ -807,7 +804,6 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { gfx::ColorSpace raster_color_space_; bool clear_caches_on_next_commit_ = false; - uint32_t content_source_id_; viz::LocalSurfaceIdAllocation local_surface_id_allocation_from_parent_; // Used to detect surface invariant violations. bool has_pushed_local_surface_id_from_parent_ = false; @@ -882,7 +878,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient { // Used to vend weak pointers to LayerTreeHost to ScopedDeferMainFrameUpdate // objects. - base::WeakPtrFactory<LayerTreeHost> defer_main_frame_update_weak_ptr_factory_; + base::WeakPtrFactory<LayerTreeHost> defer_main_frame_update_weak_ptr_factory_{ + this}; }; } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_client.h b/chromium/cc/trees/layer_tree_host_client.h index 5ddc975a950..f48af6c6d1e 100644 --- a/chromium/cc/trees/layer_tree_host_client.h +++ b/chromium/cc/trees/layer_tree_host_client.h @@ -53,6 +53,14 @@ struct ApplyViewportChangesArgs { bool scroll_gesture_did_end; }; +using ManipulationInfo = uint32_t; +constexpr ManipulationInfo kManipulationInfoNone = 0; +constexpr ManipulationInfo kManipulationInfoHasScrolledByWheel = 1 << 0; +constexpr ManipulationInfo kManipulationInfoHasScrolledByTouch = 1 << 1; +constexpr ManipulationInfo kManipulationInfoHasScrolledByPrecisionTouchPad = + 1 << 2; +constexpr ManipulationInfo kManipulationInfoHasPinchZoomed = 1 << 3; + // A LayerTreeHost is bound to a LayerTreeHostClient. The main rendering // loop (in ProxyMain or SingleThreadProxy) calls methods on the // LayerTreeHost, which then handles them and also calls into the equivalent @@ -104,9 +112,9 @@ class LayerTreeHostClient { // related to pinch-zoom, browser controls (aka URL bar), overscroll, etc. virtual void ApplyViewportChanges(const ApplyViewportChangesArgs& args) = 0; - virtual void RecordWheelAndTouchScrollingCount( - bool has_scrolled_by_wheel, - bool has_scrolled_by_touch) = 0; + // Record use counts of different methods of scrolling (e.g. wheel, touch, + // precision touchpad, etc.). + virtual void RecordManipulationTypeCounts(ManipulationInfo info) = 0; // Notifies the client when an overscroll has happened. virtual void SendOverscrollEventFromImplSide( diff --git a/chromium/cc/trees/layer_tree_host_common.cc b/chromium/cc/trees/layer_tree_host_common.cc index 6eb7ec07102..7ea7995ee11 100644 --- a/chromium/cc/trees/layer_tree_host_common.cc +++ b/chromium/cc/trees/layer_tree_host_common.cc @@ -81,7 +81,6 @@ LayerTreeHostCommon::CalcDrawPropsImplInputs::CalcDrawPropsImplInputs( const gfx::Vector2dF& elastic_overscroll, const ElementId elastic_overscroll_element_id, int max_texture_size, - bool can_adjust_raster_scales, RenderSurfaceList* render_surface_list, PropertyTrees* property_trees, TransformNode* page_scale_transform_node) @@ -96,7 +95,6 @@ LayerTreeHostCommon::CalcDrawPropsImplInputs::CalcDrawPropsImplInputs( elastic_overscroll(elastic_overscroll), elastic_overscroll_element_id(elastic_overscroll_element_id), max_texture_size(max_texture_size), - can_adjust_raster_scales(can_adjust_raster_scales), render_surface_list(render_surface_list), property_trees(property_trees), page_scale_transform_node(page_scale_transform_node) {} @@ -118,7 +116,6 @@ LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting:: gfx::Vector2dF(), ElementId(), std::numeric_limits<int>::max() / 2, - false, render_surface_list, GetPropertyTrees(root_layer), nullptr) { @@ -181,9 +178,8 @@ ScrollAndScaleSet::ScrollAndScaleSet() top_controls_delta(0.f), browser_controls_constraint(BrowserControlsState::kBoth), browser_controls_constraint_changed(false), - has_scrolled_by_wheel(false), - has_scrolled_by_touch(false), - scroll_gesture_did_end(false) {} + scroll_gesture_did_end(false), + manipulation_info(kManipulationInfoNone) {} ScrollAndScaleSet::~ScrollAndScaleSet() = default; @@ -541,8 +537,7 @@ void CalculateDrawPropertiesInternal( gfx::Rect(inputs->device_viewport_size), inputs->device_transform, inputs->property_trees); draw_property_utils::UpdatePropertyTreesAndRenderSurfaces( - inputs->root_layer, inputs->property_trees, - inputs->can_adjust_raster_scales); + inputs->root_layer, inputs->property_trees); // Property trees are normally constructed on the main thread and // passed to compositor thread. Source to parent updates on them are not @@ -609,8 +604,7 @@ void CalculateDrawPropertiesInternal( inputs->device_scale_factor, page_scale_factor_for_root, inputs->device_transform); draw_property_utils::UpdatePropertyTreesAndRenderSurfaces( - inputs->root_layer, inputs->property_trees, - inputs->can_adjust_raster_scales); + inputs->root_layer, inputs->property_trees); break; } } diff --git a/chromium/cc/trees/layer_tree_host_common.h b/chromium/cc/trees/layer_tree_host_common.h index c27157a9939..07c0bc586c2 100644 --- a/chromium/cc/trees/layer_tree_host_common.h +++ b/chromium/cc/trees/layer_tree_host_common.h @@ -74,7 +74,6 @@ class CC_EXPORT LayerTreeHostCommon { const gfx::Vector2dF& elastic_overscroll, const ElementId elastic_overscroll_element_id, int max_texture_size, - bool can_adjust_raster_scales, RenderSurfaceList* render_surface_list, PropertyTrees* property_trees, TransformNode* page_scale_transform_node); @@ -90,7 +89,6 @@ class CC_EXPORT LayerTreeHostCommon { gfx::Vector2dF elastic_overscroll; const ElementId elastic_overscroll_element_id; int max_texture_size; - bool can_adjust_raster_scales; RenderSurfaceList* render_surface_list; PropertyTrees* property_trees; TransformNode* page_scale_transform_node; @@ -189,12 +187,14 @@ struct CC_EXPORT ScrollAndScaleSet { std::vector<std::unique_ptr<SwapPromise>> swap_promises; BrowserControlsState browser_controls_constraint; bool browser_controls_constraint_changed; - bool has_scrolled_by_wheel; - bool has_scrolled_by_touch; // Set to true when a scroll gesture being handled on the compositor has // ended. bool scroll_gesture_did_end; + + // Tracks different methods of scrolling (e.g. wheel, touch, precision + // touchpad, etc.). + ManipulationInfo manipulation_info; }; template <typename Function> diff --git a/chromium/cc/trees/layer_tree_host_common_perftest.cc b/chromium/cc/trees/layer_tree_host_common_perftest.cc index 2c1c4ab1d82..4cb8bc9e760 100644 --- a/chromium/cc/trees/layer_tree_host_common_perftest.cc +++ b/chromium/cc/trees/layer_tree_host_common_perftest.cc @@ -107,7 +107,6 @@ class CalcDrawPropsTest : public LayerTreeHostCommonPerfTest { active_tree->OuterViewportScrollLayer(), active_tree->elastic_overscroll()->Current(active_tree->IsActiveTree()), active_tree->OverscrollElasticityElementId(), max_texture_size, - host_impl->settings().layer_transforms_should_scale_layer_contents, &update_list, active_tree->property_trees(), active_tree->property_trees()->transform_tree.Node( active_tree->InnerViewportContainerLayer() diff --git a/chromium/cc/trees/layer_tree_host_common_unittest.cc b/chromium/cc/trees/layer_tree_host_common_unittest.cc index 93750755adb..12652e57e9f 100644 --- a/chromium/cc/trees/layer_tree_host_common_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_common_unittest.cc @@ -168,7 +168,6 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { inputs.page_scale_layer = page_scale_layer; inputs.inner_viewport_scroll_layer = inner_viewport_scroll_layer; inputs.outer_viewport_scroll_layer = outer_viewport_scroll_layer; - inputs.can_adjust_raster_scales = true; if (page_scale_layer) { PropertyTrees* property_trees = root_layer->layer_tree_impl()->property_trees(); @@ -236,7 +235,6 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { void ExecuteCalculateDrawPropertiesAndSaveUpdateLayerList( LayerImpl* root_layer) { DCHECK(root_layer->layer_tree_impl()); - bool can_adjust_raster_scales = true; const LayerImpl* page_scale_layer = nullptr; LayerImpl* inner_viewport_scroll_layer = @@ -262,8 +260,8 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { outer_viewport_scroll_layer, overscroll_elasticity_element_id, elastic_overscroll, page_scale_factor, device_scale_factor, gfx::Rect(device_viewport_size), gfx::Transform(), property_trees); - draw_property_utils::UpdatePropertyTreesAndRenderSurfaces( - root_layer, property_trees, can_adjust_raster_scales); + draw_property_utils::UpdatePropertyTreesAndRenderSurfaces(root_layer, + property_trees); draw_property_utils::FindLayersThatNeedUpdates( root_layer->layer_tree_impl(), property_trees, update_layer_impl_list_.get()); @@ -280,7 +278,6 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { DCHECK(!root_layer->bounds().IsEmpty()); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root_layer, device_viewport_size, render_surface_list_impl_.get()); - inputs.can_adjust_raster_scales = false; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); } @@ -311,7 +308,7 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { bool VerifyLayerInList(scoped_refptr<Layer> layer, const LayerList* layer_list) { - return base::ContainsValue(*layer_list, layer); + return base::Contains(*layer_list, layer); } private: @@ -323,18 +320,11 @@ class LayerTreeHostCommonTestBase : public LayerTestCommon::LayerImplTest { class LayerTreeHostCommonTest : public LayerTreeHostCommonTestBase, public testing::Test {}; -class LayerTreeSettingsScaleContent : public VerifyTreeCalcsLayerTreeSettings { - public: - LayerTreeSettingsScaleContent() { - layer_transforms_should_scale_layer_contents = true; - } -}; - class LayerTreeHostCommonScalingTest : public LayerTreeHostCommonTestBase, public testing::Test { public: LayerTreeHostCommonScalingTest() - : LayerTreeHostCommonTestBase(LayerTreeSettingsScaleContent()) {} + : LayerTreeHostCommonTestBase(VerifyTreeCalcsLayerTreeSettings()) {} }; class LayerTreeHostCommonDrawRectsTest : public LayerTreeHostCommonTest { @@ -1401,7 +1391,6 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceListForTransparentChild) { RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root, root->bounds(), &render_surface_list); - inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); // Since the layer is transparent, render_surface1->GetRenderSurface() should @@ -1437,7 +1426,6 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root, root->bounds(), &render_surface_list); - inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); EXPECT_EQ(2U, render_surface_list.size()); } @@ -1461,7 +1449,6 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root, root->bounds(), &render_surface_list); - inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); } @@ -1496,7 +1483,6 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceListForFilter) { RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root, root->bounds(), &render_surface_list); - inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); ASSERT_TRUE(GetRenderSurface(parent)); @@ -4436,7 +4422,6 @@ TEST_F(LayerTreeHostCommonTest, OpacityAnimatingOnPendingTree) { RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root_layer, root_layer->bounds(), &render_surface_list); - inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); // We should have one render surface and two layers. The child @@ -4451,7 +4436,6 @@ TEST_F(LayerTreeHostCommonTest, OpacityAnimatingOnPendingTree) { RenderSurfaceList render_surface_list2; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs2( root_layer, root_layer->bounds(), &render_surface_list2); - inputs2.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs2); LayerImpl* child_ptr = root_layer->layer_tree_impl()->LayerById(2); @@ -4468,7 +4452,6 @@ TEST_F(LayerTreeHostCommonTest, OpacityAnimatingOnPendingTree) { RenderSurfaceList render_surface_list3; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs3( root_layer, root_layer->bounds(), &render_surface_list3); - inputs3.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs3); child_ptr = root_layer->layer_tree_impl()->LayerById(2); @@ -4754,7 +4737,6 @@ TEST_F(LayerTreeHostCommonTest, SubtreeHidden_SingleLayerImpl) { RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root_layer, root_layer->bounds(), &render_surface_list); - inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); // We should have one render surface and two layers. The grand child has @@ -4798,7 +4780,6 @@ TEST_F(LayerTreeHostCommonTest, SubtreeHidden_TwoLayersImpl) { RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root_layer, root_layer->bounds(), &render_surface_list); - inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); // We should have one render surface and one layer. The child has @@ -4894,7 +4875,6 @@ TEST_F(LayerTreeHostCommonTest, SubtreeHiddenWithCopyRequest) { RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root_layer, root_layer->bounds(), &render_surface_list); - inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); auto& effect_tree = @@ -5002,7 +4982,6 @@ TEST_F(LayerTreeHostCommonTest, ClippedOutCopyRequest) { root_layer->layer_tree_impl()->SetRootLayerForTesting(std::move(root)); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root_layer, root_layer->bounds(), &render_surface_list); - inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); // We should have two render surface, as the others are clipped out. @@ -6442,7 +6421,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionSubpixelScroll) { sticky_position.is_sticky = true; sticky_position.is_anchored_bottom = true; sticky_position.bottom_offset = 10.0f; - sticky_position.constraint_box_rect = gfx::RectF(0, 0, 100, 100); + sticky_position.constraint_box_rect = gfx::Rect(0, 0, 100, 100); sticky_position.scroll_container_relative_sticky_box_rect = gfx::Rect(0, 200, 10, 10); sticky_position.scroll_container_relative_containing_block_rect = @@ -6489,7 +6468,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionBottom) { sticky_position.is_sticky = true; sticky_position.is_anchored_bottom = true; sticky_position.bottom_offset = 10.0f; - sticky_position.constraint_box_rect = gfx::RectF(0, 0, 100, 100); + sticky_position.constraint_box_rect = gfx::Rect(0, 0, 100, 100); sticky_position.scroll_container_relative_sticky_box_rect = gfx::Rect(0, 150, 10, 10); sticky_position.scroll_container_relative_containing_block_rect = @@ -6571,7 +6550,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionBottomOuterViewportDelta) { sticky_position.is_sticky = true; sticky_position.is_anchored_bottom = true; sticky_position.bottom_offset = 10.0f; - sticky_position.constraint_box_rect = gfx::RectF(0, 0, 100, 100); + sticky_position.constraint_box_rect = gfx::Rect(0, 0, 100, 100); sticky_position.scroll_container_relative_sticky_box_rect = gfx::Rect(0, 70, 10, 10); sticky_position.scroll_container_relative_containing_block_rect = @@ -6650,7 +6629,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionLeftRight) { sticky_position.is_anchored_right = true; sticky_position.left_offset = 10.f; sticky_position.right_offset = 10.f; - sticky_position.constraint_box_rect = gfx::RectF(0, 0, 100, 100); + sticky_position.constraint_box_rect = gfx::Rect(0, 0, 100, 100); sticky_position.scroll_container_relative_sticky_box_rect = gfx::Rect(145, 0, 10, 10); sticky_position.scroll_container_relative_containing_block_rect = @@ -7303,7 +7282,6 @@ TEST_F(LayerTreeHostCommonTest, MaximumAnimationScaleFactor) { FakeImplTaskRunnerProvider task_runner_provider; TestTaskGraphRunner task_graph_runner; LayerTreeSettings settings = host()->GetSettings(); - settings.layer_transforms_should_scale_layer_contents = true; FakeLayerTreeHostImpl host_impl(settings, &task_runner_provider, &task_graph_runner); std::unique_ptr<AnimationScaleFactorTrackingLayerImpl> grand_parent = @@ -7790,7 +7768,6 @@ TEST_F(LayerTreeHostCommonTest, DrawPropertyScales) { FakeImplTaskRunnerProvider task_runner_provider; TestTaskGraphRunner task_graph_runner; LayerTreeSettings settings = host()->GetSettings(); - settings.layer_transforms_should_scale_layer_contents = true; FakeLayerTreeHostImpl host_impl(settings, &task_runner_provider, &task_graph_runner); @@ -7864,7 +7841,6 @@ TEST_F(LayerTreeHostCommonTest, DrawPropertyScales) { root_layer, device_viewport_size, &render_surface_list); inputs.page_scale_factor = page_scale_factor; - inputs.can_adjust_raster_scales = true; inputs.page_scale_layer = root_layer; inputs.page_scale_transform_node = inputs.property_trees->transform_tree.Node( inputs.page_scale_layer->transform_tree_index()); @@ -7886,7 +7862,6 @@ TEST_F(LayerTreeHostCommonTest, DrawPropertyScales) { device_scale_factor = 4.0f; inputs.device_scale_factor = device_scale_factor; - inputs.can_adjust_raster_scales = true; root_layer->layer_tree_impl()->property_trees()->needs_rebuild = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); @@ -7906,7 +7881,6 @@ TEST_F(LayerTreeHostCommonTest, AnimationScales) { FakeImplTaskRunnerProvider task_runner_provider; TestTaskGraphRunner task_graph_runner; LayerTreeSettings settings = host()->GetSettings(); - settings.layer_transforms_should_scale_layer_contents = true; FakeLayerTreeHostImpl host_impl(settings, &task_runner_provider, &task_graph_runner); @@ -7974,56 +7948,6 @@ TEST_F(LayerTreeHostCommonTest, AnimationScales) { EXPECT_FLOAT_EQ(100.f, GetStartingAnimationScale(child2_layer)); } -TEST_F(LayerTreeHostCommonTest, - AnimationScaleWhenLayerTransformShouldNotScaleLayerBounds) { - // Returns empty scale if layer_transforms_should_scale_layer_contents is - // false. - FakeImplTaskRunnerProvider task_runner_provider; - TestTaskGraphRunner task_graph_runner; - LayerTreeSettings settings = host()->GetSettings(); - settings.layer_transforms_should_scale_layer_contents = false; - FakeLayerTreeHostImpl host_impl(settings, &task_runner_provider, - &task_graph_runner); - - std::unique_ptr<LayerImpl> root = - LayerImpl::Create(host_impl.active_tree(), 1); - LayerImpl* root_layer = root.get(); - std::unique_ptr<LayerImpl> child = - LayerImpl::Create(host_impl.active_tree(), 2); - LayerImpl* child_layer = child.get(); - - root->test_properties()->AddChild(std::move(child)); - host_impl.active_tree()->SetRootLayerForTesting(std::move(root)); - - host_impl.active_tree()->SetElementIdsForTesting(); - - gfx::Transform scale_transform_child; - scale_transform_child.Scale(4, 5); - - root_layer->SetBounds(gfx::Size(1, 1)); - child_layer->test_properties()->transform = scale_transform_child; - child_layer->SetBounds(gfx::Size(1, 1)); - - TransformOperations scale; - scale.AppendScale(5.f, 8.f, 3.f); - - scoped_refptr<AnimationTimeline> timeline = - AnimationTimeline::Create(AnimationIdProvider::NextTimelineId()); - host_impl.animation_host()->AddAnimationTimeline(timeline); - - AddAnimatedTransformToElementWithAnimation( - child_layer->element_id(), timeline, 1.0, TransformOperations(), scale); - - root_layer->layer_tree_impl()->property_trees()->needs_rebuild = true; - ExecuteCalculateDrawProperties(root_layer); - - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(root_layer)); - EXPECT_FLOAT_EQ(kNotScaled, GetMaximumAnimationScale(child_layer)); - - EXPECT_FLOAT_EQ(kNotScaled, GetStartingAnimationScale(root_layer)); - EXPECT_FLOAT_EQ(kNotScaled, GetStartingAnimationScale(child_layer)); -} - TEST_F(LayerTreeHostCommonTest, VisibleContentRectInChildRenderSurface) { LayerImpl* root = root_layer_for_testing(); LayerImpl* clip = AddChild<LayerImpl>(root); @@ -9789,7 +9713,7 @@ TEST_F(LayerTreeHostCommonTest, LargeTransformTest) { // The root layer should be in the RenderSurfaceList. const auto* rsl = render_surface_list_impl(); - EXPECT_TRUE(base::ContainsValue(*rsl, GetRenderSurface(root))); + EXPECT_TRUE(base::Contains(*rsl, GetRenderSurface(root))); } TEST_F(LayerTreeHostCommonTest, PropertyTreesRebuildWithOpacityChanges) { @@ -10143,40 +10067,7 @@ TEST_F(LayerTreeHostCommonTest, ScrollTreeBuilderTest) { EXPECT_EQ(scroll_root1.id, grand_child12->scroll_tree_index()); } -TEST_F(LayerTreeHostCommonTest, CanAdjustRasterScaleTest) { - LayerImpl* root = root_layer_for_testing(); - LayerImpl* render_surface = AddChild<LayerImpl>(root); - LayerImpl* child = AddChild<LayerImpl>(render_surface); - - root->SetBounds(gfx::Size(50, 50)); - - render_surface->SetBounds(gfx::Size(10, 10)); - render_surface->test_properties()->force_render_surface = true; - gfx::Transform transform; - transform.Scale(5.f, 5.f); - render_surface->test_properties()->transform = transform; - - child->SetDrawsContent(true); - child->SetMasksToBounds(true); - child->SetBounds(gfx::Size(10, 10)); - - ExecuteCalculateDrawPropertiesWithoutAdjustingRasterScales(root); - - // Check surface draw properties. - EXPECT_EQ(gfx::Rect(10, 10), - GetRenderSurface(render_surface)->content_rect()); - EXPECT_EQ(transform, GetRenderSurface(render_surface)->draw_transform()); - EXPECT_EQ(gfx::RectF(50.0f, 50.0f), - GetRenderSurface(render_surface)->DrawableContentRect()); - - // Check child layer draw properties. - EXPECT_EQ(gfx::Rect(10, 10), child->visible_layer_rect()); - EXPECT_EQ(gfx::Transform(), child->DrawTransform()); - EXPECT_EQ(gfx::Rect(10, 10), child->clip_rect()); - EXPECT_EQ(gfx::Rect(10, 10), child->drawable_content_rect()); -} - -TEST_F(LayerTreeHostCommonTest, SurfaceContentsScaleChangeWithCopyRequestTest) { +TEST_F(LayerTreeHostCommonTest, CopyRequestScalingTest) { LayerImpl* root = root_layer_for_testing(); LayerImpl* scale_layer = AddChild<LayerImpl>(root); LayerImpl* copy_layer = AddChild<LayerImpl>(scale_layer); @@ -10203,7 +10094,7 @@ TEST_F(LayerTreeHostCommonTest, SurfaceContentsScaleChangeWithCopyRequestTest) { test_layer->SetMasksToBounds(true); test_layer->SetBounds(gfx::Size(20, 20)); - ExecuteCalculateDrawPropertiesWithoutAdjustingRasterScales(root); + ExecuteCalculateDrawProperties(root); // Check surface with copy request draw properties. EXPECT_EQ(gfx::Rect(50, 50), GetRenderSurface(copy_layer)->content_rect()); @@ -10219,19 +10110,7 @@ TEST_F(LayerTreeHostCommonTest, SurfaceContentsScaleChangeWithCopyRequestTest) { // Clear the copy request and call UpdateSurfaceContentsScale. host_impl()->active_tree()->property_trees()->effect_tree.ClearCopyRequests(); - ExecuteCalculateDrawPropertiesWithoutAdjustingRasterScales(root); - - // Check surface draw properties without copy request. - EXPECT_EQ(gfx::Rect(10, 10), GetRenderSurface(copy_layer)->content_rect()); - EXPECT_EQ(transform, GetRenderSurface(copy_layer)->draw_transform()); - EXPECT_EQ(gfx::RectF(50.0f, 50.0f), - GetRenderSurface(copy_layer)->DrawableContentRect()); - - // Check test layer draw properties without copy request. - EXPECT_EQ(gfx::Rect(10, 10), test_layer->visible_layer_rect()); - EXPECT_EQ(gfx::Transform(), test_layer->DrawTransform()); - EXPECT_EQ(gfx::Rect(10, 10), test_layer->clip_rect()); - EXPECT_EQ(gfx::Rect(10, 10), test_layer->drawable_content_rect()); + ExecuteCalculateDrawProperties(root); } TEST_F(LayerTreeHostCommonTest, SubtreeHiddenWithCacheRenderSurface) { @@ -10316,7 +10195,6 @@ TEST_F(LayerTreeHostCommonTest, SubtreeHiddenWithCacheRenderSurface) { RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root_layer, root_layer->bounds(), &render_surface_list); - inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); // We should have four render surfaces, one for the root, one for the grand @@ -10541,7 +10419,6 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceListForTrilinearFiltering) { RenderSurfaceList render_surface_list; LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root, root->bounds(), &render_surface_list); - inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); ASSERT_TRUE(GetRenderSurface(parent)); @@ -11677,5 +11554,229 @@ TEST_F(LayerTreeHostCommonTest, kRoundedCorner4Radius * kDeviceScale); } +TEST_F(LayerTreeHostCommonTest, CustomLayerClipBounds) { + // The custom clip API should have the same effect as if an intermediate + // clip layer has been added to the layer tree. To check this the test creates + // 2 subtree for a root layer. One of the subtree uses the clip API to clip + // its subtree while the other uses an intermediate layer. The resulting clip + // in draw properties are expected to be the same. + // -Root + // - Parent [Clip set to |kClipBounds| using API] + // - Child + // - Clip Layer [Masks to bounds = true] [Layer bounds set to |kClipBounds|] + // - Expected Parent + // - Expected Child + constexpr float kDeviceScale = 1.f; + + const gfx::Rect kRootLayerBounds(0, 0, 100, 100); + const gfx::Rect kParentLayerBounds(0, 0, 50, 100); + const gfx::Rect kChildLayerBounds(20, 20, 30, 60); + + constexpr gfx::Rect kClipBounds(10, 10, 50, 50); + + // The position of |Expected Parent| on screen should be same as |Parent|. + const gfx::Rect kExpectedParentLayerBounds( + gfx::Point(0, 0) - kClipBounds.OffsetFromOrigin(), gfx::Size(50, 100)); + + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> parent = Layer::Create(); + scoped_refptr<Layer> child = Layer::Create(); + + scoped_refptr<Layer> clip_layer = Layer::Create(); + scoped_refptr<Layer> expected_parent = Layer::Create(); + scoped_refptr<Layer> expected_child = Layer::Create(); + + root->AddChild(parent); + parent->AddChild(child); + + root->AddChild(clip_layer); + clip_layer->AddChild(expected_parent); + expected_parent->AddChild(expected_child); + + host()->SetRootLayer(root); + + root->SetIsDrawable(true); + parent->SetIsDrawable(true); + child->SetIsDrawable(true); + expected_parent->SetIsDrawable(true); + expected_child->SetIsDrawable(true); + + // Set layer positions. + root->SetPosition(gfx::PointF(kRootLayerBounds.origin())); + parent->SetPosition(gfx::PointF(kParentLayerBounds.origin())); + child->SetPosition(gfx::PointF(kChildLayerBounds.origin())); + + clip_layer->SetPosition(gfx::PointF(kClipBounds.origin())); + expected_parent->SetPosition( + gfx::PointF(kExpectedParentLayerBounds.origin())); + expected_child->SetPosition(gfx::PointF(kChildLayerBounds.origin())); + + root->SetBounds(kRootLayerBounds.size()); + parent->SetBounds(kParentLayerBounds.size()); + child->SetBounds(kChildLayerBounds.size()); + + clip_layer->SetBounds(kClipBounds.size()); + expected_parent->SetBounds(kExpectedParentLayerBounds.size()); + expected_child->SetBounds(kChildLayerBounds.size()); + + parent->SetClipRect(kClipBounds); + clip_layer->SetMasksToBounds(true); + + ExecuteCalculateDrawProperties(root.get(), kDeviceScale); + + const ClipTree& clip_tree = + root->layer_tree_host()->property_trees()->clip_tree; + + const ClipNode* parent_clip_node = clip_tree.Node(parent->clip_tree_index()); + EXPECT_EQ(parent_clip_node->clip, gfx::RectF(kClipBounds)); + EXPECT_TRUE(!parent->clip_rect().IsEmpty()); + + const ClipNode* child_clip_node = clip_tree.Node(child->clip_tree_index()); + EXPECT_EQ(child_clip_node->clip, gfx::RectF(kClipBounds)); + + host()->host_impl()->CreatePendingTree(); + host()->CommitAndCreatePendingTree(); + // TODO(https://crbug.com/939968) This call should be handled by + // FakeLayerTreeHost instead of manually pushing the properties from the + // layer tree host to the pending tree. + root->layer_tree_host()->PushLayerTreePropertiesTo(host()->pending_tree()); + host()->host_impl()->ActivateSyncTree(); + LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree(); + + // Get the layer impl for each Layer. + LayerImpl* root_impl = layer_tree_impl->LayerById(root->id()); + LayerImpl* parent_impl = layer_tree_impl->LayerById(parent->id()); + LayerImpl* child_impl = layer_tree_impl->LayerById(child->id()); + LayerImpl* expected_parent_impl = + layer_tree_impl->LayerById(expected_parent->id()); + LayerImpl* expected_child_impl = + layer_tree_impl->LayerById(expected_child->id()); + + ExecuteCalculateDrawProperties(root_impl, kDeviceScale); + + EXPECT_TRUE(parent_impl->is_clipped()); + EXPECT_TRUE(child_impl->is_clipped()); + ASSERT_TRUE(expected_parent_impl->is_clipped()); + ASSERT_TRUE(expected_child_impl->is_clipped()); + + EXPECT_EQ(parent_impl->clip_rect(), expected_parent_impl->clip_rect()); + EXPECT_EQ(child_impl->clip_rect(), expected_child_impl->clip_rect()); +} + +TEST_F(LayerTreeHostCommonTest, CustomLayerClipBoundsWithMaskToBounds) { + // The custom clip API should have the same effect as if an intermediate + // clip layer has been added to the layer tree. To check this the test creates + // 2 subtree for a root layer. One of the subtree uses the clip API to clip + // its subtree while the other uses an intermediate layer. The resulting clip + // in draw properties are expected to be the same. In this test, the subtree + // roots also have their masks to bounds property set. + // -Root + // - Parent [Clip set to |kClipBounds| using API] + // - Child + // - Clip Layer [Masks to bounds = true] [Layer bounds set to |kClipBounds|] + // - Expected Parent [Masks to bounds = true] + // - Expected Child + constexpr float kDeviceScale = 1.f; + + const gfx::Rect kRootLayerBounds(0, 0, 100, 100); + const gfx::Rect kParentLayerBounds(0, 0, 50, 100); + const gfx::Rect kChildLayerBounds(20, 20, 30, 60); + + constexpr gfx::Rect kClipBounds(10, 10, 50, 50); + + // The position of |Expected Parent| on screen should be same as |Parent|. + const gfx::Rect kExpectedParentLayerBounds( + gfx::Point(0, 0) - kClipBounds.OffsetFromOrigin(), gfx::Size(50, 100)); + + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> parent = Layer::Create(); + scoped_refptr<Layer> child = Layer::Create(); + + scoped_refptr<Layer> clip_layer = Layer::Create(); + scoped_refptr<Layer> expected_parent = Layer::Create(); + scoped_refptr<Layer> expected_child = Layer::Create(); + + root->AddChild(parent); + parent->AddChild(child); + + root->AddChild(clip_layer); + clip_layer->AddChild(expected_parent); + expected_parent->AddChild(expected_child); + + host()->SetRootLayer(root); + + root->SetIsDrawable(true); + parent->SetIsDrawable(true); + child->SetIsDrawable(true); + expected_parent->SetIsDrawable(true); + expected_child->SetIsDrawable(true); + + // Set layer positions. + root->SetPosition(gfx::PointF(kRootLayerBounds.origin())); + parent->SetPosition(gfx::PointF(kParentLayerBounds.origin())); + child->SetPosition(gfx::PointF(kChildLayerBounds.origin())); + + clip_layer->SetPosition(gfx::PointF(kClipBounds.origin())); + expected_parent->SetPosition( + gfx::PointF(kExpectedParentLayerBounds.origin())); + expected_child->SetPosition(gfx::PointF(kChildLayerBounds.origin())); + + root->SetBounds(kRootLayerBounds.size()); + parent->SetBounds(kParentLayerBounds.size()); + child->SetBounds(kChildLayerBounds.size()); + + clip_layer->SetBounds(kClipBounds.size()); + expected_parent->SetBounds(kExpectedParentLayerBounds.size()); + expected_child->SetBounds(kChildLayerBounds.size()); + + parent->SetClipRect(kClipBounds); + parent->SetMasksToBounds(true); + + clip_layer->SetMasksToBounds(true); + expected_parent->SetMasksToBounds(true); + + ExecuteCalculateDrawProperties(root.get(), kDeviceScale); + + const ClipTree& clip_tree = + root->layer_tree_host()->property_trees()->clip_tree; + + const ClipNode* parent_clip_node = clip_tree.Node(parent->clip_tree_index()); + const gfx::RectF expected_clip_bounds = gfx::IntersectRects( + gfx::RectF(kClipBounds), gfx::RectF(kParentLayerBounds)); + EXPECT_EQ(parent_clip_node->clip, expected_clip_bounds); + EXPECT_TRUE(!parent->clip_rect().IsEmpty()); + + const ClipNode* child_clip_node = clip_tree.Node(child->clip_tree_index()); + EXPECT_EQ(child_clip_node->clip, expected_clip_bounds); + + host()->host_impl()->CreatePendingTree(); + host()->CommitAndCreatePendingTree(); + // TODO(https://crbug.com/939968) This call should be handled by + // FakeLayerTreeHost instead of manually pushing the properties from the + // layer tree host to the pending tree. + root->layer_tree_host()->PushLayerTreePropertiesTo(host()->pending_tree()); + host()->host_impl()->ActivateSyncTree(); + LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree(); + + // Get the layer impl for each Layer. + LayerImpl* root_impl = layer_tree_impl->LayerById(root->id()); + LayerImpl* parent_impl = layer_tree_impl->LayerById(parent->id()); + LayerImpl* child_impl = layer_tree_impl->LayerById(child->id()); + LayerImpl* expected_parent_impl = + layer_tree_impl->LayerById(expected_parent->id()); + LayerImpl* expected_child_impl = + layer_tree_impl->LayerById(expected_child->id()); + + ExecuteCalculateDrawProperties(root_impl, kDeviceScale); + + EXPECT_TRUE(parent_impl->is_clipped()); + EXPECT_TRUE(child_impl->is_clipped()); + ASSERT_TRUE(expected_parent_impl->is_clipped()); + ASSERT_TRUE(expected_child_impl->is_clipped()); + + EXPECT_EQ(parent_impl->clip_rect(), expected_parent_impl->clip_rect()); + EXPECT_EQ(child_impl->clip_rect(), expected_child_impl->clip_rect()); +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc index e2921219d25..0585a41551c 100644 --- a/chromium/cc/trees/layer_tree_host_impl.cc +++ b/chromium/cc/trees/layer_tree_host_impl.cc @@ -38,6 +38,7 @@ #include "cc/input/page_scale_animation.h" #include "cc/input/scroll_elasticity_helper.h" #include "cc/input/scroll_state.h" +#include "cc/input/scrollbar.h" #include "cc/input/scrollbar_animation_controller.h" #include "cc/input/scroller_size_metrics.h" #include "cc/input/snap_selection_strategy.h" @@ -323,7 +324,8 @@ LayerTreeHostImpl::LayerTreeHostImpl( task_runner_provider_(task_runner_provider), current_begin_frame_tracker_(BEGINFRAMETRACKER_FROM_HERE), compositor_frame_reporting_controller_( - std::make_unique<CompositorFrameReportingController>()), + std::make_unique<CompositorFrameReportingController>( + settings.single_thread_proxy_scheduler)), settings_(settings), is_synchronous_single_threaded_(!task_runner_provider->HasImplThread() && !settings_.single_thread_proxy_scheduler), @@ -358,8 +360,7 @@ LayerTreeHostImpl::LayerTreeHostImpl( is_animating_for_snap_(false), paint_image_generator_client_id_(PaintImage::GetNextGeneratorClientId()), scrollbar_controller_(std::make_unique<ScrollbarController>(this)), - scroll_gesture_did_end_(false), - weak_factory_(this) { + scroll_gesture_did_end_(false) { DCHECK(mutator_host_); mutator_host_->SetMutatorHostClient(this); @@ -427,9 +428,21 @@ LayerTreeHostImpl::~LayerTreeHostImpl() { mutator_host_->SetMutatorHostClient(nullptr); } +void LayerTreeHostImpl::DidSendBeginMainFrame(const viz::BeginFrameArgs& args) { + if (impl_thread_phase_ == ImplThreadPhase::INSIDE_IMPL_FRAME) + begin_main_frame_sent_during_impl_ = true; + frame_trackers_.NotifyBeginMainFrame(args); +} + void LayerTreeHostImpl::BeginMainFrameAborted( CommitEarlyOutReason reason, - std::vector<std::unique_ptr<SwapPromise>> swap_promises) { + std::vector<std::unique_ptr<SwapPromise>> swap_promises, + const viz::BeginFrameArgs& args) { + if (reason == CommitEarlyOutReason::ABORTED_NOT_VISIBLE || + reason == CommitEarlyOutReason::FINISHED_NO_UPDATES) { + frame_trackers_.NotifyMainFrameCausedNoDamage(args); + } + // If the begin frame data was handled, then scroll and scale set was applied // by the main thread, so the active tree needs to be updated as if these sent // values were applied and committed. @@ -457,7 +470,7 @@ void LayerTreeHostImpl::CommitComplete() { // In high latency mode commit cannot finish within the same frame. We need to // flush input here to make sure they got picked up by |PrepareTiles()|. if (input_handler_client_ && impl_thread_phase_ == ImplThreadPhase::IDLE) - input_handler_client_->DeliverInputForBeginFrame(); + input_handler_client_->DeliverInputForHighLatencyMode(); if (CommitToActiveTree()) { active_tree_->HandleScrollbarShowRequestsFromMain(); @@ -483,6 +496,18 @@ void LayerTreeHostImpl::CommitComplete() { UpdateSyncTreeAfterCommitOrImplSideInvalidation(); micro_benchmark_controller_.DidCompleteCommit(); + + if (mutator_host_->CurrentFrameHadRAF() && + !request_animation_frame_tracker_) { + request_animation_frame_tracker_ = + frame_trackers_.CreateTracker(FrameSequenceTrackerType::kRAF); + } + + if (mutator_host_->MainThreadAnimationsCount() > 0 && + !main_thread_animation_frame_tracker_) { + main_thread_animation_frame_tracker_ = frame_trackers_.CreateTracker( + FrameSequenceTrackerType::kMainThreadAnimation); + } } void LayerTreeHostImpl::UpdateSyncTreeAfterCommitOrImplSideInvalidation() { @@ -529,6 +554,97 @@ void LayerTreeHostImpl::UpdateSyncTreeAfterCommitOrImplSideInvalidation() { if (CommitToActiveTree()) ActivateStateForImages(); + if (!paint_worklet_painter_) { + // Blink should not send us any PaintWorklet inputs until we have a painter + // registered. + DCHECK(sync_tree()->picture_layers_with_paint_worklets().empty()); + pending_tree_fully_painted_ = true; + NotifyPendingTreeFullyPainted(); + return; + } + + PaintWorkletJobMap dirty_paint_worklets = GatherDirtyPaintWorklets(); + if (!dirty_paint_worklets.size()) { + pending_tree_fully_painted_ = true; + NotifyPendingTreeFullyPainted(); + return; + } + + client_->NotifyPaintWorkletStateChange( + Scheduler::PaintWorkletState::PROCESSING); + auto done_callback = base::BindOnce( + &LayerTreeHostImpl::OnPaintWorkletResultsReady, base::Unretained(this)); + paint_worklet_painter_->DispatchWorklets(std::move(dirty_paint_worklets), + std::move(done_callback)); +} + +PaintWorkletJobMap LayerTreeHostImpl::GatherDirtyPaintWorklets() const { + PaintWorkletJobMap dirty_paint_worklets; + for (PictureLayerImpl* layer : + sync_tree()->picture_layers_with_paint_worklets()) { + for (const auto& entry : layer->GetPaintWorkletRecordMap()) { + // If we already have a record we can reuse it and so the + // PaintWorkletInput isn't dirty. + if (entry.second) + continue; + + int id = entry.first->WorkletId(); + auto result = dirty_paint_worklets.insert( + std::make_pair(id, scoped_refptr<PaintWorkletJobVector>{})); + scoped_refptr<PaintWorkletJobVector>& job_vector = result.first->second; + if (!job_vector) + job_vector = base::WrapRefCounted(new PaintWorkletJobVector); + job_vector->data.emplace_back(layer->id(), entry.first); + } + } + return dirty_paint_worklets; +} + +void LayerTreeHostImpl::OnPaintWorkletResultsReady(PaintWorkletJobMap results) { + // Nothing else should have painted the PaintWorklets while we were waiting, + // and the results should have painted every PaintWorklet, so these should be + // the same. + DCHECK_EQ(results.size(), GatherDirtyPaintWorklets().size()); + + for (const auto& entry : results) { + for (const PaintWorkletJob& job : entry.second->data) { + LayerImpl* layer_impl = + pending_tree_->FindPendingTreeLayerById(job.layer_id()); + // Painting the pending tree occurs asynchronously but stalls the pending + // tree pipeline, so nothing should have changed while we were doing that. + DCHECK(layer_impl); + static_cast<PictureLayerImpl*>(layer_impl) + ->SetPaintWorkletRecord(job.input(), job.output()); + } + } + + // While the pending tree is being painted by PaintWorklets, we restrict the + // tiles the TileManager is able to see. This may cause the TileManager to + // believe that it has finished rastering all the necessary tiles. When we + // finish painting the tree and release all the tiles, we need to mark the + // tile priorities as dirty so that the TileManager logic properly re-runs. + tile_priorities_dirty_ = true; + + // Set the painted state before calling the scheduler, to ensure any callback + // running as a result sees the correct painted state. + pending_tree_fully_painted_ = true; + client_->NotifyPaintWorkletStateChange(Scheduler::PaintWorkletState::IDLE); + + // The pending tree may have been force activated from the signal to the + // scheduler above, in which case there is no longer a tree to paint. + if (pending_tree_) + NotifyPendingTreeFullyPainted(); +} + +void LayerTreeHostImpl::NotifyPendingTreeFullyPainted() { + // The pending tree must be fully painted at this point. + DCHECK(pending_tree_fully_painted_); + + // Nobody should claim the pending tree is fully painted if there is an + // ongoing dispatch. + DCHECK(!paint_worklet_painter_ || + !paint_worklet_painter_->HasOngoingDispatch()); + // Start working on newly created tiles immediately if needed. // TODO(vmpstr): Investigate always having PrepareTiles issue // NotifyReadyToActivate, instead of handling it here. @@ -714,8 +830,7 @@ bool LayerTreeHostImpl::IsCurrentlyScrollingViewport() const { } bool LayerTreeHostImpl::IsCurrentlyScrollingLayerAt( - const gfx::Point& viewport_point, - InputHandler::ScrollInputType type) const { + const gfx::Point& viewport_point) const { auto* scrolling_node = CurrentlyScrollingNode(); if (!scrolling_node) return false; @@ -729,7 +844,7 @@ bool LayerTreeHostImpl::IsCurrentlyScrollingLayerAt( bool scroll_on_main_thread = false; uint32_t main_thread_scrolling_reasons; auto* test_scroll_node = FindScrollNodeForDeviceViewportPoint( - device_viewport_point, type, layer_impl, &scroll_on_main_thread, + device_viewport_point, layer_impl, &scroll_on_main_thread, &main_thread_scrolling_reasons); if (scroll_on_main_thread) @@ -1183,6 +1298,7 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { } frame->use_default_lower_bound_deadline |= append_quads_data.use_default_lower_bound_deadline; + frame->mirror_rect.Union(append_quads_data.mirror_rect); } // If CommitToActiveTree() is true, then we wait to draw until @@ -1338,10 +1454,19 @@ DrawResult LayerTreeHostImpl::PrepareToDraw(FrameData* frame) { if (client_name) { size_t total_memory_in_bytes = 0; size_t total_gpu_memory_for_tilings_in_bytes = 0; + int layers_with_text_count = 0; + int layers_with_text_no_lcd_text_count = 0; for (const PictureLayerImpl* layer : active_tree()->picture_layers()) { total_memory_in_bytes += layer->GetRasterSource()->GetMemoryUsage(); total_gpu_memory_for_tilings_in_bytes += layer->GPUMemoryUsageInBytes(); + if (layer->GetRasterSource()->HasText()) { + layers_with_text_count++; + if (!layer->can_use_lcd_text()) { + layers_with_text_no_lcd_text_count++; + } + } } + if (total_memory_in_bytes != 0) { UMA_HISTOGRAM_COUNTS_1M( base::StringPrintf("Compositing.%s.PictureMemoryUsageKb", @@ -1359,6 +1484,34 @@ DrawResult LayerTreeHostImpl::PrepareToDraw(FrameData* frame) { base::saturated_cast<int>(active_tree_->picture_layers().size()), 1, 400, 20); + if (layers_with_text_count > 0) { + int percent = + 100.0 * layers_with_text_no_lcd_text_count / layers_with_text_count; + + if (layers_with_text_count < 10) { + UMA_HISTOGRAM_PERCENTAGE( + base::StringPrintf( + "Compositing.%s.PercentPictureLayersWithTextButLCDTextDisabled." + "LessThan10", + client_name), + percent); + } else if (layers_with_text_count <= 30) { + UMA_HISTOGRAM_PERCENTAGE( + base::StringPrintf( + "Compositing.%s.PercentPictureLayersWithTextButLCDTextDisabled." + "10To30", + client_name), + percent); + } else { + UMA_HISTOGRAM_PERCENTAGE( + base::StringPrintf( + "Compositing.%s." + "PercentPictureLayersWithTextButLCDTextDisabled.MoreThan30", + client_name), + percent); + } + } + // TODO(yigu): Maybe we should use the same check above. Need to figure out // why exactly we skip 0. if (!active_tree()->picture_layers().empty()) { @@ -1588,11 +1741,12 @@ std::unique_ptr<RasterTilePriorityQueue> LayerTreeHostImpl::BuildRasterQueue( TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "LayerTreeHostImpl::BuildRasterQueue"); - return RasterTilePriorityQueue::Create(active_tree_->picture_layers(), - pending_tree_ - ? pending_tree_->picture_layers() - : std::vector<PictureLayerImpl*>(), - tree_priority, type); + return RasterTilePriorityQueue::Create( + active_tree_->picture_layers(), + pending_tree_ && pending_tree_fully_painted_ + ? pending_tree_->picture_layers() + : std::vector<PictureLayerImpl*>(), + tree_priority, type); } std::unique_ptr<EvictionTilePriorityQueue> @@ -1666,6 +1820,13 @@ size_t LayerTreeHostImpl::GetFrameIndexForImage(const PaintImage& paint_image, } void LayerTreeHostImpl::NotifyReadyToActivate() { + // The TileManager may call this method while the pending tree is still being + // painted, as it isn't aware of the ongoing paint. We shouldn't tell the + // scheduler we are ready to activate in that case, as if we do it will + // immediately activate once we call NotifyPaintWorkletStateChange, rather + // than wait for the TileManager to actually raster the content! + if (!pending_tree_fully_painted_) + return; pending_tree_raster_duration_timer_.reset(); client_->NotifyReadyToActivate(); } @@ -1800,6 +1961,7 @@ void LayerTreeHostImpl::DidReceiveCompositorFrameAck() { void LayerTreeHostImpl::DidPresentCompositorFrame( uint32_t frame_token, const gfx::PresentationFeedback& feedback) { + frame_trackers_.NotifyFramePresented(frame_token, feedback); PresentationTimeCallbackBuffer::PendingCallbacks activated = presentation_time_callbacks_.PopPendingCallbacks(frame_token); @@ -1817,6 +1979,7 @@ void LayerTreeHostImpl::DidPresentCompositorFrame( void LayerTreeHostImpl::DidNotNeedBeginFrame() { skipped_frame_tracker_.WillNotProduceFrame(); + frame_trackers_.NotifyPauseFrameProduction(); } void LayerTreeHostImpl::ReclaimResources( @@ -1909,7 +2072,6 @@ viz::CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() { metadata.page_scale_factor = active_tree_->current_page_scale_factor(); metadata.scrollable_viewport_size = active_tree_->ScrollableViewportSize(); metadata.root_background_color = active_tree_->background_color(); - metadata.content_source_id = active_tree_->content_source_id(); if (active_tree_->has_presentation_callbacks()) { presentation_time_callbacks_.RegisterMainThreadPresentationCallbacks( @@ -2061,6 +2223,7 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { if (frame->has_no_damage) { DCHECK(!resourceless_software_draw_); + frame_trackers_.NotifyImplFrameCausedNoDamage(frame->begin_frame_ack); TRACE_EVENT_INSTANT0("cc", "EarlyOut_NoDamage", TRACE_EVENT_SCOPE_THREAD); active_tree()->BreakSwapPromises(SwapPromise::SWAP_FAILS); return false; @@ -2070,10 +2233,26 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { active_tree_->source_frame_number()); auto compositor_frame = GenerateCompositorFrame(frame); + frame->frame_token = compositor_frame.metadata.frame_token; layer_tree_frame_sink_->SubmitCompositorFrame( std::move(compositor_frame), /*hit_test_data_changed=*/false, debug_state_.show_hit_test_borders); + frame_trackers_.NotifySubmitFrame(compositor_frame.metadata.frame_token, + frame->begin_frame_ack, + frame->origin_begin_main_frame_args); + if (request_animation_frame_tracker_ && + !mutator_host_->NextFrameHasPendingRAF()) { + frame_trackers_.ScheduleRemoval( + std::move(request_animation_frame_tracker_)); + } + + if (main_thread_animation_frame_tracker_ && + mutator_host_->MainThreadAnimationsCount() == 0) { + frame_trackers_.ScheduleRemoval( + std::move(main_thread_animation_frame_tracker_)); + } + // Clears the list of swap promises after calling DidSwap on each of them to // signal that the swap is over. active_tree()->ClearSwapPromises(); @@ -2097,7 +2276,6 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) { viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( FrameData* frame) { - TRACE_EVENT0("cc,benchmark", "LayerTreeHostImpl::GenerateCompositorFrame"); TRACE_EVENT_WITH_FLOW1("viz,benchmark", "Graphics.Pipeline", TRACE_ID_GLOBAL(CurrentBeginFrameArgs().trace_id), TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, @@ -2177,15 +2355,15 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( ui::LatencyInfo& new_latency_info = metadata.latency_info.back(); if (CommitToActiveTree()) { new_latency_info.AddLatencyNumberWithTimestamp( - ui::LATENCY_BEGIN_FRAME_UI_COMPOSITOR_COMPONENT, frame_time, 1); + ui::LATENCY_BEGIN_FRAME_UI_COMPOSITOR_COMPONENT, frame_time); } else { new_latency_info.AddLatencyNumberWithTimestamp( - ui::LATENCY_BEGIN_FRAME_RENDERER_COMPOSITOR_COMPONENT, frame_time, 1); + ui::LATENCY_BEGIN_FRAME_RENDERER_COMPOSITOR_COMPONENT, frame_time); base::TimeTicks draw_time = base::TimeTicks::Now(); for (auto& latency : metadata.latency_info) { latency.AddLatencyNumberWithTimestamp( - ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, draw_time, 1); + ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, draw_time); } } ui::LatencyInfo::TraceIntermediateFlowEvents(metadata.latency_info, @@ -2204,6 +2382,8 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( frame->begin_frame_ack.sequence_number); metadata.begin_frame_ack = frame->begin_frame_ack; + metadata.mirror_rect = frame->mirror_rect; + viz::CompositorFrame compositor_frame; compositor_frame.metadata = std::move(metadata); resource_provider_.PrepareSendToParent( @@ -2226,15 +2406,6 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame( } last_draw_local_surface_id_allocation_ = child_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation(); - if (const char* client_name = GetClientNameForMetrics()) { - size_t total_quad_count = 0; - for (const auto& pass : compositor_frame.render_pass_list) - total_quad_count += pass->quad_list.size(); - UMA_HISTOGRAM_COUNTS_1000( - base::StringPrintf("Compositing.%s.CompositorFrame.Quads", client_name), - total_quad_count); - } - return compositor_frame; } @@ -2305,13 +2476,16 @@ void LayerTreeHostImpl::GetGpuRasterizationCapabilities( if (!*gpu_rasterization_enabled && !settings_.gpu_rasterization_forced) return; + bool use_msaa = !caps.msaa_is_slow && !caps.avoid_stencil_buffers; + if (use_oop_rasterization_) { *gpu_rasterization_supported = true; *supports_disable_msaa = caps.multisample_compatibility; // For OOP raster, the gpu service side will disable msaa if the // requested samples are not enough. GPU raster does this same // logic below client side. - *max_msaa_samples = RequestedMSAASampleCount(); + if (use_msaa) + *max_msaa_samples = RequestedMSAASampleCount(); return; } @@ -2326,7 +2500,7 @@ void LayerTreeHostImpl::GetGpuRasterizationCapabilities( return; *supports_disable_msaa = caps.multisample_compatibility; - if (!caps.msaa_is_slow && !caps.avoid_stencil_buffers) { + if (use_msaa) { // Skia may blacklist MSAA independently of Chrome. Query Skia for its max // supported sample count. Assume gpu compositing + gpu raster for this, as // that is what we are hoping to use. @@ -2432,6 +2606,10 @@ void LayerTreeHostImpl::UpdateTreeResourcesForGpuRasterizationIfNeeded() { bool LayerTreeHostImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) { impl_thread_phase_ = ImplThreadPhase::INSIDE_IMPL_FRAME; current_begin_frame_tracker_.Start(args); + frame_trackers_.NotifyBeginImplFrame(args); + + begin_main_frame_expected_during_impl_ = client_->IsBeginMainFrameExpected(); + begin_main_frame_sent_during_impl_ = false; if (is_likely_to_require_a_draw_) { // Optimistically schedule a draw. This will let us expect the tile manager @@ -2440,8 +2618,10 @@ bool LayerTreeHostImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) { SetNeedsRedraw(); } - if (input_handler_client_) - input_handler_client_->DeliverInputForBeginFrame(); + if (input_handler_client_) { + scrollbar_controller_->WillBeginImplFrame(); + input_handler_client_->DeliverInputForBeginFrame(args); + } Animate(); @@ -2475,6 +2655,18 @@ bool LayerTreeHostImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) { } void LayerTreeHostImpl::DidFinishImplFrame() { + if (!begin_main_frame_sent_during_impl_ && + !begin_main_frame_expected_during_impl_) { + // A begin-main-frame was never dispatched for this BeginFrameArgs, and one + // was not expected to be dispatched either. So notify the trackers of the + // begin-main-frame, and not to expect any updates from it. This is + // necessary to make sure the trackers can correctly know which frames were + // not expected to produce any updates. + frame_trackers_.NotifyBeginMainFrame( + current_begin_frame_tracker_.Current()); + frame_trackers_.NotifyMainFrameCausedNoDamage( + current_begin_frame_tracker_.Current()); + } skipped_frame_tracker_.FinishFrame(); impl_thread_phase_ = ImplThreadPhase::IDLE; current_begin_frame_tracker_.Finish(); @@ -2812,6 +3004,7 @@ void LayerTreeHostImpl::CreatePendingTree() { active_tree()->top_controls_shown_ratio(), active_tree()->elastic_overscroll()); } + pending_tree_fully_painted_ = false; client_->OnCanDrawStateChanged(CanDraw()); TRACE_EVENT_ASYNC_BEGIN0("cc", "PendingTree:waiting", pending_tree_.get()); @@ -2967,7 +3160,11 @@ void LayerTreeHostImpl::ActivateSyncTree() { // Dump property trees and layers if run with: // --vmodule=layer_tree_host_impl=3 if (VLOG_IS_ON(3)) { - VLOG(3) << "After activating sync tree, the active tree:" + const char* client_name = GetClientNameForMetrics(); + if (!client_name) + client_name = "<unknown client>"; + VLOG(3) << "After activating (" << client_name + << ") sync tree, the active tree:" << "\nproperty_trees:\n" << active_tree_->property_trees()->ToString() << "\n" << "cc::LayerImpls:\n" @@ -3204,7 +3401,7 @@ void LayerTreeHostImpl::SetLayerTreeMutator( void LayerTreeHostImpl::SetPaintWorkletLayerPainter( std::unique_ptr<PaintWorkletLayerPainter> painter) { - tile_manager_.SetPaintWorkletLayerPainter(std::move(painter)); + paint_worklet_painter_ = std::move(painter); } LayerImpl* LayerTreeHostImpl::ViewportMainScrollLayer() { @@ -3320,6 +3517,14 @@ void LayerTreeHostImpl::ReleaseLayerTreeFrameSink() { bool all_resources_are_lost = layer_tree_frame_sink_->context_provider(); + // Destroy the submit-frame trackers before destroying the frame sink. + pinch_frame_tracker_ = nullptr; + scroll_frame_tracker_ = nullptr; + compositor_animation_frame_tracker_ = nullptr; + request_animation_frame_tracker_ = nullptr; + main_thread_animation_frame_tracker_ = nullptr; + frame_trackers_.ClearAll(); + // Detach from the old LayerTreeFrameSink and reset |layer_tree_frame_sink_| // pointer as this surface is going to be destroyed independent of if binding // the new LayerTreeFrameSink succeeds or not. @@ -3329,15 +3534,24 @@ void LayerTreeHostImpl::ReleaseLayerTreeFrameSink() { // If gpu compositing, then any resources created with the gpu context in the // LayerTreeFrameSink were exported to the display compositor may be modified // by it, and thus we would be unable to determine what state they are in, in - // order to reuse them, so they must be lost. In software compositing, the - // resources are not modified by the display compositor (there is no stateful - // metadata for shared memory), so we do not need to consider them lost. + // order to reuse them, so they must be lost. Note that this includes + // resources created using the gpu context associated with + // |layer_tree_frame_sink_| internally by the compositor and any resources + // received from an external source (for instance, TextureLayers). This is + // because the API contract for releasing these external resources requires + // that the compositor return them with a valid sync token and no + // modifications to their GL state. Since that can not be guaranteed, these + // must also be marked lost. + // + // In software compositing, the resources are not modified by the display + // compositor (there is no stateful metadata for shared memory), so we do not + // need to consider them lost. // // In both cases, the resources that are exported to the display compositor // will have no means of being returned to this client without the - // LayerTreeFrameSink, so they should no longer be considered as exported. - // Do this *after* any interactions with the |layer_tree_frame_sink_| in case - // it tries to return resources during destruction. + // LayerTreeFrameSink, so they should no longer be considered as exported. Do + // this *after* any interactions with the |layer_tree_frame_sink_| in case it + // tries to return resources during destruction. // // The assumption being made here is that the display compositor WILL NOT use // any resources previously exported when the CompositorFrameSink is closed. @@ -3472,7 +3686,6 @@ void LayerTreeHostImpl::BindToClient(InputHandlerClient* client) { InputHandler::ScrollStatus LayerTreeHostImpl::TryScroll( const gfx::PointF& screen_space_point, - InputHandler::ScrollInputType type, const ScrollTree& scroll_tree, ScrollNode* scroll_node) const { InputHandler::ScrollStatus scroll_status; @@ -3516,29 +3729,6 @@ InputHandler::ScrollStatus LayerTreeHostImpl::TryScroll( return scroll_status; } - if (layer && !layer->non_fast_scrollable_region().IsEmpty()) { - bool clipped = false; - gfx::Transform inverse_screen_space_transform( - gfx::Transform::kSkipInitialization); - if (!screen_space_transform.GetInverse(&inverse_screen_space_transform)) { - // TODO(shawnsingh): We shouldn't be applying a projection if screen space - // transform is uninvertible here. Perhaps we should be returning - // SCROLL_ON_MAIN_THREAD in this case? - } - - gfx::PointF hit_test_point_in_layer_space = MathUtil::ProjectPoint( - inverse_screen_space_transform, screen_space_point, &clipped); - if (!clipped && layer->non_fast_scrollable_region().Contains( - gfx::ToRoundedPoint(hit_test_point_in_layer_space))) { - TRACE_EVENT0("cc", - "LayerImpl::tryScroll: Failed NonFastScrollableRegion"); - scroll_status.thread = InputHandler::SCROLL_ON_MAIN_THREAD; - scroll_status.main_thread_scrolling_reasons = - MainThreadScrollingReason::kNonFastScrollableRegion; - return scroll_status; - } - } - if (!scroll_node->scrollable) { TRACE_EVENT0("cc", "LayerImpl::tryScroll: Ignored not scrollable"); scroll_status.thread = InputHandler::SCROLL_IGNORED; @@ -3578,9 +3768,21 @@ static bool IsMainThreadScrolling(const InputHandler::ScrollStatus& status, return false; } +base::flat_set<int> LayerTreeHostImpl::NonFastScrollableNodes( + const gfx::PointF& device_viewport_point) const { + base::flat_set<int> non_fast_scrollable_nodes; + + const auto& non_fast_layers = + active_tree_->FindLayersHitByPointInNonFastScrollableRegion( + device_viewport_point); + for (const auto* layer : non_fast_layers) + non_fast_scrollable_nodes.insert(layer->scroll_tree_index()); + + return non_fast_scrollable_nodes; +} + ScrollNode* LayerTreeHostImpl::FindScrollNodeForDeviceViewportPoint( const gfx::PointF& device_viewport_point, - InputHandler::ScrollInputType type, LayerImpl* layer_impl, bool* scroll_on_main_thread, uint32_t* main_thread_scrolling_reasons) const { @@ -3589,6 +3791,9 @@ ScrollNode* LayerTreeHostImpl::FindScrollNodeForDeviceViewportPoint( *main_thread_scrolling_reasons = MainThreadScrollingReason::kNotScrollingOnMain; + const auto& non_fast_scrollable_nodes = + NonFastScrollableNodes(device_viewport_point); + // Walk up the hierarchy and look for a scrollable layer. ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree; ScrollNode* impl_scroll_node = nullptr; @@ -3611,13 +3816,20 @@ ScrollNode* LayerTreeHostImpl::FindScrollNodeForDeviceViewportPoint( // The content layer can also block attempts to scroll outside the main // thread. ScrollStatus status = - TryScroll(device_viewport_point, type, scroll_tree, scroll_node); + TryScroll(device_viewport_point, scroll_tree, scroll_node); if (IsMainThreadScrolling(status, scroll_node)) { *scroll_on_main_thread = true; *main_thread_scrolling_reasons = status.main_thread_scrolling_reasons; return scroll_node; } + if (non_fast_scrollable_nodes.contains(scroll_node->id)) { + *scroll_on_main_thread = true; + *main_thread_scrolling_reasons = + MainThreadScrollingReason::kNonFastScrollableRegion; + return scroll_node; + } + if (status.thread == InputHandler::SCROLL_ON_IMPL_THREAD && !impl_scroll_node) { impl_scroll_node = scroll_node; @@ -3638,10 +3850,14 @@ ScrollNode* LayerTreeHostImpl::FindScrollNodeForDeviceViewportPoint( if (impl_scroll_node) { // Ensure that final layer scrolls on impl thread (crbug.com/625100) ScrollStatus status = - TryScroll(device_viewport_point, type, scroll_tree, impl_scroll_node); + TryScroll(device_viewport_point, scroll_tree, impl_scroll_node); if (IsMainThreadScrolling(status, impl_scroll_node)) { *scroll_on_main_thread = true; *main_thread_scrolling_reasons = status.main_thread_scrolling_reasons; + } else if (non_fast_scrollable_nodes.contains(impl_scroll_node->id)) { + *scroll_on_main_thread = true; + *main_thread_scrolling_reasons = + MainThreadScrollingReason::kNonFastScrollableRegion; } } @@ -3710,10 +3926,13 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBeginImpl( scroll_status.bubble = true; } + scroll_frame_tracker_ = frame_trackers_.CreateTracker( + wheel_scrolling_ ? FrameSequenceTrackerType::kWheelScroll + : FrameSequenceTrackerType::kTouchScroll); client_->RenewTreePriority(); RecordCompositorSlowScrollMetric(type, CC_THREAD); - UpdateScrollSourceInfo(type); + UpdateScrollSourceInfo(type, scroll_state); return scroll_status; } @@ -3796,7 +4015,7 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin( } scrolling_node = FindScrollNodeForDeviceViewportPoint( - device_viewport_point, type, layer_impl, &scroll_on_main_thread, + device_viewport_point, layer_impl, &scroll_on_main_thread, &scroll_status.main_thread_scrolling_reasons); } @@ -3943,14 +4162,31 @@ gfx::Vector2dF LayerTreeHostImpl::ComputeScrollDelta( return gfx::Vector2dF(scrolled.x(), scrolled.y()); } +bool LayerTreeHostImpl::AutoScrollAnimationCreate(ScrollNode* scroll_node, + const gfx::Vector2dF& delta, + float autoscroll_velocity) { + return ScrollAnimationCreateInternal(scroll_node, delta, base::TimeDelta(), + autoscroll_velocity); +} + bool LayerTreeHostImpl::ScrollAnimationCreate(ScrollNode* scroll_node, const gfx::Vector2dF& delta, base::TimeDelta delayed_by) { + return ScrollAnimationCreateInternal(scroll_node, delta, delayed_by, + base::nullopt); +} + +bool LayerTreeHostImpl::ScrollAnimationCreateInternal( + ScrollNode* scroll_node, + const gfx::Vector2dF& delta, + base::TimeDelta delayed_by, + base::Optional<float> autoscroll_velocity) { ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree; const float kEpsilon = 0.1f; bool scroll_animated = - (std::abs(delta.x()) > kEpsilon || std::abs(delta.y()) > kEpsilon); + (std::abs(delta.x()) > kEpsilon || std::abs(delta.y()) > kEpsilon) || + autoscroll_velocity; if (!scroll_animated) { scroll_tree.ScrollBy(scroll_node, delta, active_tree()); TRACE_EVENT_INSTANT0("cc", "no scroll animation due to small delta", @@ -3970,9 +4206,15 @@ bool LayerTreeHostImpl::ScrollAnimationCreate(ScrollNode* scroll_node, // input latency tracking architecture from working. base::TimeDelta animation_start_offset = CurrentBeginFrameArgs().interval; - mutator_host_->ImplOnlyScrollAnimationCreate( - scroll_node->element_id, target_offset, current_offset, delayed_by, - animation_start_offset); + if (autoscroll_velocity) { + mutator_host_->ImplOnlyAutoScrollAnimationCreate( + scroll_node->element_id, gfx::ScrollOffset(delta), current_offset, + autoscroll_velocity.value(), animation_start_offset); + } else { + mutator_host_->ImplOnlyScrollAnimationCreate( + scroll_node->element_id, target_offset, current_offset, delayed_by, + animation_start_offset); + } SetNeedsOneBeginImplFrame(); @@ -4762,6 +5004,7 @@ void LayerTreeHostImpl::ScrollEndImpl(ScrollState* scroll_state) { DistributeScrollDelta(scroll_state); browser_controls_offset_manager_->ScrollEnd(); ClearCurrentlyScrollingNode(); + frame_trackers_.ScheduleRemoval(std::move(scroll_frame_tracker_)); } void LayerTreeHostImpl::ScrollEnd(ScrollState* scroll_state, bool should_snap) { @@ -4843,8 +5086,8 @@ InputHandlerPointerResult LayerTreeHostImpl::MouseMoveAt( bool scroll_on_main_thread = false; uint32_t main_thread_scrolling_reasons; auto* scroll_node = FindScrollNodeForDeviceViewportPoint( - device_viewport_point, InputHandler::TOUCHSCREEN, layer_impl, - &scroll_on_main_thread, &main_thread_scrolling_reasons); + device_viewport_point, layer_impl, &scroll_on_main_thread, + &main_thread_scrolling_reasons); if (scroll_node) scroll_element_id = scroll_node->element_id; @@ -4895,6 +5138,8 @@ void LayerTreeHostImpl::PinchGestureBegin() { OuterViewportScrollNode() ? false : true); active_tree_->SetCurrentlyScrollingNode(OuterViewportScrollNode()); browser_controls_offset_manager_->PinchBegin(); + pinch_frame_tracker_ = + frame_trackers_.CreateTracker(FrameSequenceTrackerType::kPinchZoom); } void LayerTreeHostImpl::PinchGestureUpdate(float magnify_delta, @@ -4902,6 +5147,7 @@ void LayerTreeHostImpl::PinchGestureUpdate(float magnify_delta, TRACE_EVENT0("cc", "LayerTreeHostImpl::PinchGestureUpdate"); if (!InnerViewportScrollNode()) return; + has_pinch_zoomed_ = true; viewport()->PinchUpdate(magnify_delta, anchor); client_->SetNeedsCommitOnImplThread(); SetNeedsRedraw(); @@ -4926,6 +5172,7 @@ void LayerTreeHostImpl::PinchGestureEnd(const gfx::Point& anchor, // scales that we want when we're not inside a pinch. active_tree_->set_needs_update_draw_properties(); SetNeedsRedraw(); + frame_trackers_.ScheduleRemoval(std::move(pinch_frame_tracker_)); } void LayerTreeHostImpl::CollectScrollDeltas( @@ -4971,10 +5218,23 @@ std::unique_ptr<ScrollAndScaleSet> LayerTreeHostImpl::ProcessScrollDeltas() { scroll_info->swap_promises.swap(swap_promises_for_main_thread_scroll_update_); // Record and reset scroll source flags. - scroll_info->has_scrolled_by_wheel = has_scrolled_by_wheel_; - scroll_info->has_scrolled_by_touch = has_scrolled_by_touch_; + if (has_scrolled_by_wheel_) { + scroll_info->manipulation_info |= kManipulationInfoHasScrolledByWheel; + } + if (has_scrolled_by_touch_) { + scroll_info->manipulation_info |= kManipulationInfoHasScrolledByTouch; + } + if (has_scrolled_by_precisiontouchpad_) { + scroll_info->manipulation_info |= + kManipulationInfoHasScrolledByPrecisionTouchPad; + } + if (has_pinch_zoomed_) { + scroll_info->manipulation_info |= kManipulationInfoHasPinchZoomed; + } + + has_scrolled_by_wheel_ = has_scrolled_by_touch_ = + has_scrolled_by_precisiontouchpad_ = has_pinch_zoomed_ = false; scroll_info->scroll_gesture_did_end = scroll_gesture_did_end_; - has_scrolled_by_wheel_ = has_scrolled_by_touch_ = false; // Record and reset overscroll delta. scroll_info->overscroll_delta = overscroll_delta_for_main_thread_; @@ -5091,8 +5351,17 @@ bool LayerTreeHostImpl::AnimateLayers(base::TimeTicks monotonic_time, // TODO(crbug.com/551138): We currently have a single signal from the // animation_host, so on the last frame of an animation we will // still request an extra SetNeedsAnimate here. - if (animated) + if (animated) { SetNeedsOneBeginImplFrame(); + if (!compositor_animation_frame_tracker_) { + compositor_animation_frame_tracker_ = frame_trackers_.CreateTracker( + FrameSequenceTrackerType::kCompositorAnimation); + } + } else { + frame_trackers_.ScheduleRemoval( + std::move(compositor_animation_frame_tracker_)); + } + // TODO(crbug.com/551138): We could return true only if the animaitons are on // the active tree. There's no need to cause a draw to take place from // animations starting/ticking on the pending tree. @@ -5371,7 +5640,7 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, GLenum texture_target = GL_TEXTURE_2D; // For software compositing, shared memory will be allocated and the // UIResource will be copied into it. - base::MappedReadOnlyRegion mapped_region; + base::MappedReadOnlyRegion shm; viz::SharedBitmapId shared_bitmap_id; bool overlay_candidate = false; @@ -5389,8 +5658,7 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, BufferFormat(format), caps); } } else { - mapped_region = - viz::bitmap_allocation::AllocateSharedBitmap(upload_size, format); + shm = viz::bitmap_allocation::AllocateSharedBitmap(upload_size, format); shared_bitmap_id = viz::SharedBitmap::GenerateId(); } @@ -5415,7 +5683,7 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, SkImageInfo::MakeN32Premul(gfx::SizeToSkISize(upload_size)); sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect( - dst_info, mapped_region.mapping.memory(), dst_info.minRowBytes()); + dst_info, shm.mapping.memory(), dst_info.minRowBytes()); surface->getCanvas()->writePixels( src_info, const_cast<uint8_t*>(bitmap.GetPixels()), src_info.minRowBytes(), 0, 0); @@ -5451,7 +5719,7 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, SkImageInfo dst_info = SkImageInfo::MakeN32Premul(gfx::SizeToSkISize(upload_size)); scaled_surface = SkSurface::MakeRasterDirect( - dst_info, mapped_region.mapping.memory(), dst_info.minRowBytes()); + dst_info, shm.mapping.memory(), dst_info.minRowBytes()); } SkCanvas* scaled_canvas = scaled_surface->getCanvas(); scaled_canvas->scale(canvas_scale_x, canvas_scale_y); @@ -5494,9 +5762,8 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, overlay_candidate); transferable.format = format; } else { - layer_tree_frame_sink_->DidAllocateSharedBitmap( - viz::bitmap_allocation::ToMojoHandle(std::move(mapped_region.region)), - shared_bitmap_id); + layer_tree_frame_sink_->DidAllocateSharedBitmap(std::move(shm.region), + shared_bitmap_id); transferable = viz::TransferableResource::MakeSoftware(shared_bitmap_id, upload_size, format); } @@ -5514,7 +5781,7 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, data.opaque = bitmap.GetOpaque(); data.format = format; data.shared_bitmap_id = shared_bitmap_id; - data.shared_mapping = std::move(mapped_region.mapping); + data.shared_mapping = std::move(shm.mapping); data.mailbox = mailbox; data.resource_id_for_export = id; ui_resource_map_[uid] = std::move(data); @@ -5916,11 +6183,18 @@ void LayerTreeHostImpl::SetContextVisibility(bool is_visible) { } void LayerTreeHostImpl::UpdateScrollSourceInfo( - InputHandler::ScrollInputType type) { - if (type == InputHandler::WHEEL) + InputHandler::ScrollInputType type, + ScrollState* scroll_state) { + if (type == InputHandler::WHEEL && + scroll_state->delta_granularity() == + static_cast<double>( + ui::input_types::ScrollGranularity::kScrollByPrecisePixel)) { + has_scrolled_by_precisiontouchpad_ = true; + } else if (type == InputHandler::WHEEL) { has_scrolled_by_wheel_ = true; - else if (type == InputHandler::TOUCHSCREEN) + } else if (type == InputHandler::TOUCHSCREEN) { has_scrolled_by_touch_ = true; + } } void LayerTreeHostImpl::ShowScrollbarsForImplScroll(ElementId element_id) { diff --git a/chromium/cc/trees/layer_tree_host_impl.h b/chromium/cc/trees/layer_tree_host_impl.h index bb89bc998a4..ca403dbfd97 100644 --- a/chromium/cc/trees/layer_tree_host_impl.h +++ b/chromium/cc/trees/layer_tree_host_impl.h @@ -27,14 +27,17 @@ #include "cc/input/scrollbar_animation_controller.h" #include "cc/input/scrollbar_controller.h" #include "cc/layers/layer_collections.h" +#include "cc/paint/paint_worklet_job.h" #include "cc/resources/ui_resource_client.h" #include "cc/scheduler/begin_frame_tracker.h" #include "cc/scheduler/commit_earlyout_reason.h" #include "cc/scheduler/draw_result.h" +#include "cc/scheduler/scheduler.h" #include "cc/scheduler/video_frame_controller.h" #include "cc/tiles/decoded_image_tracker.h" #include "cc/tiles/image_decode_cache.h" #include "cc/tiles/tile_manager.h" +#include "cc/trees/frame_sequence_tracker.h" #include "cc/trees/layer_tree_frame_sink_client.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_mutator.h" @@ -156,10 +159,16 @@ class LayerTreeHostImplClient { std::vector<LayerTreeHost::PresentationTimeCallback> callbacks, const gfx::PresentationFeedback& feedback) = 0; + // Returns whether the main-thread is expected to receive a BeginMainFrame. + virtual bool IsBeginMainFrameExpected() = 0; + virtual void NotifyAnimationWorkletStateChange( AnimationWorkletMutationState state, ElementListType tree_type) = 0; + virtual void NotifyPaintWorkletStateChange( + Scheduler::PaintWorkletState state) = 0; + protected: virtual ~LayerTreeHostImplClient() {} }; @@ -187,6 +196,9 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, FrameData& operator=(const FrameData&) = delete; void AsValueInto(base::trace_event::TracedValue* value) const; + uint32_t frame_token = 0; + // frame_token is populated by the LayerTreeHostImpl when submitted. + std::vector<viz::SurfaceId> activation_dependencies; base::Optional<uint32_t> deadline_in_frames; bool use_default_lower_bound_deadline = false; @@ -196,6 +208,13 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, bool has_no_damage = false; bool may_contain_video = false; viz::BeginFrameAck begin_frame_ack; + // The original BeginFrameArgs that triggered the latest update from the + // main thread. + viz::BeginFrameArgs origin_begin_main_frame_args; + + // Union of visible rects of MirrorLayers in the frame, used to force damage + // on the surface. + gfx::Rect mirror_rect; }; // A struct of data for a single UIResource, including the backing @@ -275,8 +294,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, void SetNeedsAnimateInput() override; bool IsCurrentlyScrollingViewport() const override; bool IsCurrentlyScrollingLayerAt( - const gfx::Point& viewport_point, - InputHandler::ScrollInputType type) const override; + const gfx::Point& viewport_point) const override; EventListenerProperties GetEventListenerProperties( EventListenerClass event_class) const override; InputHandler::TouchStartOrMoveEventListenerType @@ -320,10 +338,11 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, } virtual void WillSendBeginMainFrame() {} - virtual void DidSendBeginMainFrame() {} + virtual void DidSendBeginMainFrame(const viz::BeginFrameArgs& args); virtual void BeginMainFrameAborted( CommitEarlyOutReason reason, - std::vector<std::unique_ptr<SwapPromise>> swap_promises); + std::vector<std::unique_ptr<SwapPromise>> swap_promises, + const viz::BeginFrameArgs& args); virtual void ReadyToCommit() {} // For tests. virtual void BeginCommit(); virtual void CommitComplete(); @@ -432,6 +451,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, DrawMode GetDrawMode() const; + void DidNotNeedBeginFrame(); + // TileManagerClient implementation. void NotifyReadyToActivate() override; void NotifyReadyToDraw() override; @@ -471,7 +492,6 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, void DidPresentCompositorFrame( uint32_t frame_token, const gfx::PresentationFeedback& feedback) override; - void DidNotNeedBeginFrame() override; void ReclaimResources( const std::vector<viz::ReturnedResource>& resources) override; void SetMemoryPolicy(const ManagedMemoryPolicy& policy) override; @@ -506,6 +526,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, int* max_msaa_samples, bool* supports_disable_msaa); bool use_gpu_rasterization() const { return use_gpu_rasterization_; } + bool use_oop_rasterization() const { return use_oop_rasterization_; } bool use_msaa() const { return use_msaa_; } GpuRasterizationStatus gpu_rasterization_status() const { @@ -710,19 +731,30 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, } InputHandler::ScrollStatus TryScroll(const gfx::PointF& screen_space_point, - InputHandler::ScrollInputType type, const ScrollTree& scroll_tree, ScrollNode* scroll_node) const; + // Return all ScrollNode indices that have an associated layer with a non-fast + // region that intersects the point. + base::flat_set<int> NonFastScrollableNodes( + const gfx::PointF& device_viewport_point) const; + // Returns true if a scroll offset animation is created and false if we scroll // by the desired amount without an animation. bool ScrollAnimationCreate(ScrollNode* scroll_node, const gfx::Vector2dF& scroll_amount, base::TimeDelta delayed_by); + bool AutoScrollAnimationCreate(ScrollNode* scroll_node, + const gfx::Vector2dF& scroll_amount, + float autoscroll_velocity); void SetLayerTreeMutator(std::unique_ptr<LayerTreeMutator> mutator); + void SetPaintWorkletLayerPainter( std::unique_ptr<PaintWorkletLayerPainter> painter); + PaintWorkletLayerPainter* GetPaintWorkletLayerPainterForTesting() const { + return paint_worklet_painter_.get(); + } // The viewport has two scroll nodes, corresponding to the visual and layout // viewports. However, when we compute the scroll chain we include only one @@ -759,6 +791,10 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, return compositor_frame_reporting_controller_.get(); } + void set_pending_tree_fully_painted_for_testing(bool painted) { + pending_tree_fully_painted_ = painted; + } + protected: LayerTreeHostImpl( const LayerTreeSettings& settings, @@ -810,6 +846,10 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, const gfx::PointF& viewport_point, const gfx::Vector2dF& viewport_delta, ScrollTree* scroll_tree); + bool ScrollAnimationCreateInternal(ScrollNode* scroll_node, + const gfx::Vector2dF& delta, + base::TimeDelta delayed_by, + base::Optional<float> autoscroll_velocity); void CleanUpTileManagerResources(); void CreateTileManagerResources(); @@ -824,6 +864,18 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, // impl thread. void UpdateSyncTreeAfterCommitOrImplSideInvalidation(); + // Returns a job map for all 'dirty' PaintWorklets, e.g. PaintWorkletInputs + // that do not map to a PaintRecord. + PaintWorkletJobMap GatherDirtyPaintWorklets() const; + + // Called when all PaintWorklet results are ready (i.e. have been painted) for + // the current pending tree. + void OnPaintWorkletResultsReady(PaintWorkletJobMap results); + + // Called when the pending tree has been fully painted, i.e. all required data + // is available to raster the tree. + void NotifyPendingTreeFullyPainted(); + // Returns true if status changed. bool UpdateGpuRasterizationStatus(); void UpdateTreeResourcesForGpuRasterizationIfNeeded(); @@ -862,7 +914,6 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, ScrollNode* FindScrollNodeForDeviceViewportPoint( const gfx::PointF& device_viewport_point, - InputHandler::ScrollInputType type, LayerImpl* layer_hit_by_point, bool* scroll_on_main_thread, uint32_t* main_thread_scrolling_reason) const; @@ -910,7 +961,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, // thread side to keep track of the frequency of scrolling with different // sources per page load. TODO(crbug.com/691886): Use GRC API to plumb the // scroll source info for Use Counters. - void UpdateScrollSourceInfo(InputHandler::ScrollInputType type); + void UpdateScrollSourceInfo(InputHandler::ScrollInputType type, + ScrollState* scroll_state); bool IsScrolledBy(LayerImpl* child, ScrollNode* ancestor); void ShowScrollbarsForImplScroll(ElementId element_id); @@ -931,6 +983,13 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, void AllocateLocalSurfaceId(); const LayerTreeSettings settings_; + + // This is set to true only if: + // . The compositor is running single-threaded (i.e. there is no separate + // compositor/impl thread). + // . There is no scheduler (which means layer-update, composite, etc. steps + // happen explicitly via. synchronous calls to appropriate functions). + // This is usually turned on only in some tests (e.g. web-tests). const bool is_synchronous_single_threaded_; const int default_color_space_id_ = gfx::ColorSpace::GetNextId(); @@ -1138,13 +1197,21 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, // success) state. std::vector<std::pair<int, bool>> completed_image_decode_requests_; - // These are used to transfer usage of touch and wheel scrolls to the main - // thread. + // These are used to transfer usage of different types of scrolling to the + // main thread. bool has_scrolled_by_wheel_ = false; bool has_scrolled_by_touch_ = false; + bool has_scrolled_by_precisiontouchpad_ = false; + bool has_pinch_zoomed_ = false; ImplThreadPhase impl_thread_phase_ = ImplThreadPhase::IDLE; + // Tracks whether a BeginMainFrame is expected to be dispatched during an + // 'impl frame' (i.e. between WillBeginImplFrame() and DidFinishImplFrame()), + // and whether it was actually dispatched during the impl frame. + bool begin_main_frame_expected_during_impl_ = false; + bool begin_main_frame_sent_during_impl_ = false; + ImageAnimationController image_animation_controller_; std::unique_ptr<UkmManager> ukm_manager_; @@ -1172,6 +1239,13 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, // Manages composited scrollbar hit testing. std::unique_ptr<ScrollbarController> scrollbar_controller_; + FrameSequenceTrackerCollection frame_trackers_; + std::unique_ptr<FrameSequenceTracker> pinch_frame_tracker_; + std::unique_ptr<FrameSequenceTracker> scroll_frame_tracker_; + std::unique_ptr<FrameSequenceTracker> compositor_animation_frame_tracker_; + std::unique_ptr<FrameSequenceTracker> request_animation_frame_tracker_; + std::unique_ptr<FrameSequenceTracker> main_thread_animation_frame_tracker_; + // Set to true when a scroll gesture being handled on the compositor has // ended. i.e. When a GSE has arrived and any ongoing scroll animation has // ended. @@ -1187,9 +1261,18 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, // once the animation is over. base::Optional<ScrollState> deferred_scroll_end_state_; + // PaintWorklet painting is controlled from the LayerTreeHostImpl, dispatched + // to the worklet thread via |paint_worklet_painter_|. + std::unique_ptr<PaintWorkletLayerPainter> paint_worklet_painter_; + + // While PaintWorklet painting is ongoing the PendingTree is not yet fully + // painted and cannot be rastered or activated. This boolean tracks whether or + // not we are in that state. + bool pending_tree_fully_painted_ = false; + // 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_; + base::WeakPtrFactory<LayerTreeHostImpl> weak_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_impl_unittest.cc b/chromium/cc/trees/layer_tree_host_impl_unittest.cc index 80d1f14a001..24ceca1c40c 100644 --- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc @@ -55,6 +55,8 @@ #include "cc/test/layer_test_common.h" #include "cc/test/layer_tree_test.h" #include "cc/test/skia_common.h" +#include "cc/test/test_layer_tree_frame_sink.h" +#include "cc/test/test_paint_worklet_layer_painter.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/clip_node.h" #include "cc/trees/draw_property_utils.h" @@ -81,7 +83,6 @@ #include "components/viz/test/begin_frame_args_test.h" #include "components/viz/test/fake_output_surface.h" #include "components/viz/test/fake_skia_output_surface.h" -#include "components/viz/test/test_layer_tree_frame_sink.h" #include "gpu/GLES2/gl2extchromium.h" #include "media/base/media.h" #include "testing/gmock/include/gmock/gmock.h" @@ -135,6 +136,7 @@ class LayerTreeHostImplTest : public testing::Test, did_request_redraw_(false), did_request_next_frame_(false), did_request_prepare_tiles_(false), + did_prepare_tiles_(false), did_complete_page_scale_animation_(false), reduce_memory_result_(true), did_request_impl_side_invalidation_(false) { @@ -159,6 +161,13 @@ class LayerTreeHostImplTest : public testing::Test, host_impl_->active_tree()->GetDeviceViewport().size()); pending_tree->SetDeviceScaleFactor( host_impl_->active_tree()->device_scale_factor()); + // Normally a pending tree will not be fully painted until the commit has + // happened and any PaintWorklets have been resolved. However many of the + // unittests never actually commit the pending trees that they create, so to + // enable them to still treat the tree as painted we forcibly override the + // state here. Note that this marks a distinct departure from reality in the + // name of easier testing. + host_impl_->set_pending_tree_fully_painted_for_testing(true); } void TearDown() override { @@ -189,6 +198,7 @@ class LayerTreeHostImplTest : public testing::Test, void PostAnimationEventsToMainThreadOnImplThread( std::unique_ptr<MutatorEvents> events) override {} bool IsInsideDraw() override { return false; } + bool IsBeginMainFrameExpected() override { return true; } void RenewTreePriority() override {} void PostDelayedAnimationTaskOnImplThread(base::OnceClosure task, base::TimeDelta delay) override { @@ -203,7 +213,7 @@ class LayerTreeHostImplTest : public testing::Test, base::TimeTicks::Now())); } void WillPrepareTiles() override {} - void DidPrepareTiles() override {} + void DidPrepareTiles() override { did_prepare_tiles_ = true; } void DidCompletePageScaleAnimationOnImplThread() override { did_complete_page_scale_animation_ = true; } @@ -229,6 +239,8 @@ class LayerTreeHostImplTest : public testing::Test, const gfx::PresentationFeedback& feedback) override {} void NotifyAnimationWorkletStateChange(AnimationWorkletMutationState state, ElementListType tree_type) override {} + void NotifyPaintWorkletStateChange( + Scheduler::PaintWorkletState state) override {} void set_reduce_memory_result(bool reduce_memory_result) { reduce_memory_result_ = reduce_memory_result; @@ -796,6 +808,7 @@ class LayerTreeHostImplTest : public testing::Test, bool did_request_redraw_; bool did_request_next_frame_; bool did_request_prepare_tiles_; + bool did_prepare_tiles_; bool did_complete_page_scale_animation_; bool reduce_memory_result_; bool did_request_impl_side_invalidation_; @@ -852,7 +865,8 @@ class TestInputHandlerClient : public InputHandlerClient { min_page_scale_factor_ = min_page_scale_factor; max_page_scale_factor_ = max_page_scale_factor; } - void DeliverInputForBeginFrame() override {} + void DeliverInputForBeginFrame(const viz::BeginFrameArgs& args) override {} + void DeliverInputForHighLatencyMode() override {} gfx::ScrollOffset last_set_scroll_offset() { return last_set_scroll_offset_; } @@ -1095,14 +1109,11 @@ TEST_F(LayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) { EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(), - InputHandler::WHEEL)); + EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point())); host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, 10), - InputHandler::WHEEL)); + EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, 10))); host_impl_->ScrollEnd(EndState().get()); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(), - InputHandler::WHEEL)); + EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point())); EXPECT_TRUE(did_request_redraw_); EXPECT_TRUE(did_request_commit_); } @@ -1374,16 +1385,14 @@ TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) { EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, status.main_thread_scrolling_reasons); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25), - InputHandler::WHEEL)); + EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25))); status = host_impl_->ScrollBegin(BeginState(gfx::Point(25, 25)).get(), InputHandler::TOUCHSCREEN); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, status.main_thread_scrolling_reasons); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt( - gfx::Point(25, 25), InputHandler::TOUCHSCREEN)); + EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25))); // All scroll types outside this region should succeed. status = host_impl_->ScrollBegin(BeginState(gfx::Point(75, 75)).get(), @@ -1392,26 +1401,21 @@ TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) { EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt( - gfx::Point(75, 75), InputHandler::TOUCHSCREEN)); + EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75))); host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt( - gfx::Point(25, 25), InputHandler::TOUCHSCREEN)); + EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25))); host_impl_->ScrollEnd(EndState().get()); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt( - gfx::Point(75, 75), InputHandler::TOUCHSCREEN)); + EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75))); status = host_impl_->ScrollBegin(BeginState(gfx::Point(75, 75)).get(), InputHandler::TOUCHSCREEN); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt( - gfx::Point(75, 75), InputHandler::TOUCHSCREEN)); + EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75))); host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 10)).get()); host_impl_->ScrollEnd(EndState().get()); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt( - gfx::Point(75, 75), InputHandler::TOUCHSCREEN)); + EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75))); } TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) { @@ -1434,8 +1438,7 @@ TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) { EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(40, 10), - InputHandler::WHEEL)); + EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(40, 10))); host_impl_->ScrollBy(UpdateState(gfx::Point(), gfx::Vector2d(0, 1)).get()); host_impl_->ScrollEnd(EndState().get()); @@ -10410,7 +10413,88 @@ TEST_F(LayerTreeHostImplTest, EXPECT_FALSE(host_impl_->EvictedUIResourcesExist()); } -class FrameSinkClient : public viz::TestLayerTreeFrameSinkClient { +TEST_F(LayerTreeHostImplTest, ObeyMSAACaps) { + LayerTreeSettings msaaSettings = DefaultSettings(); + msaaSettings.gpu_rasterization_msaa_sample_count = 4; + + // gpu raster, msaa on + { + bool msaa_is_slow = false; + EXPECT_TRUE(CreateHostImpl( + msaaSettings, + FakeLayerTreeFrameSink::Create3dForGpuRasterization( + msaaSettings.gpu_rasterization_msaa_sample_count, msaa_is_slow))); + + host_impl_->SetHasGpuRasterizationTrigger(true); + host_impl_->SetContentHasSlowPaths(true); + host_impl_->CommitComplete(); + + EXPECT_EQ(GpuRasterizationStatus::MSAA_CONTENT, + host_impl_->gpu_rasterization_status()); + EXPECT_TRUE(host_impl_->use_gpu_rasterization()); + EXPECT_FALSE(host_impl_->use_oop_rasterization()); + EXPECT_TRUE(host_impl_->use_msaa()); + } + + // gpu raster, msaa off + { + bool msaa_is_slow = true; + EXPECT_TRUE(CreateHostImpl( + msaaSettings, + FakeLayerTreeFrameSink::Create3dForGpuRasterization( + msaaSettings.gpu_rasterization_msaa_sample_count, msaa_is_slow))); + + host_impl_->SetHasGpuRasterizationTrigger(true); + host_impl_->SetContentHasSlowPaths(true); + host_impl_->CommitComplete(); + + EXPECT_EQ(GpuRasterizationStatus::ON, + host_impl_->gpu_rasterization_status()); + EXPECT_TRUE(host_impl_->use_gpu_rasterization()); + EXPECT_FALSE(host_impl_->use_oop_rasterization()); + EXPECT_FALSE(host_impl_->use_msaa()); + } + + // oop raster, msaa on + { + bool msaa_is_slow = false; + EXPECT_TRUE(CreateHostImpl( + msaaSettings, + FakeLayerTreeFrameSink::Create3dForOopRasterization( + msaaSettings.gpu_rasterization_msaa_sample_count, msaa_is_slow))); + + host_impl_->SetHasGpuRasterizationTrigger(true); + host_impl_->SetContentHasSlowPaths(true); + host_impl_->CommitComplete(); + + EXPECT_EQ(GpuRasterizationStatus::MSAA_CONTENT, + host_impl_->gpu_rasterization_status()); + EXPECT_TRUE(host_impl_->use_gpu_rasterization()); + EXPECT_TRUE(host_impl_->use_oop_rasterization()); + EXPECT_TRUE(host_impl_->use_msaa()); + } + + // oop raster, msaa off + { + bool msaa_is_slow = true; + EXPECT_TRUE(CreateHostImpl( + msaaSettings, + FakeLayerTreeFrameSink::Create3dForOopRasterization( + msaaSettings.gpu_rasterization_msaa_sample_count, msaa_is_slow))); + + host_impl_->SetHasGpuRasterizationTrigger(true); + host_impl_->SetContentHasSlowPaths(true); + host_impl_->CommitComplete(); + + EXPECT_EQ(GpuRasterizationStatus::ON, + host_impl_->gpu_rasterization_status()); + EXPECT_TRUE(host_impl_->use_gpu_rasterization()); + EXPECT_TRUE(host_impl_->use_oop_rasterization()); + EXPECT_FALSE(host_impl_->use_msaa()); + } +} + +class FrameSinkClient : public TestLayerTreeFrameSinkClient { public: explicit FrameSinkClient( scoped_refptr<viz::ContextProvider> display_context_provider) @@ -10464,7 +10548,7 @@ TEST_P(LayerTreeHostImplTestWithRenderer, ShutdownReleasesContext) { constexpr double refresh_rate = 60.0; viz::RendererSettings renderer_settings = viz::RendererSettings(); renderer_settings.use_skia_renderer = renderer_type() == RENDERER_SKIA; - auto layer_tree_frame_sink = std::make_unique<viz::TestLayerTreeFrameSink>( + auto layer_tree_frame_sink = std::make_unique<TestLayerTreeFrameSink>( context_provider, viz::TestContextProvider::CreateWorker(), nullptr, renderer_settings, base::ThreadTaskRunnerHandle::Get().get(), synchronous_composite, disable_display_vsync, refresh_rate); @@ -11611,16 +11695,14 @@ TEST_F(LayerTreeHostImplVirtualViewportTest, ->ScrollBegin(BeginState(gfx::Point()).get(), InputHandler::TOUCHSCREEN) .thread); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt( - gfx::Point(), InputHandler::TOUCHSCREEN)); + EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point())); // Scroll near the edge of the outer viewport. gfx::Vector2d scroll_delta(inner_viewport.width() / 2.f, inner_viewport.height() / 2.f); host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get()); inner_expected += scroll_delta; - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt( - gfx::Point(), InputHandler::TOUCHSCREEN)); + EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point())); EXPECT_VECTOR_EQ(inner_expected, inner_scroll->CurrentScrollOffset()); EXPECT_VECTOR_EQ(outer_expected, outer_scroll->CurrentScrollOffset()); @@ -11630,13 +11712,11 @@ TEST_F(LayerTreeHostImplVirtualViewportTest, // and outer viewport layers is perfect. host_impl_->ScrollBy( UpdateState(gfx::Point(), gfx::ScaleVector2d(scroll_delta, 2)).get()); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt( - gfx::Point(), InputHandler::TOUCHSCREEN)); + EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point())); outer_expected += scroll_delta; inner_expected += scroll_delta; host_impl_->ScrollEnd(EndState().get()); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt( - gfx::Point(), InputHandler::TOUCHSCREEN)); + EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point())); EXPECT_VECTOR_EQ(inner_expected, inner_scroll->CurrentScrollOffset()); EXPECT_VECTOR_EQ(outer_expected, outer_scroll->CurrentScrollOffset()); @@ -12452,16 +12532,13 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedAborted) { host_impl_ ->ScrollBegin(BeginState(gfx::Point(0, y)).get(), InputHandler::WHEEL) .thread); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, y), - InputHandler::WHEEL)); + EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, y))); host_impl_->ScrollBy( UpdateState(gfx::Point(0, y), gfx::Vector2d(0, 50)).get()); - EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, y + 50), - InputHandler::WHEEL)); + EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, y + 50))); std::unique_ptr<ScrollState> scroll_state_end = EndState(); host_impl_->ScrollEnd(scroll_state_end.get()); - EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(), - InputHandler::WHEEL)); + EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point())); // The instant scroll should have marked the smooth scroll animation as // aborted. @@ -12851,7 +12928,7 @@ TEST_F(LayerTreeHostImplTest, InvalidLayerNotAddedToRasterQueue) { layer->SetDrawsContent(true); layer->tilings()->AddTiling(gfx::AxisTransform2d(), raster_source_with_tiles); layer->UpdateRasterSource(raster_source_with_tiles, &empty_invalidation, - nullptr); + nullptr, nullptr); layer->tilings()->tiling_at(0)->set_resolution( TileResolution::HIGH_RESOLUTION); layer->tilings()->tiling_at(0)->CreateAllTilesForTesting(); @@ -13447,9 +13524,7 @@ TEST_F(LayerTreeHostImplTest, UpdatePageScaleFactorOnActiveTree) { CreateScrollAndContentsLayers(host_impl_->pending_tree(), gfx::Size(100, 100)); host_impl_->pending_tree()->BuildPropertyTreesForTesting(); - LOG(ERROR) << "ACTIVATE SYNC TREE"; host_impl_->ActivateSyncTree(); - LOG(ERROR) << "DONE ACTIVATE SYNC TREE"; DrawFrame(); CreatePendingTree(); @@ -13475,9 +13550,7 @@ TEST_F(LayerTreeHostImplTest, UpdatePageScaleFactorOnActiveTree) { page_scale_layer->transform_tree_index()); EXPECT_EQ(pending_tree_node->post_local_scale_factor, 2.f); - LOG(ERROR) << "2 ACTIVATE SYNC TREE"; host_impl_->ActivateSyncTree(); - LOG(ERROR) << "DONE 2 ACTIVATE SYNC TREE"; host_impl_->active_tree()->UpdateDrawProperties(); active_tree_node = host_impl_->active_tree()->property_trees()->transform_tree.Node( @@ -14839,5 +14912,194 @@ TEST_F(LayerTreeHostImplTest, TouchScrollOnAndroidScrollbar) { EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); } +TEST_F(CommitToPendingTreeLayerTreeHostImplTest, + CommitWithNoPaintWorkletLayerPainter) { + ASSERT_FALSE(host_impl_->GetPaintWorkletLayerPainterForTesting()); + host_impl_->CreatePendingTree(); + + // When there is no PaintWorkletLayerPainter registered, commits should finish + // immediately and move onto preparing tiles. + ASSERT_FALSE(did_prepare_tiles_); + host_impl_->CommitComplete(); + EXPECT_TRUE(did_prepare_tiles_); +} + +TEST_F(CommitToPendingTreeLayerTreeHostImplTest, CommitWithNoPaintWorklets) { + host_impl_->SetPaintWorkletLayerPainter( + std::make_unique<TestPaintWorkletLayerPainter>()); + host_impl_->CreatePendingTree(); + + // When there are no PaintWorklets in the committed display lists, commits + // should finish immediately and move onto preparing tiles. + ASSERT_FALSE(did_prepare_tiles_); + host_impl_->CommitComplete(); + EXPECT_TRUE(did_prepare_tiles_); +} + +TEST_F(CommitToPendingTreeLayerTreeHostImplTest, CommitWithDirtyPaintWorklets) { + auto painter_owned = std::make_unique<TestPaintWorkletLayerPainter>(); + TestPaintWorkletLayerPainter* painter = painter_owned.get(); + host_impl_->SetPaintWorkletLayerPainter(std::move(painter_owned)); + + // Setup the pending tree with a PictureLayerImpl that will contain + // PaintWorklets. + host_impl_->CreatePendingTree(); + std::unique_ptr<PictureLayerImpl> root_owned = PictureLayerImpl::Create( + host_impl_->pending_tree(), 1, Layer::LayerMaskType::NOT_MASK); + PictureLayerImpl* root = root_owned.get(); + host_impl_->pending_tree()->SetRootLayerForTesting(std::move(root_owned)); + + root->SetBounds(gfx::Size(100, 100)); + root->test_properties()->force_render_surface = true; + root->SetNeedsPushProperties(); + + // Add a PaintWorkletInput to the PictureLayerImpl. + scoped_refptr<RasterSource> raster_source_with_pws( + FakeRasterSource::CreateFilledWithPaintWorklet(root->bounds())); + Region empty_invalidation; + root->UpdateRasterSource(raster_source_with_pws, &empty_invalidation, nullptr, + nullptr); + + host_impl_->pending_tree()->SetElementIdsForTesting(); + host_impl_->pending_tree()->BuildPropertyTreesForTesting(); + + // Since we have dirty PaintWorklets, committing will not cause tile + // preparation to happen. Instead, it will be delayed until the callback + // passed to the PaintWorkletLayerPainter is called. + did_prepare_tiles_ = false; + host_impl_->CommitComplete(); + EXPECT_FALSE(did_prepare_tiles_); + + // Set up a result to have been 'painted'. + ASSERT_EQ(root->GetPaintWorkletRecordMap().size(), 1u); + scoped_refptr<PaintWorkletInput> input = + root->GetPaintWorkletRecordMap().begin()->first; + int worklet_id = input->WorkletId(); + + PaintWorkletJob painted_job(worklet_id, input); + sk_sp<PaintRecord> record = sk_make_sp<PaintRecord>(); + painted_job.SetOutput(record); + + auto painted_job_vector = base::MakeRefCounted<PaintWorkletJobVector>(); + painted_job_vector->data.push_back(std::move(painted_job)); + PaintWorkletJobMap painted_job_map; + painted_job_map[worklet_id] = std::move(painted_job_vector); + + // Finally, 'paint' the content. This should unlock tile preparation and + // update the PictureLayerImpl's map. + std::move(painter->TakeDoneCallback()).Run(std::move(painted_job_map)); + EXPECT_EQ(root->GetPaintWorkletRecordMap().find(input)->second, record); + EXPECT_TRUE(did_prepare_tiles_); +} + +TEST_F(CommitToPendingTreeLayerTreeHostImplTest, + CommitWithNoDirtyPaintWorklets) { + host_impl_->SetPaintWorkletLayerPainter( + std::make_unique<TestPaintWorkletLayerPainter>()); + + host_impl_->CreatePendingTree(); + std::unique_ptr<PictureLayerImpl> root_owned = PictureLayerImpl::Create( + host_impl_->pending_tree(), 1, Layer::LayerMaskType::NOT_MASK); + PictureLayerImpl* root = root_owned.get(); + host_impl_->pending_tree()->SetRootLayerForTesting(std::move(root_owned)); + + root->SetBounds(gfx::Size(100, 100)); + root->test_properties()->force_render_surface = true; + root->SetNeedsPushProperties(); + + // Add some PaintWorklets. + scoped_refptr<RasterSource> raster_source_with_pws( + FakeRasterSource::CreateFilledWithPaintWorklet(root->bounds())); + Region empty_invalidation; + root->UpdateRasterSource(raster_source_with_pws, &empty_invalidation, nullptr, + nullptr); + + host_impl_->pending_tree()->SetElementIdsForTesting(); + host_impl_->pending_tree()->BuildPropertyTreesForTesting(); + + // Pretend that our worklets were already painted. + ASSERT_EQ(root->GetPaintWorkletRecordMap().size(), 1u); + root->SetPaintWorkletRecord(root->GetPaintWorkletRecordMap().begin()->first, + sk_make_sp<PaintRecord>()); + + // Since there are no dirty PaintWorklets, the commit should immediately + // prepare tiles. + ASSERT_FALSE(did_prepare_tiles_); + host_impl_->CommitComplete(); + EXPECT_TRUE(did_prepare_tiles_); +} + +class ForceActivateAfterPaintWorkletPaintLayerTreeHostImplTest + : public CommitToPendingTreeLayerTreeHostImplTest { + public: + void NotifyPaintWorkletStateChange( + Scheduler::PaintWorkletState state) override { + if (state == Scheduler::PaintWorkletState::IDLE) { + // Pretend a force activation happened. + host_impl_->ActivateSyncTree(); + ASSERT_FALSE(host_impl_->pending_tree()); + } + } +}; + +TEST_F(ForceActivateAfterPaintWorkletPaintLayerTreeHostImplTest, + ForceActivationAfterPaintWorkletsFinishPainting) { + auto painter_owned = std::make_unique<TestPaintWorkletLayerPainter>(); + TestPaintWorkletLayerPainter* painter = painter_owned.get(); + host_impl_->SetPaintWorkletLayerPainter(std::move(painter_owned)); + + // Setup the pending tree with a PictureLayerImpl that will contain + // PaintWorklets. + host_impl_->CreatePendingTree(); + std::unique_ptr<PictureLayerImpl> root_owned = PictureLayerImpl::Create( + host_impl_->pending_tree(), 1, Layer::LayerMaskType::NOT_MASK); + PictureLayerImpl* root = root_owned.get(); + host_impl_->pending_tree()->SetRootLayerForTesting(std::move(root_owned)); + + root->SetBounds(gfx::Size(100, 100)); + root->test_properties()->force_render_surface = true; + root->SetNeedsPushProperties(); + + // Add a PaintWorkletInput to the PictureLayerImpl. + scoped_refptr<RasterSource> raster_source_with_pws( + FakeRasterSource::CreateFilledWithPaintWorklet(root->bounds())); + Region empty_invalidation; + root->UpdateRasterSource(raster_source_with_pws, &empty_invalidation, nullptr, + nullptr); + + host_impl_->pending_tree()->SetElementIdsForTesting(); + host_impl_->pending_tree()->BuildPropertyTreesForTesting(); + + // Since we have dirty PaintWorklets, committing will not cause tile + // preparation to happen. Instead, it will be delayed until the callback + // passed to the PaintWorkletLayerPainter is called. + did_prepare_tiles_ = false; + host_impl_->CommitComplete(); + EXPECT_FALSE(did_prepare_tiles_); + + // Set up a result to have been 'painted'. + ASSERT_EQ(root->GetPaintWorkletRecordMap().size(), 1u); + scoped_refptr<PaintWorkletInput> input = + root->GetPaintWorkletRecordMap().begin()->first; + int worklet_id = input->WorkletId(); + + PaintWorkletJob painted_job(worklet_id, input); + sk_sp<PaintRecord> record = sk_make_sp<PaintRecord>(); + painted_job.SetOutput(record); + + auto painted_job_vector = base::MakeRefCounted<PaintWorkletJobVector>(); + painted_job_vector->data.push_back(std::move(painted_job)); + PaintWorkletJobMap painted_job_map; + painted_job_map[worklet_id] = std::move(painted_job_vector); + + // Finally, 'paint' the content. The test class causes a forced activation + // during NotifyPaintWorkletStateChange. The PictureLayerImpl should still be + // updated, but since the tree was force activated there should be no tile + // preparation. + std::move(painter->TakeDoneCallback()).Run(std::move(painted_job_map)); + EXPECT_EQ(root->GetPaintWorkletRecordMap().find(input)->second, record); + EXPECT_FALSE(did_prepare_tiles_); +} + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_perftest.cc b/chromium/cc/trees/layer_tree_host_perftest.cc index 92fd3570952..c6036f13f59 100644 --- a/chromium/cc/trees/layer_tree_host_perftest.cc +++ b/chromium/cc/trees/layer_tree_host_perftest.cc @@ -21,10 +21,10 @@ #include "cc/test/fake_content_layer_client.h" #include "cc/test/layer_tree_json_parser.h" #include "cc/test/layer_tree_test.h" +#include "cc/test/test_layer_tree_frame_sink.h" #include "cc/trees/layer_tree_impl.h" #include "components/viz/common/resources/single_release_callback.h" #include "components/viz/test/paths.h" -#include "components/viz/test/test_layer_tree_frame_sink.h" #include "gpu/command_buffer/common/mailbox.h" #include "gpu/command_buffer/common/sync_token.h" #include "testing/perf/perf_test.h" @@ -48,7 +48,7 @@ class LayerTreeHostPerfTest : public LayerTreeTest { measure_commit_cost_(false) { } - std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( const viz::RendererSettings& renderer_settings, double refresh_rate, scoped_refptr<viz::ContextProvider> compositor_context_provider, @@ -58,7 +58,7 @@ class LayerTreeHostPerfTest : public LayerTreeTest { bool synchronous_composite = !HasImplThread() && !layer_tree_host()->GetSettings().single_thread_proxy_scheduler; - return std::make_unique<viz::TestLayerTreeFrameSink>( + return std::make_unique<TestLayerTreeFrameSink>( compositor_context_provider, std::move(worker_context_provider), gpu_memory_buffer_manager(), renderer_settings, ImplThreadTaskRunner(), synchronous_composite, disable_display_vsync, refresh_rate); diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc index 3f0c28b4311..7d5ea2a2d08 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc @@ -14,7 +14,7 @@ #include "cc/paint/skia_paint_canvas.h" #include "cc/test/layer_tree_pixel_resource_test.h" #include "cc/test/pixel_comparator.h" -#include "components/viz/test/test_layer_tree_frame_sink.h" +#include "cc/test/test_layer_tree_frame_sink.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkSurface.h" @@ -71,7 +71,7 @@ class LayerTreeHostBlendingPixelTest public: LayerTreeHostBlendingPixelTest() : force_antialiasing_(false), force_blending_with_shaders_(false) { - pixel_comparator_.reset(new FuzzyPixelOffByOneComparator(true)); + pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true); } PixelResourceTestCase resource_type() const { @@ -82,7 +82,7 @@ class LayerTreeHostBlendingPixelTest } protected: - std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( const viz::RendererSettings& renderer_settings, double refresh_rate, scoped_refptr<viz::ContextProvider> compositor_context_provider, @@ -132,7 +132,7 @@ class LayerTreeHostBlendingPixelTest gfx::Size bounds = layer->bounds(); scoped_refptr<PictureImageLayer> mask = PictureImageLayer::Create(); mask->SetIsDrawable(true); - mask->SetLayerMaskType(Layer::LayerMaskType::MULTI_TEXTURE_MASK); + mask->SetLayerMaskType(Layer::LayerMaskType::SINGLE_TEXTURE_MASK); mask->SetBounds(bounds); sk_sp<SkSurface> surface = @@ -213,10 +213,10 @@ class LayerTreeHostBlendingPixelTest InitializeFromTestCase(resource_type()); // Force shaders only applies to gl renderer. - if (renderer_type_ != RENDERER_GL && flags & kForceShaders) + if (renderer_type() != RENDERER_GL && flags & kForceShaders) return; - SCOPED_TRACE(TestTypeToString(renderer_type_)); + SCOPED_TRACE(TestTypeToString(renderer_type())); SCOPED_TRACE(SkBlendMode_Name(current_blend_mode())); scoped_refptr<SolidColorLayer> root = CreateSolidColorLayer( @@ -229,28 +229,23 @@ class LayerTreeHostBlendingPixelTest CreateBlendingColorLayers(kRootWidth, kRootHeight, background.get(), flags); - this->force_antialiasing_ = (flags & kUseAntialiasing); - this->force_blending_with_shaders_ = (flags & kForceShaders); + force_antialiasing_ = (flags & kUseAntialiasing); + force_blending_with_shaders_ = (flags & kForceShaders); - if ((flags & kUseAntialiasing) && (renderer_type_ == RENDERER_GL)) { + if ((renderer_type() == RENDERER_GL && force_antialiasing_) || + renderer_type() == RENDERER_SKIA_VK) { // Blending results might differ with one pixel. - // Don't allow large errors here, only off by ones. - // However, large error still has to be specified to satisfy - // the pixel comparator so set it equivalent to small errors. + float percentage_pixels_error = 35.f; + float percentage_pixels_small_error = 0.f; + float average_error_allowed_in_bad_pixels = 1.f; int large_error_allowed = 1; - int small_error_allowed = 1; - float percentage_pixels_small_error = 35.0f; - float percentage_pixels_error = 35.0f; - // The average error is still close to 1. - float average_error_allowed_in_bad_pixels = 1.4f; - - pixel_comparator_.reset( - new FuzzyPixelComparator(false, // discard_alpha - percentage_pixels_error, - percentage_pixels_small_error, - average_error_allowed_in_bad_pixels, - large_error_allowed, - small_error_allowed)); + int small_error_allowed = 0; + + pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( + false, // discard_alpha + percentage_pixels_error, percentage_pixels_small_error, + average_error_allowed_in_bad_pixels, large_error_allowed, + small_error_allowed); } RunPixelResourceTest(root, CreateBlendingWithRenderPassExpected( @@ -262,19 +257,18 @@ class LayerTreeHostBlendingPixelTest SkColor misc_opaque_color_ = 0xffc86464; }; -INSTANTIATE_TEST_SUITE_P( - B, - LayerTreeHostBlendingPixelTest, - ::testing::Combine(::testing::Values(SOFTWARE, ZERO_COPY, SKIA_GL), - ::testing::ValuesIn(kBlendModes))); - -using LayerTreeHostBlendingPixelTestNonSkia = LayerTreeHostBlendingPixelTest; +std::vector<PixelResourceTestCase> const kTestCases = { + {LayerTreeTest::RENDERER_SOFTWARE, SOFTWARE}, + {LayerTreeTest::RENDERER_GL, ZERO_COPY}, + {LayerTreeTest::RENDERER_SKIA_GL, GPU}, +#if defined(ENABLE_CC_VULKAN_TESTS) + {LayerTreeTest::RENDERER_SKIA_VK, GPU}, +#endif +}; -// TODO(crbug.com/948128): Enable these tests for Skia. INSTANTIATE_TEST_SUITE_P(B, - LayerTreeHostBlendingPixelTestNonSkia, - ::testing::Combine(::testing::Values(SOFTWARE, - ZERO_COPY), + LayerTreeHostBlendingPixelTest, + ::testing::Combine(::testing::ValuesIn(kTestCases), ::testing::ValuesIn(kBlendModes))); TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithRoot) { @@ -304,7 +298,7 @@ TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithRoot) { RunPixelResourceTest(background, expected); } -TEST_P(LayerTreeHostBlendingPixelTestNonSkia, BlendingWithBackdropFilter) { +TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithBackdropFilter) { const int kRootWidth = 2; const int kRootHeight = 2; InitializeFromTestCase(resource_type()); diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc index 165f1d8879c..3ac848ed527 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc @@ -4,6 +4,7 @@ #include <stddef.h> +#include "base/strings/string_number_conversions.h" #include "build/build_config.h" #include "cc/layers/picture_layer.h" #include "cc/layers/solid_color_layer.h" @@ -21,14 +22,6 @@ class LayerTreeHostFiltersPixelTest : public LayerTreePixelTest, public ::testing::WithParamInterface<LayerTreeTest::RendererType> { protected: - void InitializeSettings(LayerTreeSettings* settings) override { - LayerTreePixelTest::InitializeSettings(settings); - // Required so that device scale is inherited by content scale. True for - // most tests, but can be overwritten before RunPixelTest() is called. - settings->layer_transforms_should_scale_layer_contents = - layer_transforms_should_scale_layer_contents_; - } - RendererType renderer_type() { return GetParam(); } // Text string for graphics backend of the RendererType. Suitable for @@ -38,7 +31,9 @@ class LayerTreeHostFiltersPixelTest case RENDERER_GL: return "gl"; case RENDERER_SKIA_GL: - return "skia"; + return "skia_gl"; + case RENDERER_SKIA_VK: + return "skia_vk"; case RENDERER_SOFTWARE: return "sw"; } @@ -71,23 +66,20 @@ class LayerTreeHostFiltersPixelTest return background; } +}; - bool layer_transforms_should_scale_layer_contents_ = true; +LayerTreeTest::RendererType const kRendererTypes[] = { + LayerTreeTest::RENDERER_GL, + LayerTreeTest::RENDERER_SKIA_GL, + LayerTreeTest::RENDERER_SOFTWARE, +#if defined(ENABLE_CC_VULKAN_TESTS) + LayerTreeTest::RENDERER_SKIA_VK, +#endif }; INSTANTIATE_TEST_SUITE_P(, LayerTreeHostFiltersPixelTest, - ::testing::Values(LayerTreeTest::RENDERER_GL, - LayerTreeTest::RENDERER_SKIA_GL, - LayerTreeTest::RENDERER_SOFTWARE)); - -using LayerTreeHostFiltersPixelTestNonSkia = LayerTreeHostFiltersPixelTest; - -// TODO(crbug.com/948128): Enable these tests for Skia. -INSTANTIATE_TEST_SUITE_P(, - LayerTreeHostFiltersPixelTestNonSkia, - ::testing::Values(LayerTreeTest::RENDERER_GL, - LayerTreeTest::RENDERER_SOFTWARE)); + ::testing::ValuesIn(kRendererTypes)); using LayerTreeHostFiltersPixelTestGL = LayerTreeHostFiltersPixelTest; @@ -98,12 +90,19 @@ INSTANTIATE_TEST_SUITE_P(, using LayerTreeHostFiltersPixelTestGPU = LayerTreeHostFiltersPixelTest; +LayerTreeTest::RendererType const kRendererTypesGpu[] = { + LayerTreeTest::RENDERER_GL, + LayerTreeTest::RENDERER_SKIA_GL, +#if defined(ENABLE_CC_VULKAN_TESTS) + LayerTreeTest::RENDERER_SKIA_VK, +#endif +}; + INSTANTIATE_TEST_SUITE_P(, LayerTreeHostFiltersPixelTestGPU, - ::testing::Values(LayerTreeTest::RENDERER_GL, - LayerTreeTest::RENDERER_SKIA_GL)); + ::testing::ValuesIn(kRendererTypesGpu)); -TEST_P(LayerTreeHostFiltersPixelTestGPU, BackdropFilterBlurRect) { +TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurRect) { scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( gfx::Rect(200, 200), SK_ColorWHITE); @@ -139,11 +138,56 @@ TEST_P(LayerTreeHostFiltersPixelTestGPU, BackdropFilterBlurRect) { small_error_allowed)); #endif - RunPixelTest(renderer_type(), background, - base::FilePath(FILE_PATH_LITERAL("backdrop_filter_blur.png"))); + RunPixelTest( + renderer_type(), background, + (renderer_type() == RENDERER_SOFTWARE) + ? base::FilePath(FILE_PATH_LITERAL("backdrop_filter_blur_sw.png")) + : base::FilePath(FILE_PATH_LITERAL("backdrop_filter_blur.png"))); +} + +TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurRadius) { + if (renderer_type() == RENDERER_SOFTWARE) { + // TODO(989238): Software renderer does not support/implement + // kClamp_TileMode. + return; + } + scoped_refptr<SolidColorLayer> background = + CreateSolidColorLayer(gfx::Rect(400, 400), SK_ColorRED); + scoped_refptr<SolidColorLayer> green = + CreateSolidColorLayer(gfx::Rect(200, 200, 200, 200), kCSSLime); + gfx::Rect blur_rect(100, 100, 200, 200); + scoped_refptr<SolidColorLayer> blur = + CreateSolidColorLayer(blur_rect, SkColorSetARGB(40, 10, 20, 200)); + background->AddChild(green); + background->AddChild(blur); + + FilterOperations filters; + filters.Append(FilterOperation::CreateBlurFilter( + 30.f, SkBlurImageFilter::kClamp_TileMode)); + blur->SetBackdropFilters(filters); + gfx::RRectF backdrop_filter_bounds(gfx::RectF(gfx::SizeF(blur->bounds())), 0); + blur->SetBackdropFilterBounds(backdrop_filter_bounds); + +#if defined(OS_WIN) || defined(ARCH_CPU_ARM64) + // Windows and ARM64 have 436 pixels off by 1: crbug.com/259915 + float percentage_pixels_large_error = 1.09f; // 436px / (200*200) + float percentage_pixels_small_error = 0.0f; + float average_error_allowed_in_bad_pixels = 1.f; + int large_error_allowed = 1; + int small_error_allowed = 0; + pixel_comparator_.reset(new FuzzyPixelComparator( + true, // discard_alpha + percentage_pixels_large_error, percentage_pixels_small_error, + average_error_allowed_in_bad_pixels, large_error_allowed, + small_error_allowed)); +#endif + RunPixelTest( + renderer_type(), background, + base::FilePath(FILE_PATH_LITERAL("backdrop_filter_blur_radius_.png")) + .InsertBeforeExtensionASCII(GetRendererSuffix())); } -TEST_P(LayerTreeHostFiltersPixelTestGPU, BackdropFilterBlurRounded) { +TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurRounded) { scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE); @@ -180,17 +224,17 @@ TEST_P(LayerTreeHostFiltersPixelTestGPU, BackdropFilterBlurRounded) { pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(false); #endif - RunPixelTest( - renderer_type(), background, - base::FilePath(FILE_PATH_LITERAL("backdrop_filter_blur_rounded.png"))); + RunPixelTest(renderer_type(), background, + (renderer_type() == RENDERER_SOFTWARE) + ? base::FilePath(FILE_PATH_LITERAL( + "backdrop_filter_blur_rounded_sw.png")) + : base::FilePath(FILE_PATH_LITERAL( + "backdrop_filter_blur_rounded.png"))); } -TEST_P(LayerTreeHostFiltersPixelTestGPU, BackdropFilterBlurOutsets) { - if (renderer_type() == RENDERER_SKIA_GL -#if defined(ENABLE_CC_VULKAN_TESTS) - || renderer_type() == RENDERER_SKIA_VK -#endif - ) { +TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurOutsets) { + if (renderer_type() == RENDERER_SKIA_GL || + renderer_type() == RENDERER_SKIA_VK) { // TODO(973696): Implement bounds clipping in skia_renderer. return; } @@ -233,6 +277,9 @@ TEST_P(LayerTreeHostFiltersPixelTestGPU, BackdropFilterBlurOutsets) { average_error_allowed_in_bad_pixels, large_error_allowed, small_error_allowed)); +#else + if (renderer_type() == RENDERER_SKIA_VK) + pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true); #endif RunPixelTest( @@ -380,9 +427,7 @@ class LayerTreeHostFiltersScaledPixelTest INSTANTIATE_TEST_SUITE_P(, LayerTreeHostFiltersScaledPixelTest, - ::testing::Values(LayerTreeTest::RENDERER_GL, - LayerTreeTest::RENDERER_SKIA_GL, - LayerTreeTest::RENDERER_SOFTWARE)); + ::testing::ValuesIn(kRendererTypes)); TEST_P(LayerTreeHostFiltersScaledPixelTest, StandardDpi) { RunPixelTestType(100, 1.f); @@ -393,8 +438,6 @@ TEST_P(LayerTreeHostFiltersScaledPixelTest, HiDpi) { } TEST_P(LayerTreeHostFiltersPixelTest, NullFilter) { - layer_transforms_should_scale_layer_contents_ = false; - scoped_refptr<SolidColorLayer> foreground = CreateSolidColorLayer(gfx::Rect(0, 0, 200, 200), SK_ColorGREEN); @@ -407,8 +450,6 @@ TEST_P(LayerTreeHostFiltersPixelTest, NullFilter) { } TEST_P(LayerTreeHostFiltersPixelTest, CroppedFilter) { - layer_transforms_should_scale_layer_contents_ = false; - scoped_refptr<SolidColorLayer> foreground = CreateSolidColorLayer(gfx::Rect(0, 0, 200, 200), SK_ColorGREEN); @@ -668,7 +709,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, ImageRenderSurfaceScaled) { .InsertBeforeExtensionASCII(GetRendererSuffix())); } -TEST_P(LayerTreeHostFiltersPixelTestNonSkia, ZoomFilter) { +TEST_P(LayerTreeHostFiltersPixelTest, ZoomFilter) { scoped_refptr<SolidColorLayer> root = CreateSolidColorLayer(gfx::Rect(300, 300), SK_ColorWHITE); @@ -945,8 +986,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, EnlargedTextureWithCropOffsetFilter) { base::FilePath(FILE_PATH_LITERAL("enlarged_texture_on_crop_offset.png"))); } -// TODO(crbug.com/948128): Enable this test for SkiaRenderer. -TEST_P(LayerTreeHostFiltersPixelTestNonSkia, BlurFilterWithClip) { +TEST_P(LayerTreeHostFiltersPixelTest, BlurFilterWithClip) { scoped_refptr<SolidColorLayer> child1 = CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorBLUE); scoped_refptr<SolidColorLayer> child2 = @@ -1010,32 +1050,96 @@ TEST_P(LayerTreeHostFiltersPixelTestGPU, FilterWithGiantCropRectNoClip) { base::FilePath(FILE_PATH_LITERAL("filter_with_giant_crop_rect.png"))); } -class BackdropFilterWithDeviceScaleFactorTest - : public LayerTreeHostFiltersPixelTest { +class BackdropFilterOffsetTest : public LayerTreeHostFiltersPixelTest { protected: - void RunPixelTestType(float device_scale_factor, - const base::FilePath& expected_result) { - device_scale_factor_ = device_scale_factor; - + void RunPixelTestType(int device_scale_factor) { scoped_refptr<Layer> root = CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorWHITE); - scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(gfx::Rect(100, 120), SK_ColorBLACK); root->AddChild(background); - scoped_refptr<SolidColorLayer> filtered = CreateSolidColorLayer( gfx::Rect(0, 100, 200, 100), SkColorSetA(SK_ColorGREEN, 127)); FilterOperations filters; + // TODO(989329): This test actually also tests how the OffsetPaintFilter + // handles the edge condition. Because the OffsetPaintFilter is pulling + // content from outside the filter region (just the bottom 200x100 square), + // it must create content for all but the bottom 20px of the filter rect. + // The GPU implementation of OffsetPaintFilter effectively clamps to the + // edge pixels and copies them into the top 80px of the image. The CPU + // implementation, on the other hand, pulls in transparent pixels for those + // 80px. The behavior is basically unspecified, though for blur filters the + // explicitly specified behavior is edgemode:duplicate, as seen in [1]. And + // the default for svg filters, including feOffset, is also duplicate. + // [1] https://drafts.fxtf.org/filter-effects-2/#backdrop-filter-operation + // [2] https://www.w3.org/TR/SVG11/filters.html#feConvolveMatrixElementEdgeModeAttribute filters.Append(FilterOperation::CreateReferenceFilter( sk_make_sp<OffsetPaintFilter>(0, 80, nullptr))); filtered->SetBackdropFilters(filters); filtered->ClearBackdropFilterBounds(); root->AddChild(filtered); - // This should appear as a grid of 4 100x100 squares which are: // BLACK WHITE - // DARK GREEN LIGHT GREEN + // DARK GREEN* LIGHT GREEN + // + // *except for software (see crbug.com/989329) which will be a + // dark-light-dark horizontal sandwich. + device_scale_factor_ = device_scale_factor; + + base::FilePath expected_result = + base::FilePath(FILE_PATH_LITERAL("offset_backdrop_filter_.png")); + expected_result = expected_result.InsertBeforeExtensionASCII( + base::NumberToString(device_scale_factor) + "x"); + if (renderer_type() == RENDERER_SOFTWARE) { + expected_result = expected_result.InsertBeforeExtensionASCII("_sw"); + } + RunPixelTest(renderer_type(), std::move(root), expected_result); + } + + private: + // LayerTreePixelTest overrides + void SetupTree() override { + SetInitialDeviceScaleFactor(device_scale_factor_); + LayerTreeHostFiltersPixelTest::SetupTree(); + } + + float device_scale_factor_ = 1; +}; + +INSTANTIATE_TEST_SUITE_P(, + BackdropFilterOffsetTest, + ::testing::ValuesIn(kRendererTypes)); + +TEST_P(BackdropFilterOffsetTest, StandardDpi) { + RunPixelTestType(1.f); +} + +TEST_P(BackdropFilterOffsetTest, HiDpi) { + RunPixelTestType(2.f); +} + +class BackdropFilterInvertTest : public LayerTreeHostFiltersPixelTest { + protected: + void RunPixelTestType(int device_scale_factor) { + scoped_refptr<Layer> root = + CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorGREEN); + scoped_refptr<SolidColorLayer> filtered = + CreateSolidColorLayer(gfx::Rect(50, 50, 100, 100), SK_ColorTRANSPARENT); + FilterOperations filters; + filters.Append(FilterOperation::CreateInvertFilter(1.0)); + filtered->SetBackdropFilters(filters); + gfx::RRectF backdrop_filter_bounds( + gfx::RectF(gfx::SizeF(filtered->bounds())), 20); + filtered->SetBackdropFilterBounds(backdrop_filter_bounds); + root->AddChild(filtered); + device_scale_factor_ = device_scale_factor; + base::FilePath expected_result = + base::FilePath(FILE_PATH_LITERAL("invert_backdrop_filter_.png")); + expected_result = expected_result.InsertBeforeExtensionASCII( + base::NumberToString(device_scale_factor) + "x"); + if (renderer_type() == RENDERER_SOFTWARE) { + expected_result = expected_result.InsertBeforeExtensionASCII("_sw"); + } RunPixelTest(renderer_type(), std::move(root), expected_result); } @@ -1049,21 +1153,16 @@ class BackdropFilterWithDeviceScaleFactorTest float device_scale_factor_ = 1; }; -// TODO(973699): This test is broken in software_renderer. Re-enable this test -// when fixed. INSTANTIATE_TEST_SUITE_P(, - BackdropFilterWithDeviceScaleFactorTest, - ::testing::Values(LayerTreeTest::RENDERER_GL, - LayerTreeTest::RENDERER_SKIA_GL)); + BackdropFilterInvertTest, + ::testing::ValuesIn(kRendererTypes)); -TEST_P(BackdropFilterWithDeviceScaleFactorTest, StandardDpi) { - RunPixelTestType( - 1.f, base::FilePath(FILE_PATH_LITERAL("offset_backdrop_filter_1x.png"))); +TEST_P(BackdropFilterInvertTest, StandardDpi) { + RunPixelTestType(1.f); } -TEST_P(BackdropFilterWithDeviceScaleFactorTest, HiDpi) { - RunPixelTestType( - 2.f, base::FilePath(FILE_PATH_LITERAL("offset_backdrop_filter_2x.png"))); +TEST_P(BackdropFilterInvertTest, HiDpi) { + RunPixelTestType(2.f); } } // namespace diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc index 9e125057e0d..1dcc664c201 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc @@ -18,7 +18,7 @@ #include "cc/test/layer_tree_pixel_resource_test.h" #include "cc/test/pixel_comparator.h" #include "cc/test/solid_color_content_layer_client.h" -#include "components/viz/test/test_layer_tree_frame_sink.h" +#include "cc/test/test_layer_tree_frame_sink.h" #include "third_party/skia/include/core/SkImage.h" #if !defined(OS_ANDROID) @@ -26,9 +26,33 @@ namespace cc { namespace { +auto CombineWithLayerMaskTypes( + const std::vector<PixelResourceTestCase>& test_cases) { + return ::testing::Combine( + ::testing::ValuesIn(test_cases), + ::testing::Values(Layer::LayerMaskType::SINGLE_TEXTURE_MASK)); +} + +// TODO(penghuang): Fix vulkan with one copy or zero copy +// https://crbug.com/979703 +std::vector<PixelResourceTestCase> const kTestCases = { + {LayerTreeTest::RENDERER_SOFTWARE, SOFTWARE}, + {LayerTreeTest::RENDERER_GL, GPU}, + {LayerTreeTest::RENDERER_GL, ONE_COPY}, + {LayerTreeTest::RENDERER_GL, ZERO_COPY}, + {LayerTreeTest::RENDERER_SKIA_GL, GPU}, + {LayerTreeTest::RENDERER_SKIA_GL, ONE_COPY}, + {LayerTreeTest::RENDERER_SKIA_GL, ZERO_COPY}, +#if defined(ENABLE_CC_VULKAN_TESTS) + {LayerTreeTest::RENDERER_SKIA_VK, GPU}, +#endif +}; + using LayerTreeHostMasksPixelTest = ParameterizedPixelResourceTest; -INSTANTIATE_PIXEL_RESOURCE_TEST_SUITE_P(LayerTreeHostMasksPixelTest); +INSTANTIATE_TEST_SUITE_P(PixelResourceTest, + LayerTreeHostMasksPixelTest, + CombineWithLayerMaskTypes(kTestCases)); class MaskContentLayerClient : public ContentLayerClient { public: @@ -89,17 +113,23 @@ TEST_P(LayerTreeHostMasksPixelTest, MaskOfLayer) { mask->SetLayerMaskType(mask_type_); green->SetMaskLayer(mask.get()); + pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true); + RunPixelResourceTest(background, base::FilePath(FILE_PATH_LITERAL("mask_of_layer.png"))); } class LayerTreeHostLayerListPixelTest : public ParameterizedPixelResourceTest { + protected: void InitializeSettings(LayerTreeSettings* settings) override { settings->use_layer_lists = true; + settings->gpu_rasterization_forced = use_vulkan(); } }; -INSTANTIATE_PIXEL_RESOURCE_TEST_SUITE_P(LayerTreeHostLayerListPixelTest); +INSTANTIATE_TEST_SUITE_P(PixelResourceTest, + LayerTreeHostLayerListPixelTest, + CombineWithLayerMaskTypes(kTestCases)); TEST_P(LayerTreeHostLayerListPixelTest, MaskWithEffect) { PropertyTrees property_trees; @@ -153,6 +183,9 @@ TEST_P(LayerTreeHostLayerListPixelTest, MaskWithEffect) { mask->SetTransformTreeIndex(1); root_layer->AddChild(mask); + pixel_comparator_ = + std::make_unique<FuzzyPixelOffByOneComparator>(true /* discard_alpha */); + RunPixelResourceTestWithLayerList( root_layer, base::FilePath(FILE_PATH_LITERAL("mask_with_effect.png")), &property_trees); @@ -418,18 +451,7 @@ TEST_P(LayerTreeHostLayerListPixelTest, MaskWithEffectNoContentToMask) { &property_trees); } -using LayerTreeHostLayerListPixelTestNonSkia = LayerTreeHostLayerListPixelTest; - -// TODO(crbug.com/948128): Enable this test for Skia. -INSTANTIATE_TEST_SUITE_P( - PixelResourceTest, - LayerTreeHostLayerListPixelTestNonSkia, - ::testing::Combine( - ::testing::Values(SOFTWARE, GPU, ONE_COPY, ZERO_COPY), - ::testing::Values(Layer::LayerMaskType::SINGLE_TEXTURE_MASK, - Layer::LayerMaskType::MULTI_TEXTURE_MASK))); - -TEST_P(LayerTreeHostLayerListPixelTestNonSkia, ScaledMaskWithEffect) { +TEST_P(LayerTreeHostLayerListPixelTest, ScaledMaskWithEffect) { PropertyTrees property_trees; scoped_refptr<Layer> root_layer; InitializeForLayerListMode(&root_layer, &property_trees); @@ -450,10 +472,12 @@ TEST_P(LayerTreeHostLayerListPixelTestNonSkia, ScaledMaskWithEffect) { // Scale the mask with a non-integral transform. This will trigger the // AA path in the renderer. - TransformNode transform; - transform.local = gfx::Transform(); - transform.local.Scale(1.5, 1.5); - property_trees.transform_tree.Insert(transform, 1); + TransformTree& transform_tree = property_trees.transform_tree; + auto& transform_node = + *transform_tree.Node(transform_tree.Insert(TransformNode(), 1)); + transform_node.source_node_id = transform_node.parent_id; + transform_node.local = gfx::Transform(); + transform_node.local.Scale(1.5, 1.5); scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); @@ -488,20 +512,13 @@ TEST_P(LayerTreeHostLayerListPixelTestNonSkia, ScaledMaskWithEffect) { mask->SetTransformTreeIndex(2); root_layer->AddChild(mask); - float percentage_pixels_large_error = 2.5f; // 2.5%, ~250px / (100*100) - float percentage_pixels_small_error = 0.0f; - float average_error_allowed_in_bad_pixels = 100.0f; - int large_error_allowed = 256; - int small_error_allowed = 0; - 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); + pixel_comparator_ = + std::make_unique<FuzzyPixelOffByOneComparator>(true /* discard_alpha */); RunPixelResourceTestWithLayerList( root_layer, - base::FilePath(FILE_PATH_LITERAL("scaled_mask_with_effect.png")), + base::FilePath(FILE_PATH_LITERAL("scaled_mask_with_effect_.png")) + .InsertBeforeExtensionASCII(GetRendererSuffix()), &property_trees); } @@ -557,6 +574,9 @@ TEST_P(LayerTreeHostLayerListPixelTest, MaskWithEffectDifferentSize) { mask->SetTransformTreeIndex(1); root_layer->AddChild(mask); + pixel_comparator_ = + std::make_unique<FuzzyPixelOffByOneComparator>(true /* discard_alpha */); + // The mask is half the size of thing it's masking. In layer-list mode, // the mask is not automatically scaled to match the other layer. RunPixelResourceTestWithLayerList( @@ -631,6 +651,9 @@ TEST_P(LayerTreeHostLayerListPixelTest, ImageMaskWithEffect) { SkMatrix::I(), false); root_layer->AddChild(mask); + pixel_comparator_ = + std::make_unique<FuzzyPixelOffByOneComparator>(true /* discard_alpha */); + // The mask is half the size of thing it's masking. In layer-list mode, // the mask is not automatically scaled to match the other layer. RunPixelResourceTestWithLayerList( @@ -670,6 +693,9 @@ TEST_P(LayerTreeHostMasksPixelTest, ImageMaskOfLayer) { green->SetMaskLayer(mask.get()); background->AddChild(green); + pixel_comparator_ = + std::make_unique<FuzzyPixelOffByOneComparator>(true /* discard_alpha */); + RunPixelResourceTest( background, base::FilePath(FILE_PATH_LITERAL("image_mask_of_layer.png"))); } @@ -697,6 +723,9 @@ TEST_P(LayerTreeHostMasksPixelTest, MaskOfClippedLayer) { mask->SetLayerMaskType(mask_type_); green->SetMaskLayer(mask.get()); + pixel_comparator_ = + std::make_unique<FuzzyPixelOffByOneComparator>(true /* discard_alpha */); + RunPixelResourceTest( background, base::FilePath(FILE_PATH_LITERAL("mask_of_clipped_layer.png"))); @@ -719,6 +748,9 @@ TEST_P(LayerTreeHostMasksPixelTest, MaskOfLayerNonExactTextureSize) { mask->set_fixed_tile_size(gfx::Size(173, 135)); green->SetMaskLayer(mask.get()); + pixel_comparator_ = + std::make_unique<FuzzyPixelOffByOneComparator>(true /* discard_alpha */); + RunPixelResourceTest(background, base::FilePath(FILE_PATH_LITERAL( "mask_with_non_exact_texture_size.png"))); @@ -817,20 +849,9 @@ class CircleContentLayerClient : public ContentLayerClient { using LayerTreeHostMasksForBackdropFiltersPixelTest = ParameterizedPixelResourceTest; -INSTANTIATE_PIXEL_RESOURCE_TEST_SUITE_P( - LayerTreeHostMasksForBackdropFiltersPixelTest); - -using LayerTreeHostMasksForBackdropFiltersPixelTestNonSkia = - ParameterizedPixelResourceTest; - -// TODO(crbug.com/948128): Enable these tests for Skia. -INSTANTIATE_TEST_SUITE_P( - PixelResourceTest, - LayerTreeHostMasksForBackdropFiltersPixelTestNonSkia, - ::testing::Combine( - ::testing::Values(SOFTWARE, GPU, ONE_COPY, ZERO_COPY), - ::testing::Values(Layer::LayerMaskType::SINGLE_TEXTURE_MASK, - Layer::LayerMaskType::MULTI_TEXTURE_MASK))); +INSTANTIATE_TEST_SUITE_P(PixelResourceTest, + LayerTreeHostMasksForBackdropFiltersPixelTest, + CombineWithLayerMaskTypes(kTestCases)); TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTest, MaskOfLayerWithBackdropFilter) { @@ -863,9 +884,24 @@ TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTest, CHECK_EQ(Layer::LayerMaskType::SINGLE_TEXTURE_MASK, mask->mask_type()); base::FilePath image_name = - (test_case_ == GPU || test_case_ == SKIA_GL) + (raster_type() == GPU) ? base::FilePath(FILE_PATH_LITERAL("mask_of_backdrop_filter_gpu.png")) : base::FilePath(FILE_PATH_LITERAL("mask_of_backdrop_filter.png")); + + if (renderer_type() == RENDERER_SKIA_VK && raster_type() == GPU) { + // Vulkan with GPU raster has 4 pixels errors (the circle mask shape is + // slight different). + float percentage_pixels_large_error = 0.04f; // 4px / (100*100) + float percentage_pixels_small_error = 0.0f; + float average_error_allowed_in_bad_pixels = 182.f; + int large_error_allowed = 182; + int small_error_allowed = 0; + 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); + } + RunPixelResourceTest(background, image_name); } @@ -962,22 +998,16 @@ class LayerTreeHostMaskAsBlendingPixelTest Layer::LayerMaskType::SINGLE_TEXTURE_MASK), use_antialiasing_(GetParam().flags & kUseAntialiasing), force_shaders_(GetParam().flags & kForceShaders) { - float percentage_pixels_small_error = 0.f; float percentage_pixels_error = 0.f; + float percentage_pixels_small_error = 0.f; float average_error_allowed_in_bad_pixels = 0.f; int large_error_allowed = 0; int small_error_allowed = 0; - if (use_antialiasing_) { - percentage_pixels_small_error = 0.9f; - percentage_pixels_error = 6.7f; - average_error_allowed_in_bad_pixels = 3.5f; - large_error_allowed = 15; - small_error_allowed = 1; - } else if (test_case_ != SOFTWARE) { - percentage_pixels_small_error = 0.9f; - percentage_pixels_error = 6.5f; - average_error_allowed_in_bad_pixels = 3.5f; - large_error_allowed = 15; + if (renderer_type() != RENDERER_SOFTWARE) { + percentage_pixels_error = 6.0f; + percentage_pixels_small_error = 2.f; + average_error_allowed_in_bad_pixels = 2.1f; + large_error_allowed = 11; small_error_allowed = 1; } else { #if defined(ARCH_CPU_ARM64) @@ -1061,7 +1091,7 @@ class LayerTreeHostMaskAsBlendingPixelTest } protected: - std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( const viz::RendererSettings& renderer_settings, double refresh_rate, scoped_refptr<viz::ContextProvider> compositor_context_provider, @@ -1080,13 +1110,21 @@ class LayerTreeHostMaskAsBlendingPixelTest bool force_shaders_; }; -// TODO(crbug.com/948128): Enable these tests for Skia. MaskTestConfig const kTestConfigs[] = { - MaskTestConfig{SOFTWARE, 0}, - MaskTestConfig{ZERO_COPY, 0}, - MaskTestConfig{ZERO_COPY, kUseAntialiasing}, - MaskTestConfig{ZERO_COPY, kForceShaders}, - MaskTestConfig{ZERO_COPY, kUseAntialiasing | kForceShaders}, + MaskTestConfig{{LayerTreeTest::RENDERER_SOFTWARE, SOFTWARE}, 0}, + MaskTestConfig{{LayerTreeTest::RENDERER_GL, ZERO_COPY}, 0}, + MaskTestConfig{{LayerTreeTest::RENDERER_GL, ZERO_COPY}, kUseAntialiasing}, + MaskTestConfig{{LayerTreeTest::RENDERER_GL, ZERO_COPY}, kForceShaders}, + MaskTestConfig{{LayerTreeTest::RENDERER_GL, ZERO_COPY}, + kUseAntialiasing | kForceShaders}, + MaskTestConfig{{LayerTreeTest::RENDERER_SKIA_GL, ZERO_COPY}, 0}, + MaskTestConfig{{LayerTreeTest::RENDERER_SKIA_GL, ZERO_COPY}, + kUseAntialiasing}, +#if defined(ENABLE_CC_VULKAN_TESTS) + MaskTestConfig{{LayerTreeTest::RENDERER_SKIA_VK, ZERO_COPY}, 0}, + MaskTestConfig{{LayerTreeTest::RENDERER_SKIA_VK, ZERO_COPY}, + kUseAntialiasing}, +#endif }; INSTANTIATE_TEST_SUITE_P(All, @@ -1229,7 +1267,7 @@ TEST_P(LayerTreeHostMaskAsBlendingPixelTest, RotatedClippedCircle) { mask_isolation->AddChild(mask_layer); base::FilePath image_name = - (test_case_ == SOFTWARE) + (raster_type() == SOFTWARE) ? base::FilePath( FILE_PATH_LITERAL("mask_as_blending_rotated_circle.png")) : base::FilePath( @@ -1276,7 +1314,7 @@ TEST_P(LayerTreeHostMaskAsBlendingPixelTest, RotatedClippedCircleUnderflow) { mask_isolation->AddChild(mask_layer); base::FilePath image_name = - (test_case_ == SOFTWARE) + (raster_type() == SOFTWARE) ? base::FilePath(FILE_PATH_LITERAL( "mask_as_blending_rotated_circle_underflow.png")) : base::FilePath(FILE_PATH_LITERAL( @@ -1284,7 +1322,7 @@ TEST_P(LayerTreeHostMaskAsBlendingPixelTest, RotatedClippedCircleUnderflow) { RunPixelResourceTest(root, image_name); } -TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTestNonSkia, +TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTest, MaskOfLayerWithBackdropFilterAndBlend) { scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(gfx::Rect(128, 128), SK_ColorWHITE); @@ -1322,20 +1360,14 @@ TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTestNonSkia, mask->SetLayerMaskType(mask_type_); picture_horizontal->SetMaskLayer(mask.get()); - float percentage_pixels_large_error = 0.062f; // 0.062%, ~10px / (128*128) - float percentage_pixels_small_error = 0.0f; - float average_error_allowed_in_bad_pixels = 200.0f; - int large_error_allowed = 256; - int small_error_allowed = 0; - 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); - - RunPixelResourceTest(background, - base::FilePath(FILE_PATH_LITERAL( - "mask_of_backdrop_filter_and_blend.png"))); + base::FilePath result_path( + FILE_PATH_LITERAL("mask_of_backdrop_filter_and_blend_.png")); + if (raster_type() != GPU) { + result_path = result_path.InsertBeforeExtensionASCII("sw"); + } else { + result_path = result_path.InsertBeforeExtensionASCII(GetRendererSuffix()); + } + RunPixelResourceTest(background, result_path); } } // namespace diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_mirror.cc b/chromium/cc/trees/layer_tree_host_pixeltest_mirror.cc new file mode 100644 index 00000000000..ed011f43887 --- /dev/null +++ b/chromium/cc/trees/layer_tree_host_pixeltest_mirror.cc @@ -0,0 +1,83 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "build/build_config.h" +#include "cc/layers/mirror_layer.h" +#include "cc/layers/solid_color_layer.h" +#include "cc/test/layer_tree_pixel_test.h" +#include "cc/test/pixel_comparator.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/transform_util.h" + +#if !defined(OS_ANDROID) + +namespace cc { +namespace { + +class LayerTreeHostMirrorPixelTest + : public LayerTreePixelTest, + public ::testing::WithParamInterface<LayerTreeTest::RendererType> { + protected: + RendererType renderer_type() { return GetParam(); } +}; + +const LayerTreeTest::RendererType kRendererTypes[] = { + LayerTreeTest::RENDERER_GL, + LayerTreeTest::RENDERER_SKIA_GL, + LayerTreeTest::RENDERER_SOFTWARE, +#if defined(ENABLE_CC_VULKAN_TESTS) + LayerTreeTest::RENDERER_SKIA_VK, +#endif +}; + +INSTANTIATE_TEST_SUITE_P(, + LayerTreeHostMirrorPixelTest, + ::testing::ValuesIn(kRendererTypes)); + +// Verifies that a mirror layer with a scale mirrors another layer correctly. +TEST_P(LayerTreeHostMirrorPixelTest, MirrorLayer) { + const float scale = 2.f; + gfx::Rect background_bounds(120, 180); + gfx::Rect mirrored_bounds(10, 10, 50, 50); + gfx::Rect mirror_bounds(10, 70, 100, 100); + + auto background = CreateSolidColorLayer(background_bounds, SK_ColorWHITE); + + auto mirrored_layer = CreateSolidColorLayerWithBorder( + mirrored_bounds, SK_ColorGREEN, 5, SK_ColorBLUE); + + auto mirror_layer = MirrorLayer::Create(mirrored_layer); + mirror_layer->SetIsDrawable(true); + mirror_layer->SetBounds(mirror_bounds.size()); + mirror_layer->SetPosition(gfx::PointF(mirror_bounds.origin())); + mirror_layer->SetTransform(gfx::GetScaleTransform(gfx::Point(), scale)); + background->AddChild(mirrored_layer); + background->AddChild(mirror_layer); + + if (renderer_type() == RENDERER_SOFTWARE) { + const bool discard_alpha = true; + const float error_pixels_percentage_limit = 3.f; + const float small_error_pixels_percentage_limit = 0.f; + const float avg_abs_error_limit = 65.f; + const int max_abs_error_limit = 120; + const int small_error_threshold = 0; + pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( + discard_alpha, error_pixels_percentage_limit, + small_error_pixels_percentage_limit, avg_abs_error_limit, + max_abs_error_limit, small_error_threshold); + } + +#if defined(ENABLE_CC_VULKAN_TESTS) && defined(OS_LINUX) + if (renderer_type() == RENDERER_SKIA_VK) + pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true); +#endif + + RunPixelTest(renderer_type(), background, + base::FilePath(FILE_PATH_LITERAL("mirror_layer.png"))); +} + +} // namespace +} // namespace cc + +#endif // !defined(OS_ANDROID) diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc b/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc index 41660e1fb34..52317bb5fa2 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc @@ -10,11 +10,11 @@ #include "cc/test/fake_picture_layer_impl.h" #include "cc/test/layer_tree_pixel_test.h" #include "cc/test/solid_color_content_layer_client.h" +#include "cc/test/test_layer_tree_frame_sink.h" #include "cc/trees/layer_tree_impl.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_result.h" #include "components/viz/test/paths.h" -#include "components/viz/test/test_layer_tree_frame_sink.h" #if !defined(OS_ANDROID) @@ -29,9 +29,6 @@ enum ReadbackType { }; struct ReadbackTestConfig { - ReadbackTestConfig(LayerTreeTest::RendererType renderer_type_, - ReadbackType readback_type_) - : renderer_type(renderer_type_), readback_type(readback_type_) {} LayerTreeTest::RendererType renderer_type; ReadbackType readback_type; }; @@ -191,7 +188,11 @@ TEST_P(LayerTreeHostReadbackPixelTest, ReadbackSmallNonRootLayerWithChild) { base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png"))); } -TEST_P(LayerTreeHostReadbackPixelTest, ReadbackSubtreeSurroundsTargetLayer) { +using LayerTreeHostReadbackPixelTestMaybeVulkan = + LayerTreeHostReadbackPixelTest; + +TEST_P(LayerTreeHostReadbackPixelTestMaybeVulkan, + ReadbackSubtreeSurroundsTargetLayer) { scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(gfx::Rect(0, 0, 200, 200), SK_ColorWHITE); @@ -379,7 +380,7 @@ TEST_P(LayerTreeHostReadbackPixelTest, ReadbackNonRootLayerOutsideViewport) { base::FilePath(FILE_PATH_LITERAL("green_with_blue_corner.png"))); } -TEST_P(LayerTreeHostReadbackPixelTest, ReadbackNonRootOrFirstLayer) { +TEST_P(LayerTreeHostReadbackPixelTestMaybeVulkan, ReadbackNonRootOrFirstLayer) { // This test has 3 render passes with the copy request on the render pass in // the middle. This test caught an issue where copy requests on non-root // non-first render passes were being treated differently from the first @@ -415,15 +416,40 @@ TEST_P(LayerTreeHostReadbackPixelTest, MultipleReadbacksOnLayer) { base::FilePath(FILE_PATH_LITERAL("green.png"))); } -INSTANTIATE_TEST_SUITE_P( - , - LayerTreeHostReadbackPixelTest, - ::testing::Values( - ReadbackTestConfig(LayerTreeTest::RENDERER_SOFTWARE, READBACK_BITMAP), - ReadbackTestConfig(LayerTreeTest::RENDERER_GL, READBACK_TEXTURE), - ReadbackTestConfig(LayerTreeTest::RENDERER_GL, READBACK_BITMAP), - ReadbackTestConfig(LayerTreeTest::RENDERER_SKIA_GL, READBACK_BITMAP), - ReadbackTestConfig(LayerTreeTest::RENDERER_SKIA_GL, READBACK_TEXTURE))); +// TODO(crbug.com/963446): Enable these tests for Skia Vulkan using texture +// readback. +ReadbackTestConfig const kTestConfigs[] = { + ReadbackTestConfig{LayerTreeTest::RENDERER_SOFTWARE, READBACK_BITMAP}, + ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_TEXTURE}, + ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_BITMAP}, + ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_TEXTURE}, + ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_BITMAP}, +#if defined(ENABLE_CC_VULKAN_TESTS) + ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_VK, READBACK_BITMAP}, +#endif +}; + +INSTANTIATE_TEST_SUITE_P(, + LayerTreeHostReadbackPixelTest, + ::testing::ValuesIn(kTestConfigs)); + +// TODO(crbug.com/974283): These tests are crashing with vulkan when TSan or +// MSan are used. +ReadbackTestConfig const kMaybeVulkanTestConfigs[] = { + ReadbackTestConfig{LayerTreeTest::RENDERER_SOFTWARE, READBACK_BITMAP}, + ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_TEXTURE}, + ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_BITMAP}, + ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_TEXTURE}, + ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_BITMAP}, +#if defined(ENABLE_CC_VULKAN_TESTS) && !defined(THREAD_SANITIZER) && \ + !defined(MEMORY_SANITIZER) + ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_VK, READBACK_BITMAP}, +#endif +}; + +INSTANTIATE_TEST_SUITE_P(, + LayerTreeHostReadbackPixelTestMaybeVulkan, + ::testing::ValuesIn(kMaybeVulkanTestConfigs)); class LayerTreeHostReadbackDeviceScalePixelTest : public LayerTreeHostReadbackPixelTest { @@ -434,11 +460,6 @@ class LayerTreeHostReadbackDeviceScalePixelTest green_client_(SK_ColorGREEN, gfx::Size(200, 200)), blue_client_(SK_ColorBLUE, gfx::Size(200, 200)) {} - void InitializeSettings(LayerTreeSettings* settings) override { - // Cause the device scale factor to be inherited by contents scales. - settings->layer_transforms_should_scale_layer_contents = true; - } - void SetupTree() override { SetInitialDeviceScaleFactor(device_scale_factor_); LayerTreePixelTest::SetupTree(); @@ -510,15 +531,9 @@ TEST_P(LayerTreeHostReadbackDeviceScalePixelTest, ReadbackNonRootLayerSubrect) { base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png"))); } -INSTANTIATE_TEST_SUITE_P( - , - LayerTreeHostReadbackDeviceScalePixelTest, - ::testing::Values( - ReadbackTestConfig(LayerTreeTest::RENDERER_SOFTWARE, READBACK_BITMAP), - ReadbackTestConfig(LayerTreeTest::RENDERER_GL, READBACK_TEXTURE), - ReadbackTestConfig(LayerTreeTest::RENDERER_GL, READBACK_BITMAP), - ReadbackTestConfig(LayerTreeTest::RENDERER_SKIA_GL, READBACK_BITMAP), - ReadbackTestConfig(LayerTreeTest::RENDERER_SKIA_GL, READBACK_TEXTURE))); +INSTANTIATE_TEST_SUITE_P(, + LayerTreeHostReadbackDeviceScalePixelTest, + ::testing::ValuesIn(kTestConfigs)); class LayerTreeHostReadbackColorSpacePixelTest : public LayerTreeHostReadbackPixelTest { @@ -528,17 +543,17 @@ class LayerTreeHostReadbackColorSpacePixelTest output_color_space_ = gfx::ColorSpace::CreateDisplayP3D65(); } - std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( const viz::RendererSettings& renderer_settings, double refresh_rate, scoped_refptr<viz::ContextProvider> compositor_context_provider, scoped_refptr<viz::RasterContextProvider> worker_context_provider) override { - std::unique_ptr<viz::TestLayerTreeFrameSink> frame_sink = + std::unique_ptr<TestLayerTreeFrameSink> frame_sink = LayerTreePixelTest::CreateLayerTreeFrameSink( renderer_settings, refresh_rate, compositor_context_provider, worker_context_provider); - frame_sink->SetDisplayColorSpace(output_color_space_, output_color_space_); + frame_sink->SetDisplayColorSpace(output_color_space_); return frame_sink; } @@ -556,15 +571,9 @@ TEST_P(LayerTreeHostReadbackColorSpacePixelTest, Readback) { base::FilePath(FILE_PATH_LITERAL("srgb_green_in_p3.png"))); } -INSTANTIATE_TEST_SUITE_P( - , - LayerTreeHostReadbackColorSpacePixelTest, - ::testing::Values( - ReadbackTestConfig(LayerTreeTest::RENDERER_SOFTWARE, READBACK_BITMAP), - ReadbackTestConfig(LayerTreeTest::RENDERER_GL, READBACK_TEXTURE), - ReadbackTestConfig(LayerTreeTest::RENDERER_GL, READBACK_BITMAP), - ReadbackTestConfig(LayerTreeTest::RENDERER_SKIA_GL, READBACK_BITMAP), - ReadbackTestConfig(LayerTreeTest::RENDERER_SKIA_GL, READBACK_TEXTURE))); +INSTANTIATE_TEST_SUITE_P(, + LayerTreeHostReadbackColorSpacePixelTest, + ::testing::ValuesIn(kTestConfigs)); } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc index 8955987ba23..b271f513c0c 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc @@ -13,8 +13,8 @@ #include "cc/paint/paint_flags.h" #include "cc/test/layer_tree_pixel_test.h" #include "cc/test/pixel_comparator.h" -#include "cc/test/test_in_process_context_provider.h" #include "cc/trees/layer_tree_impl.h" +#include "components/viz/test/test_in_process_context_provider.h" #include "gpu/command_buffer/client/gles2_interface.h" #if !defined(OS_ANDROID) @@ -30,10 +30,6 @@ class LayerTreeHostScrollbarsPixelTest RendererType renderer_type() { return GetParam(); } - void InitializeSettings(LayerTreeSettings* settings) override { - settings->layer_transforms_should_scale_layer_contents = true; - } - void SetupTree() override { SetInitialDeviceScaleFactor(device_scale_factor_); LayerTreePixelTest::SetupTree(); @@ -89,10 +85,17 @@ class PaintedScrollbar : public Scrollbar { gfx::Rect rect_; }; +LayerTreeTest::RendererType const kRendererTypes[] = { + LayerTreeTest::RENDERER_GL, + LayerTreeTest::RENDERER_SKIA_GL, +#if defined(ENABLE_CC_VULKAN_TESTS) + LayerTreeTest::RENDERER_SKIA_VK, +#endif +}; + INSTANTIATE_TEST_SUITE_P(, LayerTreeHostScrollbarsPixelTest, - ::testing::Values(LayerTreeTest::RENDERER_GL, - LayerTreeTest::RENDERER_SKIA_GL)); + ::testing::ValuesIn(kRendererTypes)); TEST_P(LayerTreeHostScrollbarsPixelTest, NoScale) { scoped_refptr<SolidColorLayer> background = @@ -167,9 +170,9 @@ TEST_P(LayerTreeHostScrollbarsPixelTest, MAYBE_HugeTransformScale) { layer->SetBounds(gfx::Size(10, 400)); background->AddChild(layer); - scoped_refptr<TestInProcessContextProvider> context( - new TestInProcessContextProvider(/*enable_oop_rasterization=*/false, - /*support_locking=*/false)); + auto context = base::MakeRefCounted<viz::TestInProcessContextProvider>( + /*enable_oop_rasterization=*/false, + /*support_locking=*/false); gpu::ContextResult result = context->BindToCurrentThread(); DCHECK_EQ(result, gpu::ContextResult::kSuccess); int max_texture_size = 0; @@ -189,7 +192,8 @@ TEST_P(LayerTreeHostScrollbarsPixelTest, MAYBE_HugeTransformScale) { scale_transform.Scale(scale, scale); layer->SetTransform(scale_transform); - if (renderer_type() == RENDERER_SKIA_GL) + if (renderer_type() == RENDERER_SKIA_GL || + renderer_type() == RENDERER_SKIA_VK) pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true); RunPixelTest(renderer_type(), background, @@ -256,8 +260,7 @@ class PaintedOverlayScrollbar : public PaintedScrollbar { INSTANTIATE_TEST_SUITE_P(, LayerTreeHostOverlayScrollbarsPixelTest, - ::testing::Values(LayerTreeTest::RENDERER_GL, - LayerTreeTest::RENDERER_SKIA_GL)); + ::testing::ValuesIn(kRendererTypes)); // Simulate increasing the thickness of a painted overlay scrollbar. Ensure that // the scrollbar border remains crisp. diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc b/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc index fac28b34708..708eefad33a 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc @@ -54,10 +54,17 @@ class LayerTreeHostSynchronousPixelTest bool use_zero_copy_ = false; }; +LayerTreeTest::RendererType const kRendererTypesGpu[] = { + LayerTreeTest::RENDERER_GL, + LayerTreeTest::RENDERER_SKIA_GL, +#if defined(ENABLE_CC_VULKAN_TESTS) + LayerTreeTest::RENDERER_SKIA_VK, +#endif +}; + INSTANTIATE_TEST_SUITE_P(, LayerTreeHostSynchronousPixelTest, - ::testing::Values(LayerTreeTest::RENDERER_GL, - LayerTreeTest::RENDERER_SKIA_GL)); + ::testing::ValuesIn(kRendererTypesGpu)); TEST_P(LayerTreeHostSynchronousPixelTest, OneContentLayerZeroCopy) { use_zero_copy_ = true; diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc b/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc index ad22f279d6a..0da4873f16f 100644 --- a/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc +++ b/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc @@ -11,8 +11,8 @@ #include "cc/paint/paint_flags.h" #include "cc/paint/paint_op_buffer.h" #include "cc/test/layer_tree_pixel_test.h" +#include "cc/test/test_layer_tree_frame_sink.h" #include "components/viz/common/frame_sinks/copy_output_request.h" -#include "components/viz/test/test_layer_tree_frame_sink.h" #include "gpu/command_buffer/client/raster_interface.h" #if !defined(OS_ANDROID) @@ -172,15 +172,21 @@ class LayerTreeHostTilesTestPartialInvalidation scoped_refptr<PictureLayer> picture_layer_; }; -INSTANTIATE_TEST_SUITE_P( - , - LayerTreeHostTilesTestPartialInvalidation, - ::testing::Values(TilesTestConfig{LayerTreeTest::RENDERER_SOFTWARE, BITMAP}, - TilesTestConfig{LayerTreeTest::RENDERER_GL, ONE_COPY}, - TilesTestConfig{LayerTreeTest::RENDERER_GL, GPU}, - TilesTestConfig{LayerTreeTest::RENDERER_SKIA_GL, - ONE_COPY}, - TilesTestConfig{LayerTreeTest::RENDERER_SKIA_GL, GPU})); +std::vector<TilesTestConfig> const kTestCases = { + {LayerTreeTest::RENDERER_SOFTWARE, BITMAP}, + {LayerTreeTest::RENDERER_GL, ONE_COPY}, + {LayerTreeTest::RENDERER_GL, GPU}, + {LayerTreeTest::RENDERER_SKIA_GL, ONE_COPY}, + {LayerTreeTest::RENDERER_SKIA_GL, GPU}, +#if defined(ENABLE_CC_VULKAN_TESTS) + {LayerTreeTest::RENDERER_SKIA_VK, ONE_COPY}, + {LayerTreeTest::RENDERER_SKIA_VK, GPU}, +#endif +}; + +INSTANTIATE_TEST_SUITE_P(, + LayerTreeHostTilesTestPartialInvalidation, + ::testing::ValuesIn(kTestCases)); TEST_P(LayerTreeHostTilesTestPartialInvalidation, PartialRaster) { use_partial_raster_ = true; @@ -195,15 +201,20 @@ TEST_P(LayerTreeHostTilesTestPartialInvalidation, FullRaster) { base::FilePath(FILE_PATH_LITERAL("blue_yellow_flipped.png"))); } +std::vector<TilesTestConfig> const kTestCasesMultiThread = { + {LayerTreeTest::RENDERER_GL, ONE_COPY}, + {LayerTreeTest::RENDERER_SKIA_GL, ONE_COPY}, +#if defined(ENABLE_CC_VULKAN_TESTS) + {LayerTreeTest::RENDERER_SKIA_VK, ONE_COPY}, +#endif +}; + using LayerTreeHostTilesTestPartialInvalidationMultiThread = LayerTreeHostTilesTestPartialInvalidation; -INSTANTIATE_TEST_SUITE_P( - , - LayerTreeHostTilesTestPartialInvalidationMultiThread, - ::testing::Values(TilesTestConfig{LayerTreeTest::RENDERER_GL, ONE_COPY}, - TilesTestConfig{LayerTreeTest::RENDERER_SKIA_GL, - ONE_COPY})); +INSTANTIATE_TEST_SUITE_P(, + LayerTreeHostTilesTestPartialInvalidationMultiThread, + ::testing::ValuesIn(kTestCasesMultiThread)); // Flaky on Linux TSAN. https://crbug.com/707711 #if defined(OS_LINUX) && defined(THREAD_SANITIZER) @@ -227,6 +238,7 @@ TEST_P(LayerTreeHostTilesTestPartialInvalidationMultiThread, FullRaster) { using LayerTreeHostTilesTestPartialInvalidationLowBitDepth = LayerTreeHostTilesTestPartialInvalidation; +// TODO(crbug.com/963446): Enable these tests for Vulkan. INSTANTIATE_TEST_SUITE_P( , LayerTreeHostTilesTestPartialInvalidationLowBitDepth, diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc index f10dada2a20..023517fc874 100644 --- a/chromium/cc/trees/layer_tree_host_unittest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest.cc @@ -47,6 +47,7 @@ #include "cc/test/push_properties_counting_layer_impl.h" #include "cc/test/render_pass_test_utils.h" #include "cc/test/skia_common.h" +#include "cc/test/test_layer_tree_frame_sink.h" #include "cc/trees/clip_node.h" #include "cc/trees/effect_node.h" #include "cc/trees/frame_rate_counter.h" @@ -69,7 +70,6 @@ #include "components/viz/test/begin_frame_args_test.h" #include "components/viz/test/fake_output_surface.h" #include "components/viz/test/test_gles2_interface.h" -#include "components/viz/test/test_layer_tree_frame_sink.h" #include "gpu/GLES2/gl2extchromium.h" #include "testing/gmock/include/gmock/gmock.h" #include "third_party/khronos/GLES2/gl2.h" @@ -501,7 +501,7 @@ SINGLE_THREAD_TEST_F(LayerTreeHostTestReadyToDrawVisibility); class LayerTreeHostContextCacheTest : public LayerTreeHostTest { public: - std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( const viz::RendererSettings& renderer_settings, double refresh_rate, scoped_refptr<viz::ContextProvider> compositor_context_provider, @@ -2468,10 +2468,6 @@ MULTI_THREAD_TEST_F(LayerTreeHostTestGpuRasterDeviceSizeChanged); class LayerTreeHostTestNoExtraCommitFromInvalidate : public LayerTreeHostTest { public: - void InitializeSettings(LayerTreeSettings* settings) override { - settings->layer_transforms_should_scale_layer_contents = true; - } - void SetupTree() override { root_layer_ = Layer::Create(); root_layer_->SetBounds(gfx::Size(10, 20)); @@ -2519,10 +2515,6 @@ SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestNoExtraCommitFromInvalidate); class LayerTreeHostTestNoExtraCommitFromScrollbarInvalidate : public LayerTreeHostTest { public: - void InitializeSettings(LayerTreeSettings* settings) override { - settings->layer_transforms_should_scale_layer_contents = true; - } - void SetupTree() override { root_layer_ = Layer::Create(); root_layer_->SetBounds(gfx::Size(10, 20)); @@ -2578,10 +2570,6 @@ SINGLE_AND_MULTI_THREAD_TEST_F( class LayerTreeHostTestDeviceScaleFactorChange : public LayerTreeHostTest { public: - void InitializeSettings(LayerTreeSettings* settings) override { - settings->layer_transforms_should_scale_layer_contents = true; - } - void SetupTree() override { root_layer_ = Layer::Create(); root_layer_->SetBounds(gfx::Size(10, 20)); @@ -4006,25 +3994,25 @@ class LayerTreeHostTestLCDChange : public LayerTreeHostTest { // The first draw. EXPECT_EQ(1, num_tiles_rastered_); EXPECT_TRUE(can_use_lcd_text); - EXPECT_TRUE(root_layer->RasterSourceUsesLCDTextForTesting()); + EXPECT_TRUE(root_layer->can_use_lcd_text()); break; case 1: // Nothing changed on the layer. EXPECT_EQ(1, num_tiles_rastered_); EXPECT_TRUE(can_use_lcd_text); - EXPECT_TRUE(root_layer->RasterSourceUsesLCDTextForTesting()); + EXPECT_TRUE(root_layer->can_use_lcd_text()); break; case 2: // LCD text was disabled; it should be re-rastered with LCD text off. EXPECT_EQ(2, num_tiles_rastered_); EXPECT_FALSE(can_use_lcd_text); - EXPECT_FALSE(root_layer->RasterSourceUsesLCDTextForTesting()); + EXPECT_FALSE(root_layer->can_use_lcd_text()); break; case 3: // LCD text was enabled, but it's sticky and stays off. EXPECT_EQ(2, num_tiles_rastered_); EXPECT_TRUE(can_use_lcd_text); - EXPECT_FALSE(root_layer->RasterSourceUsesLCDTextForTesting()); + EXPECT_FALSE(root_layer->can_use_lcd_text()); break; } } @@ -4106,7 +4094,7 @@ class LayerTreeHostTestAbortedCommitDoesntStall : public LayerTreeHostTest { int commit_complete_count_; }; -class OnDrawLayerTreeFrameSink : public viz::TestLayerTreeFrameSink { +class OnDrawLayerTreeFrameSink : public TestLayerTreeFrameSink { public: OnDrawLayerTreeFrameSink( scoped_refptr<viz::ContextProvider> compositor_context_provider, @@ -4148,7 +4136,7 @@ class LayerTreeHostTestAbortedCommitDoesntStallSynchronousCompositor settings->using_synchronous_renderer_compositor = true; } - std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( const viz::RendererSettings& renderer_settings, double refresh_rate, scoped_refptr<viz::ContextProvider> compositor_context_provider, @@ -4406,37 +4394,36 @@ class LayerTreeHostTestLayersPushProperties : public LayerTreeHostTest { // The scrollbar layer always needs to be pushed. if (root_->layer_tree_host()) { - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( root_->layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); } if (child2_->layer_tree_host()) { - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( child2_->layer_tree_host()->LayersThatShouldPushProperties(), child2_.get())); } if (leaf_always_pushing_layer_->layer_tree_host()) { leaf_always_pushing_layer_->SetNeedsPushProperties(); - EXPECT_TRUE( - base::ContainsKey(leaf_always_pushing_layer_->layer_tree_host() - ->LayersThatShouldPushProperties(), - leaf_always_pushing_layer_.get())); + EXPECT_TRUE(base::Contains(leaf_always_pushing_layer_->layer_tree_host() + ->LayersThatShouldPushProperties(), + leaf_always_pushing_layer_.get())); } // child_ and grandchild_ don't persist their need to push properties. if (child_->layer_tree_host()) { - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( child_->layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); } if (grandchild_->layer_tree_host()) { - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( grandchild_->layer_tree_host()->LayersThatShouldPushProperties(), grandchild_.get())); } if (other_root_->layer_tree_host()) { - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( other_root_->layer_tree_host()->LayersThatShouldPushProperties(), other_root_.get())); } @@ -4789,9 +4776,9 @@ class LayerTreeHostTestPropertyChangesDuringUpdateArePushed scrollbar_layer_->SetBounds(gfx::Size(30, 30)); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - scrollbar_layer_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + scrollbar_layer_.get())); layer_tree_host()->SetNeedsCommit(); scrollbar_layer_->reset_push_properties_count(); @@ -4835,9 +4822,9 @@ class LayerTreeHostTestSetDrawableCausesCommit : public LayerTreeHostTest { // avoid causing a second commit to be scheduled. If a property change // is made during this, however, it needs to be pushed in the upcoming // commit. - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); EXPECT_EQ(0, root_->NumDescendantsThatDrawContent()); root_->reset_push_properties_count(); @@ -4846,18 +4833,18 @@ class LayerTreeHostTestSetDrawableCausesCommit : public LayerTreeHostTest { EXPECT_EQ(1, root_->NumDescendantsThatDrawContent()); EXPECT_EQ(0u, root_->push_properties_count()); EXPECT_EQ(0u, child_->push_properties_count()); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); break; } case 2: EXPECT_EQ(1u, root_->push_properties_count()); EXPECT_EQ(1u, child_->push_properties_count()); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); EndTest(); break; @@ -4925,19 +4912,19 @@ class LayerTreeHostTestPushPropertiesAddingToTreeRequiresPush case 0: // All layers will need push properties as we set their layer tree host layer_tree_host()->SetRootLayer(root_); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild1_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild2_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild3_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild1_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild2_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild3_.get())); break; case 1: EndTest(); @@ -4958,81 +4945,81 @@ class LayerTreeHostTestPushPropertiesRemovingChildStopsRecursion layer_tree_host()->SetRootLayer(root_); break; case 1: - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild1_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild2_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild3_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild1_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild2_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild3_.get())); grandchild1_->RemoveFromParent(); grandchild1_->SetPosition(gfx::PointF(1.f, 1.f)); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild2_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild3_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild2_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild3_.get())); child_->AddChild(grandchild1_); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild1_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild2_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild3_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild1_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild2_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild3_.get())); grandchild2_->SetPosition(gfx::PointF(1.f, 1.f)); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild1_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild2_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild3_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild1_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild2_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild3_.get())); // grandchild2_ will still need a push properties. grandchild1_->RemoveFromParent(); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); // grandchild3_ does not need a push properties, so recursing should // no longer be needed. grandchild2_->RemoveFromParent(); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); EndTest(); break; @@ -5057,35 +5044,35 @@ class LayerTreeHostTestPushPropertiesRemovingChildStopsRecursionWithPersistence layer_tree_host()->SetRootLayer(root_); break; case 1: - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild1_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild2_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild3_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild1_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild2_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild3_.get())); // grandchild2_ will still need a push properties. grandchild1_->RemoveFromParent(); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); // grandchild3_ does not need a push properties, so recursing should // no longer be needed. grandchild2_->RemoveFromParent(); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); EndTest(); break; @@ -5106,19 +5093,19 @@ class LayerTreeHostTestPushPropertiesSetPropertiesWhileOutsideTree layer_tree_host()->SetRootLayer(root_); break; case 1: - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild1_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild2_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild3_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild1_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild2_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild3_.get())); // Change grandchildren while their parent is not in the tree. child_->RemoveFromParent(); @@ -5126,39 +5113,39 @@ class LayerTreeHostTestPushPropertiesSetPropertiesWhileOutsideTree grandchild2_->SetPosition(gfx::PointF(1.f, 1.f)); root_->AddChild(child_); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild1_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild2_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild3_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild1_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild2_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild3_.get())); grandchild1_->RemoveFromParent(); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); grandchild2_->RemoveFromParent(); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); grandchild3_->RemoveFromParent(); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); EndTest(); @@ -5180,55 +5167,55 @@ class LayerTreeHostTestPushPropertiesSetPropertyInParentThenChild layer_tree_host()->SetRootLayer(root_); break; case 1: - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild1_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild2_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild3_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild1_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild2_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild3_.get())); child_->SetPosition(gfx::PointF(1.f, 1.f)); grandchild1_->SetPosition(gfx::PointF(1.f, 1.f)); grandchild2_->SetPosition(gfx::PointF(1.f, 1.f)); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild1_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild2_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild3_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild1_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild2_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild3_.get())); grandchild1_->RemoveFromParent(); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); grandchild2_->RemoveFromParent(); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); child_->RemoveFromParent(); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); EndTest(); @@ -5250,55 +5237,55 @@ class LayerTreeHostTestPushPropertiesSetPropertyInChildThenParent layer_tree_host()->SetRootLayer(root_); break; case 1: - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild1_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild2_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild3_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild1_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild2_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild3_.get())); grandchild1_->SetPosition(gfx::PointF(1.f, 1.f)); grandchild2_->SetPosition(gfx::PointF(1.f, 1.f)); child_->SetPosition(gfx::PointF(1.f, 1.f)); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild1_.get())); - EXPECT_TRUE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild2_.get())); - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - grandchild3_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild1_.get())); + EXPECT_TRUE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild2_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + grandchild3_.get())); grandchild1_->RemoveFromParent(); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); grandchild2_->RemoveFromParent(); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), child_.get())); child_->RemoveFromParent(); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( layer_tree_host()->LayersThatShouldPushProperties(), root_.get())); EndTest(); @@ -5460,9 +5447,9 @@ class LayerTreeHostTestPushHiddenLayer : public LayerTreeHostTest { switch (layer_tree_host()->SourceFrameNumber()) { case 1: // The layer type used does not need to push properties every frame. - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - child_layer_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + child_layer_.get())); // Change the bounds of the child layer, but make it skipped // by CalculateDrawProperties. @@ -5471,9 +5458,9 @@ class LayerTreeHostTestPushHiddenLayer : public LayerTreeHostTest { break; case 2: // The bounds of the child layer were pushed to the impl side. - EXPECT_FALSE(base::ContainsKey( - layer_tree_host()->LayersThatShouldPushProperties(), - child_layer_.get())); + EXPECT_FALSE( + base::Contains(layer_tree_host()->LayersThatShouldPushProperties(), + child_layer_.get())); EndTest(); break; @@ -6410,7 +6397,7 @@ MULTI_THREAD_TEST_F(LayerTreeHostTestEmptyLayerGpuRasterization); class LayerTreeHostWithGpuRasterizationTest : public LayerTreeHostTest { protected: - std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( const viz::RendererSettings& renderer_settings, double refresh_rate, scoped_refptr<viz::ContextProvider> ignored_compositor_context_provider, @@ -6939,7 +6926,7 @@ class LayerTreeHostTestSynchronousCompositeSwapPromise settings->use_zero_copy = true; } - std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( const viz::RendererSettings& renderer_settings, double refresh_rate, scoped_refptr<viz::ContextProvider> compositor_context_provider, @@ -6949,7 +6936,7 @@ class LayerTreeHostTestSynchronousCompositeSwapPromise bool synchronous_composite = !HasImplThread() && !layer_tree_host()->GetSettings().single_thread_proxy_scheduler; - return std::make_unique<viz::TestLayerTreeFrameSink>( + return std::make_unique<TestLayerTreeFrameSink>( compositor_context_provider, std::move(worker_context_provider), gpu_memory_buffer_manager(), renderer_settings, ImplThreadTaskRunner(), synchronous_composite, disable_display_vsync, refresh_rate); @@ -7807,7 +7794,7 @@ class LayerTreeTestPageScaleFlags : public LayerTreeTest { layer->IsAffectedByPageScale() ? this->affected_by_page_scale_ : this->not_affected_by_page_scale_; - EXPECT_TRUE(base::ContainsValue(list, layer->id())); + EXPECT_TRUE(base::Contains(list, layer->id())); }); EndTest(); @@ -8179,7 +8166,7 @@ SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSubmitFrameMetadata); class LayerTreeHostTestSubmitFrameResources : public LayerTreeHostTest { protected: - std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( const viz::RendererSettings& renderer_settings, double refresh_rate, scoped_refptr<viz::ContextProvider> compositor_context_provider, @@ -8248,33 +8235,6 @@ class LayerTreeHostTestSubmitFrameResources : public LayerTreeHostTest { SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSubmitFrameResources); -// Ensure that content_source_id is propagated to the frame's metadata. -class LayerTreeHostTestContentSourceId : public LayerTreeHostTest { - protected: - void BeginTest() override { - layer_tree_host()->SetContentSourceId(5); - PostSetNeedsCommitToMainThread(); - } - - DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, - LayerTreeHostImpl::FrameData* frame_data, - DrawResult draw_result) override { - EXPECT_EQ(DRAW_SUCCESS, draw_result); - EXPECT_EQ(5U, host_impl->active_tree()->content_source_id()); - return draw_result; - } - - void DisplayReceivedCompositorFrameOnThread( - const viz::CompositorFrame& frame) override { - EXPECT_EQ(5U, frame.metadata.content_source_id); - EndTest(); - } - - void AfterTest() override {} -}; - -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestContentSourceId); - class LayerTreeHostTestBeginFrameAcks : public LayerTreeHostTest { protected: void BeginTest() override { PostSetNeedsCommitToMainThread(); } diff --git a/chromium/cc/trees/layer_tree_host_unittest_animation.cc b/chromium/cc/trees/layer_tree_host_unittest_animation.cc index 47a27fd67c8..d5a0aa983c0 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_animation.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_animation.cc @@ -369,6 +369,15 @@ class LayerTreeHostAnimationTestAddKeyframeModelWithTimingFunction if (host_impl->active_tree()->source_frame_number() != 0) return; + // The impl thread may start a second frame before the test finishes. This + // can lead to a race as if the main thread has committed a new sync tree + // the impl thread can now delete the KeyframeModel as the animation only + // lasts a single frame and no longer affects either tree. Only test the + // first frame the animation shows up for. + if (!first_animation_frame_) + return; + first_animation_frame_ = false; + scoped_refptr<AnimationTimeline> timeline_impl = GetImplAnimationHost(host_impl)->GetTimelineById(timeline_id_); scoped_refptr<SingleKeyframeEffectAnimation> animation_child_impl = @@ -397,6 +406,7 @@ class LayerTreeHostAnimationTestAddKeyframeModelWithTimingFunction FakeContentLayerClient client_; scoped_refptr<FakePictureLayer> picture_; + bool first_animation_frame_ = true; }; SINGLE_AND_MULTI_THREAD_TEST_F( @@ -712,13 +722,6 @@ class LayerTreeHostAnimationTestCheckerboardDoesntStartAnimations animation_child_->set_animation_delegate(this); } - void InitializeSettings(LayerTreeSettings* settings) override { - // Make sure that drawing many times doesn't cause a checkerboarded - // animation to start so we avoid flake in this test. - settings->timeout_and_draw_when_animation_checkerboards = false; - LayerTreeHostAnimationTest::InitializeSettings(settings); - } - void BeginTest() override { prevented_draw_ = 0; started_times_ = 0; diff --git a/chromium/cc/trees/layer_tree_host_unittest_capture_content.cc b/chromium/cc/trees/layer_tree_host_unittest_capture_content.cc index 75f69a3cbef..1b6d70d9527 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_capture_content.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_capture_content.cc @@ -12,44 +12,42 @@ namespace cc { namespace { -class FakeTextHolder : public TextHolder { +class FakeTextHolder { public: - FakeTextHolder(const std::string& text, const gfx::Rect& rect) - : text_(text), rect_(rect) {} + FakeTextHolder(const std::string& text, const gfx::Rect& rect, NodeId node_id) + : text_(text), rect_(rect), node_id_(node_id) {} std::string text() const { return text_; } gfx::Rect rect() const { return rect_; } - - protected: - ~FakeTextHolder() override = default; + NodeId node_id() const { return node_id_; } private: std::string text_; gfx::Rect rect_; + NodeId node_id_; }; class FakeCaptureContentLayerClient : public FakeContentLayerClient { public: - void addTextHolder(scoped_refptr<FakeTextHolder> holder) { + void addTextHolder(const FakeTextHolder& holder) { holders_.push_back(holder); } scoped_refptr<DisplayItemList> PaintContentsToDisplayList( PaintingControlSetting painting_control) override { auto display_list = base::MakeRefCounted<DisplayItemList>(); - for (auto holder : holders_) { + for (auto& holder : holders_) { display_list->StartPaint(); display_list->push<DrawTextBlobOp>( - SkTextBlob::MakeFromString(holder->text().data(), SkFont()), - holder->rect().x(), holder->rect().y(), PaintFlags(), - NodeHolder(holder)); - display_list->EndPaintOfUnpaired(holder->rect()); + SkTextBlob::MakeFromString(holder.text().data(), SkFont()), + holder.rect().x(), holder.rect().y(), holder.node_id(), PaintFlags()); + display_list->EndPaintOfUnpaired(holder.rect()); } display_list->Finalize(); return display_list; } private: - std::vector<scoped_refptr<FakeTextHolder>> holders_; + std::vector<const FakeTextHolder> holders_; }; // These tests are for LayerTreeHost::CaptureContent(). @@ -58,8 +56,7 @@ class LayerTreeHostCaptureContentTest : public LayerTreeTest { ~LayerTreeHostCaptureContentTest() override = default; protected: - LayerTreeHostCaptureContentTest() - : device_bounds_(10, 10), weak_factory_(this) {} + LayerTreeHostCaptureContentTest() : device_bounds_(10, 10) {} void BeginTest() override { PostSetNeedsCommitToMainThread(); } @@ -78,17 +75,13 @@ class LayerTreeHostCaptureContentTest : public LayerTreeTest { layer_tree_host()->SetViewportVisibleRect(gfx::Rect(device_bounds_)); } - void VerifyCapturedContent( - std::vector<scoped_refptr<FakeTextHolder>>* expected_result) { + void VerifyCapturedContent(std::vector<FakeTextHolder>* expected_result) { EXPECT_EQ(expected_result->size(), captured_content_.size()); size_t expected_left_result = expected_result->size(); - for (auto c : captured_content_) { - EXPECT_EQ(c.type, NodeHolder::Type::kTextHolder); + for (auto& c : captured_content_) { for (auto it = expected_result->begin(); it != expected_result->end(); ++it) { - if (it->get() == c.text_holder.get()) { - EXPECT_EQ(static_cast<FakeTextHolder*>(c.text_holder.get())->text(), - it->get()->text()); + if (it->node_id() == c) { expected_result->erase(it); break; } @@ -114,23 +107,23 @@ class LayerTreeHostCaptureContentTest : public LayerTreeTest { } scoped_refptr<FakePictureLayer> root_picture_layer_; - std::vector<NodeHolder> captured_content_; + std::vector<NodeId> captured_content_; const gfx::Size device_bounds_; - base::WeakPtrFactory<LayerTreeHostCaptureContentTest> weak_factory_; + base::WeakPtrFactory<LayerTreeHostCaptureContentTest> weak_factory_{this}; }; class LayerTreeHostCaptureContentTestBasic : public LayerTreeHostCaptureContentTest { protected: void SetupTextHolders(const gfx::Rect& rect1, const gfx::Rect& rect2) { - text_holder_1_ = base::MakeRefCounted<FakeTextHolder>("Text1", rect1); - client_.addTextHolder(text_holder_1_); - text_holder_2_ = base::MakeRefCounted<FakeTextHolder>("Text2", rect2); - client_.addTextHolder(text_holder_2_); + text_holder_1_ = std::make_unique<FakeTextHolder>("Text1", rect1, 1); + client_.addTextHolder(*text_holder_1_); + text_holder_2_ = std::make_unique<FakeTextHolder>("Text2", rect2, 2); + client_.addTextHolder(*text_holder_2_); } - scoped_refptr<FakeTextHolder> text_holder_1_; - scoped_refptr<FakeTextHolder> text_holder_2_; + std::unique_ptr<FakeTextHolder> text_holder_1_; + std::unique_ptr<FakeTextHolder> text_holder_2_; }; // Test that one DrawTextBlobOp is on-screen, another isn't. @@ -144,8 +137,8 @@ class LayerTreeHostCaptureContentTestOneVisible } void AfterTest() override { - std::vector<scoped_refptr<FakeTextHolder>> expected_result; - expected_result.push_back(text_holder_1_); + std::vector<FakeTextHolder> expected_result; + expected_result.push_back(*text_holder_1_); VerifyCapturedContent(&expected_result); } }; @@ -163,9 +156,9 @@ class LayerTreeHostCaptureContentTestTwoVisible } void AfterTest() override { - std::vector<scoped_refptr<FakeTextHolder>> expected_result; - expected_result.push_back(text_holder_1_); - expected_result.push_back(text_holder_2_); + std::vector<FakeTextHolder> expected_result; + expected_result.push_back(*text_holder_1_); + expected_result.push_back(*text_holder_2_); VerifyCapturedContent(&expected_result); } }; @@ -208,11 +201,11 @@ class LayerTreeHostCaptureContentTestTwoLayers void SetupSecondaryPictureLayer(const gfx::Size& size) { // Add text to layer. text_holder_21_ = - base::MakeRefCounted<FakeTextHolder>("Text21", gfx::Rect(0, 0, 10, 5)); - client2_.addTextHolder(text_holder_21_); + std::make_unique<FakeTextHolder>("Text21", gfx::Rect(0, 0, 10, 5), 21); + client2_.addTextHolder(*text_holder_21_); text_holder_22_ = - base::MakeRefCounted<FakeTextHolder>("Text22", gfx::Rect(0, 5, 10, 5)); - client2_.addTextHolder(text_holder_22_); + std::make_unique<FakeTextHolder>("Text22", gfx::Rect(0, 5, 10, 5), 22); + client2_.addTextHolder(*text_holder_22_); client2_.set_bounds(size); // Create layer. @@ -223,8 +216,8 @@ class LayerTreeHostCaptureContentTestTwoLayers } scoped_refptr<FakePictureLayer> picture_layer; - scoped_refptr<FakeTextHolder> text_holder_21_; - scoped_refptr<FakeTextHolder> text_holder_22_; + std::unique_ptr<FakeTextHolder> text_holder_21_; + std::unique_ptr<FakeTextHolder> text_holder_22_; FakeCaptureContentLayerClient client2_; }; @@ -237,9 +230,9 @@ class LayerTreeHostCaptureContentTestOneLayerVisible } void AfterTest() override { - std::vector<scoped_refptr<FakeTextHolder>> expected_result; - expected_result.push_back(text_holder_1_); - expected_result.push_back(text_holder_2_); + std::vector<FakeTextHolder> expected_result; + expected_result.push_back(*text_holder_1_); + expected_result.push_back(*text_holder_2_); VerifyCapturedContent(&expected_result); } }; @@ -256,10 +249,10 @@ class LayerTreeHostCaptureContentTestTwoLayersVisible } void AfterTest() override { - std::vector<scoped_refptr<FakeTextHolder>> expected_result; - expected_result.push_back(text_holder_1_); - expected_result.push_back(text_holder_2_); - expected_result.push_back(text_holder_21_); + std::vector<FakeTextHolder> expected_result; + expected_result.push_back(*text_holder_1_); + expected_result.push_back(*text_holder_2_); + expected_result.push_back(*text_holder_21_); VerifyCapturedContent(&expected_result); } }; @@ -274,10 +267,10 @@ class LayerTreeHostCaptureContentTestTwoLayersVisibleAndTransparent void AfterTest() override { // All 3 TextHolders are returned. - std::vector<scoped_refptr<FakeTextHolder>> expected_result; - expected_result.push_back(text_holder_1_); - expected_result.push_back(text_holder_2_); - expected_result.push_back(text_holder_21_); + std::vector<FakeTextHolder> expected_result; + expected_result.push_back(*text_holder_1_); + expected_result.push_back(*text_holder_2_); + expected_result.push_back(*text_holder_21_); VerifyCapturedContent(&expected_result); } }; @@ -295,11 +288,11 @@ class LayerTreeHostCaptureContentTestUpperLayerPartialOverlay } void AfterTest() override { - std::vector<scoped_refptr<FakeTextHolder>> expected_result; - expected_result.push_back(text_holder_1_); - expected_result.push_back(text_holder_2_); - expected_result.push_back(text_holder_21_); - expected_result.push_back(text_holder_22_); + std::vector<FakeTextHolder> expected_result; + expected_result.push_back(*text_holder_1_); + expected_result.push_back(*text_holder_2_); + expected_result.push_back(*text_holder_21_); + expected_result.push_back(*text_holder_22_); VerifyCapturedContent(&expected_result); } }; diff --git a/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc b/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc index 5ad5e2a6977..dfdecdb3ef6 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_checkerimaging.cc @@ -7,11 +7,11 @@ #include "cc/test/fake_picture_layer.h" #include "cc/test/layer_tree_test.h" #include "cc/test/skia_common.h" +#include "cc/test/test_layer_tree_frame_sink.h" #include "cc/trees/layer_tree_impl.h" #include "components/ukm/test_ukm_recorder.h" #include "components/viz/test/begin_frame_args_test.h" #include "components/viz/test/fake_external_begin_frame_source.h" -#include "components/viz/test/test_layer_tree_frame_sink.h" namespace cc { namespace { diff --git a/chromium/cc/trees/layer_tree_host_unittest_context.cc b/chromium/cc/trees/layer_tree_host_unittest_context.cc index a06f23dc57f..b658d15f6fe 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_context.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_context.cc @@ -29,6 +29,7 @@ #include "cc/test/fake_video_frame_provider.h" #include "cc/test/layer_tree_test.h" #include "cc/test/render_pass_test_utils.h" +#include "cc/test/test_layer_tree_frame_sink.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_host_impl.h" #include "cc/trees/layer_tree_impl.h" @@ -38,7 +39,6 @@ #include "components/viz/test/fake_output_surface.h" #include "components/viz/test/test_context_provider.h" #include "components/viz/test/test_gles2_interface.h" -#include "components/viz/test/test_layer_tree_frame_sink.h" #include "components/viz/test/test_shared_bitmap_manager.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/raster_interface.h" @@ -85,7 +85,7 @@ class LayerTreeHostContextTest : public LayerTreeTest { gl_ = nullptr; } - std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( const viz::RendererSettings& renderer_settings, double refresh_rate, scoped_refptr<viz::ContextProvider> compositor_context_provider, @@ -1682,7 +1682,7 @@ SINGLE_AND_MULTI_THREAD_TEST_F(TileResourceFreedIfLostWhileExported); class SoftwareTileResourceFreedIfLostWhileExported : public LayerTreeTest { protected: - std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( const viz::RendererSettings& renderer_settings, double refresh_rate, scoped_refptr<viz::ContextProvider> compositor_context_provider, diff --git a/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc b/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc index 5bac6e88f25..3f335e7d9d5 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc @@ -13,6 +13,7 @@ #include "cc/test/fake_content_layer_client.h" #include "cc/test/fake_picture_layer.h" #include "cc/test/layer_tree_test.h" +#include "cc/test/test_layer_tree_frame_sink.h" #include "cc/trees/layer_tree_impl.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_result.h" @@ -20,7 +21,6 @@ #include "components/viz/test/fake_output_surface.h" #include "components/viz/test/fake_skia_output_surface.h" #include "components/viz/test/test_gles2_interface.h" -#include "components/viz/test/test_layer_tree_frame_sink.h" #include "gpu/GLES2/gl2extchromium.h" namespace cc { @@ -514,7 +514,7 @@ class LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest client_.set_bounds(root_->bounds()); } - std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( + std::unique_ptr<TestLayerTreeFrameSink> CreateLayerTreeFrameSink( const viz::RendererSettings& renderer_settings, double refresh_rate, scoped_refptr<viz::ContextProvider> compositor_context_provider, @@ -589,7 +589,7 @@ class LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest viz::RenderPassId parent_render_pass_id = 0; viz::RenderPassId copy_layer_render_pass_id = 0; - viz::TestLayerTreeFrameSink* frame_sink_ = nullptr; + TestLayerTreeFrameSink* frame_sink_ = nullptr; bool did_swap_ = false; FakeContentLayerClient client_; scoped_refptr<FakePictureLayer> root_; @@ -671,10 +671,6 @@ TEST_P(LayerTreeHostCopyRequestTestClippedOut, Test) { class LayerTreeHostCopyRequestTestScaledLayer : public LayerTreeHostCopyRequestTest { protected: - void InitializeSettings(LayerTreeSettings* settings) override { - settings->layer_transforms_should_scale_layer_contents = true; - } - void SetupTree() override { root_ = Layer::Create(); root_->SetBounds(gfx::Size(20, 20)); diff --git a/chromium/cc/trees/layer_tree_host_unittest_masks.cc b/chromium/cc/trees/layer_tree_host_unittest_masks.cc index b0a8b0c9ac2..3870aba8671 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_masks.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_masks.cc @@ -50,7 +50,7 @@ class LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin gfx::Size mask_size(100, 100); mask_layer->SetBounds(mask_size); - mask_layer->SetLayerMaskType(Layer::LayerMaskType::MULTI_TEXTURE_MASK); + mask_layer->SetLayerMaskType(Layer::LayerMaskType::SINGLE_TEXTURE_MASK); mask_layer_id_ = mask_layer->id(); layer_tree_host()->SetRootLayer(root); @@ -89,21 +89,9 @@ class LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin render_pass_quad->rect); EXPECT_EQ(gfx::Rect(0, 0, 50, 50).ToString(), rect_in_target_space.ToString()); - if (host_impl->settings().enable_mask_tiling) { - PictureLayerImpl* mask_layer_impl = static_cast<PictureLayerImpl*>( - host_impl->active_tree()->LayerById(mask_layer_id_)); - gfx::SizeF texture_size( - mask_layer_impl->CalculateTileSize(mask_layer_impl->bounds())); - EXPECT_EQ( - gfx::RectF(50.f / texture_size.width(), 50.f / texture_size.height(), - 50.f / texture_size.width(), 50.f / texture_size.height()) - .ToString(), - render_pass_quad->mask_uv_rect.ToString()); - } else { - EXPECT_EQ(gfx::ScaleRect(gfx::RectF(50.f, 50.f, 50.f, 50.f), 1.f / 100.f) - .ToString(), - render_pass_quad->mask_uv_rect.ToString()); - } + EXPECT_EQ(gfx::ScaleRect(gfx::RectF(50.f, 50.f, 50.f, 50.f), 1.f / 100.f) + .ToString(), + render_pass_quad->mask_uv_rect.ToString()); EndTest(); return draw_result; } @@ -114,27 +102,8 @@ class LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin FakeContentLayerClient client_; }; -class LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin_Untiled - : public LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin { - public: - void InitializeSettings(LayerTreeSettings* settings) override { - settings->enable_mask_tiling = false; - } -}; - -SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin_Untiled); - -class LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin_Tiled - : public LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin { - public: - void InitializeSettings(LayerTreeSettings* settings) override { - settings->enable_mask_tiling = true; - } -}; - SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin_Tiled); + LayerTreeTestMaskLayerForSurfaceWithContentRectNotAtOrigin); class LayerTreeTestMaskLayerForSurfaceWithClippedLayer : public LayerTreeTest { protected: @@ -191,7 +160,7 @@ class LayerTreeTestMaskLayerForSurfaceWithClippedLayer : public LayerTreeTest { gfx::Size mask_size(50, 50); mask_layer->SetBounds(mask_size); - mask_layer->SetLayerMaskType(Layer::LayerMaskType::MULTI_TEXTURE_MASK); + mask_layer->SetLayerMaskType(Layer::LayerMaskType::SINGLE_TEXTURE_MASK); mask_layer_id_ = mask_layer->id(); layer_tree_host()->SetRootLayer(root); @@ -226,21 +195,9 @@ class LayerTreeTestMaskLayerForSurfaceWithClippedLayer : public LayerTreeTest { // coords in the mask are scaled by 10/50 and 20/50. // The surface is clipped to (20,10) so the mask texture coords are offset // by 20/50 and 10/50 - if (host_impl->settings().enable_mask_tiling) { - PictureLayerImpl* mask_layer_impl = static_cast<PictureLayerImpl*>( - host_impl->active_tree()->LayerById(mask_layer_id_)); - gfx::SizeF texture_size( - mask_layer_impl->CalculateTileSize(mask_layer_impl->bounds())); - EXPECT_EQ( - gfx::RectF(20.f / texture_size.width(), 10.f / texture_size.height(), - 10.f / texture_size.width(), 20.f / texture_size.height()) - .ToString(), - render_pass_quad->mask_uv_rect.ToString()); - } else { - EXPECT_EQ(gfx::ScaleRect(gfx::RectF(20.f, 10.f, 10.f, 20.f), 1.f / 50.f) - .ToString(), - render_pass_quad->mask_uv_rect.ToString()); - } + EXPECT_EQ(gfx::ScaleRect(gfx::RectF(20.f, 10.f, 10.f, 20.f), 1.f / 50.f) + .ToString(), + render_pass_quad->mask_uv_rect.ToString()); EndTest(); return draw_result; } @@ -251,27 +208,8 @@ class LayerTreeTestMaskLayerForSurfaceWithClippedLayer : public LayerTreeTest { FakeContentLayerClient client_; }; -class LayerTreeTestMaskLayerForSurfaceWithClippedLayer_Untiled - : public LayerTreeTestMaskLayerForSurfaceWithClippedLayer { - public: - void InitializeSettings(LayerTreeSettings* settings) override { - settings->enable_mask_tiling = false; - } -}; - SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeTestMaskLayerForSurfaceWithClippedLayer_Untiled); - -class LayerTreeTestMaskLayerForSurfaceWithClippedLayer_Tiled - : public LayerTreeTestMaskLayerForSurfaceWithClippedLayer { - public: - void InitializeSettings(LayerTreeSettings* settings) override { - settings->enable_mask_tiling = true; - } -}; - -SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeTestMaskLayerForSurfaceWithClippedLayer_Tiled); + LayerTreeTestMaskLayerForSurfaceWithClippedLayer); class LayerTreeTestMaskLayerForSurfaceWithDifferentScale : public LayerTreeTest { @@ -333,7 +271,7 @@ class LayerTreeTestMaskLayerForSurfaceWithDifferentScale gfx::Size mask_size(50, 50); mask_layer->SetBounds(mask_size); - mask_layer->SetLayerMaskType(Layer::LayerMaskType::MULTI_TEXTURE_MASK); + mask_layer->SetLayerMaskType(Layer::LayerMaskType::SINGLE_TEXTURE_MASK); // Setting will change transform on mask layer will make it not adjust // raster scale, which will remain 1. This means the mask_layer and render // surface will have a scale of 2 during draw time. @@ -378,21 +316,9 @@ class LayerTreeTestMaskLayerForSurfaceWithDifferentScale // coords in the mask are scaled by 10/50 and 20/50. // The surface is clipped to (20,10) so the mask texture coords are offset // by 20/50 and 10/50 - if (host_impl->settings().enable_mask_tiling) { - PictureLayerImpl* mask_layer_impl = static_cast<PictureLayerImpl*>( - host_impl->active_tree()->LayerById(mask_layer_id_)); - gfx::SizeF texture_size( - mask_layer_impl->CalculateTileSize(mask_layer_impl->bounds())); - EXPECT_EQ( - gfx::RectF(20.f / texture_size.width(), 10.f / texture_size.height(), - 10.f / texture_size.width(), 20.f / texture_size.height()) - .ToString(), - render_pass_quad->mask_uv_rect.ToString()); - } else { - EXPECT_EQ(gfx::ScaleRect(gfx::RectF(20.f, 10.f, 10.f, 20.f), 1.f / 50.f) - .ToString(), - render_pass_quad->mask_uv_rect.ToString()); - } + EXPECT_EQ(gfx::ScaleRect(gfx::RectF(20.f, 10.f, 10.f, 20.f), 1.f / 50.f) + .ToString(), + render_pass_quad->mask_uv_rect.ToString()); EndTest(); return draw_result; } @@ -403,27 +329,8 @@ class LayerTreeTestMaskLayerForSurfaceWithDifferentScale FakeContentLayerClient client_; }; -class LayerTreeTestMaskLayerForSurfaceWithDifferentScale_Untiled - : public LayerTreeTestMaskLayerForSurfaceWithDifferentScale { - public: - void InitializeSettings(LayerTreeSettings* settings) override { - settings->enable_mask_tiling = false; - } -}; - SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeTestMaskLayerForSurfaceWithDifferentScale_Untiled); - -class LayerTreeTestMaskLayerForSurfaceWithDifferentScale_Tiled - : public LayerTreeTestMaskLayerForSurfaceWithDifferentScale { - public: - void InitializeSettings(LayerTreeSettings* settings) override { - settings->enable_mask_tiling = true; - } -}; - -SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeTestMaskLayerForSurfaceWithDifferentScale_Tiled); + LayerTreeTestMaskLayerForSurfaceWithDifferentScale); class LayerTreeTestMaskLayerWithScaling : public LayerTreeTest { protected: @@ -471,7 +378,7 @@ class LayerTreeTestMaskLayerWithScaling : public LayerTreeTest { content_layer->SetBounds(scaling_layer_size); mask_layer->SetBounds(scaling_layer_size); - mask_layer->SetLayerMaskType(Layer::LayerMaskType::MULTI_TEXTURE_MASK); + mask_layer->SetLayerMaskType(Layer::LayerMaskType::SINGLE_TEXTURE_MASK); layer_tree_host()->SetRootLayer(root); LayerTreeTest::SetupTree(); @@ -495,34 +402,25 @@ class LayerTreeTestMaskLayerWithScaling : public LayerTreeTest { root_pass->quad_list.front()->material); const viz::RenderPassDrawQuad* render_pass_quad = viz::RenderPassDrawQuad::MaterialCast(root_pass->quad_list.front()); + gfx::Rect rect_in_target_space = MathUtil::MapEnclosingClippedRect( + render_pass_quad->shared_quad_state->quad_to_target_transform, + render_pass_quad->rect); switch (host_impl->active_tree()->source_frame_number()) { case 0: // Check that the tree scaling is correctly taken into account for the // mask, that should fully map onto the quad. EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(), - render_pass_quad->rect.ToString()); - if (host_impl->settings().enable_mask_tiling) { - EXPECT_EQ( - gfx::RectF(0.f, 0.f, 100.f / 128.f, 100.f / 128.f).ToString(), - render_pass_quad->mask_uv_rect.ToString()); - } else { - EXPECT_EQ(gfx::RectF(0.f, 0.f, 1.f, 1.f).ToString(), - render_pass_quad->mask_uv_rect.ToString()); - } + rect_in_target_space.ToString()); + EXPECT_EQ(gfx::RectF(0.f, 0.f, 1.f, 1.f).ToString(), + render_pass_quad->mask_uv_rect.ToString()); break; case 1: // Applying a DSF should change the render surface size, but won't // affect which part of the mask is used. EXPECT_EQ(gfx::Rect(0, 0, 200, 200).ToString(), - render_pass_quad->rect.ToString()); - if (host_impl->settings().enable_mask_tiling) { - EXPECT_EQ( - gfx::RectF(0.f, 0.f, 100.f / 128.f, 100.f / 128.f).ToString(), - render_pass_quad->mask_uv_rect.ToString()); - } else { - EXPECT_EQ(gfx::RectF(0.f, 0.f, 1.f, 1.f).ToString(), - render_pass_quad->mask_uv_rect.ToString()); - } + rect_in_target_space.ToString()); + EXPECT_EQ(gfx::RectF(0.f, 0.f, 1.f, 1.f).ToString(), + render_pass_quad->mask_uv_rect.ToString()); EndTest(); break; } @@ -544,27 +442,7 @@ class LayerTreeTestMaskLayerWithScaling : public LayerTreeTest { FakeContentLayerClient client_; }; -class LayerTreeTestMaskLayerWithScaling_Untiled - : public LayerTreeTestMaskLayerWithScaling { - public: - void InitializeSettings(LayerTreeSettings* settings) override { - settings->enable_mask_tiling = false; - settings->layer_transforms_should_scale_layer_contents = true; - } -}; - -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeTestMaskLayerWithScaling_Untiled); - -class LayerTreeTestMaskLayerWithScaling_Tiled - : public LayerTreeTestMaskLayerWithScaling { - public: - void InitializeSettings(LayerTreeSettings* settings) override { - settings->enable_mask_tiling = true; - settings->layer_transforms_should_scale_layer_contents = true; - } -}; - -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeTestMaskLayerWithScaling_Tiled); +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeTestMaskLayerWithScaling); class LayerTreeTestMaskWithNonExactTextureSize : public LayerTreeTest { protected: @@ -642,26 +520,7 @@ class LayerTreeTestMaskWithNonExactTextureSize : public LayerTreeTest { FakeContentLayerClient client_; }; -class LayerTreeTestMaskWithNonExactTextureSize_Untiled - : public LayerTreeTestMaskWithNonExactTextureSize { - public: - void InitializeSettings(LayerTreeSettings* settings) override { - settings->enable_mask_tiling = false; - } -}; - -SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeTestMaskWithNonExactTextureSize_Untiled); - -class LayerTreeTestMaskWithNonExactTextureSize_Tiled - : public LayerTreeTestMaskWithNonExactTextureSize { - public: - void InitializeSettings(LayerTreeSettings* settings) override { - settings->enable_mask_tiling = true; - } -}; - -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeTestMaskWithNonExactTextureSize_Tiled); +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeTestMaskWithNonExactTextureSize); } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_host_unittest_picture.cc b/chromium/cc/trees/layer_tree_host_unittest_picture.cc index feea9ffa2cb..8c103dfc473 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_picture.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_picture.cc @@ -439,10 +439,6 @@ class LayerTreeHostPictureTestRSLLMembershipWithScale client_.set_bounds(picture_->bounds()); } - void InitializeSettings(LayerTreeSettings* settings) override { - settings->layer_transforms_should_scale_layer_contents = true; - } - void BeginTest() override { frame_ = 0; draws_in_frame_ = 0; @@ -607,10 +603,6 @@ class LayerTreeHostPictureTestForceRecalculateScales client_.set_bounds(size); } - void InitializeSettings(LayerTreeSettings* settings) override { - settings->layer_transforms_should_scale_layer_contents = true; - } - void BeginTest() override { PostSetNeedsCommitToMainThread(); } void DrawLayersOnThread(LayerTreeHostImpl* impl) override { diff --git a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc index 667f67d8909..8ebf210f056 100644 --- a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc @@ -25,6 +25,8 @@ #include "cc/test/layer_tree_test.h" #include "cc/test/test_task_graph_runner.h" #include "cc/test/test_ukm_recorder_factory.h" +#include "cc/trees/clip_node.h" +#include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_host_common.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/scroll_node.h" @@ -977,7 +979,7 @@ class LayerTreeHostScrollTestImplOnlyScroll : public LayerTreeHostScrollTest { Layer* scroll_layer = layer_tree_host()->outer_viewport_scroll_layer(); switch (layer_tree_host()->SourceFrameNumber()) { case 0: - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( scroll_layer->layer_tree_host()->LayersThatShouldPushProperties(), scroll_layer)); break; @@ -985,7 +987,7 @@ class LayerTreeHostScrollTestImplOnlyScroll : public LayerTreeHostScrollTest { // Even if this layer doesn't need push properties, it should // still pick up scrolls that happen on the active layer during // commit. - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( scroll_layer->layer_tree_host()->LayersThatShouldPushProperties(), scroll_layer)); break; @@ -1155,8 +1157,7 @@ class LayerTreeHostScrollTestScrollZeroMaxScrollOffset ScrollNode* scroll_node = scroll_tree.Node(scroll_layer->scroll_tree_index()); InputHandler::ScrollStatus status = - impl->TryScroll(gfx::PointF(0.0f, 1.0f), InputHandler::TOUCHSCREEN, - scroll_tree, scroll_node); + impl->TryScroll(gfx::PointF(0.0f, 1.0f), scroll_tree, scroll_node); switch (impl->active_tree()->source_frame_number()) { case 0: EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); @@ -1206,26 +1207,17 @@ class LayerTreeHostScrollTestScrollNonDrawnLayer } void DrawLayersOnThread(LayerTreeHostImpl* impl) override { - LayerImpl* scroll_layer = impl->OuterViewportScrollLayer(); - - ScrollTree& scroll_tree = - impl->active_tree()->property_trees()->scroll_tree; - ScrollNode* scroll_node = - scroll_tree.Node(scroll_layer->scroll_tree_index()); - // Verify that the scroll layer's scroll offset is taken into account when // checking whether the screen space point is inside the non-fast // scrollable region. - - InputHandler::ScrollStatus status = - impl->TryScroll(gfx::PointF(1.f, 1.f), InputHandler::TOUCHSCREEN, - scroll_tree, scroll_node); + InputHandler::ScrollStatus status = impl->ScrollBegin( + BeginState(gfx::Point(0, 0)).get(), InputHandler::TOUCHSCREEN); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, status.main_thread_scrolling_reasons); - status = impl->TryScroll(gfx::PointF(21.f, 21.f), InputHandler::TOUCHSCREEN, - scroll_tree, scroll_node); + status = impl->ScrollBegin(BeginState(gfx::Point(21, 21)).get(), + InputHandler::TOUCHSCREEN); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); @@ -1265,14 +1257,13 @@ class LayerTreeHostScrollTestImplScrollUnderMainThreadScrollingParent scroll_tree.Node(outer_scroll_layer->scroll_tree_index()); InputHandler::ScrollStatus status = - impl->TryScroll(gfx::PointF(1.f, 1.f), InputHandler::TOUCHSCREEN, - scroll_tree, inner_scroll_node); + impl->TryScroll(gfx::PointF(1.f, 1.f), scroll_tree, inner_scroll_node); EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kScrollbarScrolling, status.main_thread_scrolling_reasons); - status = impl->TryScroll(gfx::PointF(1.f, 1.f), InputHandler::TOUCHSCREEN, - scroll_tree, outer_scroll_node); + status = + impl->TryScroll(gfx::PointF(1.f, 1.f), scroll_tree, outer_scroll_node); EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread); EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, status.main_thread_scrolling_reasons); @@ -1321,12 +1312,18 @@ class ThreadCheckingInputHandlerClient : public InputHandlerClient { } } - void DeliverInputForBeginFrame() override { + void DeliverInputForBeginFrame(const viz::BeginFrameArgs& args) override { if (!task_runner_->BelongsToCurrentThread()) { ADD_FAILURE() << "DeliverInputForBeginFrame called on wrong thread"; } } + void DeliverInputForHighLatencyMode() override { + if (!task_runner_->BelongsToCurrentThread()) { + ADD_FAILURE() << "DeliverInputForHighLatencyMode called on wrong thread"; + } + } + private: base::SingleThreadTaskRunner* task_runner_; bool* received_stop_flinging_; @@ -1825,7 +1822,8 @@ class MockInputHandlerClient : public InputHandlerClient { float page_scale_factor, float min_page_scale_factor, float max_page_scale_factor) override {} - void DeliverInputForBeginFrame() override {} + void DeliverInputForBeginFrame(const viz::BeginFrameArgs& args) override {} + void DeliverInputForHighLatencyMode() override {} }; // This is a regression test, see crbug.com/639046. @@ -2233,5 +2231,189 @@ class LayerTreeHostScrollTestImplSideInvalidation MULTI_THREAD_TEST_F(LayerTreeHostScrollTestImplSideInvalidation); +// Version of LayerTreeHostScrollTest that uses layer lists which means the +// property trees and layer list are explicitly specified instead of running +// the cc property tree builder. +class LayerListLayerTreeHostScrollTest : public LayerTreeHostScrollTest { + public: + // The id of the root property tree nodes. + static constexpr int kRootNodeId = 1; + + LayerListLayerTreeHostScrollTest() { SetUseLayerList(); } + + void SetupTree() override { + // Setup the root transform, effect, clip, and scroll property tree nodes. + auto& transform_tree = layer_tree_host()->property_trees()->transform_tree; + auto& root_transform_node = *transform_tree.Node( + transform_tree.Insert(TransformNode(), kRealRootNodeId)); + DCHECK_EQ(root_transform_node.id, kRootNodeId); + root_transform_node.source_node_id = root_transform_node.parent_id; + transform_tree.set_needs_update(true); + + auto& effect_tree = layer_tree_host()->property_trees()->effect_tree; + auto& root_effect_node = + *effect_tree.Node(effect_tree.Insert(EffectNode(), kRealRootNodeId)); + DCHECK_EQ(root_effect_node.id, kRootNodeId); + root_effect_node.stable_id = 1; + root_effect_node.transform_id = kRealRootNodeId; + root_effect_node.clip_id = kRealRootNodeId; + root_effect_node.render_surface_reason = RenderSurfaceReason::kRoot; + effect_tree.set_needs_update(true); + + auto& clip_tree = layer_tree_host()->property_trees()->clip_tree; + auto& root_clip_node = + *clip_tree.Node(clip_tree.Insert(ClipNode(), kRealRootNodeId)); + DCHECK_EQ(root_clip_node.id, kRootNodeId); + root_clip_node.clip_type = ClipNode::ClipType::APPLIES_LOCAL_CLIP; + root_clip_node.clip = gfx::RectF(gfx::SizeF(800, 600)); + root_clip_node.transform_id = kRealRootNodeId; + clip_tree.set_needs_update(true); + + auto& scroll_tree = layer_tree_host()->property_trees()->scroll_tree; + auto& root_scroll_node = + *scroll_tree.Node(scroll_tree.Insert(ScrollNode(), kRealRootNodeId)); + DCHECK_EQ(root_scroll_node.id, kRootNodeId); + root_scroll_node.transform_id = kRealRootNodeId; + scroll_tree.set_needs_update(true); + + // Setup the root Layer which should be used to attach the layer list. + root_ = Layer::Create(); + root_->SetBounds(gfx::Size(800, 600)); + root_->SetIsDrawable(true); + root_->SetHitTestable(true); + root_->SetTransformTreeIndex(root_transform_node.id); + root_->SetEffectTreeIndex(root_effect_node.id); + root_->SetScrollTreeIndex(root_scroll_node.id); + root_->SetClipTreeIndex(root_clip_node.id); + layer_tree_host()->SetRootLayer(root_); + + layer_tree_host()->SetViewportSizeAndScale(gfx::Size(800, 600), 1.f, + viz::LocalSurfaceIdAllocation()); + + layer_tree_host()->property_trees()->sequence_number = + root_->property_tree_sequence_number(); + + root_->SetNeedsCommit(); + } + + Layer* root() const { return root_.get(); } + + private: + // The compositor is hard-coded to use 0 for the root nodes (always non-null). + static constexpr int kRealRootNodeId = 0; + + scoped_refptr<Layer> root_; +}; + +class NonScrollingNonFastScrollableRegion + : public LayerListLayerTreeHostScrollTest { + public: + // Setup 3 Layers: + // 1) bottom_ which has a non-fast region in the bottom-right. + // 2) middle_scrollable_ which is scrollable. + // 3) top_ which has a non-fast region in the top-left and is offset by + // |middle_scrollable_|'s scroll offset. + void SetupTree() override { + LayerListLayerTreeHostScrollTest::SetupTree(); + + fake_content_layer_client_.set_bounds(root()->bounds()); + + std::vector<scoped_refptr<Layer>> layer_list; + + bottom_ = FakePictureLayer::Create(&fake_content_layer_client_); + bottom_->SetElementId(LayerIdToElementIdForTesting(bottom_->id())); + bottom_->SetBounds(gfx::Size(100, 100)); + bottom_->SetNonFastScrollableRegion(Region(gfx::Rect(50, 50, 50, 50))); + bottom_->SetHitTestable(true); + bottom_->SetTransformTreeIndex(kRootNodeId); + bottom_->SetEffectTreeIndex(kRootNodeId); + bottom_->SetScrollTreeIndex(kRootNodeId); + bottom_->SetClipTreeIndex(kRootNodeId); + bottom_->set_property_tree_sequence_number( + root()->property_tree_sequence_number()); + layer_list.push_back(bottom_); + + auto& scroll_tree = layer_tree_host()->property_trees()->scroll_tree; + auto& scroll_node = + *scroll_tree.Node(scroll_tree.Insert(ScrollNode(), kRootNodeId)); + scroll_node.transform_id = kRootNodeId; + middle_scrollable_ = FakePictureLayer::Create(&fake_content_layer_client_); + middle_scrollable_->SetElementId( + LayerIdToElementIdForTesting(middle_scrollable_->id())); + scroll_node.element_id = middle_scrollable_->element_id(); + scroll_node.scrollable = true; + scroll_node.bounds = gfx::Size(100, 200); + scroll_node.container_bounds = gfx::Size(100, 100); + middle_scrollable_->SetBounds(gfx::Size(100, 100)); + middle_scrollable_->SetIsDrawable(true); + middle_scrollable_->SetScrollable(gfx::Size(100, 200)); + middle_scrollable_->SetHitTestable(true); + middle_scrollable_->SetTransformTreeIndex(kRootNodeId); + middle_scrollable_->SetEffectTreeIndex(kRootNodeId); + middle_scrollable_->SetScrollTreeIndex(scroll_node.id); + middle_scrollable_->SetClipTreeIndex(kRootNodeId); + middle_scrollable_->set_property_tree_sequence_number( + root()->property_tree_sequence_number()); + layer_list.push_back(middle_scrollable_); + + top_ = FakePictureLayer::Create(&fake_content_layer_client_); + top_->SetElementId(LayerIdToElementIdForTesting(top_->id())); + top_->SetBounds(gfx::Size(100, 100)); + top_->SetNonFastScrollableRegion(Region(gfx::Rect(0, 0, 50, 50))); + top_->SetHitTestable(true); + top_->SetTransformTreeIndex(kRootNodeId); + top_->SetEffectTreeIndex(kRootNodeId); + top_->SetScrollTreeIndex(scroll_node.id); + top_->SetClipTreeIndex(kRootNodeId); + top_->set_property_tree_sequence_number( + root()->property_tree_sequence_number()); + + layer_list.push_back(top_); + root()->SetChildLayerList(std::move(layer_list)); + } + + void BeginTest() override { PostSetNeedsCommitToMainThread(); } + + void CommitCompleteOnThread(LayerTreeHostImpl* impl) override { + // The top-left hit should immediately hit the top layer's non-fast region + // which forces main-thread scrolling. + auto top_left_status = impl->ScrollBegin( + BeginState(gfx::Point(20, 20)).get(), InputHandler::TOUCHSCREEN); + EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, top_left_status.thread); + EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, + top_left_status.main_thread_scrolling_reasons); + + // The top-right hit should hit the top layer but not the non-fast region so + // the scroll should continue to scroll on the impl. + InputHandler::ScrollStatus top_right_status = impl->ScrollBegin( + BeginState(gfx::Point(80, 20)).get(), InputHandler::TOUCHSCREEN); + EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, top_right_status.thread); + EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, + top_right_status.main_thread_scrolling_reasons); + + // The bottom-right should hit the bottom layer's non-fast region. Though + // the middle layer is a composited scroller and is hit first, we cannot do + // a fast scroll because an ancestor on the scroll chain has hit a non-fast + // region. + InputHandler::ScrollStatus bottom_right_status = impl->ScrollBegin( + BeginState(gfx::Point(80, 80)).get(), InputHandler::TOUCHSCREEN); + EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, bottom_right_status.thread); + EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion, + bottom_right_status.main_thread_scrolling_reasons); + + EndTest(); + } + + void AfterTest() override {} + + private: + FakeContentLayerClient fake_content_layer_client_; + scoped_refptr<Layer> bottom_; + scoped_refptr<Layer> middle_scrollable_; + scoped_refptr<Layer> top_; +}; + +SINGLE_THREAD_TEST_F(NonScrollingNonFastScrollableRegion); + } // namespace } // namespace cc diff --git a/chromium/cc/trees/layer_tree_impl.cc b/chromium/cc/trees/layer_tree_impl.cc index fbd783c9798..fe75edcd991 100644 --- a/chromium/cc/trees/layer_tree_impl.cc +++ b/chromium/cc/trees/layer_tree_impl.cc @@ -89,7 +89,6 @@ LayerTreeImpl::LayerTreeImpl( external_page_scale_factor_(1.f), device_scale_factor_(1.f), painted_device_scale_factor_(1.f), - content_source_id_(0), elastic_overscroll_(elastic_overscroll), layers_(new OwnedLayerImplList), needs_update_draw_properties_(true), @@ -484,8 +483,6 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { target_tree->SetRasterColorSpace(raster_color_space_id_, raster_color_space_); target_tree->elastic_overscroll()->PushPendingToActive(); - target_tree->set_content_source_id(content_source_id()); - target_tree->set_painted_device_scale_factor(painted_device_scale_factor()); target_tree->SetDeviceScaleFactor(device_scale_factor()); target_tree->SetDeviceViewportSize(device_viewport_size_); @@ -1290,7 +1287,6 @@ bool LayerTreeImpl::UpdateDrawProperties( InnerViewportScrollLayer(), OuterViewportScrollLayer(), elastic_overscroll()->Current(IsActiveTree()), OverscrollElasticityElementId(), max_texture_size(), - settings().layer_transforms_should_scale_layer_contents, &render_surface_list_, &property_trees_, PageScaleTransformNode()); LayerTreeHostCommon::CalculateDrawProperties(&inputs); if (const char* client_name = GetClientNameForMetrics()) { @@ -1456,6 +1452,18 @@ 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 +// a layer_element_map_ approach similar to LayerById(). +LayerImpl* LayerTreeImpl::LayerByElementId(ElementId element_id) const { + auto it = std::find_if(layer_list_.rbegin(), layer_list_.rend(), + [&element_id](LayerImpl* layer_impl) { + return layer_impl->element_id() == element_id; + }); + if (it == layer_list_.rend()) + return nullptr; + return *it; +} + LayerImpl* LayerTreeImpl::ScrollableLayerByElementId( ElementId element_id) const { auto iter = element_id_to_scrollable_layer_.find(element_id); @@ -1482,7 +1490,7 @@ void LayerTreeImpl::AddLayerShouldPushProperties(LayerImpl* layer) { DCHECK(!IsActiveTree()) << "The active tree does not push layer properties"; // TODO(crbug.com/303943): PictureLayerImpls always push properties so should // not go into this set or we'd push them twice. - DCHECK(!base::ContainsValue(picture_layers_, layer)); + DCHECK(!base::Contains(picture_layers_, layer)); layers_that_should_push_properties_.insert(layer); } @@ -1503,7 +1511,7 @@ void LayerTreeImpl::UnregisterLayer(LayerImpl* layer) { // These manage ownership of the LayerImpl. void LayerTreeImpl::AddLayer(std::unique_ptr<LayerImpl> layer) { - DCHECK(!base::ContainsValue(*layers_, layer)); + DCHECK(!base::Contains(*layers_, layer)); DCHECK(layer); layers_->push_back(std::move(layer)); set_needs_update_draw_properties(); @@ -1862,7 +1870,7 @@ void LayerTreeImpl::ProcessUIResourceRequestQueue() { } void LayerTreeImpl::RegisterPictureLayerImpl(PictureLayerImpl* layer) { - DCHECK(!base::ContainsValue(picture_layers_, layer)); + DCHECK(!base::Contains(picture_layers_, layer)); picture_layers_.push_back(layer); } @@ -1870,6 +1878,23 @@ void LayerTreeImpl::UnregisterPictureLayerImpl(PictureLayerImpl* layer) { auto it = std::find(picture_layers_.begin(), picture_layers_.end(), layer); DCHECK(it != picture_layers_.end()); picture_layers_.erase(it); + + // Make sure that |picture_layers_with_paint_worklets_| doesn't get left with + // dead layers. They should already have been removed (via calling + // NotifyLayerHasPaintWorkletsChanged) before the layer was unregistered. + DCHECK(!picture_layers_with_paint_worklets_.contains(layer)); +} + +void LayerTreeImpl::NotifyLayerHasPaintWorkletsChanged(PictureLayerImpl* layer, + bool has_worklets) { + if (has_worklets) { + auto insert_pair = picture_layers_with_paint_worklets_.insert(layer); + DCHECK(insert_pair.second); + } else { + auto it = picture_layers_with_paint_worklets_.find(layer); + DCHECK(it != picture_layers_with_paint_worklets_.end()); + picture_layers_with_paint_worklets_.erase(it); + } } void LayerTreeImpl::RegisterScrollbar(ScrollbarLayerImplBase* scrollbar_layer) { @@ -2179,8 +2204,10 @@ LayerImpl* LayerTreeImpl::FindLayerThatIsHitByPoint( struct FindTouchEventLayerFunctor { bool operator()(LayerImpl* layer) const { + if (!layer->has_touch_action_regions()) + return false; return PointHitsRegion(screen_space_point, layer->ScreenSpaceTransform(), - layer->touch_action_region().region(), layer); + layer->GetAllTouchActionRegions(), layer); } const gfx::PointF screen_space_point; }; @@ -2220,6 +2247,28 @@ LayerImpl* LayerTreeImpl::FindLayerThatIsHitByPointInWheelEventHandlerRegion( func); } +std::vector<const LayerImpl*> +LayerTreeImpl::FindLayersHitByPointInNonFastScrollableRegion( + const gfx::PointF& screen_space_point) { + std::vector<const LayerImpl*> layers; + if (layer_list_.empty()) + return layers; + if (!UpdateDrawProperties()) + return layers; + for (const auto* layer : *this) { + if (layer->non_fast_scrollable_region().IsEmpty()) + continue; + if (!PointHitsLayer(layer, screen_space_point, nullptr)) + continue; + if (PointHitsRegion(screen_space_point, layer->ScreenSpaceTransform(), + layer->non_fast_scrollable_region(), layer)) { + layers.push_back(layer); + } + } + + return layers; +} + void LayerTreeImpl::RegisterSelection(const LayerSelection& selection) { if (selection_ == selection) return; diff --git a/chromium/cc/trees/layer_tree_impl.h b/chromium/cc/trees/layer_tree_impl.h index 21f3f87674a..5efd40a6f8f 100644 --- a/chromium/cc/trees/layer_tree_impl.h +++ b/chromium/cc/trees/layer_tree_impl.h @@ -343,9 +343,6 @@ class CC_EXPORT LayerTreeImpl { return painted_device_scale_factor_; } - void set_content_source_id(uint32_t id) { content_source_id_ = id; } - uint32_t content_source_id() { return content_source_id_; } - void SetLocalSurfaceIdAllocationFromParent( const viz::LocalSurfaceIdAllocation& local_surface_id_allocation_from_parent); @@ -460,6 +457,7 @@ class CC_EXPORT LayerTreeImpl { gfx::Rect RootScrollLayerDeviceViewportBounds() const; LayerImpl* LayerById(int id) const; + LayerImpl* LayerByElementId(ElementId element_id) const; LayerImpl* ScrollableLayerByElementId(ElementId element_id) const; bool IsElementInPropertyTree(ElementId element_id) const; @@ -545,6 +543,13 @@ class CC_EXPORT LayerTreeImpl { return picture_layers_; } + void NotifyLayerHasPaintWorkletsChanged(PictureLayerImpl* layer, + bool has_worklets); + const base::flat_set<PictureLayerImpl*>& picture_layers_with_paint_worklets() + const { + return picture_layers_with_paint_worklets_; + } + void RegisterScrollbar(ScrollbarLayerImplBase* scrollbar_layer); void UnregisterScrollbar(ScrollbarLayerImplBase* scrollbar_layer); ScrollbarSet ScrollbarsFor(ElementId scroll_element_id) const; @@ -560,6 +565,10 @@ class CC_EXPORT LayerTreeImpl { LayerImpl* FindLayerThatIsHitByPointInWheelEventHandlerRegion( const gfx::PointF& screen_space_point); + // Return all layers with a hit non-fast scrollable region. + std::vector<const LayerImpl*> FindLayersHitByPointInNonFastScrollableRegion( + const gfx::PointF& screen_space_point); + void RegisterSelection(const LayerSelection& selection); bool HandleVisibilityChanged() const { return handle_visibility_changed_; } @@ -704,7 +713,6 @@ class CC_EXPORT LayerTreeImpl { int raster_color_space_id_ = -1; gfx::ColorSpace raster_color_space_; - uint32_t content_source_id_; viz::LocalSurfaceIdAllocation local_surface_id_allocation_from_parent_; bool new_local_surface_id_request_ = false; gfx::Size device_viewport_size_; @@ -745,6 +753,11 @@ class CC_EXPORT LayerTreeImpl { std::vector<PictureLayerImpl*> picture_layers_; + // After commit (or impl-side invalidation), the LayerTreeHostImpl must walk + // all PictureLayerImpls that have PaintWorklets to ensure they are painted. + // To avoid unnecessary walking, we track that set here. + base::flat_set<PictureLayerImpl*> picture_layers_with_paint_worklets_; + base::flat_set<viz::SurfaceRange> surface_layer_ranges_; // List of render surfaces for the most recently prepared frame. diff --git a/chromium/cc/trees/layer_tree_impl_unittest.cc b/chromium/cc/trees/layer_tree_impl_unittest.cc index 4dc46346a58..11d6bbc38d7 100644 --- a/chromium/cc/trees/layer_tree_impl_unittest.cc +++ b/chromium/cc/trees/layer_tree_impl_unittest.cc @@ -6,6 +6,7 @@ #include "cc/layers/heads_up_display_layer_impl.h" #include "cc/test/fake_layer_tree_host_impl.h" +#include "cc/test/fake_raster_source.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/layer_test_common.h" #include "cc/trees/clip_node.h" @@ -18,17 +19,10 @@ namespace cc { namespace { -class LayerTreeImplTestSettings : public LayerTreeSettings { - public: - LayerTreeImplTestSettings() { - layer_transforms_should_scale_layer_contents = true; - } -}; - class LayerTreeImplTest : public testing::Test { public: - LayerTreeImplTest( - const LayerTreeSettings& settings = LayerTreeImplTestSettings()) + explicit LayerTreeImplTest( + const LayerTreeSettings& settings = LayerTreeSettings()) : impl_test_(settings) {} FakeLayerTreeHostImpl& host_impl() const { return *impl_test_.host_impl(); } @@ -47,7 +41,6 @@ class LayerTreeImplTest : public testing::Test { render_surface_list_impl_.clear(); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root_layer, root_layer->bounds(), &render_surface_list_impl_); - inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); } @@ -2468,6 +2461,61 @@ TEST_F(LayerTreeImplTest, NotPersistentSwapPromisesAreDroppedWhenSwapFails) { } } +TEST_F(LayerTreeImplTest, TrackPictureLayersWithPaintWorklets) { + host_impl().CreatePendingTree(); + LayerTreeImpl* pending_tree = host_impl().pending_tree(); + + // Initially there are no layers in the set. + EXPECT_EQ(pending_tree->picture_layers_with_paint_worklets().size(), 0u); + + // Add three layers; two with PaintWorklets and one without. + std::unique_ptr<PictureLayerImpl> child1_owned = + PictureLayerImpl::Create(pending_tree, 2, Layer::LayerMaskType::NOT_MASK); + child1_owned->SetBounds(gfx::Size(100, 100)); + std::unique_ptr<PictureLayerImpl> child2_owned = + PictureLayerImpl::Create(pending_tree, 3, Layer::LayerMaskType::NOT_MASK); + child2_owned->SetBounds(gfx::Size(100, 100)); + std::unique_ptr<PictureLayerImpl> child3_owned = + PictureLayerImpl::Create(pending_tree, 4, Layer::LayerMaskType::NOT_MASK); + child3_owned->SetBounds(gfx::Size(100, 100)); + + PictureLayerImpl* child1 = child1_owned.get(); + PictureLayerImpl* child3 = child3_owned.get(); + + root_layer()->test_properties()->AddChild(std::move(child1_owned)); + root_layer()->test_properties()->AddChild(std::move(child2_owned)); + root_layer()->test_properties()->AddChild(std::move(child3_owned)); + + Region empty_invalidation; + scoped_refptr<RasterSource> raster_source1( + FakeRasterSource::CreateFilledWithPaintWorklet(child1->bounds())); + child1->UpdateRasterSource(raster_source1, &empty_invalidation, nullptr, + nullptr); + scoped_refptr<RasterSource> raster_source3( + FakeRasterSource::CreateFilledWithPaintWorklet(child3->bounds())); + child3->UpdateRasterSource(raster_source3, &empty_invalidation, nullptr, + nullptr); + + // The set should correctly track which layers are in it. + const base::flat_set<PictureLayerImpl*>& layers = + pending_tree->picture_layers_with_paint_worklets(); + EXPECT_EQ(layers.size(), 2u); + EXPECT_TRUE(layers.contains(child1)); + EXPECT_TRUE(layers.contains(child3)); + + // Test explicitly removing a layer from the set. + scoped_refptr<RasterSource> empty_raster_source( + FakeRasterSource::CreateFilled(child1->bounds())); + child1->UpdateRasterSource(empty_raster_source, &empty_invalidation, nullptr, + nullptr); + EXPECT_EQ(layers.size(), 1u); + EXPECT_FALSE(layers.contains(child1)); + + // Deleting a layer should also cause it to be removed from the set. + root_layer()->test_properties()->RemoveChild(child3); + EXPECT_EQ(layers.size(), 0u); +} + namespace { class CommitToPendingTreeLayerTreeImplTestSettings : public LayerTreeSettings { public: diff --git a/chromium/cc/trees/layer_tree_settings.cc b/chromium/cc/trees/layer_tree_settings.cc index 047b36948e7..ce4ba0308eb 100644 --- a/chromium/cc/trees/layer_tree_settings.cc +++ b/chromium/cc/trees/layer_tree_settings.cc @@ -24,19 +24,11 @@ SchedulerSettings LayerTreeSettings::ToSchedulerSettings() const { SchedulerSettings scheduler_settings; scheduler_settings.main_frame_before_activation_enabled = main_frame_before_activation_enabled; - scheduler_settings.timeout_and_draw_when_animation_checkerboards = - timeout_and_draw_when_animation_checkerboards; scheduler_settings.using_synchronous_renderer_compositor = using_synchronous_renderer_compositor; scheduler_settings.enable_latency_recovery = enable_latency_recovery; - scheduler_settings.background_frame_interval = - base::TimeDelta::FromSecondsD(1.0 / background_animation_rate); scheduler_settings.wait_for_all_pipeline_stages_before_draw = wait_for_all_pipeline_stages_before_draw; - scheduler_settings.enable_surface_synchronization = - enable_surface_synchronization; - scheduler_settings.compositor_threaded_scrollbar_scrolling = - compositor_threaded_scrollbar_scrolling; return scheduler_settings; } diff --git a/chromium/cc/trees/layer_tree_settings.h b/chromium/cc/trees/layer_tree_settings.h index 092fb1b7ea3..d62c52b5326 100644 --- a/chromium/cc/trees/layer_tree_settings.h +++ b/chromium/cc/trees/layer_tree_settings.h @@ -62,13 +62,11 @@ class CC_EXPORT LayerTreeSettings { SkColor solid_color_scrollbar_color = SK_ColorWHITE; base::TimeDelta scroll_animation_duration_for_testing; bool timeout_and_draw_when_animation_checkerboards = true; - bool layer_transforms_should_scale_layer_contents = false; bool layers_always_allowed_lcd_text = false; float minimum_contents_scale = 0.0625f; float low_res_contents_scale_factor = 0.25f; float top_controls_show_threshold = 0.5f; float top_controls_hide_threshold = 0.5f; - double background_animation_rate = 1.0; gfx::Size default_tile_size; gfx::Size max_untiled_layer_size; // If set, indicates the largest tile size we will use for GPU Raster. If not @@ -97,8 +95,6 @@ class CC_EXPORT LayerTreeSettings { bool use_rgba_4444 = false; bool unpremultiply_and_dither_low_bit_depth_tiles = false; - bool enable_mask_tiling = true; - // If set to true, the compositor may selectively defer image decodes to the // Image Decode Service and raster tiles without images until the decode is // ready. diff --git a/chromium/cc/trees/mutator_host.h b/chromium/cc/trees/mutator_host.h index 03e3ce4d4a0..c52ce403301 100644 --- a/chromium/cc/trees/mutator_host.h +++ b/chromium/cc/trees/mutator_host.h @@ -9,7 +9,7 @@ #include "base/callback_forward.h" #include "base/time/time.h" -#include "cc/trees/element_id.h" +#include "cc/paint/element_id.h" #include "cc/trees/layer_tree_mutator.h" #include "cc/trees/mutator_host_client.h" #include "ui/gfx/geometry/box_f.h" @@ -131,6 +131,13 @@ class MutatorHost { virtual bool HasTickingKeyframeModelForTesting( ElementId element_id) const = 0; + virtual void ImplOnlyAutoScrollAnimationCreate( + ElementId element_id, + const gfx::ScrollOffset& target_offset, + const gfx::ScrollOffset& current_offset, + float autoscroll_velocity, + base::TimeDelta animation_start_offset) = 0; + virtual void ImplOnlyScrollAnimationCreate( ElementId element_id, const gfx::ScrollOffset& target_offset, diff --git a/chromium/cc/trees/mutator_host_client.h b/chromium/cc/trees/mutator_host_client.h index 5d221f3051c..406b10abfc5 100644 --- a/chromium/cc/trees/mutator_host_client.h +++ b/chromium/cc/trees/mutator_host_client.h @@ -5,7 +5,7 @@ #ifndef CC_TREES_MUTATOR_HOST_CLIENT_H_ #define CC_TREES_MUTATOR_HOST_CLIENT_H_ -#include "cc/trees/element_id.h" +#include "cc/paint/element_id.h" #include "cc/trees/property_animation_state.h" #include "cc/trees/target_property.h" diff --git a/chromium/cc/trees/occlusion_tracker_unittest.cc b/chromium/cc/trees/occlusion_tracker_unittest.cc index 04827454e5e..638a253ceb6 100644 --- a/chromium/cc/trees/occlusion_tracker_unittest.cc +++ b/chromium/cc/trees/occlusion_tracker_unittest.cc @@ -203,7 +203,6 @@ class OcclusionTrackerTest : public testing::Test { LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root, root->bounds(), &render_surface_list_impl_); - inputs.can_adjust_raster_scales = true; LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs); layer_iterator_ = std::make_unique<EffectTreeLayerListIterator>( diff --git a/chromium/cc/trees/property_tree.cc b/chromium/cc/trees/property_tree.cc index 5d6b0b82fa1..f91deac77f3 100644 --- a/chromium/cc/trees/property_tree.cc +++ b/chromium/cc/trees/property_tree.cc @@ -233,6 +233,8 @@ void TransformTree::UpdateTransforms(int id) { UpdateNodeAndAncestorsHaveIntegerTranslations(node, parent_node); UpdateTransformChanged(node, parent_node, source_node); UpdateNodeAndAncestorsAreAnimatedOrInvertible(node, parent_node); + + DCHECK(!node->needs_local_transform_update); } bool TransformTree::IsDescendant(int desc_id, int source_id) const { @@ -374,7 +376,7 @@ gfx::Vector2dF StickyPositionOffset(TransformTree* tree, TransformNode* node) { scroll_position -= transform_node->snap_amount; } - gfx::RectF clip = constraint.constraint_box_rect; + gfx::Rect clip = constraint.constraint_box_rect; clip.Offset(scroll_position.x(), scroll_position.y()); // The clip region may need to be offset by the outer viewport bounds, e.g. if @@ -831,7 +833,6 @@ void EffectTree::UpdateHasMaskingChild(EffectNode* node, // when we actually encounter a masking child. node->has_masking_child = false; if (node->blend_mode == SkBlendMode::kDstIn) { - DCHECK(parent_node->HasRenderSurface()); parent_node->has_masking_child = true; } } @@ -850,16 +851,10 @@ void EffectTree::UpdateSurfaceContentsScale(EffectNode* effect_node) { if (transform_node->in_subtree_of_page_scale_layer) layer_scale_factor *= transform_tree.page_scale_factor(); - // Note: Copy requests currently expect transform to effect output size. - bool use_transform_for_contents_scale = - property_trees()->can_adjust_raster_scales || - effect_node->has_copy_request; const gfx::Vector2dF old_scale = effect_node->surface_contents_scale; effect_node->surface_contents_scale = - use_transform_for_contents_scale - ? MathUtil::ComputeTransform2dScaleComponents( - transform_tree.ToScreen(transform_node->id), layer_scale_factor) - : gfx::Vector2dF(layer_scale_factor, layer_scale_factor); + MathUtil::ComputeTransform2dScaleComponents( + transform_tree.ToScreen(transform_node->id), layer_scale_factor); // If surface contents scale changes, draw transforms are no longer valid. // Invalidates the draw transform cache and updates the clip for the surface. @@ -1757,7 +1752,6 @@ PropertyTreesCachedData::~PropertyTreesCachedData() = default; PropertyTrees::PropertyTrees() : needs_rebuild(true), - can_adjust_raster_scales(true), changed(false), full_tree_damaged(false), sequence_number(0), @@ -1785,7 +1779,6 @@ bool PropertyTrees::operator==(const PropertyTrees& other) const { full_tree_damaged == other.full_tree_damaged && is_main_thread == other.is_main_thread && is_active == other.is_active && - can_adjust_raster_scales == other.can_adjust_raster_scales && sequence_number == other.sequence_number; } @@ -1800,7 +1793,6 @@ PropertyTrees& PropertyTrees::operator=(const PropertyTrees& from) { needs_rebuild = from.needs_rebuild; changed = from.changed; full_tree_damaged = from.full_tree_damaged; - can_adjust_raster_scales = from.can_adjust_raster_scales; sequence_number = from.sequence_number; is_main_thread = from.is_main_thread; is_active = from.is_active; @@ -1830,7 +1822,6 @@ void PropertyTrees::clear() { needs_rebuild = true; full_tree_damaged = false; changed = false; - can_adjust_raster_scales = true; sequence_number++; #if DCHECK_IS_ON() @@ -2077,17 +2068,6 @@ CombinedAnimationScale PropertyTrees::GetAnimationScales( &cached_data_.animation_scales[transform_node_id]; if (animation_scales->update_number != cached_data_.transform_tree_update_number) { - if (!layer_tree_impl->settings() - .layer_transforms_should_scale_layer_contents) { - animation_scales->update_number = - cached_data_.transform_tree_update_number; - animation_scales->combined_maximum_animation_target_scale = kNotScaled; - animation_scales->combined_starting_animation_scale = kNotScaled; - return CombinedAnimationScale( - animation_scales->combined_maximum_animation_target_scale, - animation_scales->combined_starting_animation_scale); - } - TransformNode* node = transform_tree.Node(transform_node_id); TransformNode* parent_node = transform_tree.parent(node); bool ancestor_is_animating_scale = false; diff --git a/chromium/cc/trees/property_tree.h b/chromium/cc/trees/property_tree.h index f61d2c26a84..75b972dd86c 100644 --- a/chromium/cc/trees/property_tree.h +++ b/chromium/cc/trees/property_tree.h @@ -15,8 +15,8 @@ #include "cc/base/synced_property.h" #include "cc/cc_export.h" #include "cc/layers/layer_sticky_position_constraint.h" +#include "cc/paint/element_id.h" #include "cc/paint/filter_operations.h" -#include "cc/trees/element_id.h" #include "cc/trees/mutator_host_client.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/scroll_offset.h" @@ -631,7 +631,6 @@ class CC_EXPORT PropertyTrees final { ClipTree clip_tree; ScrollTree scroll_tree; bool needs_rebuild; - bool can_adjust_raster_scales; // Change tracking done on property trees needs to be preserved across commits // (when they are not rebuild). We cache a global bool which stores whether // we did any change tracking so that we can skip copying the change status diff --git a/chromium/cc/trees/property_tree_builder.cc b/chromium/cc/trees/property_tree_builder.cc index bd5882ee4f9..44a2033de9a 100644 --- a/chromium/cc/trees/property_tree_builder.cc +++ b/chromium/cc/trees/property_tree_builder.cc @@ -180,6 +180,14 @@ static LayerImpl* ClipParent(LayerImpl* layer) { return layer->test_properties()->clip_parent; } +static bool HasClipRect(Layer* layer) { + return !layer->clip_rect().IsEmpty(); +} + +static bool HasClipRect(LayerImpl* layer) { + return false; +} + static inline const FilterOperations& Filters(Layer* layer) { return layer->filters(); } @@ -204,15 +212,6 @@ static bool HasRoundedCorner(LayerImpl* layer) { return !layer->test_properties()->rounded_corner_bounds.IsEmpty(); } -static gfx::RRectF RoundedCornerBounds(Layer* layer) { - return gfx::RRectF(gfx::RectF(gfx::Rect(layer->bounds())), - layer->corner_radii()); -} - -static gfx::RRectF RoundedCornerBounds(LayerImpl* layer) { - return layer->test_properties()->rounded_corner_bounds; -} - static PictureLayer* MaskLayer(Layer* layer) { return layer->mask_layer(); } @@ -310,9 +309,30 @@ static int GetTransformParent(const DataForRecursion& data, LayerType* layer) { } template <typename LayerType> +static bool LayerClipsSubtreeToItsBounds(LayerType* layer) { + return layer->masks_to_bounds() || MaskLayer(layer); +} + +template <typename LayerType> static bool LayerClipsSubtree(LayerType* layer) { - return layer->masks_to_bounds() || MaskLayer(layer) || - HasRoundedCorner(layer); + return LayerClipsSubtreeToItsBounds(layer) || HasRoundedCorner(layer) || + HasClipRect(layer); +} + +gfx::RectF EffectiveClipRect(Layer* layer) { + return layer->EffectiveClipRect(); +} + +gfx::RectF EffectiveClipRect(LayerImpl* layer) { + return gfx::RectF(gfx::PointF(), gfx::SizeF(layer->bounds())); +} + +static gfx::RRectF RoundedCornerBounds(Layer* layer) { + return gfx::RRectF(EffectiveClipRect(layer), layer->corner_radii()); +} + +static gfx::RRectF RoundedCornerBounds(LayerImpl* layer) { + return layer->test_properties()->rounded_corner_bounds; } template <typename LayerType> @@ -355,6 +375,12 @@ static inline bool HasLatestSequenceNumber(const LayerImpl*, int) { return true; } +static inline void SetHasClipNode(Layer* layer, bool val) { + layer->SetHasClipNode(val); +} + +static inline void SetHasClipNode(LayerImpl* layer, bool val) {} + template <typename LayerType> void PropertyTreeBuilderContext<LayerType>::AddClipNodeIfNeeded( const DataForRecursion& data_from_ancestor, @@ -376,8 +402,11 @@ void PropertyTreeBuilderContext<LayerType>::AddClipNodeIfNeeded( data_for_children->clip_tree_parent = parent_id; } else { ClipNode node; - node.clip = gfx::RectF(gfx::PointF() + layer->offset_to_transform_parent(), - gfx::SizeF(layer->bounds())); + node.clip = EffectiveClipRect(layer); + + // Move the clip bounds so that it is relative to the transform parent. + node.clip += layer->offset_to_transform_parent(); + node.transform_id = created_transform_node ? data_for_children->transform_tree_parent : GetTransformParent(data_from_ancestor, layer); @@ -391,6 +420,7 @@ void PropertyTreeBuilderContext<LayerType>::AddClipNodeIfNeeded( data_for_children->clip_tree_parent = clip_tree_.Insert(node, parent_id); } + SetHasClipNode(layer, requires_node); layer->SetClipTreeIndex(data_for_children->clip_tree_parent); } @@ -797,6 +827,14 @@ static inline const base::Optional<gfx::RRectF>& BackdropFilterBounds( return layer->test_properties()->backdrop_filter_bounds; } +static inline ElementId BackdropMaskElementId(Layer* layer) { + return layer->backdrop_mask_element_id(); +} + +static inline ElementId BackdropMaskElementId(LayerImpl* layer) { + return layer->test_properties()->backdrop_mask_element_id; +} + static inline float BackdropFilterQuality(Layer* layer) { return layer->backdrop_filter_quality(); } @@ -821,6 +859,14 @@ static inline bool HasCopyRequest(LayerImpl* layer) { return !layer->test_properties()->copy_requests.empty(); } +static inline int MirrorCount(Layer* layer) { + return layer->mirror_count(); +} + +static inline int MirrorCount(LayerImpl* layer) { + return 0; +} + static inline bool PropertyChanged(Layer* layer) { return layer->subtree_property_changed(); } @@ -933,6 +979,10 @@ RenderSurfaceReason ComputeRenderSurfaceReason(const MutatorHost& mutator_host, if (HasCopyRequest(layer)) return RenderSurfaceReason::kCopyRequest; + // If the layer is mirrored. + if (MirrorCount(layer)) + return RenderSurfaceReason::kMirrored; + return RenderSurfaceReason::kNone; } @@ -1040,6 +1090,7 @@ bool PropertyTreeBuilderContext<LayerType>::AddEffectNodeIfNeeded( node->backdrop_filters = BackdropFilters(layer); node->backdrop_filter_bounds = BackdropFilterBounds(layer); node->backdrop_filter_quality = BackdropFilterQuality(layer); + node->backdrop_mask_element_id = BackdropMaskElementId(layer); node->filters_origin = FiltersOrigin(layer); node->trilinear_filtering = TrilinearFiltering(layer); node->has_potential_opacity_animation = has_potential_opacity_animation; diff --git a/chromium/cc/trees/proxy.h b/chromium/cc/trees/proxy.h index 10a58a32998..0fe7a90fe3f 100644 --- a/chromium/cc/trees/proxy.h +++ b/chromium/cc/trees/proxy.h @@ -39,10 +39,6 @@ class CC_EXPORT Proxy { virtual bool IsStarted() const = 0; - // This function retruns true if the commits go directly to active tree by - // skipping commit to pending tree. - virtual bool CommitToActiveTree() const = 0; - virtual void SetLayerTreeFrameSink( LayerTreeFrameSink* layer_tree_frame_sink) = 0; virtual void ReleaseLayerTreeFrameSink() = 0; diff --git a/chromium/cc/trees/proxy_impl.cc b/chromium/cc/trees/proxy_impl.cc index cdfd8d3a135..2e69eddc64e 100644 --- a/chromium/cc/trees/proxy_impl.cc +++ b/chromium/cc/trees/proxy_impl.cc @@ -191,7 +191,9 @@ void ProxyImpl::BeginMainFrameAbortedOnImpl( DCHECK(IsImplThread()); DCHECK(scheduler_->CommitPending()); - host_impl_->BeginMainFrameAborted(reason, std::move(swap_promises)); + host_impl_->BeginMainFrameAborted( + reason, std::move(swap_promises), + scheduler_->last_dispatched_begin_main_frame_args()); scheduler_->NotifyBeginMainFrameStarted(main_thread_start_time); scheduler_->BeginMainFrameAborted(reason); } @@ -365,7 +367,7 @@ void ProxyImpl::PostAnimationEventsToMainThreadOnImplThread( DCHECK(IsImplThread()); MainThreadTaskRunner()->PostTask( FROM_HERE, base::BindOnce(&ProxyMain::SetAnimationEvents, - proxy_main_weak_ptr_, base::Passed(&events))); + proxy_main_weak_ptr_, std::move(events))); } size_t ProxyImpl::CompositedAnimationsCount() const { @@ -388,6 +390,14 @@ bool ProxyImpl::IsInsideDraw() { return inside_draw_; } +bool ProxyImpl::IsBeginMainFrameExpected() { + // Check whether the main-thread has requested for updates. If main-thread has + // not responded to a previously dispatched BeginMainFrame, then assume that + // main-thread would want to produce an update for the current frame too. + return scheduler_->needs_begin_main_frame() || + scheduler_->IsBeginMainFrameSent(); +} + void ProxyImpl::RenewTreePriority() { DCHECK(IsImplThread()); const bool user_interaction_in_progress = @@ -495,6 +505,8 @@ void ProxyImpl::DidPresentCompositorFrameOnImplThread( FROM_HERE, base::BindOnce(&ProxyMain::DidPresentCompositorFrame, proxy_main_weak_ptr_, frame_token, std::move(callbacks), feedback)); + if (scheduler_) + scheduler_->DidPresentCompositorFrame(frame_token, feedback.timestamp); } void ProxyImpl::NotifyAnimationWorkletStateChange( @@ -512,6 +524,12 @@ void ProxyImpl::NotifyAnimationWorkletStateChange( tree_type); } +void ProxyImpl::NotifyPaintWorkletStateChange( + Scheduler::PaintWorkletState state) { + DCHECK(IsImplThread()); + scheduler_->NotifyPaintWorkletStateChange(state); +} + bool ProxyImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) { DCHECK(IsImplThread()); return host_impl_->WillBeginImplFrame(args); @@ -551,8 +569,8 @@ void ProxyImpl::ScheduledActionSendBeginMainFrame( MainThreadTaskRunner()->PostTask( FROM_HERE, base::BindOnce(&ProxyMain::BeginMainFrame, proxy_main_weak_ptr_, - base::Passed(&begin_main_frame_state))); - host_impl_->DidSendBeginMainFrame(); + std::move(begin_main_frame_state))); + host_impl_->DidSendBeginMainFrame(args); devtools_instrumentation::DidRequestMainThreadFrame(layer_tree_host_id_); } @@ -685,6 +703,8 @@ DrawResult ProxyImpl::DrawInternal(bool forced_draw) { LayerTreeHostImpl::FrameData frame; frame.begin_frame_ack = scheduler_->CurrentBeginFrameAckForActiveTree(); + frame.origin_begin_main_frame_args = + scheduler_->last_activate_origin_frame_args(); bool draw_frame = false; DrawResult result; @@ -696,9 +716,11 @@ DrawResult ProxyImpl::DrawInternal(bool forced_draw) { } if (draw_frame) { - if (host_impl_->DrawLayers(&frame)) + if (host_impl_->DrawLayers(&frame)) { + DCHECK_NE(frame.frame_token, 0u); // Drawing implies we submitted a frame to the LayerTreeFrameSink. - scheduler_->DidSubmitCompositorFrame(); + scheduler_->DidSubmitCompositorFrame(frame.frame_token); + } result = DRAW_SUCCESS; } else { DCHECK_NE(DRAW_SUCCESS, result); @@ -709,7 +731,7 @@ DrawResult ProxyImpl::DrawInternal(bool forced_draw) { bool start_ready_animations = draw_frame; host_impl_->UpdateAnimationState(start_ready_animations); - // Tell the main thread that the the newly-commited frame was drawn. + // Tell the main thread that the newly-commited frame was drawn. if (next_frame_is_newly_committed_frame_) { next_frame_is_newly_committed_frame_ = false; MainThreadTaskRunner()->PostTask( diff --git a/chromium/cc/trees/proxy_impl.h b/chromium/cc/trees/proxy_impl.h index 62636b88386..d8222df2013 100644 --- a/chromium/cc/trees/proxy_impl.h +++ b/chromium/cc/trees/proxy_impl.h @@ -94,6 +94,7 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient, void PostAnimationEventsToMainThreadOnImplThread( std::unique_ptr<MutatorEvents> events) override; bool IsInsideDraw() override; + bool IsBeginMainFrameExpected() override; void RenewTreePriority() override; void PostDelayedAnimationTaskOnImplThread(base::OnceClosure task, base::TimeDelta delay) override; @@ -112,6 +113,8 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient, void NotifyAnimationWorkletStateChange( AnimationWorkletMutationState state, ElementListType element_list_type) override; + void NotifyPaintWorkletStateChange( + Scheduler::PaintWorkletState state) override; // SchedulerClient implementation bool WillBeginImplFrame(const viz::BeginFrameArgs& args) override; diff --git a/chromium/cc/trees/proxy_main.cc b/chromium/cc/trees/proxy_main.cc index 20299713e6a..dc7b697ae2b 100644 --- a/chromium/cc/trees/proxy_main.cc +++ b/chromium/cc/trees/proxy_main.cc @@ -39,9 +39,7 @@ ProxyMain::ProxyMain(LayerTreeHost* layer_tree_host, commit_waits_for_activation_(false), started_(false), defer_main_frame_update_(false), - defer_commits_(true), - frame_sink_bound_weak_factory_(this), - weak_factory_(this) { + defer_commits_(true) { TRACE_EVENT0("cc", "ProxyMain::ProxyMain"); DCHECK(task_runner_provider_); DCHECK(IsMainThread()); @@ -157,7 +155,7 @@ void ProxyMain::BeginMainFrame( base::Unretained(proxy_impl_.get()), CommitEarlyOutReason::ABORTED_NOT_VISIBLE, begin_main_frame_start_time, - base::Passed(&empty_swap_promises))); + std::move(empty_swap_promises))); return; } @@ -188,7 +186,7 @@ void ProxyMain::BeginMainFrame( base::Unretained(proxy_impl_.get()), CommitEarlyOutReason::ABORTED_DEFERRED_MAIN_FRAME_UPDATE, begin_main_frame_start_time, - base::Passed(&empty_swap_promises))); + std::move(empty_swap_promises))); // When we stop deferring main frame updates, we should resume any // previously requested pipeline stages. deferred_final_pipeline_stage_ = @@ -202,11 +200,22 @@ void ProxyMain::BeginMainFrame( current_pipeline_stage_ = ANIMATE_PIPELINE_STAGE; - // Synchronizes scroll offsets and page scale deltas (for pinch zoom) from the - // compositor thread thread to the main thread for both cc and and its - // client (e.g. Blink). - layer_tree_host_->ApplyScrollAndScale( - begin_main_frame_state->scroll_info.get()); + // Check now if we should stop deferring commits due to a timeout. We + // may also stop deferring in BeginMainFrame, but maintain the status + // from this point to keep scroll in sync. + if (defer_commits_ && base::TimeTicks::Now() > commits_restart_time_) { + StopDeferringCommits(PaintHoldingCommitTrigger::kTimeout); + } + skip_commit |= defer_commits_; + + if (!skip_commit) { + // Synchronizes scroll offsets and page scale deltas (for pinch zoom) from + // the compositor thread to the main thread for both cc and and its client + // (e.g. Blink). Do not do this if we explicitly plan to not commit the + // layer tree, to prevent scroll offsets getting out of sync. + layer_tree_host_->ApplyScrollAndScale( + begin_main_frame_state->scroll_info.get()); + } layer_tree_host_->WillBeginMainFrame(); layer_tree_host_->RecordStartOfFrameMetrics(); @@ -230,14 +239,10 @@ void ProxyMain::BeginMainFrame( // what this does. layer_tree_host_->RequestMainFrameUpdate(); - // Check now if we should stop deferring commits - if (defer_commits_ && base::TimeTicks::Now() > commits_restart_time_) { - StopDeferringCommits(PaintHoldingCommitTrigger::kTimeout); - } - - // At this point the main frame may have deferred commits to avoid committing - // right now, or we may be deferring commits but not deferring main - // frame updates. + // At this point the main frame may have deferred main frame updates to + // avoid committing right now, or we may be deferring commits but not + // deferring main frame updates. Either may have changed the status + // of the defer... flags, so re-evaluate skip_commit. skip_commit |= defer_main_frame_update_ || defer_commits_; if (skip_commit) { @@ -252,7 +257,7 @@ void ProxyMain::BeginMainFrame( base::Unretained(proxy_impl_.get()), CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT, begin_main_frame_start_time, - base::Passed(&empty_swap_promises))); + std::move(empty_swap_promises))); // We intentionally don't report CommitComplete() here since it was aborted // prematurely and we're waiting to do another commit in the future. // When we stop deferring commits, we should resume any previously requested @@ -295,11 +300,11 @@ void ProxyMain::BeginMainFrame( std::vector<std::unique_ptr<SwapPromise>> swap_promises = layer_tree_host_->GetSwapPromiseManager()->TakeSwapPromises(); ImplThreadTaskRunner()->PostTask( - FROM_HERE, base::BindOnce(&ProxyImpl::BeginMainFrameAbortedOnImpl, - base::Unretained(proxy_impl_.get()), - CommitEarlyOutReason::FINISHED_NO_UPDATES, - begin_main_frame_start_time, - base::Passed(&swap_promises))); + FROM_HERE, + base::BindOnce(&ProxyImpl::BeginMainFrameAbortedOnImpl, + base::Unretained(proxy_impl_.get()), + CommitEarlyOutReason::FINISHED_NO_UPDATES, + begin_main_frame_start_time, std::move(swap_promises))); // Although the commit is internally aborted, this is because it has been // detected to be a no-op. From the perspective of an embedder, this commit @@ -315,7 +320,7 @@ void ProxyMain::BeginMainFrame( ui::LatencyInfo new_latency_info(ui::SourceEventType::FRAME); new_latency_info.AddLatencyNumberWithTimestamp( ui::LATENCY_BEGIN_FRAME_RENDERER_MAIN_COMPONENT, - begin_main_frame_state->begin_frame_args.frame_time, 1); + begin_main_frame_state->begin_frame_args.frame_time); layer_tree_host_->QueueSwapPromise( std::make_unique<LatencyInfoSwapPromise>(new_latency_info)); @@ -365,12 +370,6 @@ bool ProxyMain::IsStarted() const { return started_; } -bool ProxyMain::CommitToActiveTree() const { - // With ProxyMain, we use a pending tree and activate it once it's ready to - // draw to allow input to modify the active tree and draw during raster. - return false; -} - void ProxyMain::SetLayerTreeFrameSink( LayerTreeFrameSink* layer_tree_frame_sink) { ImplThreadTaskRunner()->PostTask( diff --git a/chromium/cc/trees/proxy_main.h b/chromium/cc/trees/proxy_main.h index 7b110c08a7d..515f1e10de9 100644 --- a/chromium/cc/trees/proxy_main.h +++ b/chromium/cc/trees/proxy_main.h @@ -72,7 +72,6 @@ class CC_EXPORT ProxyMain : public Proxy { private: // Proxy implementation. bool IsStarted() const override; - bool CommitToActiveTree() const override; void SetLayerTreeFrameSink( LayerTreeFrameSink* layer_tree_frame_sink) override; void SetVisible(bool visible) override; @@ -156,9 +155,9 @@ class CC_EXPORT ProxyMain : public Proxy { // WeakPtrs generated by this factory will be invalidated when // LayerTreeFrameSink is released. - base::WeakPtrFactory<ProxyMain> frame_sink_bound_weak_factory_; + base::WeakPtrFactory<ProxyMain> frame_sink_bound_weak_factory_{this}; - base::WeakPtrFactory<ProxyMain> weak_factory_; + base::WeakPtrFactory<ProxyMain> weak_factory_{this}; }; } // namespace cc diff --git a/chromium/cc/trees/scroll_node.cc b/chromium/cc/trees/scroll_node.cc index 69b7b3318f5..50227da93d4 100644 --- a/chromium/cc/trees/scroll_node.cc +++ b/chromium/cc/trees/scroll_node.cc @@ -7,7 +7,7 @@ #include "cc/base/math_util.h" #include "cc/input/main_thread_scrolling_reason.h" #include "cc/layers/layer.h" -#include "cc/trees/element_id.h" +#include "cc/paint/element_id.h" #include "cc/trees/property_tree.h" #include "base/trace_event/traced_value.h" diff --git a/chromium/cc/trees/scroll_node.h b/chromium/cc/trees/scroll_node.h index de9f9343c8d..1d2e6fa789e 100644 --- a/chromium/cc/trees/scroll_node.h +++ b/chromium/cc/trees/scroll_node.h @@ -9,8 +9,8 @@ #include "cc/cc_export.h" #include "cc/input/overscroll_behavior.h" #include "cc/input/scroll_snap_data.h" +#include "cc/paint/element_id.h" #include "cc/paint/filter_operations.h" -#include "cc/trees/element_id.h" #include "ui/gfx/geometry/size.h" namespace base { diff --git a/chromium/cc/trees/single_thread_proxy.cc b/chromium/cc/trees/single_thread_proxy.cc index d74fe26e408..0bc215ea7c4 100644 --- a/chromium/cc/trees/single_thread_proxy.cc +++ b/chromium/cc/trees/single_thread_proxy.cc @@ -27,6 +27,7 @@ #include "cc/trees/scoped_abort_remaining_swap_promises.h" #include "components/viz/common/frame_sinks/delay_based_time_source.h" #include "components/viz/common/gpu/context_provider.h" +#include "ui/gfx/presentation_feedback.h" namespace cc { @@ -56,9 +57,7 @@ SingleThreadProxy::SingleThreadProxy(LayerTreeHost* layer_tree_host, inside_synchronous_composite_(false), needs_impl_frame_(false), layer_tree_frame_sink_creation_requested_(false), - layer_tree_frame_sink_lost_(true), - frame_sink_bound_weak_factory_(this), - weak_factory_(this) { + layer_tree_frame_sink_lost_(true) { TRACE_EVENT0("cc", "SingleThreadProxy::SingleThreadProxy"); DCHECK(task_runner_provider_); DCHECK(task_runner_provider_->IsMainThread()); @@ -75,7 +74,7 @@ void SingleThreadProxy::Start() { host_impl_ = layer_tree_host_->CreateLayerTreeHostImpl(this); if (settings.single_thread_proxy_scheduler && !scheduler_on_impl_thread_) { SchedulerSettings scheduler_settings(settings.ToSchedulerSettings()); - scheduler_settings.commit_to_active_tree = CommitToActiveTree(); + scheduler_settings.commit_to_active_tree = true; std::unique_ptr<CompositorTimingHistory> compositor_timing_history( new CompositorTimingHistory( @@ -102,12 +101,6 @@ bool SingleThreadProxy::IsStarted() const { return !!host_impl_; } -bool SingleThreadProxy::CommitToActiveTree() const { - // With SingleThreadProxy we skip the pending tree and commit directly to the - // active tree. - return true; -} - void SingleThreadProxy::SetVisible(bool visible) { TRACE_EVENT1("cc", "SingleThreadProxy::SetVisible", "visible", visible); DebugScopedSetImplThread impl(task_runner_provider_); @@ -436,6 +429,10 @@ bool SingleThreadProxy::IsInsideDraw() { return inside_draw_; } +bool SingleThreadProxy::IsBeginMainFrameExpected() { + return true; +} + void SingleThreadProxy::DidActivateSyncTree() { CommitComplete(); } @@ -525,6 +522,11 @@ void SingleThreadProxy::DidPresentCompositorFrameOnImplThread( const gfx::PresentationFeedback& feedback) { layer_tree_host_->DidPresentCompositorFrame(frame_token, std::move(callbacks), feedback); + + if (scheduler_on_impl_thread_) { + scheduler_on_impl_thread_->DidPresentCompositorFrame(frame_token, + feedback.timestamp); + } } void SingleThreadProxy::NotifyAnimationWorkletStateChange( @@ -533,6 +535,12 @@ void SingleThreadProxy::NotifyAnimationWorkletStateChange( layer_tree_host_->NotifyAnimationWorkletStateChange(state, element_list_type); } +void SingleThreadProxy::NotifyPaintWorkletStateChange( + Scheduler::PaintWorkletState state) { + // Off-Thread PaintWorklet is only supported on the threaded compositor. + NOTREACHED(); +} + void SingleThreadProxy::RequestBeginMainFrameNotExpected(bool new_state) { if (scheduler_on_impl_thread_) { scheduler_on_impl_thread_->SetMainThreadWantsBeginMainFrameNotExpected( @@ -605,6 +613,7 @@ void SingleThreadProxy::CompositeImmediately(base::TimeTicks frame_begin_time, if (raster) { LayerTreeHostImpl::FrameData frame; frame.begin_frame_ack = viz::BeginFrameAck(begin_frame_args, true); + frame.origin_begin_main_frame_args = begin_frame_args; DoComposite(&frame); } @@ -666,9 +675,11 @@ DrawResult SingleThreadProxy::DoComposite(LayerTreeHostImpl::FrameData* frame) { draw_frame = draw_result == DRAW_SUCCESS; if (draw_frame) { if (host_impl_->DrawLayers(frame)) { - if (scheduler_on_impl_thread_) + if (scheduler_on_impl_thread_) { // Drawing implies we submitted a frame to the LayerTreeFrameSink. - scheduler_on_impl_thread_->DidSubmitCompositorFrame(); + scheduler_on_impl_thread_->DidSubmitCompositorFrame( + frame->frame_token); + } single_thread_client_->DidSubmitCompositorFrame(); } } @@ -744,7 +755,7 @@ void SingleThreadProxy::ScheduledActionSendBeginMainFrame( task_runner_provider_->MainThreadTaskRunner()->PostTask( FROM_HERE, base::BindOnce(&SingleThreadProxy::BeginMainFrame, weak_factory_.GetWeakPtr(), begin_frame_args)); - host_impl_->DidSendBeginMainFrame(); + host_impl_->DidSendBeginMainFrame(begin_frame_args); } void SingleThreadProxy::FrameIntervalUpdated(base::TimeDelta interval) { @@ -823,8 +834,7 @@ void SingleThreadProxy::BeginMainFrame( // know we will commit since QueueSwapPromise itself requests a commit. ui::LatencyInfo new_latency_info(ui::SourceEventType::FRAME); new_latency_info.AddLatencyNumberWithTimestamp( - ui::LATENCY_BEGIN_FRAME_UI_MAIN_COMPONENT, begin_frame_args.frame_time, - 1); + ui::LATENCY_BEGIN_FRAME_UI_MAIN_COMPONENT, begin_frame_args.frame_time); layer_tree_host_->QueueSwapPromise( std::make_unique<LatencyInfoSwapPromise>(new_latency_info)); @@ -863,7 +873,9 @@ void SingleThreadProxy::BeginMainFrameAbortedOnImplThread( DCHECK(!host_impl_->pending_tree()); std::vector<std::unique_ptr<SwapPromise>> empty_swap_promises; - host_impl_->BeginMainFrameAborted(reason, std::move(empty_swap_promises)); + host_impl_->BeginMainFrameAborted( + reason, std::move(empty_swap_promises), + scheduler_on_impl_thread_->last_dispatched_begin_main_frame_args()); scheduler_on_impl_thread_->BeginMainFrameAborted(reason); } @@ -872,6 +884,8 @@ DrawResult SingleThreadProxy::ScheduledActionDrawIfPossible() { LayerTreeHostImpl::FrameData frame; frame.begin_frame_ack = scheduler_on_impl_thread_->CurrentBeginFrameAckForActiveTree(); + frame.origin_begin_main_frame_args = + scheduler_on_impl_thread_->last_activate_origin_frame_args(); return DoComposite(&frame); } diff --git a/chromium/cc/trees/single_thread_proxy.h b/chromium/cc/trees/single_thread_proxy.h index 97a449b3442..320688ea79b 100644 --- a/chromium/cc/trees/single_thread_proxy.h +++ b/chromium/cc/trees/single_thread_proxy.h @@ -41,7 +41,6 @@ class CC_EXPORT SingleThreadProxy : public Proxy, // Proxy implementation bool IsStarted() const override; - bool CommitToActiveTree() const override; void SetLayerTreeFrameSink( LayerTreeFrameSink* layer_tree_frame_sink) override; void ReleaseLayerTreeFrameSink() override; @@ -115,6 +114,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy, void PostAnimationEventsToMainThreadOnImplThread( std::unique_ptr<MutatorEvents> events) override; bool IsInsideDraw() override; + bool IsBeginMainFrameExpected() override; void RenewTreePriority() override {} void PostDelayedAnimationTaskOnImplThread(base::OnceClosure task, base::TimeDelta delay) override {} @@ -134,6 +134,8 @@ class CC_EXPORT SingleThreadProxy : public Proxy, void NotifyAnimationWorkletStateChange( AnimationWorkletMutationState state, ElementListType element_list_type) override; + void NotifyPaintWorkletStateChange( + Scheduler::PaintWorkletState state) override; void RequestNewLayerTreeFrameSink(); @@ -206,9 +208,9 @@ class CC_EXPORT SingleThreadProxy : public Proxy, // WeakPtrs generated by this factory will be invalidated when // LayerTreeFrameSink is released. - base::WeakPtrFactory<SingleThreadProxy> frame_sink_bound_weak_factory_; + base::WeakPtrFactory<SingleThreadProxy> frame_sink_bound_weak_factory_{this}; - base::WeakPtrFactory<SingleThreadProxy> weak_factory_; + base::WeakPtrFactory<SingleThreadProxy> weak_factory_{this}; }; // For use in the single-threaded case. In debug builds, it pretends that the diff --git a/chromium/cc/trees/transform_node.h b/chromium/cc/trees/transform_node.h index 5a19ccec9e0..4f123a6390f 100644 --- a/chromium/cc/trees/transform_node.h +++ b/chromium/cc/trees/transform_node.h @@ -6,7 +6,7 @@ #define CC_TREES_TRANSFORM_NODE_H_ #include "cc/cc_export.h" -#include "cc/trees/element_id.h" +#include "cc/paint/element_id.h" #include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/scroll_offset.h" #include "ui/gfx/transform.h" @@ -67,7 +67,8 @@ struct CC_EXPORT TransformNode { // context. int sorting_context_id; - // TODO(vollick): will be moved when accelerated effects are implemented. + // True if |TransformTree::UpdateLocalTransform| needs to be called which + // will update |to_parent| and |source_to_parent| (if possible). bool needs_local_transform_update : 1; // Whether this node or any ancestor has a potentially running diff --git a/chromium/cc/trees/tree_synchronizer.cc b/chromium/cc/trees/tree_synchronizer.cc index 9e329f02eaa..393e45b1167 100644 --- a/chromium/cc/trees/tree_synchronizer.cc +++ b/chromium/cc/trees/tree_synchronizer.cc @@ -41,14 +41,14 @@ static bool LayerHasValidPropertyTreeIndices(LayerImpl* layer) { } static bool LayerWillPushProperties(LayerTreeHost* host, Layer* layer) { - return base::ContainsKey(host->LayersThatShouldPushProperties(), layer); + return base::Contains(host->LayersThatShouldPushProperties(), layer); } static bool LayerWillPushProperties(LayerTreeImpl* tree, LayerImpl* layer) { - return base::ContainsKey(tree->LayersThatShouldPushProperties(), layer) || + return base::Contains(tree->LayersThatShouldPushProperties(), layer) || // TODO(crbug.com/303943): Stop always pushing PictureLayerImpl // properties. - base::ContainsValue(tree->picture_layers(), layer); + base::Contains(tree->picture_layers(), layer); } #endif diff --git a/chromium/cc/trees/tree_synchronizer_unittest.cc b/chromium/cc/trees/tree_synchronizer_unittest.cc index b7a3dc02eee..6d0d4e5688e 100644 --- a/chromium/cc/trees/tree_synchronizer_unittest.cc +++ b/chromium/cc/trees/tree_synchronizer_unittest.cc @@ -200,7 +200,7 @@ TEST_F(TreeSynchronizerTest, SyncSimpleTreeFromEmpty) { host_->pending_tree()); LayerImpl* root = host_->pending_tree()->root_layer_for_testing(); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( host_->pending_tree()->LayersThatShouldPushProperties(), root)); ExpectTreesAreIdentical(layer_tree_root.get(), @@ -226,7 +226,7 @@ TEST_F(TreeSynchronizerTest, SyncSimpleTreeAndPushPropertiesFromEmpty) { // layers are created on pending tree and they all need to push properties to // active tree. LayerImpl* root = host_->pending_tree()->root_layer_for_testing(); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( host_->pending_tree()->LayersThatShouldPushProperties(), root)); ExpectTreesAreIdentical(layer_tree_root.get(), @@ -242,20 +242,20 @@ TEST_F(TreeSynchronizerTest, SyncSimpleTreeAndPushPropertiesFromEmpty) { host_->active_tree()); TreeSynchronizer::PushLayerProperties(host_->pending_tree(), host_->active_tree()); - EXPECT_FALSE(base::ContainsKey( + EXPECT_FALSE(base::Contains( host_->pending_tree()->LayersThatShouldPushProperties(), root)); // Set the main thread root layer needs push properties. layer_tree_root->SetNeedsPushProperties(); - EXPECT_TRUE(base::ContainsKey(host_->LayersThatShouldPushProperties(), - layer_tree_root.get())); + EXPECT_TRUE(base::Contains(host_->LayersThatShouldPushProperties(), + layer_tree_root.get())); // When sync from main thread, the needs push properties status is carried // over to pending tree. TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(), host_->pending_tree()); TreeSynchronizer::PushLayerProperties(host_.get(), host_->pending_tree()); - EXPECT_TRUE(base::ContainsKey( + EXPECT_TRUE(base::Contains( host_->pending_tree()->LayersThatShouldPushProperties(), root)); } @@ -278,8 +278,8 @@ TEST_F(TreeSynchronizerTest, SyncSimpleTreeReusingLayers) { LayerImpl* layer_impl_tree_root = host_->pending_tree()->root_layer_for_testing(); EXPECT_TRUE( - base::ContainsKey(host_->pending_tree()->LayersThatShouldPushProperties(), - layer_impl_tree_root)); + base::Contains(host_->pending_tree()->LayersThatShouldPushProperties(), + layer_impl_tree_root)); ExpectTreesAreIdentical(layer_tree_root.get(), layer_impl_tree_root, host_->pending_tree()); @@ -522,12 +522,12 @@ TEST_F(TreeSynchronizerTest, SyncSimpleTreeThenDestroy) { ASSERT_EQ(3u, layer_impl_destruction_list.size()); - EXPECT_TRUE(base::ContainsValue(layer_impl_destruction_list, - old_tree_root_layer_id)); - EXPECT_TRUE(base::ContainsValue(layer_impl_destruction_list, - old_tree_first_child_layer_id)); - EXPECT_TRUE(base::ContainsValue(layer_impl_destruction_list, - old_tree_second_child_layer_id)); + EXPECT_TRUE( + base::Contains(layer_impl_destruction_list, old_tree_root_layer_id)); + EXPECT_TRUE(base::Contains(layer_impl_destruction_list, + old_tree_first_child_layer_id)); + EXPECT_TRUE(base::Contains(layer_impl_destruction_list, + old_tree_second_child_layer_id)); } // Constructs+syncs a tree with mask layer. diff --git a/chromium/cc/trees/viewport_layers.h b/chromium/cc/trees/viewport_layers.h index 7cdb88bd27e..b92d1190bc1 100644 --- a/chromium/cc/trees/viewport_layers.h +++ b/chromium/cc/trees/viewport_layers.h @@ -6,7 +6,8 @@ #define CC_TREES_VIEWPORT_LAYERS_H_ #include "base/memory/ref_counted.h" -#include "cc/trees/element_id.h" +#include "cc/cc_export.h" +#include "cc/paint/element_id.h" namespace cc { class Layer; |