summaryrefslogtreecommitdiff
path: root/chromium/cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/cc')
-rw-r--r--chromium/cc/BUILD.gn11
-rw-r--r--chromium/cc/animation/animation.cc23
-rw-r--r--chromium/cc/animation/animation.h13
-rw-r--r--chromium/cc/animation/animation_delegate.h6
-rw-r--r--chromium/cc/animation/animation_host.cc45
-rw-r--r--chromium/cc/animation/animation_host.h1
-rw-r--r--chromium/cc/animation/animation_host_unittest.cc7
-rw-r--r--chromium/cc/animation/element_animations_unittest.cc20
-rw-r--r--chromium/cc/animation/keyframe_effect.cc34
-rw-r--r--chromium/cc/animation/keyframe_effect.h5
-rw-r--r--chromium/cc/animation/keyframe_model.cc5
-rw-r--r--chromium/cc/animation/transform_operations.h2
-rw-r--r--chromium/cc/animation/worklet_animation.cc9
-rw-r--r--chromium/cc/animation/worklet_animation_unittest.cc14
-rw-r--r--chromium/cc/base/completion_event.h2
-rw-r--r--chromium/cc/base/devtools_instrumentation.cc66
-rw-r--r--chromium/cc/base/devtools_instrumentation.h4
-rw-r--r--chromium/cc/base/features.cc4
-rw-r--r--chromium/cc/base/features.h1
-rw-r--r--chromium/cc/base/list_container.h2
-rw-r--r--chromium/cc/base/list_container_helper.cc1
-rw-r--r--chromium/cc/base/math_util.h2
-rw-r--r--chromium/cc/base/reverse_spiral_iterator.h1
-rw-r--r--chromium/cc/base/rtree.h2
-rw-r--r--chromium/cc/base/spiral_iterator.h1
-rw-r--r--chromium/cc/base/switches.h2
-rw-r--r--chromium/cc/base/tiling_data.h2
-rw-r--r--chromium/cc/input/browser_controls_offset_manager.cc26
-rw-r--r--chromium/cc/input/browser_controls_offset_manager_unittest.cc56
-rw-r--r--chromium/cc/input/input_handler.cc17
-rw-r--r--chromium/cc/input/input_handler.h39
-rw-r--r--chromium/cc/input/main_thread_scrolling_reason.cc12
-rw-r--r--chromium/cc/input/main_thread_scrolling_reason.h11
-rw-r--r--chromium/cc/input/main_thread_scrolling_reason_unittest.cc6
-rw-r--r--chromium/cc/input/scroll_elasticity_helper.cc7
-rw-r--r--chromium/cc/input/scroll_elasticity_helper.h4
-rw-r--r--chromium/cc/input/scroll_state.h8
-rw-r--r--chromium/cc/input/scroll_state_data.cc3
-rw-r--r--chromium/cc/input/scroll_state_data.h10
-rw-r--r--chromium/cc/input/scroll_utils.cc45
-rw-r--r--chromium/cc/input/scroll_utils.h43
-rw-r--r--chromium/cc/input/scrollbar.h8
-rw-r--r--chromium/cc/input/scrollbar_animation_controller.cc39
-rw-r--r--chromium/cc/input/scrollbar_animation_controller.h5
-rw-r--r--chromium/cc/input/scrollbar_animation_controller_unittest.cc109
-rw-r--r--chromium/cc/input/scrollbar_controller.cc171
-rw-r--r--chromium/cc/input/scrollbar_controller.h79
-rw-r--r--chromium/cc/input/touch_action.h2
-rw-r--r--chromium/cc/layers/deadline_policy.h2
-rw-r--r--chromium/cc/layers/draw_properties.h6
-rw-r--r--chromium/cc/layers/heads_up_display_layer_impl.cc211
-rw-r--r--chromium/cc/layers/heads_up_display_layer_impl.h42
-rw-r--r--chromium/cc/layers/layer.cc12
-rw-r--r--chromium/cc/layers/layer.h16
-rw-r--r--chromium/cc/layers/layer_impl.cc8
-rw-r--r--chromium/cc/layers/layer_impl.h11
-rw-r--r--chromium/cc/layers/layer_unittest.cc37
-rw-r--r--chromium/cc/layers/mirror_layer_impl.cc3
-rw-r--r--chromium/cc/layers/painted_scrollbar_layer.cc3
-rw-r--r--chromium/cc/layers/painted_scrollbar_layer.h1
-rw-r--r--chromium/cc/layers/painted_scrollbar_layer_impl.cc13
-rw-r--r--chromium/cc/layers/painted_scrollbar_layer_impl.h3
-rw-r--r--chromium/cc/layers/picture_image_layer.cc116
-rw-r--r--chromium/cc/layers/picture_image_layer.h52
-rw-r--r--chromium/cc/layers/picture_image_layer_unittest.cc63
-rw-r--r--chromium/cc/layers/picture_layer.cc22
-rw-r--r--chromium/cc/layers/picture_layer_impl.cc41
-rw-r--r--chromium/cc/layers/picture_layer_impl_unittest.cc28
-rw-r--r--chromium/cc/layers/render_surface_impl.cc8
-rw-r--r--chromium/cc/layers/render_surface_impl.h1
-rw-r--r--chromium/cc/layers/scrollbar_layer_impl_base.cc4
-rw-r--r--chromium/cc/layers/scrollbar_layer_impl_base.h1
-rw-r--r--chromium/cc/layers/solid_color_layer_impl.cc3
-rw-r--r--chromium/cc/layers/texture_layer_impl.cc1
-rw-r--r--chromium/cc/layers/video_layer_impl_unittest.cc6
-rw-r--r--chromium/cc/metrics/compositor_frame_reporter.cc410
-rw-r--r--chromium/cc/metrics/compositor_frame_reporter.h42
-rw-r--r--chromium/cc/metrics/compositor_frame_reporter_unittest.cc3
-rw-r--r--chromium/cc/metrics/compositor_frame_reporting_controller.cc51
-rw-r--r--chromium/cc/metrics/compositor_frame_reporting_controller.h10
-rw-r--r--chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc8
-rw-r--r--chromium/cc/metrics/compositor_timing_history.cc58
-rw-r--r--chromium/cc/metrics/compositor_timing_history.h8
-rw-r--r--chromium/cc/metrics/compositor_timing_history_unittest.cc216
-rw-r--r--chromium/cc/metrics/dropped_frame_counter.cc45
-rw-r--r--chromium/cc/metrics/dropped_frame_counter.h55
-rw-r--r--chromium/cc/metrics/event_metrics.cc168
-rw-r--r--chromium/cc/metrics/event_metrics.h67
-rw-r--r--chromium/cc/metrics/frame_sequence_metrics.cc122
-rw-r--r--chromium/cc/metrics/frame_sequence_metrics.h33
-rw-r--r--chromium/cc/metrics/frame_sequence_metrics_unittest.cc26
-rw-r--r--chromium/cc/metrics/frame_sequence_tracker.cc69
-rw-r--r--chromium/cc/metrics/frame_sequence_tracker.h19
-rw-r--r--chromium/cc/metrics/frame_sequence_tracker_collection.cc161
-rw-r--r--chromium/cc/metrics/frame_sequence_tracker_collection.h48
-rw-r--r--chromium/cc/metrics/frame_sequence_tracker_unittest.cc242
-rw-r--r--chromium/cc/metrics/latency_ukm_reporter.cc129
-rw-r--r--chromium/cc/metrics/latency_ukm_reporter.h48
-rw-r--r--chromium/cc/metrics/throughput_ukm_reporter.cc43
-rw-r--r--chromium/cc/metrics/throughput_ukm_reporter.h33
-rw-r--r--chromium/cc/metrics/video_playback_roughness_reporter.cc34
-rw-r--r--chromium/cc/metrics/video_playback_roughness_reporter.h19
-rw-r--r--chromium/cc/metrics/video_playback_roughness_reporter_unittest.cc192
-rw-r--r--chromium/cc/paint/BUILD.gn1
-rw-r--r--chromium/cc/paint/decoded_draw_image.cc8
-rw-r--r--chromium/cc/paint/decoded_draw_image.h3
-rw-r--r--chromium/cc/paint/discardable_image_map_unittest.cc4
-rw-r--r--chromium/cc/paint/display_item_list.cc1
-rw-r--r--chromium/cc/paint/display_item_list.h11
-rw-r--r--chromium/cc/paint/display_item_list_unittest.cc1
-rw-r--r--chromium/cc/paint/element_id.cc5
-rw-r--r--chromium/cc/paint/element_id.h2
-rw-r--r--chromium/cc/paint/filter_operation.h2
-rw-r--r--chromium/cc/paint/filter_operations.h2
-rw-r--r--chromium/cc/paint/filter_operations_unittest.cc76
-rw-r--r--chromium/cc/paint/image_transfer_cache_entry.cc11
-rw-r--r--chromium/cc/paint/image_transfer_cache_entry_unittest.cc2
-rw-r--r--chromium/cc/paint/oop_pixeltest.cc41
-rw-r--r--chromium/cc/paint/paint_canvas.h7
-rw-r--r--chromium/cc/paint/paint_filter.cc2
-rw-r--r--chromium/cc/paint/paint_filter.h2
-rw-r--r--chromium/cc/paint/paint_flags.h9
-rw-r--r--chromium/cc/paint/paint_image.cc25
-rw-r--r--chromium/cc/paint/paint_image.h14
-rw-r--r--chromium/cc/paint/paint_image_builder.h27
-rw-r--r--chromium/cc/paint/paint_op_buffer.cc103
-rw-r--r--chromium/cc/paint/paint_op_buffer.h46
-rw-r--r--chromium/cc/paint/paint_op_buffer_fuzzer.cc1
-rw-r--r--chromium/cc/paint/paint_op_buffer_serializer.cc64
-rw-r--r--chromium/cc/paint/paint_op_buffer_serializer.h2
-rw-r--r--chromium/cc/paint/paint_op_buffer_unittest.cc171
-rw-r--r--chromium/cc/paint/paint_op_helper_unittest.cc2
-rw-r--r--chromium/cc/paint/paint_op_reader.cc13
-rw-r--r--chromium/cc/paint/paint_op_reader.h4
-rw-r--r--chromium/cc/paint/paint_op_writer.cc4
-rw-r--r--chromium/cc/paint/paint_op_writer.h2
-rw-r--r--chromium/cc/paint/paint_shader.cc3
-rw-r--r--chromium/cc/paint/paint_shader_unittest.cc8
-rw-r--r--chromium/cc/paint/record_paint_canvas.cc2
-rw-r--r--chromium/cc/paint/record_paint_canvas.h3
-rw-r--r--chromium/cc/paint/scoped_raster_flags_unittest.cc8
-rw-r--r--chromium/cc/paint/skia_paint_canvas.cc2
-rw-r--r--chromium/cc/paint/skia_paint_canvas.h3
-rw-r--r--chromium/cc/raster/gpu_raster_buffer_provider.cc1
-rw-r--r--chromium/cc/raster/playback_image_provider.cc13
-rw-r--r--chromium/cc/raster/playback_image_provider.h4
-rw-r--r--chromium/cc/raster/raster_source.cc12
-rw-r--r--chromium/cc/raster/raster_source.h22
-rw-r--r--chromium/cc/raster/scoped_gpu_raster.h1
-rw-r--r--chromium/cc/raster/staging_buffer_pool.cc6
-rw-r--r--chromium/cc/raster/task_graph_runner.h1
-rw-r--r--chromium/cc/resources/resource_pool.cc7
-rw-r--r--chromium/cc/resources/ui_resource_request.h2
-rw-r--r--chromium/cc/scheduler/commit_earlyout_reason.h2
-rw-r--r--chromium/cc/scheduler/scheduler.cc33
-rw-r--r--chromium/cc/scheduler/scheduler.h15
-rw-r--r--chromium/cc/scheduler/scheduler_state_machine.cc2
-rw-r--r--chromium/cc/scheduler/scheduler_state_machine.h8
-rw-r--r--chromium/cc/scheduler/scheduler_unittest.cc48
-rw-r--r--chromium/cc/tiles/checker_image_tracker.cc5
-rw-r--r--chromium/cc/tiles/checker_image_tracker_unittest.cc2
-rw-r--r--chromium/cc/tiles/gpu_image_decode_cache.cc21
-rw-r--r--chromium/cc/tiles/gpu_image_decode_cache.h1
-rw-r--r--chromium/cc/tiles/gpu_image_decode_cache_perftest.cc2
-rw-r--r--chromium/cc/tiles/gpu_image_decode_cache_unittest.cc15
-rw-r--r--chromium/cc/tiles/image_controller_unittest.cc2
-rw-r--r--chromium/cc/tiles/image_decode_cache.h23
-rw-r--r--chromium/cc/tiles/software_image_decode_cache.cc23
-rw-r--r--chromium/cc/tiles/software_image_decode_cache_utils.cc1
-rw-r--r--chromium/cc/tiles/tile_priority.cc12
-rw-r--r--chromium/cc/tiles/tile_priority.h6
-rw-r--r--chromium/cc/trees/damage_tracker_unittest.cc19
-rw-r--r--chromium/cc/trees/draw_properties_unittest.cc238
-rw-r--r--chromium/cc/trees/draw_property_utils.cc163
-rw-r--r--chromium/cc/trees/draw_property_utils.h12
-rw-r--r--chromium/cc/trees/effect_node.cc7
-rw-r--r--chromium/cc/trees/effect_node.h7
-rw-r--r--chromium/cc/trees/frame_rate_counter.cc133
-rw-r--r--chromium/cc/trees/frame_rate_counter.h60
-rw-r--r--chromium/cc/trees/image_animation_controller.cc21
-rw-r--r--chromium/cc/trees/layer_tree_host.cc72
-rw-r--r--chromium/cc/trees/layer_tree_host.h50
-rw-r--r--chromium/cc/trees/layer_tree_host_client.h10
-rw-r--r--chromium/cc/trees/layer_tree_host_impl.cc419
-rw-r--r--chromium/cc/trees/layer_tree_host_impl.h96
-rw-r--r--chromium/cc/trees/layer_tree_host_impl_unittest.cc1771
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_blending.cc38
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_filters.cc49
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_masks.cc38
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc1
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest.cc137
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc10
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest_scroll.cc494
-rw-r--r--chromium/cc/trees/layer_tree_impl.cc111
-rw-r--r--chromium/cc/trees/layer_tree_impl.h21
-rw-r--r--chromium/cc/trees/layer_tree_settings.h8
-rw-r--r--chromium/cc/trees/mutator_host.h1
-rw-r--r--chromium/cc/trees/occlusion_tracker.h2
-rw-r--r--chromium/cc/trees/property_tree.cc46
-rw-r--r--chromium/cc/trees/property_tree.h1
-rw-r--r--chromium/cc/trees/property_tree_builder.cc1
-rw-r--r--chromium/cc/trees/property_tree_builder_unittest.cc2
-rw-r--r--chromium/cc/trees/proxy.h1
-rw-r--r--chromium/cc/trees/proxy_impl.cc52
-rw-r--r--chromium/cc/trees/proxy_impl.h14
-rw-r--r--chromium/cc/trees/proxy_main.cc16
-rw-r--r--chromium/cc/trees/proxy_main.h6
-rw-r--r--chromium/cc/trees/render_frame_metadata.h8
-rw-r--r--chromium/cc/trees/scroll_and_scale_set.h4
-rw-r--r--chromium/cc/trees/single_thread_proxy.cc24
-rw-r--r--chromium/cc/trees/single_thread_proxy.h19
-rw-r--r--chromium/cc/trees/task_runner_provider.h2
-rw-r--r--chromium/cc/trees/transform_node.cc3
-rw-r--r--chromium/cc/trees/transform_node.h4
-rw-r--r--chromium/cc/trees/ukm_manager.cc90
-rw-r--r--chromium/cc/trees/ukm_manager.h13
-rw-r--r--chromium/cc/trees/ukm_manager_unittest.cc353
217 files changed, 6476 insertions, 3216 deletions
diff --git a/chromium/cc/BUILD.gn b/chromium/cc/BUILD.gn
index deaca4f894b..7e5e837b8e7 100644
--- a/chromium/cc/BUILD.gn
+++ b/chromium/cc/BUILD.gn
@@ -32,7 +32,6 @@ cc_component("cc") {
"input/browser_controls_offset_manager.cc",
"input/browser_controls_offset_manager.h",
"input/browser_controls_offset_manager_client.h",
- "input/input_handler.cc",
"input/input_handler.h",
"input/layer_selection_bound.cc",
"input/layer_selection_bound.h",
@@ -49,6 +48,9 @@ cc_component("cc") {
"input/scroll_state.h",
"input/scroll_state_data.cc",
"input/scroll_state_data.h",
+ "input/scroll_utils.cc",
+ "input/scroll_utils.h",
+ "input/scrollbar.h",
"input/scrollbar_animation_controller.cc",
"input/scrollbar_animation_controller.h",
"input/scrollbar_controller.cc",
@@ -101,8 +103,6 @@ cc_component("cc") {
"layers/painted_scrollbar_layer.h",
"layers/painted_scrollbar_layer_impl.cc",
"layers/painted_scrollbar_layer_impl.h",
- "layers/picture_image_layer.cc",
- "layers/picture_image_layer.h",
"layers/picture_layer.cc",
"layers/picture_layer.h",
"layers/picture_layer_impl.cc",
@@ -157,6 +157,8 @@ cc_component("cc") {
"metrics/compositor_frame_reporting_controller.h",
"metrics/compositor_timing_history.cc",
"metrics/compositor_timing_history.h",
+ "metrics/dropped_frame_counter.cc",
+ "metrics/dropped_frame_counter.h",
"metrics/event_metrics.cc",
"metrics/event_metrics.h",
"metrics/events_metrics_manager.cc",
@@ -312,8 +314,6 @@ cc_component("cc") {
"trees/draw_property_utils.h",
"trees/effect_node.cc",
"trees/effect_node.h",
- "trees/frame_rate_counter.cc",
- "trees/frame_rate_counter.h",
"trees/frame_rate_estimator.cc",
"trees/frame_rate_estimator.h",
"trees/image_animation_controller.cc",
@@ -642,7 +642,6 @@ cc_test("cc_unittests") {
"layers/painted_overlay_scrollbar_layer_unittest.cc",
"layers/painted_scrollbar_layer_impl_unittest.cc",
"layers/painted_scrollbar_layer_unittest.cc",
- "layers/picture_image_layer_unittest.cc",
"layers/picture_layer_impl_unittest.cc",
"layers/picture_layer_unittest.cc",
"layers/recording_source_unittest.cc",
diff --git a/chromium/cc/animation/animation.cc b/chromium/cc/animation/animation.cc
index 5c40eed8453..df27dc32d11 100644
--- a/chromium/cc/animation/animation.cc
+++ b/chromium/cc/animation/animation.cc
@@ -127,20 +127,15 @@ void Animation::Tick(base::TimeTicks tick_time) {
// time and then ticks it which side-steps the start time altogether. See
// crbug.com/1076012 for alternative design choices considered for future
// improvement.
- TickWithLocalTime(tick_time - base::TimeTicks());
+ keyframe_effect_->Pause(tick_time - base::TimeTicks(),
+ PauseCondition::kAfterStart);
+ keyframe_effect_->Tick(base::TimeTicks());
} else {
DCHECK(!tick_time.is_null());
keyframe_effect_->Tick(tick_time);
}
}
-void Animation::TickWithLocalTime(base::TimeDelta local_time) {
- // TODO(yigu): KeyframeEffect should support ticking KeyframeModel
- // directly without using Pause(). https://crbug.com/1076012.
- keyframe_effect_->Pause(local_time);
- keyframe_effect_->Tick(base::TimeTicks());
-}
-
bool Animation::IsScrollLinkedAnimation() const {
return animation_timeline_ && animation_timeline_->IsScrollTimeline();
}
@@ -211,10 +206,6 @@ void Animation::DelegateAnimationEvent(const AnimationEvent& event) {
}
}
-size_t Animation::TickingKeyframeModelsCount() const {
- return keyframe_effect_->TickingKeyframeModelsCount();
-}
-
bool Animation::AffectsCustomProperty() const {
return keyframe_effect_->AffectsCustomProperty();
}
@@ -287,12 +278,4 @@ void Animation::NotifyKeyframeModelFinishedForTesting(
DispatchAndDelegateAnimationEvent(event);
}
-void Animation::UpdateScrollTimeline(base::Optional<ElementId> scroller_id,
- base::Optional<double> start_scroll_offset,
- base::Optional<double> end_scroll_offset) {
- ToScrollTimeline(animation_timeline_)
- ->UpdateScrollerIdAndScrollOffsets(scroller_id, start_scroll_offset,
- end_scroll_offset);
-}
-
} // namespace cc
diff --git a/chromium/cc/animation/animation.h b/chromium/cc/animation/animation.h
index 7fa4136d591..6cc50117d1f 100644
--- a/chromium/cc/animation/animation.h
+++ b/chromium/cc/animation/animation.h
@@ -65,17 +65,6 @@ class CC_ANIMATION_EXPORT Animation : public base::RefCounted<Animation> {
}
void SetAnimationTimeline(AnimationTimeline* timeline);
- // TODO(yigu): There is a reverse dependency between AnimationTimeline and
- // Animation. ScrollTimeline update should be handled by AnimationHost instead
- // of Animation. This could be fixed once the snapshotting in blink is
- // implemented. https://crbug.com/1023508.
-
- // Should be called when the ScrollTimeline attached to this animation has a
- // change, such as when the scroll source changes ElementId.
- void UpdateScrollTimeline(base::Optional<ElementId> scroller_id,
- base::Optional<double> start_scroll_offset,
- base::Optional<double> end_scroll_offset);
-
scoped_refptr<ElementAnimations> element_animations() const;
void set_animation_delegate(AnimationDelegate* delegate) {
@@ -118,7 +107,6 @@ class CC_ANIMATION_EXPORT Animation : public base::RefCounted<Animation> {
// to be dispatched.
void DispatchAndDelegateAnimationEvent(const AnimationEvent& event);
- size_t TickingKeyframeModelsCount() const;
bool AffectsCustomProperty() const;
void SetNeedsPushProperties();
@@ -151,7 +139,6 @@ class CC_ANIMATION_EXPORT Animation : public base::RefCounted<Animation> {
explicit Animation(int id);
Animation(int id, std::unique_ptr<KeyframeEffect>);
virtual ~Animation();
- void TickWithLocalTime(base::TimeDelta local_time);
AnimationHost* animation_host_;
AnimationTimeline* animation_timeline_;
diff --git a/chromium/cc/animation/animation_delegate.h b/chromium/cc/animation/animation_delegate.h
index 0adbb115c59..6931c8b0d18 100644
--- a/chromium/cc/animation/animation_delegate.h
+++ b/chromium/cc/animation/animation_delegate.h
@@ -13,12 +13,6 @@ namespace cc {
class CC_ANIMATION_EXPORT AnimationDelegate {
public:
- // TODO(yigu): The Notify* methods will be called multiple times per
- // animation (once for effect/property pairing).
- // Ideally, we would only notify start once (e.g., wait on all effects to
- // start before notifying delegate) this way effect becomes an internal
- // details of the animation. Perhaps we can do that at some point maybe as
- // part of https://bugs.chromium.org/p/chromium/issues/detail?id=810003
virtual void NotifyAnimationStarted(base::TimeTicks monotonic_time,
int target_property,
int group) = 0;
diff --git a/chromium/cc/animation/animation_host.cc b/chromium/cc/animation/animation_host.cc
index 1dfe39e66c9..d2154f19594 100644
--- a/chromium/cc/animation/animation_host.cc
+++ b/chromium/cc/animation/animation_host.cc
@@ -219,6 +219,8 @@ void AnimationHost::SetNeedsCommit() {
}
void AnimationHost::SetNeedsPushProperties() {
+ if (needs_push_properties_)
+ return;
needs_push_properties_ = true;
if (mutator_host_client_)
mutator_host_client_->SetMutatorsNeedCommit();
@@ -227,6 +229,14 @@ void AnimationHost::SetNeedsPushProperties() {
void AnimationHost::PushPropertiesTo(MutatorHost* mutator_host_impl) {
auto* host_impl = static_cast<AnimationHost*>(mutator_host_impl);
+ // Update animation counts and whether raf was requested. These explicitly
+ // do not request push properties and are pushed as part of the next commit
+ // when it happens as requesting a commit leads to performance issues:
+ // https://crbug.com/1083244
+ host_impl->main_thread_animations_count_ = main_thread_animations_count_;
+ host_impl->current_frame_had_raf_ = current_frame_had_raf_;
+ host_impl->next_frame_has_pending_raf_ = next_frame_has_pending_raf_;
+
if (needs_push_properties_) {
needs_push_properties_ = false;
PushTimelinesToImplThread(host_impl);
@@ -289,9 +299,6 @@ void AnimationHost::PushPropertiesToImplThread(AnimationHost* host_impl) {
// Update the impl-only scroll offset animations.
scroll_offset_animations_->PushPropertiesTo(
host_impl->scroll_offset_animations_impl_.get());
- host_impl->main_thread_animations_count_ = main_thread_animations_count_;
- host_impl->current_frame_had_raf_ = current_frame_had_raf_;
- host_impl->next_frame_has_pending_raf_ = next_frame_has_pending_raf_;
// The pending info list is cleared in LayerTreeHostImpl::CommitComplete
// and should be empty when pushing properties.
@@ -752,37 +759,25 @@ void AnimationHost::SetMutationUpdate(
}
}
-size_t AnimationHost::CompositedAnimationsCount() const {
- size_t composited_animations_count = 0;
- for (const auto& it : ticking_animations_)
- composited_animations_count += it->TickingKeyframeModelsCount();
- return composited_animations_count;
-}
-
void AnimationHost::SetAnimationCounts(
size_t total_animations_count,
bool current_frame_had_raf,
bool next_frame_has_pending_raf) {
+ // Though these changes are pushed as part of AnimationHost::PushPropertiesTo
+ // we don't SetNeedsPushProperties as pushing the values requires a commit.
+ // Instead we allow them to be pushed whenever the next required commit
+ // happens to avoid unnecessary work. See https://crbug.com/1083244.
+
// If an animation is being run on the compositor, it will have a ticking
// Animation (which will have a corresponding impl-thread version). Therefore
// to find the count of main-only animations, we can simply subtract the
// number of ticking animations from the total count.
size_t ticking_animations_count = ticking_animations_.size();
- if (main_thread_animations_count_ !=
- total_animations_count - ticking_animations_count) {
- main_thread_animations_count_ =
- total_animations_count - ticking_animations_count;
- DCHECK_GE(main_thread_animations_count_, 0u);
- SetNeedsPushProperties();
- }
- if (current_frame_had_raf != current_frame_had_raf_) {
- current_frame_had_raf_ = current_frame_had_raf;
- SetNeedsPushProperties();
- }
- if (next_frame_has_pending_raf != next_frame_has_pending_raf_) {
- next_frame_has_pending_raf_ = next_frame_has_pending_raf;
- SetNeedsPushProperties();
- }
+ main_thread_animations_count_ =
+ total_animations_count - ticking_animations_count;
+ DCHECK_GE(main_thread_animations_count_, 0u);
+ current_frame_had_raf_ = current_frame_had_raf;
+ next_frame_has_pending_raf_ = next_frame_has_pending_raf;
}
size_t AnimationHost::MainThreadAnimationsCount() const {
diff --git a/chromium/cc/animation/animation_host.h b/chromium/cc/animation/animation_host.h
index de1ae6f8811..534f8c0c642 100644
--- a/chromium/cc/animation/animation_host.h
+++ b/chromium/cc/animation/animation_host.h
@@ -207,7 +207,6 @@ class CC_ANIMATION_EXPORT AnimationHost : public MutatorHost,
void SetMutationUpdate(
std::unique_ptr<MutatorOutputState> output_state) override;
- size_t CompositedAnimationsCount() const override;
size_t MainThreadAnimationsCount() const override;
bool HasCustomPropertyAnimations() const override;
bool CurrentFrameHadRAF() const override;
diff --git a/chromium/cc/animation/animation_host_unittest.cc b/chromium/cc/animation/animation_host_unittest.cc
index c2659983070..4dc4f9905d5 100644
--- a/chromium/cc/animation/animation_host_unittest.cc
+++ b/chromium/cc/animation/animation_host_unittest.cc
@@ -363,6 +363,9 @@ TEST_F(AnimationHostTest, LayerTreeMutatorUpdateReflectsScrollAnimations) {
}
TEST_F(AnimationHostTest, TickScrollLinkedAnimation) {
+ client_.RegisterElementId(element_id_, ElementListType::ACTIVE);
+ client_impl_.RegisterElementId(element_id_, ElementListType::PENDING);
+ client_impl_.RegisterElementId(element_id_, ElementListType::ACTIVE);
PropertyTrees property_trees;
property_trees.is_main_thread = false;
property_trees.is_active = true;
@@ -378,7 +381,7 @@ TEST_F(AnimationHostTest, TickScrollLinkedAnimation) {
scoped_refptr<Animation> animation = Animation::Create(animation_id);
host_impl_->AddAnimationTimeline(scroll_timeline);
scroll_timeline->AttachAnimation(animation);
- animation->AddToTicking();
+
ASSERT_TRUE(animation->IsScrollLinkedAnimation());
animation->AttachElement(element_id_);
@@ -393,7 +396,7 @@ TEST_F(AnimationHostTest, TickScrollLinkedAnimation) {
EXPECT_TRUE(host_impl_->TickAnimations(base::TimeTicks(),
property_trees.scroll_tree, false));
- EXPECT_EQ(keyframe_model->run_state(), KeyframeModel::PAUSED);
+ EXPECT_EQ(keyframe_model->run_state(), KeyframeModel::STARTING);
double tick_time = (scroll_timeline->CurrentTime(scroll_tree, false).value() -
base::TimeTicks())
.InSecondsF();
diff --git a/chromium/cc/animation/element_animations_unittest.cc b/chromium/cc/animation/element_animations_unittest.cc
index 34debd1808a..9e1fa8337f3 100644
--- a/chromium/cc/animation/element_animations_unittest.cc
+++ b/chromium/cc/animation/element_animations_unittest.cc
@@ -3874,26 +3874,6 @@ TEST_F(ElementAnimationsTest, RemoveAndReAddAnimationToTicking) {
EXPECT_EQ(1u, host_->ticking_animations_for_testing().size());
}
-TEST_F(ElementAnimationsTest, TickingKeyframeModelsCount) {
- CreateTestLayer(false, false);
- AttachTimelineAnimationLayer();
-
- // Add an animation and ensure the animation is in the host's ticking
- // animations.
- animation_->AddKeyframeModel(CreateKeyframeModel(
- std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f)),
- 2, TargetProperty::OPACITY));
- EXPECT_EQ(1u, animation_->TickingKeyframeModelsCount());
- EXPECT_EQ(1u, host_->CompositedAnimationsCount());
- animation_->AddKeyframeModel(CreateKeyframeModel(
- std::unique_ptr<AnimationCurve>(new FakeTransformTransition(1)), 1,
- TargetProperty::TRANSFORM));
- EXPECT_EQ(2u, animation_->TickingKeyframeModelsCount());
- EXPECT_EQ(2u, host_->CompositedAnimationsCount());
- animation_->keyframe_effect()->RemoveFromTicking();
- EXPECT_EQ(0u, host_->CompositedAnimationsCount());
-}
-
// This test verifies that finished keyframe models don't get copied over to
// impl thread.
TEST_F(ElementAnimationsTest, FinishedKeyframeModelsNotCopiedToImpl) {
diff --git a/chromium/cc/animation/keyframe_effect.cc b/chromium/cc/animation/keyframe_effect.cc
index 5af52eb211b..710d4f51c13 100644
--- a/chromium/cc/animation/keyframe_effect.cc
+++ b/chromium/cc/animation/keyframe_effect.cc
@@ -214,14 +214,28 @@ void KeyframeEffect::UpdateTickingState() {
}
}
-void KeyframeEffect::Pause(base::TimeDelta pause_offset) {
- for (auto& keyframe_model : keyframe_models_)
+void KeyframeEffect::Pause(base::TimeDelta pause_offset,
+ PauseCondition pause_condition) {
+ bool did_pause = false;
+ for (auto& keyframe_model : keyframe_models_) {
+ // TODO(crbug.com/1076012): KeyframeEffect is paused with local time for
+ // scroll-linked animations. To make sure the start event of a keyframe
+ // model is sent to blink, we should not set its run state to PAUSED until
+ // such event is sent. This should be revisited once KeyframeEffect is able
+ // to tick scroll-linked keyframe models directly.
+ if (pause_condition == PauseCondition::kAfterStart &&
+ (keyframe_model->run_state() ==
+ KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY ||
+ keyframe_model->run_state() == KeyframeModel::STARTING))
+ continue;
keyframe_model->Pause(pause_offset);
-
- if (has_bound_element_animations()) {
- animation_->SetNeedsCommit();
- SetNeedsPushProperties();
+ did_pause = true;
}
+
+ if (!did_pause || !has_bound_element_animations())
+ return;
+ animation_->SetNeedsCommit();
+ SetNeedsPushProperties();
}
void KeyframeEffect::AddKeyframeModel(
@@ -458,14 +472,6 @@ bool KeyframeEffect::HasTickingKeyframeModel() const {
return false;
}
-size_t KeyframeEffect::TickingKeyframeModelsCount() const {
- size_t ticking_keyframe_models_count = 0;
- for (const auto& it : keyframe_models_)
- if (!it->is_finished())
- ticking_keyframe_models_count++;
- return ticking_keyframe_models_count;
-}
-
bool KeyframeEffect::AffectsCustomProperty() const {
for (const auto& it : keyframe_models_)
if (it->target_property_id() == TargetProperty::CSS_CUSTOM_PROPERTY)
diff --git a/chromium/cc/animation/keyframe_effect.h b/chromium/cc/animation/keyframe_effect.h
index e7806e4cc1d..0e1280f82e1 100644
--- a/chromium/cc/animation/keyframe_effect.h
+++ b/chromium/cc/animation/keyframe_effect.h
@@ -22,6 +22,7 @@
namespace cc {
class Animation;
+enum class PauseCondition { kUnconditional, kAfterStart };
struct PropertyAnimationState;
// A KeyframeEffect owns a group of KeyframeModels for a single target
@@ -86,7 +87,8 @@ class CC_ANIMATION_EXPORT KeyframeEffect {
void UpdateState(bool start_ready_keyframe_models, AnimationEvents* events);
void UpdateTickingState();
- void Pause(base::TimeDelta pause_offset);
+ void Pause(base::TimeDelta pause_offset,
+ PauseCondition = PauseCondition::kUnconditional);
void AddKeyframeModel(std::unique_ptr<KeyframeModel> keyframe_model);
void PauseKeyframeModel(int keyframe_model_id, base::TimeDelta time_offset);
@@ -106,7 +108,6 @@ class CC_ANIMATION_EXPORT KeyframeEffect {
// Returns true if there are any KeyframeModels that have neither finished
// nor aborted.
bool HasTickingKeyframeModel() const;
- size_t TickingKeyframeModelsCount() const;
bool AffectsCustomProperty() const;
diff --git a/chromium/cc/animation/keyframe_model.cc b/chromium/cc/animation/keyframe_model.cc
index 25e2c6d232b..bb779226080 100644
--- a/chromium/cc/animation/keyframe_model.cc
+++ b/chromium/cc/animation/keyframe_model.cc
@@ -156,7 +156,7 @@ void KeyframeModel::SetRunState(RunState run_state,
void KeyframeModel::Pause(base::TimeDelta pause_offset) {
// Convert pause offset which is in local time to monotonic time.
- // TODO(yigu): This should be scaled by playbackrate. http://crbug.com/912407
+ // TODO(crbug.com/912407): This should be scaled by playbackrate.
base::TimeTicks monotonic_time =
pause_offset + start_time_ + total_paused_duration_;
SetRunState(PAUSED, monotonic_time);
@@ -246,8 +246,7 @@ bool KeyframeModel::InEffect(base::TimeTicks monotonic_time) const {
return CalculateActiveTime(monotonic_time).has_value();
}
-// TODO(yigu): Local time should be scaled by playback rate by spec.
-// https://crbug.com/912407.
+// TODO(crbug.com/912407): Local time should be scaled by playback rate by spec.
base::TimeDelta KeyframeModel::ConvertMonotonicTimeToLocalTime(
base::TimeTicks monotonic_time) const {
// When waiting on receiving a start time, then our global clock is 'stuck' at
diff --git a/chromium/cc/animation/transform_operations.h b/chromium/cc/animation/transform_operations.h
index 2fecd424325..f0c669cf3e7 100644
--- a/chromium/cc/animation/transform_operations.h
+++ b/chromium/cc/animation/transform_operations.h
@@ -9,8 +9,8 @@
#include <unordered_map>
#include <vector>
+#include "base/check_op.h"
#include "base/gtest_prod_util.h"
-#include "base/logging.h"
#include "cc/animation/animation_export.h"
#include "cc/animation/transform_operation.h"
#include "ui/gfx/transform.h"
diff --git a/chromium/cc/animation/worklet_animation.cc b/chromium/cc/animation/worklet_animation.cc
index 17ed083efcc..fd9a9c700ea 100644
--- a/chromium/cc/animation/worklet_animation.cc
+++ b/chromium/cc/animation/worklet_animation.cc
@@ -92,7 +92,8 @@ void WorkletAnimation::Tick(base::TimeTicks monotonic_time) {
// animations lifecycle. To avoid this we pause the underlying keyframe effect
// at the local time obtained from the user script - essentially turning each
// call to |WorkletAnimation::Tick| into a seek in the effect.
- TickWithLocalTime(local_time_.value());
+ keyframe_effect_->Pause(local_time_.value());
+ keyframe_effect_->Tick(base::TimeTicks());
}
void WorkletAnimation::UpdateState(bool start_ready_animations,
@@ -162,10 +163,6 @@ void WorkletAnimation::UpdateInputState(MutatorInputState* input_state,
switch (state_) {
case State::PENDING:
- // TODO(yigu): cc side WorkletAnimation is only capable of handling single
- // keyframe effect at the moment. We should pass in the number of effects
- // once Worklet Group Effect is fully implemented in cc.
- // https://crbug.com/767043.
input_state->Add({worklet_animation_id(), name(),
current_time->InMillisecondsF(), CloneOptions(),
CloneEffectTimings()});
@@ -186,8 +183,6 @@ void WorkletAnimation::UpdateInputState(MutatorInputState* input_state,
void WorkletAnimation::SetOutputState(
const MutatorOutputState::AnimationState& state) {
- // TODO(yigu): cc side WorkletAnimation is only capable of handling single
- // keyframe effect at the moment. https://crbug.com/767043.
DCHECK_EQ(state.local_times.size(), 1u);
local_time_ = state.local_times[0];
}
diff --git a/chromium/cc/animation/worklet_animation_unittest.cc b/chromium/cc/animation/worklet_animation_unittest.cc
index e827c3740e3..ed610a56c19 100644
--- a/chromium/cc/animation/worklet_animation_unittest.cc
+++ b/chromium/cc/animation/worklet_animation_unittest.cc
@@ -555,20 +555,6 @@ TEST_F(WorkletAnimationTest, SkipLockedAnimations) {
EXPECT_EQ(input->updated_animations.size(), 1u);
}
-TEST_F(WorkletAnimationTest, UpdateScrollTimelineScrollerId) {
- auto scroll_timeline = base::WrapRefCounted(new MockScrollTimeline());
- EXPECT_EQ(scroll_timeline->GetPendingIdForTest(), ElementId());
-
- scoped_refptr<WorkletAnimation> worklet_animation = WorkletAnimation::Create(
- worklet_animation_id_, "test_name", 1, nullptr, nullptr);
- host_->AddAnimationTimeline(scroll_timeline);
- scroll_timeline->AttachAnimation(worklet_animation);
- ElementId scroller_id = ElementId(1);
- worklet_animation->UpdateScrollTimeline(scroller_id, base::nullopt,
- base::nullopt);
- EXPECT_EQ(scroll_timeline->GetPendingIdForTest(), scroller_id);
-}
-
} // namespace
} // namespace cc
diff --git a/chromium/cc/base/completion_event.h b/chromium/cc/base/completion_event.h
index 5c9debd64f4..f5084d5346c 100644
--- a/chromium/cc/base/completion_event.h
+++ b/chromium/cc/base/completion_event.h
@@ -5,7 +5,7 @@
#ifndef CC_BASE_COMPLETION_EVENT_H_
#define CC_BASE_COMPLETION_EVENT_H_
-#include "base/logging.h"
+#include "base/check.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
diff --git a/chromium/cc/base/devtools_instrumentation.cc b/chromium/cc/base/devtools_instrumentation.cc
index 72b7be7a999..b386c7527c7 100644
--- a/chromium/cc/base/devtools_instrumentation.cc
+++ b/chromium/cc/base/devtools_instrumentation.cc
@@ -66,23 +66,34 @@ ScopedImageUploadTask::~ScopedImageUploadTask() {
return;
auto duration = base::TimeTicks::Now() - start_time_;
+ const char* histogram_name = nullptr;
switch (image_type_) {
- case ImageType::kWebP:
- UmaHistogramCustomMicrosecondsTimes(
- "Renderer4.ImageUploadTaskDurationUs.WebP", duration, hist_min_,
- hist_max_, bucket_count_);
+ case ImageType::kAvif:
+ histogram_name = "Renderer4.ImageUploadTaskDurationUs.Avif";
+ break;
+ case ImageType::kBmp:
+ histogram_name = "Renderer4.ImageUploadTaskDurationUs.Bmp";
+ break;
+ case ImageType::kGif:
+ histogram_name = "Renderer4.ImageUploadTaskDurationUs.Gif";
+ break;
+ case ImageType::kIco:
+ histogram_name = "Renderer4.ImageUploadTaskDurationUs.Ico";
break;
case ImageType::kJpeg:
- UmaHistogramCustomMicrosecondsTimes(
- "Renderer4.ImageUploadTaskDurationUs.Jpeg", duration, hist_min_,
- hist_max_, bucket_count_);
+ histogram_name = "Renderer4.ImageUploadTaskDurationUs.Jpeg";
break;
- case ImageType::kOther:
- UmaHistogramCustomMicrosecondsTimes(
- "Renderer4.ImageUploadTaskDurationUs.Other", duration, hist_min_,
- hist_max_, bucket_count_);
+ case ImageType::kPng:
+ histogram_name = "Renderer4.ImageUploadTaskDurationUs.Png";
break;
+ case ImageType::kWebP:
+ histogram_name = "Renderer4.ImageUploadTaskDurationUs.WebP";
+ break;
+ case ImageType::kOther:
+ histogram_name = "Renderer4.ImageUploadTaskDurationUs.Other";
}
+ UmaHistogramCustomMicrosecondsTimes(histogram_name, duration, hist_min_,
+ hist_max_, bucket_count_);
}
ScopedImageDecodeTask::ScopedImageDecodeTask(const void* image_ptr,
@@ -104,23 +115,36 @@ ScopedImageDecodeTask::~ScopedImageDecodeTask() {
return;
auto duration = base::TimeTicks::Now() - start_time_;
+ const char* histogram_name = nullptr;
switch (image_type_) {
- case ImageType::kWebP:
- RecordMicrosecondTimesUmaByDecodeType(
- "Renderer4.ImageDecodeTaskDurationUs.WebP", duration, hist_min_,
- hist_max_, bucket_count_, decode_type_);
+ case ImageType::kAvif:
+ histogram_name = "Renderer4.ImageDecodeTaskDurationUs.Avif";
+ break;
+ case ImageType::kBmp:
+ histogram_name = "Renderer4.ImageDecodeTaskDurationUs.Bmp";
+ break;
+ case ImageType::kGif:
+ histogram_name = "Renderer4.ImageDecodeTaskDurationUs.Gif";
+ break;
+ case ImageType::kIco:
+ histogram_name = "Renderer4.ImageDecodeTaskDurationUs.Ico";
break;
case ImageType::kJpeg:
- RecordMicrosecondTimesUmaByDecodeType(
- "Renderer4.ImageDecodeTaskDurationUs.Jpeg", duration, hist_min_,
- hist_max_, bucket_count_, decode_type_);
+ histogram_name = "Renderer4.ImageDecodeTaskDurationUs.Jpeg";
+ break;
+ case ImageType::kPng:
+ histogram_name = "Renderer4.ImageDecodeTaskDurationUs.Png";
+ break;
+ case ImageType::kWebP:
+ histogram_name = "Renderer4.ImageDecodeTaskDurationUs.WebP";
break;
case ImageType::kOther:
- RecordMicrosecondTimesUmaByDecodeType(
- "Renderer4.ImageDecodeTaskDurationUs.Other", duration, hist_min_,
- hist_max_, bucket_count_, decode_type_);
+ histogram_name = "Renderer4.ImageDecodeTaskDurationUs.Other";
break;
}
+ RecordMicrosecondTimesUmaByDecodeType(histogram_name, duration, hist_min_,
+ hist_max_, bucket_count_, decode_type_);
+
switch (task_type_) {
case kInRaster:
RecordMicrosecondTimesUmaByDecodeType(
diff --git a/chromium/cc/base/devtools_instrumentation.h b/chromium/cc/base/devtools_instrumentation.h
index 12e4c9c7016..8c2fd998669 100644
--- a/chromium/cc/base/devtools_instrumentation.h
+++ b/chromium/cc/base/devtools_instrumentation.h
@@ -66,9 +66,9 @@ class CC_BASE_EXPORT ScopedLayerTask {
class CC_BASE_EXPORT ScopedImageTask {
public:
- enum ImageType { kWebP, kJpeg, kOther };
+ enum ImageType { kAvif, kBmp, kGif, kIco, kJpeg, kPng, kWebP, kOther };
- ScopedImageTask(ImageType image_type)
+ explicit ScopedImageTask(ImageType image_type)
: image_type_(image_type), start_time_(base::TimeTicks::Now()) {}
ScopedImageTask(const ScopedImageTask&) = delete;
~ScopedImageTask() = default;
diff --git a/chromium/cc/base/features.cc b/chromium/cc/base/features.cc
index 8c0c599f83d..5c230565fd2 100644
--- a/chromium/cc/base/features.cc
+++ b/chromium/cc/base/features.cc
@@ -12,6 +12,10 @@ namespace features {
const base::Feature kImpulseScrollAnimations = {
"ImpulseScrollAnimations", base::FEATURE_DISABLED_BY_DEFAULT};
+// Whether the compositor should attempt to sync with the scroll handlers before submitting a frame.
+const base::Feature kSynchronizedScrolling = {"SynchronizedScrolling",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
// When enabled BeginMainFrame does not wait for activation in the compositor
// thread for texture layers (crbug.com/1046463)
const base::Feature kTextureLayerSkipWaitForActivation{
diff --git a/chromium/cc/base/features.h b/chromium/cc/base/features.h
index 56c620c6eea..ea86ee616d8 100644
--- a/chromium/cc/base/features.h
+++ b/chromium/cc/base/features.h
@@ -12,6 +12,7 @@
namespace features {
CC_BASE_EXPORT extern const base::Feature kImpulseScrollAnimations;
+CC_BASE_EXPORT extern const base::Feature kSynchronizedScrolling;
CC_BASE_EXPORT extern const base::Feature kTextureLayerSkipWaitForActivation;
#if !defined(OS_ANDROID)
diff --git a/chromium/cc/base/list_container.h b/chromium/cc/base/list_container.h
index 27ba06b9192..a01d07958dd 100644
--- a/chromium/cc/base/list_container.h
+++ b/chromium/cc/base/list_container.h
@@ -9,7 +9,7 @@
#include <memory>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/optional.h"
#include "cc/base/list_container_helper.h"
diff --git a/chromium/cc/base/list_container_helper.cc b/chromium/cc/base/list_container_helper.cc
index afd386e3a88..7b594b4a458 100644
--- a/chromium/cc/base/list_container_helper.cc
+++ b/chromium/cc/base/list_container_helper.cc
@@ -7,6 +7,7 @@
#include <stddef.h>
#include <algorithm>
+#include <cstring>
#include <vector>
#include "base/check_op.h"
diff --git a/chromium/cc/base/math_util.h b/chromium/cc/base/math_util.h
index 1001124d9af..a5f30e02252 100644
--- a/chromium/cc/base/math_util.h
+++ b/chromium/cc/base/math_util.h
@@ -9,7 +9,7 @@
#include <memory>
#include <vector>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/numerics/ranges.h"
#include "build/build_config.h"
#include "cc/base/base_export.h"
diff --git a/chromium/cc/base/reverse_spiral_iterator.h b/chromium/cc/base/reverse_spiral_iterator.h
index 3fef364871d..1d07381e62d 100644
--- a/chromium/cc/base/reverse_spiral_iterator.h
+++ b/chromium/cc/base/reverse_spiral_iterator.h
@@ -5,7 +5,6 @@
#ifndef CC_BASE_REVERSE_SPIRAL_ITERATOR_H_
#define CC_BASE_REVERSE_SPIRAL_ITERATOR_H_
-#include "base/logging.h"
#include "cc/base/base_export.h"
#include "cc/base/index_rect.h"
diff --git a/chromium/cc/base/rtree.h b/chromium/cc/base/rtree.h
index 2ee4e7cb588..9e92f955de4 100644
--- a/chromium/cc/base/rtree.h
+++ b/chromium/cc/base/rtree.h
@@ -13,7 +13,7 @@
#include <map>
#include <vector>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/numerics/clamped_math.h"
#include "ui/gfx/geometry/rect.h"
diff --git a/chromium/cc/base/spiral_iterator.h b/chromium/cc/base/spiral_iterator.h
index af64b76afde..3ed081688e7 100644
--- a/chromium/cc/base/spiral_iterator.h
+++ b/chromium/cc/base/spiral_iterator.h
@@ -5,7 +5,6 @@
#ifndef CC_BASE_SPIRAL_ITERATOR_H_
#define CC_BASE_SPIRAL_ITERATOR_H_
-#include "base/logging.h"
#include "cc/base/base_export.h"
#include "cc/base/index_rect.h"
diff --git a/chromium/cc/base/switches.h b/chromium/cc/base/switches.h
index 7b6c6b398e0..12615d5dcdc 100644
--- a/chromium/cc/base/switches.h
+++ b/chromium/cc/base/switches.h
@@ -7,7 +7,7 @@
#ifndef CC_BASE_SWITCHES_H_
#define CC_BASE_SWITCHES_H_
-#include "base/logging.h"
+#include "base/check.h"
#include "cc/base/base_export.h"
// Since cc is used from the render process, anything that goes here also needs
diff --git a/chromium/cc/base/tiling_data.h b/chromium/cc/base/tiling_data.h
index a8b9ee7ec6c..ac294ac5ad6 100644
--- a/chromium/cc/base/tiling_data.h
+++ b/chromium/cc/base/tiling_data.h
@@ -7,7 +7,7 @@
#include <utility>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "cc/base/base_export.h"
#include "cc/base/index_rect.h"
#include "cc/base/reverse_spiral_iterator.h"
diff --git a/chromium/cc/input/browser_controls_offset_manager.cc b/chromium/cc/input/browser_controls_offset_manager.cc
index ea4c6e27993..5ebc925c1e6 100644
--- a/chromium/cc/input/browser_controls_offset_manager.cc
+++ b/chromium/cc/input/browser_controls_offset_manager.cc
@@ -207,17 +207,20 @@ void BrowserControlsOffsetManager::OnBrowserControlsParamsChanged(
return;
}
+ // We continue to update both top and bottom controls even if one has a height
+ // of 0 so that animations work properly. So here, we should preserve the
+ // ratios even if the controls height is 0.
float old_top_height = old_browser_controls_params_.top_controls_height;
float new_top_ratio =
TopControlsHeight()
? TopControlsShownRatio() * old_top_height / TopControlsHeight()
- : 0.f;
+ : TopControlsShownRatio();
float old_bottom_height = old_browser_controls_params_.bottom_controls_height;
float new_bottom_ratio = BottomControlsHeight()
? BottomControlsShownRatio() *
old_bottom_height / BottomControlsHeight()
- : 0.f;
+ : BottomControlsShownRatio();
if (!animate_changes) {
// If the min-heights changed when the controls were at the min-height, the
@@ -268,8 +271,13 @@ void BrowserControlsOffsetManager::OnBrowserControlsParamsChanged(
bool bottom_controls_need_animation = animate_changes;
float top_target_ratio;
- // If the top controls height changed when they were fully shown.
- if (TopControlsShownRatio() == 1.f && TopControlsHeight() != old_top_height) {
+ // We can't animate if we don't have top controls.
+ if (!TopControlsHeight()) {
+ top_controls_need_animation = false;
+
+ // If the top controls height changed when they were fully shown.
+ } else if (TopControlsShownRatio() == 1.f &&
+ TopControlsHeight() != old_top_height) {
top_target_ratio = 1.f; // i.e. new_height / new_height
// If the top controls min-height changed when they were at the minimum
@@ -284,9 +292,13 @@ void BrowserControlsOffsetManager::OnBrowserControlsParamsChanged(
}
float bottom_target_ratio;
- // If the bottom controls height changed when they were fully shown.
- if (BottomControlsShownRatio() == 1.f &&
- BottomControlsHeight() != old_bottom_height) {
+ // We can't animate if we don't have bottom controls.
+ if (!BottomControlsHeight()) {
+ bottom_controls_need_animation = false;
+
+ // If the bottom controls height changed when they were fully shown.
+ } else if (BottomControlsShownRatio() == 1.f &&
+ BottomControlsHeight() != old_bottom_height) {
bottom_target_ratio = 1.f; // i.e. new_height / new_height
// If the bottom controls min-height changed when they were at the minimum
diff --git a/chromium/cc/input/browser_controls_offset_manager_unittest.cc b/chromium/cc/input/browser_controls_offset_manager_unittest.cc
index 8c443fd0bee..507ca1f3a5a 100644
--- a/chromium/cc/input/browser_controls_offset_manager_unittest.cc
+++ b/chromium/cc/input/browser_controls_offset_manager_unittest.cc
@@ -1087,5 +1087,61 @@ TEST(BrowserControlsOffsetManagerTest, ScrollWithMinHeightSetForBothControls) {
manager->ScrollEnd();
}
+TEST(BrowserControlsOffsetManagerTest, ChangingBottomHeightFromZeroAnimates) {
+ MockBrowserControlsOffsetManagerClient client(100, 0.5f, 0.5f);
+ client.SetBrowserControlsParams({100, 30, 0, 0, false, false});
+ BrowserControlsOffsetManager* manager = client.manager();
+ EXPECT_FLOAT_EQ(1.f, client.CurrentTopControlsShownRatio());
+ EXPECT_FLOAT_EQ(1.f, client.CurrentBottomControlsShownRatio());
+
+ // Set the bottom controls height to 100 with animation.
+ client.SetBrowserControlsParams({100, 30, 100, 0, true, false});
+ EXPECT_TRUE(manager->HasAnimation());
+ // The bottom controls should be hidden in the beginning.
+ EXPECT_FLOAT_EQ(0.f, manager->ContentBottomOffset());
+ EXPECT_FLOAT_EQ(0.f, client.CurrentBottomControlsShownRatio());
+
+ base::TimeTicks time = base::TimeTicks::Now();
+
+ // First animate will establish the animaion.
+ float previous_ratio = manager->BottomControlsShownRatio();
+ manager->Animate(time);
+ EXPECT_EQ(manager->BottomControlsShownRatio(), previous_ratio);
+
+ while (manager->HasAnimation()) {
+ previous_ratio = manager->BottomControlsShownRatio();
+ time = base::TimeDelta::FromMicroseconds(100) + time;
+ manager->Animate(time);
+ EXPECT_GT(manager->BottomControlsShownRatio(), previous_ratio);
+ }
+
+ // Now the bottom controls should be fully shown.
+ EXPECT_FLOAT_EQ(100.f, manager->ContentBottomOffset());
+ EXPECT_FLOAT_EQ(1.f, client.CurrentBottomControlsShownRatio());
+}
+
+TEST(BrowserControlsOffsetManagerTest,
+ ChangingControlsHeightToZeroWithAnimationIsNoop) {
+ MockBrowserControlsOffsetManagerClient client(100, 0.5f, 0.5f);
+ client.SetBrowserControlsParams({100, 20, 80, 10, false, false});
+ BrowserControlsOffsetManager* manager = client.manager();
+ EXPECT_FLOAT_EQ(1.f, client.CurrentTopControlsShownRatio());
+ EXPECT_FLOAT_EQ(1.f, client.CurrentBottomControlsShownRatio());
+
+ // Set the bottom controls height to 0 with animation.
+ client.SetBrowserControlsParams({100, 20, 0, 0, true, false});
+
+ // There shouldn't be an animation because we can't animate controls with 0
+ // height.
+ EXPECT_FALSE(manager->HasAnimation());
+ // Also, the bottom controls ratio should stay the same.
+ EXPECT_FLOAT_EQ(1.f, client.CurrentBottomControlsShownRatio());
+
+ // Increase the top controls height with animation.
+ client.SetBrowserControlsParams({120, 20, 0, 0, true, false});
+ // This shouldn't override the bottom controls shown ratio.
+ EXPECT_FLOAT_EQ(1.f, client.CurrentBottomControlsShownRatio());
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/input/input_handler.cc b/chromium/cc/input/input_handler.cc
deleted file mode 100644
index 55ca46ef2c0..00000000000
--- a/chromium/cc/input/input_handler.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2014 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/input/input_handler.h"
-
-namespace cc {
-
-InputHandlerScrollResult::InputHandlerScrollResult()
- : did_scroll(false), did_overscroll_root(false) {
-}
-
-InputHandlerPointerResult::InputHandlerPointerResult()
- : type(kUnhandled),
- scroll_units(ui::ScrollGranularity::kScrollByPrecisePixel) {}
-
-} // namespace cc
diff --git a/chromium/cc/input/input_handler.h b/chromium/cc/input/input_handler.h
index eaaa9afde45..43309848b23 100644
--- a/chromium/cc/input/input_handler.h
+++ b/chromium/cc/input/input_handler.h
@@ -50,13 +50,14 @@ enum class ScrollBeginThreadState {
};
struct CC_EXPORT InputHandlerPointerResult {
- InputHandlerPointerResult();
+ InputHandlerPointerResult() = default;
// Tells what type of processing occurred in the input handler as a result of
// the pointer event.
- PointerResultType type;
+ PointerResultType type = kUnhandled;
// Tells what scroll_units should be used.
- ui::ScrollGranularity scroll_units;
+ ui::ScrollGranularity scroll_units =
+ ui::ScrollGranularity::kScrollByPrecisePixel;
// If the input handler processed the event as a scrollbar scroll, it will
// return a gfx::ScrollOffset that produces the necessary scroll. However,
@@ -73,11 +74,11 @@ struct CC_EXPORT InputHandlerPointerResult {
};
struct CC_EXPORT InputHandlerScrollResult {
- InputHandlerScrollResult();
+ InputHandlerScrollResult() = default;
// Did any layer scroll as a result this ScrollUpdate call?
- bool did_scroll;
+ bool did_scroll = false;
// Was any of the scroll delta argument to this ScrollUpdate call not used?
- bool did_overscroll_root;
+ bool did_overscroll_root = false;
// The total overscroll that has been accumulated by all ScrollUpdate calls
// that have had overscroll since the last ScrollBegin call. This resets upon
// a ScrollUpdate with no overscroll.
@@ -139,17 +140,25 @@ class CC_EXPORT InputHandler {
InputHandler& operator=(const InputHandler&) = delete;
struct ScrollStatus {
- ScrollStatus()
- : thread(SCROLL_ON_IMPL_THREAD),
- main_thread_scrolling_reasons(
- MainThreadScrollingReason::kNotScrollingOnMain),
- bubble(false) {}
+ ScrollStatus() = default;
ScrollStatus(ScrollThread thread, uint32_t main_thread_scrolling_reasons)
: thread(thread),
main_thread_scrolling_reasons(main_thread_scrolling_reasons) {}
- ScrollThread thread;
- uint32_t main_thread_scrolling_reasons;
- bool bubble;
+ ScrollStatus(ScrollThread thread,
+ uint32_t main_thread_scrolling_reasons,
+ bool needs_main_thread_hit_test)
+ : thread(thread),
+ main_thread_scrolling_reasons(main_thread_scrolling_reasons),
+ needs_main_thread_hit_test(needs_main_thread_hit_test) {}
+ ScrollThread thread = SCROLL_ON_IMPL_THREAD;
+ uint32_t main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNotScrollingOnMain;
+ bool bubble = false;
+
+ // Used only in scroll unification. Tells the caller that the input handler
+ // detected a case where it cannot reliably target a scroll node and needs
+ // the main thread to perform a hit test.
+ bool needs_main_thread_hit_test = false;
};
enum class TouchStartOrMoveEventListenerType {
@@ -256,7 +265,7 @@ class CC_EXPORT InputHandler {
// suppress scrolling by consuming touch events that started at
// |viewport_point|, and whether |viewport_point| is on the currently
// scrolling layer.
- // |out_touch_action| is assigned the whitelisted touch action for the
+ // |out_touch_action| is assigned the allowed touch action for the
// |viewport_point|. In the case there are no touch handlers or touch action
// regions, |out_touch_action| is assigned TouchAction::kAuto since the
// default touch action is auto.
diff --git a/chromium/cc/input/main_thread_scrolling_reason.cc b/chromium/cc/input/main_thread_scrolling_reason.cc
index b84c31dd04c..a116970e49b 100644
--- a/chromium/cc/input/main_thread_scrolling_reason.cc
+++ b/chromium/cc/input/main_thread_scrolling_reason.cc
@@ -44,14 +44,10 @@ void MainThreadScrollingReason::AddToTracedValue(
traced_value.AppendString("Handling scroll from main thread");
if (reasons & kHasTransformAndLCDText)
traced_value.AppendString("Has transform and LCD text");
- if (reasons & kBackgroundNotOpaqueInRectAndLCDText)
- traced_value.AppendString("Background is not opaque in rect and LCD text");
- if (reasons & kCantPaintScrollingBackground)
- traced_value.AppendString("Can't paint scrolling background");
- if (reasons & kHasClipRelatedProperty)
- traced_value.AppendString("Has clip related property");
- if (reasons & kIsNotStackingContextAndLCDText)
- traced_value.AppendString("Is not stacking context and LCD text");
+ if (reasons & kNotOpaqueForTextAndLCDText)
+ traced_value.AppendString("Not opaque for text and LCD text");
+ if (reasons & kCantPaintScrollingBackgroundAndLCDText)
+ traced_value.AppendString("Can't paint scrolling background and LCD text");
// Transient scrolling reasons.
if (reasons & kNonFastScrollableRegion)
diff --git a/chromium/cc/input/main_thread_scrolling_reason.h b/chromium/cc/input/main_thread_scrolling_reason.h
index cd129cf69fc..8354bbb401d 100644
--- a/chromium/cc/input/main_thread_scrolling_reason.h
+++ b/chromium/cc/input/main_thread_scrolling_reason.h
@@ -44,10 +44,8 @@ struct CC_EXPORT MainThreadScrollingReason {
// screen position; transparency and transforms break this.
kNonCompositedReasonsFirst = 17,
kHasTransformAndLCDText = 1 << 17,
- kBackgroundNotOpaqueInRectAndLCDText = 1 << 18,
- kCantPaintScrollingBackground = 1 << 19,
- kHasClipRelatedProperty = 1 << 20,
- kIsNotStackingContextAndLCDText = 1 << 22,
+ kNotOpaqueForTextAndLCDText = 1 << 18,
+ kCantPaintScrollingBackgroundAndLCDText = 1 << 19,
kNonCompositedReasonsLast = 22,
// Transient scrolling reasons. These are computed for each scroll begin.
@@ -69,9 +67,8 @@ struct CC_EXPORT MainThreadScrollingReason {
};
static const uint32_t kNonCompositedReasons =
- kHasTransformAndLCDText | kBackgroundNotOpaqueInRectAndLCDText |
- kCantPaintScrollingBackground | kHasClipRelatedProperty |
- kIsNotStackingContextAndLCDText;
+ kHasTransformAndLCDText | kNotOpaqueForTextAndLCDText |
+ kCantPaintScrollingBackgroundAndLCDText;
// Returns true if the given MainThreadScrollingReason can be set by the main
// thread.
diff --git a/chromium/cc/input/main_thread_scrolling_reason_unittest.cc b/chromium/cc/input/main_thread_scrolling_reason_unittest.cc
index 7cf3f40d164..4f8b75516af 100644
--- a/chromium/cc/input/main_thread_scrolling_reason_unittest.cc
+++ b/chromium/cc/input/main_thread_scrolling_reason_unittest.cc
@@ -20,10 +20,8 @@ TEST_F(MainThreadScrollingReasonTest, AsText) {
"Frame overlay, "
"Handling scroll from main thread, "
"Has transform and LCD text, "
- "Background is not opaque in rect and LCD text, "
- "Can't paint scrolling background, "
- "Has clip related property, "
- "Is not stacking context and LCD text, "
+ "Not opaque for text and LCD text, "
+ "Can't paint scrolling background and LCD text, "
"Non fast scrollable region, "
"Failed hit test, "
"No scrolling layer, "
diff --git a/chromium/cc/input/scroll_elasticity_helper.cc b/chromium/cc/input/scroll_elasticity_helper.cc
index 739daaa72a3..451bfcd81ba 100644
--- a/chromium/cc/input/scroll_elasticity_helper.cc
+++ b/chromium/cc/input/scroll_elasticity_helper.cc
@@ -18,6 +18,7 @@ class ScrollElasticityHelperImpl : public ScrollElasticityHelper {
bool IsUserScrollable() const override;
gfx::Vector2dF StretchAmount() const override;
+ gfx::Size ScrollBounds() const override;
void SetStretchAmount(const gfx::Vector2dF& stretch_amount) override;
gfx::ScrollOffset ScrollOffset() const override;
gfx::ScrollOffset MaxScrollOffset() const override;
@@ -46,6 +47,12 @@ gfx::Vector2dF ScrollElasticityHelperImpl::StretchAmount() const {
return host_impl_->active_tree()->elastic_overscroll()->Current(true);
}
+gfx::Size ScrollElasticityHelperImpl::ScrollBounds() const {
+ return host_impl_->OuterViewportScrollNode()
+ ? host_impl_->OuterViewportScrollNode()->container_bounds
+ : gfx::Size();
+}
+
void ScrollElasticityHelperImpl::SetStretchAmount(
const gfx::Vector2dF& stretch_amount) {
if (stretch_amount == StretchAmount())
diff --git a/chromium/cc/input/scroll_elasticity_helper.h b/chromium/cc/input/scroll_elasticity_helper.h
index 81281310ecb..4ae282fe51b 100644
--- a/chromium/cc/input/scroll_elasticity_helper.h
+++ b/chromium/cc/input/scroll_elasticity_helper.h
@@ -8,6 +8,7 @@
#include "base/time/time.h"
#include "cc/cc_export.h"
#include "ui/gfx/geometry/scroll_offset.h"
+#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace cc {
@@ -54,6 +55,9 @@ class CC_EXPORT ScrollElasticityHelper {
virtual bool IsUserScrollable() const = 0;
+ // The bounds of the root scroller.
+ virtual gfx::Size ScrollBounds() const = 0;
+
// The amount that the view is stretched past the normal allowable bounds.
virtual gfx::Vector2dF StretchAmount() const = 0;
virtual void SetStretchAmount(const gfx::Vector2dF& stretch_amount) = 0;
diff --git a/chromium/cc/input/scroll_state.h b/chromium/cc/input/scroll_state.h
index a73b9b117c4..8ac3850ca51 100644
--- a/chromium/cc/input/scroll_state.h
+++ b/chromium/cc/input/scroll_state.h
@@ -86,6 +86,14 @@ class CC_EXPORT ScrollState {
// it's a scroll update
gfx::ScrollOffset DeltaOrHint() const;
+ ElementId target_element_id() const {
+ return data_.current_native_scrolling_element();
+ }
+
+ bool is_main_thread_hit_tested() const {
+ return data_.is_main_thread_hit_tested;
+ }
+
ScrollStateData* data() { return &data_; }
private:
diff --git a/chromium/cc/input/scroll_state_data.cc b/chromium/cc/input/scroll_state_data.cc
index 4c472790f15..f18676f4b85 100644
--- a/chromium/cc/input/scroll_state_data.cc
+++ b/chromium/cc/input/scroll_state_data.cc
@@ -25,7 +25,8 @@ ScrollStateData::ScrollStateData()
delta_granularity(ui::ScrollGranularity::kScrollByPrecisePixel),
caused_scroll_x(false),
caused_scroll_y(false),
- is_scroll_chain_cut(false) {}
+ is_scroll_chain_cut(false),
+ is_main_thread_hit_tested(false) {}
ScrollStateData::ScrollStateData(const ScrollStateData& other) = default;
diff --git a/chromium/cc/input/scroll_state_data.h b/chromium/cc/input/scroll_state_data.h
index 297517031ae..a3a2755c61b 100644
--- a/chromium/cc/input/scroll_state_data.h
+++ b/chromium/cc/input/scroll_state_data.h
@@ -70,12 +70,18 @@ class CC_EXPORT ScrollStateData {
ElementId current_native_scrolling_element() const;
void set_current_native_scrolling_element(ElementId element_id);
+ // Used in scroll unification to specify that a scroll state has been hit
+ // tested on the main thread. If this is true, the hit test result will be
+ // placed in the current_native_scrolling_element_.
+ bool is_main_thread_hit_tested;
+
private:
// The id of the last native element to respond to a scroll, or 0 if none
// exists.
// TODO(bokan): In the compositor, this is now only used as an override to
- // scroller targeting, i.e. we'll latch scrolling to the specified
- // element_id. It will be renamed when the main thread is also converted.
+ // scroller targeting. I.e. we'll latch scrolling to the specified
+ // element_id. It will be renamed to a better name (target_element_id?) when
+ // the main thread is also converted.
ElementId current_native_scrolling_element_;
};
diff --git a/chromium/cc/input/scroll_utils.cc b/chromium/cc/input/scroll_utils.cc
new file mode 100644
index 00000000000..e9c036e3896
--- /dev/null
+++ b/chromium/cc/input/scroll_utils.cc
@@ -0,0 +1,45 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/input/scroll_utils.h"
+
+#include "base/numerics/ranges.h"
+#include "ui/gfx/geometry/size_f.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace cc {
+
+// static
+gfx::Vector2dF ScrollUtils::ResolveScrollPercentageToPixels(
+ const gfx::Vector2dF& delta,
+ const gfx::SizeF& scroller,
+ const gfx::SizeF& viewport) {
+ // Work with unsigned values and keep sign information in sign_x / sign_y.
+ float sign_x = std::signbit(delta.x()) ? -1 : 1;
+ float sign_y = std::signbit(delta.y()) ? -1 : 1;
+ float delta_x = std::abs(delta.x());
+ float delta_y = std::abs(delta.y());
+
+ // Resolved deltas in percent based scrolling are clamped at min by 16 pixels.
+ float min = kMinPixelDeltaForPercentBasedScroll;
+
+ // Resolve and clamp horizontal scroll
+ if (delta_x > 0) {
+ delta_x = delta_x * std::min(scroller.width(), viewport.width());
+ if (delta_x < min)
+ delta_x = min;
+ }
+
+ // Resolve and clamps vertical scroll.
+ if (delta_y > 0) {
+ delta_y = delta_y * std::min(scroller.height(), viewport.height());
+ if (delta_y < min)
+ delta_y = min;
+ }
+
+ return gfx::Vector2dF(std::copysign(delta_x, sign_x),
+ std::copysign(delta_y, sign_y));
+}
+
+} // namespace cc
diff --git a/chromium/cc/input/scroll_utils.h b/chromium/cc/input/scroll_utils.h
new file mode 100644
index 00000000000..34fb3be7773
--- /dev/null
+++ b/chromium/cc/input/scroll_utils.h
@@ -0,0 +1,43 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_INPUT_SCROLL_UTILS_H_
+#define CC_INPUT_SCROLL_UTILS_H_
+
+#include "cc/cc_export.h"
+
+namespace gfx {
+class Vector2dF;
+class SizeF;
+} // namespace gfx
+
+namespace cc {
+
+static constexpr int kPixelsPerLineStep = 40;
+static constexpr float kMinFractionToStepWhenPaging = 0.875f;
+
+// Each directional scroll for percentage-based units should scroll 1/8th of
+// the scrollable area.
+static constexpr float kPercentDeltaForDirectionalScroll = 0.125f;
+
+// Scroll deltas are lower-bounded by 16 physical pixels in percent-based
+// scrolls.
+static constexpr float kMinPixelDeltaForPercentBasedScroll = 16;
+
+// Class for scroll helper methods in cc and blink.
+class CC_EXPORT ScrollUtils {
+ public:
+ // Transforms a |scroll_delta| in percent units to pixel units based in its
+ // |scroller_size|. Clamps it by 16 pixels to avoid too small deltas for tiny
+ // scrollers and 12.5% of |viewport_size| to avoid too large deltas.
+ // Inputs and output muest be in physical pixels.
+ static gfx::Vector2dF ResolveScrollPercentageToPixels(
+ const gfx::Vector2dF& scroll_delta,
+ const gfx::SizeF& scroller_size,
+ const gfx::SizeF& viewport_size);
+};
+
+} // namespace cc
+
+#endif // CC_INPUT_SCROLL_UTILS_H_
diff --git a/chromium/cc/input/scrollbar.h b/chromium/cc/input/scrollbar.h
index af8cef429b1..4fc7a3e57e9 100644
--- a/chromium/cc/input/scrollbar.h
+++ b/chromium/cc/input/scrollbar.h
@@ -11,13 +11,6 @@
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/rect.h"
-static constexpr int kPixelsPerLineStep = 40;
-static constexpr float kMinFractionToStepWhenPaging = 0.875f;
-
-// Each directional scroll for percentage-based units should scroll 1/8th of
-// the scrollable area.
-static constexpr float kPercentDeltaForDirectionalScroll = 0.125f;
-
// 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
@@ -58,6 +51,7 @@ class Scrollbar : public base::RefCounted<Scrollbar> {
virtual bool IsOverlay() const = 0;
virtual bool HasThumb() const = 0;
virtual bool SupportsDragSnapBack() const = 0;
+ virtual bool JumpOnTrackClick() const = 0;
// The following rects are all relative to the scrollbar's origin.
// The location of ThumbRect reflects scroll offset, but cc will ignore it
diff --git a/chromium/cc/input/scrollbar_animation_controller.cc b/chromium/cc/input/scrollbar_animation_controller.cc
index 2435aa156fe..50242fb337c 100644
--- a/chromium/cc/input/scrollbar_animation_controller.cc
+++ b/chromium/cc/input/scrollbar_animation_controller.cc
@@ -51,8 +51,6 @@ ScrollbarAnimationController::ScrollbarAnimationController(
is_animating_(false),
animation_change_(NONE),
scroll_element_id_(scroll_element_id),
- currently_scrolling_(false),
- show_in_fast_scroll_(false),
opacity_(initial_opacity),
show_scrollbars_on_scroll_gesture_(false),
need_thinning_animation_(false),
@@ -73,8 +71,6 @@ ScrollbarAnimationController::ScrollbarAnimationController(
is_animating_(false),
animation_change_(NONE),
scroll_element_id_(scroll_element_id),
- currently_scrolling_(false),
- show_in_fast_scroll_(false),
opacity_(initial_opacity),
show_scrollbars_on_scroll_gesture_(true),
need_thinning_animation_(true),
@@ -182,25 +178,6 @@ void ScrollbarAnimationController::RunAnimationFrame(float progress) {
StopAnimation();
}
-void ScrollbarAnimationController::DidScrollBegin() {
- currently_scrolling_ = true;
-}
-
-void ScrollbarAnimationController::DidScrollEnd() {
- bool has_scrolled = show_in_fast_scroll_;
- show_in_fast_scroll_ = false;
-
- currently_scrolling_ = false;
-
- // We don't fade out scrollbar if they need thinning animation and mouse is
- // near.
- if (need_thinning_animation_ && MouseIsNearAnyScrollbar())
- return;
-
- if (has_scrolled && !tickmarks_showing_)
- PostDelayedAnimation(FADE_OUT);
-}
-
void ScrollbarAnimationController::DidScrollUpdate() {
UpdateScrollbarState();
}
@@ -213,19 +190,13 @@ void ScrollbarAnimationController::UpdateScrollbarState() {
Show();
- // As an optimization, we avoid spamming fade delay tasks during active fast
- // scrolls. But if we're not within one, we need to post every scroll update.
- if (!currently_scrolling_) {
- // We don't fade out scrollbar if they need thinning animation (Aura
- // Overlay) and mouse is near or tickmarks show.
- if (need_thinning_animation_) {
- if (!MouseIsNearAnyScrollbar() && !tickmarks_showing_)
- PostDelayedAnimation(FADE_OUT);
- } else {
+ // We don't fade out scrollbar if they need thinning animation (Aura
+ // Overlay) and mouse is near or tickmarks show.
+ if (need_thinning_animation_) {
+ if (!MouseIsNearAnyScrollbar() && !tickmarks_showing_)
PostDelayedAnimation(FADE_OUT);
- }
} else {
- show_in_fast_scroll_ = true;
+ PostDelayedAnimation(FADE_OUT);
}
if (need_thinning_animation_) {
diff --git a/chromium/cc/input/scrollbar_animation_controller.h b/chromium/cc/input/scrollbar_animation_controller.h
index 67a9ed5c8fc..1f2c22af131 100644
--- a/chromium/cc/input/scrollbar_animation_controller.h
+++ b/chromium/cc/input/scrollbar_animation_controller.h
@@ -72,9 +72,6 @@ class CC_EXPORT ScrollbarAnimationController {
// Effect both Android and Aura Overlay Scrollbar.
void DidScrollUpdate();
- void DidScrollBegin();
- void DidScrollEnd();
-
void DidMouseDown();
void DidMouseUp();
void DidMouseLeave();
@@ -150,8 +147,6 @@ class CC_EXPORT ScrollbarAnimationController {
AnimationChange animation_change_;
const ElementId scroll_element_id_;
- bool currently_scrolling_;
- bool show_in_fast_scroll_;
base::CancelableOnceClosure delayed_scrollbar_animation_;
diff --git a/chromium/cc/input/scrollbar_animation_controller_unittest.cc b/chromium/cc/input/scrollbar_animation_controller_unittest.cc
index f9593321d61..985f45bae4b 100644
--- a/chromium/cc/input/scrollbar_animation_controller_unittest.cc
+++ b/chromium/cc/input/scrollbar_animation_controller_unittest.cc
@@ -163,9 +163,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, AppearOnResize) {
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
ExpectScrollbarsOpacity(1);
// Make the Layer non-scrollable, scrollbar disappears.
@@ -199,13 +197,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, HideOnResize) {
scroll_layer_->UpdateScrollable();
UpdateActiveTreeDrawProperties();
- scrollbar_controller_->DidScrollBegin();
-
scrollbar_controller_->DidScrollUpdate();
EXPECT_FLOAT_EQ(1, h_scrollbar_layer_->Opacity());
- scrollbar_controller_->DidScrollEnd();
-
// Shrink along Y axis and expand along X, horizontal scrollbar
// should disappear.
clip_layer_->SetBounds(gfx::Size(200, 100));
@@ -214,12 +208,8 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, HideOnResize) {
scroll_layer_->UpdateScrollable();
UpdateActiveTreeDrawProperties();
- scrollbar_controller_->DidScrollBegin();
-
scrollbar_controller_->DidScrollUpdate();
EXPECT_FLOAT_EQ(0.0f, h_scrollbar_layer_->Opacity());
-
- scrollbar_controller_->DidScrollEnd();
}
// Scroll content. Confirm the scrollbar appears and fades out.
@@ -232,7 +222,6 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, BasicAppearAndFadeOut) {
EXPECT_TRUE(scrollbar_controller_->ScrollbarsHidden());
// Scrollbar should appear only on scroll update.
- scrollbar_controller_->DidScrollBegin();
ExpectScrollbarsOpacity(0);
EXPECT_TRUE(scrollbar_controller_->ScrollbarsHidden());
@@ -240,7 +229,6 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, BasicAppearAndFadeOut) {
ExpectScrollbarsOpacity(1);
EXPECT_FALSE(scrollbar_controller_->ScrollbarsHidden());
- scrollbar_controller_->DidScrollEnd();
ExpectScrollbarsOpacity(1);
EXPECT_FALSE(scrollbar_controller_->ScrollbarsHidden());
@@ -295,9 +283,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest,
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
// An fade out animation should have been enqueued.
EXPECT_EQ(kFadeDelay, client_.delay());
@@ -355,9 +341,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MoveNearAndDontFadeOut) {
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
// An fade out animation should have been enqueued.
EXPECT_EQ(kFadeDelay, client_.delay());
@@ -396,9 +380,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MoveOverAndDontFadeOut) {
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
// An fade out animation should have been enqueued.
EXPECT_EQ(kFadeDelay, client_.delay());
@@ -438,9 +420,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest,
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
// An fade out animation should have been enqueued.
EXPECT_EQ(kFadeDelay, client_.delay());
@@ -466,9 +446,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest,
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
// An fade out animation should have been enqueued.
EXPECT_EQ(kFadeDelay, client_.delay());
@@ -502,9 +480,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, DontFadeWhileCaptured) {
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
// An fade out animation should have been enqueued.
EXPECT_EQ(kFadeDelay, client_.delay());
@@ -540,9 +516,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, FadeAfterReleasedFar) {
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
// An fade out animation should have been enqueued.
EXPECT_EQ(kFadeDelay, client_.delay());
@@ -591,9 +565,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, DontFadeAfterReleasedNear) {
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
// An fade out animation should have been enqueued.
EXPECT_EQ(kFadeDelay, client_.delay());
@@ -630,9 +602,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest,
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
// A fade out animation should have been enqueued. Start it.
EXPECT_EQ(kFadeDelay, client_.delay());
@@ -670,9 +640,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, TestCantCaptureWhenFaded) {
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
EXPECT_EQ(kFadeDelay, client_.delay());
EXPECT_FALSE(client_.start_fade().is_null());
@@ -748,7 +716,6 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, ScrollWithMouseNear) {
EXPECT_FLOAT_EQ(kIdleThicknessScale,
h_scrollbar_layer_->thumb_thickness_scale_factor());
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
// Now that we've received a scroll, we should be thick without an animation.
@@ -756,7 +723,6 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, ScrollWithMouseNear) {
// An animation for the fade should be either null or cancelled, since
// mouse is still near the scrollbar.
- scrollbar_controller_->DidScrollEnd();
EXPECT_TRUE(client_.start_fade().is_null() ||
client_.start_fade().IsCancelled());
@@ -775,29 +741,16 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, ScrollWithMouseNear) {
h_scrollbar_layer_->thumb_thickness_scale_factor());
}
-// Tests that main thread scroll updates immediatley queue a fade out animation
-// without requiring a ScrollEnd.
+// Tests that main thread scroll updates immediately queue a fade out animation
TEST_F(ScrollbarAnimationControllerAuraOverlayTest,
MainThreadScrollQueuesFade) {
ASSERT_TRUE(client_.start_fade().is_null());
- // A ScrollUpdate without a ScrollBegin indicates a main thread scroll update
- // so we should schedule a fade out animation without waiting for a ScrollEnd
- // (which will never come).
+ // A ScrollUpdate indicates a main thread scroll update so we should schedule
+ // a fade out animation since there is no scroll end notification.
scrollbar_controller_->DidScrollUpdate();
EXPECT_FALSE(client_.start_fade().is_null());
EXPECT_EQ(kFadeDelay, client_.delay());
-
- client_.start_fade().Reset();
-
- // If we got a ScrollBegin, we shouldn't schedule the fade out animation until
- // we get a corresponding ScrollEnd.
- scrollbar_controller_->DidScrollBegin();
- scrollbar_controller_->DidScrollUpdate();
- EXPECT_TRUE(client_.start_fade().is_null());
- scrollbar_controller_->DidScrollEnd();
- EXPECT_FALSE(client_.start_fade().is_null());
- EXPECT_EQ(kFadeDelay, client_.delay());
}
// Tests that the fade effect is animated.
@@ -806,9 +759,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, FadeAnimated) {
time += base::TimeDelta::FromSeconds(1);
// Scroll to make the scrollbars visible.
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
// Appearance is instant.
ExpectScrollbarsOpacity(1);
@@ -838,13 +789,10 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, NotifyChangedVisibility) {
EXPECT_CALL(client_, DidChangeScrollbarVisibility()).Times(1);
// Scroll to make the scrollbars visible.
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
EXPECT_FALSE(scrollbar_controller_->ScrollbarsHidden());
Mock::VerifyAndClearExpectations(&client_);
- scrollbar_controller_->DidScrollEnd();
-
// Play out the fade out animation. We shouldn't notify that the scrollbars
// are hidden until the animation is completly over. We can (but don't have
// to) notify during the animation that the scrollbars are still visible.
@@ -886,9 +834,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MouseNearEach) {
time += base::TimeDelta::FromSeconds(1);
// Scroll to make the scrollbars visible.
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
// Near vertical scrollbar.
scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(-1, 0));
@@ -979,9 +925,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MouseNearBoth) {
time += base::TimeDelta::FromSeconds(1);
// Scroll to make the scrollbars visible.
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
// Move scrollbar thumb to the end of track.
v_scrollbar_layer_->SetCurrentPos(100);
@@ -1012,9 +956,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest,
time += base::TimeDelta::FromSeconds(1);
// Scroll to make the scrollbars visible.
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
// Near vertical scrollbar.
scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(-1, 0));
@@ -1076,9 +1018,7 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, MouseLeaveFadeOut) {
scrollbar_controller_->DidMouseMove(NearVerticalScrollbarBegin(-1, 0));
// Scroll to make the scrollbars visible.
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
// Should not have delay fadeout animation.
EXPECT_TRUE(client_.start_fade().is_null() ||
@@ -1323,11 +1263,9 @@ TEST_F(ScrollbarAnimationControllerAuraOverlayTest, TickmakrsShowHide) {
client_.start_fade().IsCancelled());
// Scroll update with phase, no delay fade animation.
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
EXPECT_TRUE(client_.start_fade().is_null() ||
client_.start_fade().IsCancelled());
- scrollbar_controller_->DidScrollEnd();
EXPECT_TRUE(client_.start_fade().is_null() ||
client_.start_fade().IsCancelled());
@@ -1430,13 +1368,11 @@ TEST_F(ScrollbarAnimationControllerAndroidTest, HiddenInBegin) {
TEST_F(ScrollbarAnimationControllerAndroidTest,
HiddenAfterNonScrollingGesture) {
scrollbar_layer_->SetOverlayScrollbarLayerOpacityAnimated(0.f);
- scrollbar_controller_->DidScrollBegin();
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(100);
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->Opacity());
- scrollbar_controller_->DidScrollEnd();
EXPECT_TRUE(start_fade_.is_null());
@@ -1473,11 +1409,9 @@ TEST_F(ScrollbarAnimationControllerAndroidTest, HideOnResize) {
GetScrollNode(scroll_layer_)->container_bounds = gfx::Size(100, 200);
scroll_layer_->UpdateScrollable();
UpdateActiveTreeDrawProperties();
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
- scrollbar_controller_->DidScrollEnd();
// Shrink along Y axis and expand along X, horizontal scrollbar
// should disappear.
@@ -1485,12 +1419,8 @@ TEST_F(ScrollbarAnimationControllerAndroidTest, HideOnResize) {
scroll_layer_->UpdateScrollable();
UpdateActiveTreeDrawProperties();
- scrollbar_controller_->DidScrollBegin();
-
scrollbar_controller_->DidScrollUpdate();
EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->Opacity());
-
- scrollbar_controller_->DidScrollEnd();
}
TEST_F(VerticalScrollbarAnimationControllerAndroidTest, HideOnResize) {
@@ -1502,23 +1432,17 @@ TEST_F(VerticalScrollbarAnimationControllerAndroidTest, HideOnResize) {
GetScrollNode(scroll_layer_)->container_bounds = gfx::Size(100, 200);
scroll_layer_->UpdateScrollable();
UpdateActiveTreeDrawProperties();
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->Opacity());
- scrollbar_controller_->DidScrollEnd();
// Shrink along Y axis and expand along X, vertical scrollbar should appear.
GetScrollNode(scroll_layer_)->container_bounds = gfx::Size(200, 100);
scroll_layer_->UpdateScrollable();
UpdateActiveTreeDrawProperties();
- scrollbar_controller_->DidScrollBegin();
-
scrollbar_controller_->DidScrollUpdate();
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
-
- scrollbar_controller_->DidScrollEnd();
}
TEST_F(ScrollbarAnimationControllerAndroidTest, HideOnUserNonScrollableHorz) {
@@ -1527,12 +1451,8 @@ TEST_F(ScrollbarAnimationControllerAndroidTest, HideOnUserNonScrollableHorz) {
GetScrollNode(scroll_layer_)->user_scrollable_horizontal = false;
UpdateActiveTreeDrawProperties();
- scrollbar_controller_->DidScrollBegin();
-
scrollbar_controller_->DidScrollUpdate();
EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->Opacity());
-
- scrollbar_controller_->DidScrollEnd();
}
TEST_F(ScrollbarAnimationControllerAndroidTest, ShowOnUserNonScrollableVert) {
@@ -1541,12 +1461,8 @@ TEST_F(ScrollbarAnimationControllerAndroidTest, ShowOnUserNonScrollableVert) {
GetScrollNode(scroll_layer_)->user_scrollable_vertical = false;
UpdateActiveTreeDrawProperties();
- scrollbar_controller_->DidScrollBegin();
-
scrollbar_controller_->DidScrollUpdate();
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
-
- scrollbar_controller_->DidScrollEnd();
}
TEST_F(VerticalScrollbarAnimationControllerAndroidTest,
@@ -1556,12 +1472,8 @@ TEST_F(VerticalScrollbarAnimationControllerAndroidTest,
GetScrollNode(scroll_layer_)->user_scrollable_vertical = false;
UpdateActiveTreeDrawProperties();
- scrollbar_controller_->DidScrollBegin();
-
scrollbar_controller_->DidScrollUpdate();
EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->Opacity());
-
- scrollbar_controller_->DidScrollEnd();
}
TEST_F(VerticalScrollbarAnimationControllerAndroidTest,
@@ -1571,32 +1483,26 @@ TEST_F(VerticalScrollbarAnimationControllerAndroidTest,
GetScrollNode(scroll_layer_)->user_scrollable_horizontal = false;
UpdateActiveTreeDrawProperties();
- scrollbar_controller_->DidScrollBegin();
-
scrollbar_controller_->DidScrollUpdate();
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
-
- scrollbar_controller_->DidScrollEnd();
}
TEST_F(ScrollbarAnimationControllerAndroidTest, AwakenByScrollingGesture) {
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidScrollBegin();
EXPECT_FALSE(did_request_animate_);
scrollbar_controller_->DidScrollUpdate();
EXPECT_FALSE(did_request_animate_);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
- EXPECT_TRUE(start_fade_.is_null());
+ EXPECT_FALSE(start_fade_.is_null());
time += base::TimeDelta::FromSeconds(100);
scrollbar_controller_->Animate(time);
EXPECT_FALSE(did_request_animate_);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->Opacity());
- scrollbar_controller_->DidScrollEnd();
EXPECT_FALSE(did_request_animate_);
std::move(start_fade_).Run();
EXPECT_TRUE(did_request_animate_);
@@ -1622,9 +1528,7 @@ TEST_F(ScrollbarAnimationControllerAndroidTest, AwakenByScrollingGesture) {
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidScrollBegin();
scrollbar_controller_->DidScrollUpdate();
- scrollbar_controller_->DidScrollEnd();
std::move(start_fade_).Run();
EXPECT_TRUE(did_request_animate_);
@@ -1743,7 +1647,6 @@ TEST_F(ScrollbarAnimationControllerAndroidTest,
did_request_animate_ = false;
EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->Opacity());
- scrollbar_controller_->DidScrollBegin();
EXPECT_FALSE(did_request_animate_);
EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->Opacity());
@@ -1753,7 +1656,6 @@ TEST_F(ScrollbarAnimationControllerAndroidTest,
did_request_animate_ = false;
EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->Opacity());
- scrollbar_controller_->DidScrollEnd();
EXPECT_FALSE(did_request_animate_);
EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->Opacity());
@@ -1783,7 +1685,6 @@ TEST_F(ScrollbarAnimationControllerAndroidTest,
did_request_animate_ = false;
EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->Opacity());
- scrollbar_controller_->DidScrollBegin();
EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->Opacity());
time += base::TimeDelta::FromSeconds(1);
@@ -1798,10 +1699,10 @@ TEST_F(ScrollbarAnimationControllerAndroidTest,
EXPECT_FLOAT_EQ(1, scrollbar_layer_->Opacity());
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidScrollEnd();
EXPECT_FALSE(did_request_animate_);
EXPECT_FLOAT_EQ(1, scrollbar_layer_->Opacity());
}
} // namespace
+
} // namespace cc
diff --git a/chromium/cc/input/scrollbar_controller.cc b/chromium/cc/input/scrollbar_controller.cc
index aac1d4ceccd..490cd44d1d4 100644
--- a/chromium/cc/input/scrollbar_controller.cc
+++ b/chromium/cc/input/scrollbar_controller.cc
@@ -8,6 +8,7 @@
#include "base/cancelable_callback.h"
#include "cc/base/math_util.h"
+#include "cc/input/scroll_utils.h"
#include "cc/input/scrollbar.h"
#include "cc/input/scrollbar_controller.h"
#include "cc/trees/layer_tree_impl.h"
@@ -35,7 +36,7 @@ void ScrollbarController::WillBeginImplFrame() {
}
// Retrieves the ScrollbarLayerImplBase corresponding to the stashed ElementId.
-ScrollbarLayerImplBase* ScrollbarController::ScrollbarLayer() {
+ScrollbarLayerImplBase* ScrollbarController::ScrollbarLayer() const {
if (!captured_scrollbar_metadata_.has_value())
return nullptr;
@@ -52,7 +53,7 @@ ScrollbarLayerImplBase* ScrollbarController::ScrollbarLayer() {
// GSU.
InputHandlerPointerResult ScrollbarController::HandlePointerDown(
const gfx::PointF position_in_widget,
- bool shift_modifier) {
+ bool jump_key_modifier) {
LayerImpl* layer_impl = GetLayerHitByPoint(position_in_widget);
// If a non-custom scrollbar layer was not found, we return early as there is
@@ -88,12 +89,15 @@ InputHandlerPointerResult ScrollbarController::HandlePointerDown(
scroll_result.type = PointerResultType::kScrollbarScroll;
layer_tree_host_impl_->active_tree()->UpdateScrollbarGeometries();
const ScrollbarPart scrollbar_part =
- GetScrollbarPartFromPointerDown(scrollbar, position_in_widget);
+ GetScrollbarPartFromPointerDown(position_in_widget);
+ const bool perform_jump_click_on_track =
+ scrollbar->JumpOnTrackClick() != jump_key_modifier;
scroll_result.scroll_offset = GetScrollOffsetForScrollbarPart(
- scrollbar, scrollbar_part, shift_modifier);
+ scrollbar_part, perform_jump_click_on_track);
last_known_pointer_position_ = position_in_widget;
scrollbar_scroll_is_active_ = true;
- scroll_result.scroll_units = Granularity(scrollbar_part, shift_modifier);
+ scroll_result.scroll_units =
+ Granularity(scrollbar_part, perform_jump_click_on_track);
if (scrollbar_part == ScrollbarPart::THUMB) {
drag_state_ = DragState();
drag_state_->drag_origin = position_in_widget;
@@ -112,12 +116,12 @@ InputHandlerPointerResult ScrollbarController::HandlePointerDown(
// have the potential of initiating an autoscroll (if held down for long
// enough).
DCHECK(scrollbar_part != ScrollbarPart::THUMB);
- cancelable_autoscroll_task_ = std::make_unique<base::CancelableOnceClosure>(
- base::BindOnce(&ScrollbarController::StartAutoScrollAnimation,
- base::Unretained(this),
- InitialDeltaToAutoscrollVelocity(
- scrollbar, scroll_result.scroll_offset),
- scrollbar, scrollbar_part));
+ cancelable_autoscroll_task_ =
+ std::make_unique<base::CancelableOnceClosure>(base::BindOnce(
+ &ScrollbarController::StartAutoScrollAnimation,
+ base::Unretained(this),
+ InitialDeltaToAutoscrollVelocity(scroll_result.scroll_offset),
+ scrollbar_part));
layer_tree_host_impl_->task_runner_provider()
->ImplThreadTaskRunner()
->PostDelayedTask(FROM_HERE, cancelable_autoscroll_task_->callback(),
@@ -127,16 +131,16 @@ InputHandlerPointerResult ScrollbarController::HandlePointerDown(
}
bool ScrollbarController::SnapToDragOrigin(
- const ScrollbarLayerImplBase* scrollbar,
- const gfx::PointF pointer_position_in_widget) {
+ const gfx::PointF pointer_position_in_widget) const {
// Consult the ScrollbarTheme to check if thumb snapping is supported on the
// current platform.
+ const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
if (!(scrollbar && scrollbar->SupportsDragSnapBack()))
return false;
bool clipped = false;
- const gfx::PointF pointer_position_in_layer = GetScrollbarRelativePosition(
- scrollbar, pointer_position_in_widget, &clipped);
+ const gfx::PointF pointer_position_in_layer =
+ GetScrollbarRelativePosition(pointer_position_in_widget, &clipped);
if (clipped)
return false;
@@ -189,10 +193,10 @@ bool ScrollbarController::SnapToDragOrigin(
ui::ScrollGranularity ScrollbarController::Granularity(
const ScrollbarPart scrollbar_part,
- const bool shift_modifier) {
+ const bool jump_key_modifier) const {
const bool shift_click_on_scrollbar_track =
- shift_modifier && (scrollbar_part == ScrollbarPart::FORWARD_TRACK ||
- scrollbar_part == ScrollbarPart::BACK_TRACK);
+ jump_key_modifier && (scrollbar_part == ScrollbarPart::FORWARD_TRACK ||
+ scrollbar_part == ScrollbarPart::BACK_TRACK);
if (shift_click_on_scrollbar_track || scrollbar_part == ScrollbarPart::THUMB)
return ui::ScrollGranularity::kScrollByPrecisePixel;
@@ -201,17 +205,17 @@ ui::ScrollGranularity ScrollbarController::Granularity(
return ui::ScrollGranularity::kScrollByPixel;
}
-float ScrollbarController::GetScrollDeltaForAbsoluteJump(
- const ScrollbarLayerImplBase* scrollbar) {
+float ScrollbarController::GetScrollDeltaForAbsoluteJump() const {
layer_tree_host_impl_->active_tree()->UpdateScrollbarGeometries();
bool clipped = false;
- const gfx::PointF pointer_position_in_layer = GetScrollbarRelativePosition(
- scrollbar, last_known_pointer_position_, &clipped);
+ const gfx::PointF pointer_position_in_layer =
+ GetScrollbarRelativePosition(last_known_pointer_position_, &clipped);
if (clipped)
return 0;
+ const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
const float pointer_location =
scrollbar->orientation() == ScrollbarOrientation::VERTICAL
? pointer_position_in_layer.y()
@@ -232,19 +236,18 @@ float ScrollbarController::GetScrollDeltaForAbsoluteJump(
const float delta =
round(std::abs(desired_thumb_origin - current_thumb_origin));
- return delta * GetScrollerToScrollbarRatio(scrollbar);
+ return delta * GetScrollerToScrollbarRatio();
}
int ScrollbarController::GetScrollDeltaForDragPosition(
- const ScrollbarLayerImplBase* scrollbar,
- const gfx::PointF pointer_position_in_widget) {
+ const gfx::PointF pointer_position_in_widget) const {
+ const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
const float pointer_delta =
scrollbar->orientation() == ScrollbarOrientation::VERTICAL
? pointer_position_in_widget.y() - drag_state_->drag_origin.y()
: pointer_position_in_widget.x() - drag_state_->drag_origin.x();
- const float new_offset =
- pointer_delta * GetScrollerToScrollbarRatio(scrollbar);
+ const float new_offset = pointer_delta * GetScrollerToScrollbarRatio();
const float scroll_delta = drag_state_->scroll_position_at_start_ +
new_offset - scrollbar->current_pos();
@@ -276,7 +279,7 @@ InputHandlerPointerResult ScrollbarController::HandlePointerMove(
if (drag_processed_for_current_frame_)
return scroll_result;
- if (SnapToDragOrigin(scrollbar, position_in_widget)) {
+ if (SnapToDragOrigin(position_in_widget)) {
const float delta =
scrollbar->current_pos() - drag_state_->scroll_position_at_start_;
scroll_result.scroll_units = ui::ScrollGranularity::kScrollByPrecisePixel;
@@ -302,7 +305,7 @@ InputHandlerPointerResult ScrollbarController::HandlePointerMove(
// valid ScrollNode.
DCHECK(target_node);
- int delta = GetScrollDeltaForDragPosition(scrollbar, position_in_widget);
+ int delta = GetScrollDeltaForDragPosition(position_in_widget);
if (drag_state_->scroller_length_at_previous_move !=
scrollbar->scroll_layer_length()) {
drag_state_->scroller_displacement = delta;
@@ -339,8 +342,7 @@ InputHandlerPointerResult ScrollbarController::HandlePointerMove(
return scroll_result;
}
-float ScrollbarController::GetScrollerToScrollbarRatio(
- const ScrollbarLayerImplBase* scrollbar) {
+float ScrollbarController::GetScrollerToScrollbarRatio() const {
// Calculating the delta by which the scroller layer should move when
// dragging the thumb depends on the following factors:
// - scrollbar_track_length
@@ -381,6 +383,7 @@ float ScrollbarController::GetScrollerToScrollbarRatio(
// |<- scrollbar_thumb_length ->|
//
layer_tree_host_impl_->active_tree()->UpdateScrollbarGeometries();
+ const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
float scroll_layer_length = scrollbar->scroll_layer_length();
float scrollbar_track_length = scrollbar->TrackLength();
gfx::Rect thumb_rect(scrollbar->ComputeThumbQuadRect());
@@ -388,7 +391,7 @@ float ScrollbarController::GetScrollerToScrollbarRatio(
scrollbar->orientation() == ScrollbarOrientation::VERTICAL
? thumb_rect.height()
: thumb_rect.width();
- int viewport_length = GetViewportLength(scrollbar);
+ int viewport_length = GetViewportLength();
return (scroll_layer_length - viewport_length) /
(scrollbar_track_length - scrollbar_thumb_length);
@@ -399,11 +402,18 @@ void ScrollbarController::ResetState() {
drag_state_ = base::nullopt;
autoscroll_state_ = base::nullopt;
captured_scrollbar_metadata_ = base::nullopt;
+ if (cancelable_autoscroll_task_) {
+ cancelable_autoscroll_task_->Cancel();
+ cancelable_autoscroll_task_.reset();
+ }
}
-void ScrollbarController::DidUnregisterScrollbar(ElementId element_id) {
+void ScrollbarController::DidUnregisterScrollbar(
+ ElementId element_id,
+ ScrollbarOrientation orientation) {
if (captured_scrollbar_metadata_.has_value() &&
- captured_scrollbar_metadata_->scroll_element_id == element_id)
+ captured_scrollbar_metadata_->scroll_element_id == element_id &&
+ captured_scrollbar_metadata_->orientation == orientation)
ResetState();
}
@@ -413,12 +423,10 @@ void ScrollbarController::RecomputeAutoscrollStateIfNeeded() {
return;
layer_tree_host_impl_->active_tree()->UpdateScrollbarGeometries();
- const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
- const gfx::Rect thumb_quad = scrollbar->ComputeThumbQuadRect();
bool clipped;
- gfx::PointF scroller_relative_position(GetScrollbarRelativePosition(
- scrollbar, last_known_pointer_position_, &clipped));
+ gfx::PointF scroller_relative_position(
+ GetScrollbarRelativePosition(last_known_pointer_position_, &clipped));
if (clipped)
return;
@@ -429,6 +437,8 @@ void ScrollbarController::RecomputeAutoscrollStateIfNeeded() {
int thumb_start = 0;
int thumb_end = 0;
int pointer_position = 0;
+ const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
+ const gfx::Rect thumb_quad = scrollbar->ComputeThumbQuadRect();
if (scrollbar->orientation() == ScrollbarOrientation::VERTICAL) {
thumb_start = thumb_quad.y();
thumb_end = thumb_quad.y() + thumb_quad.height();
@@ -458,7 +468,7 @@ void ScrollbarController::RecomputeAutoscrollStateIfNeeded() {
const float scroll_layer_length = scrollbar->scroll_layer_length();
if (autoscroll_state_->scroll_layer_length != scroll_layer_length) {
layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort();
- StartAutoScrollAnimation(autoscroll_state_->velocity, scrollbar,
+ StartAutoScrollAnimation(autoscroll_state_->velocity,
autoscroll_state_->pressed_scrollbar_part);
}
}
@@ -466,8 +476,8 @@ void ScrollbarController::RecomputeAutoscrollStateIfNeeded() {
// 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.
- const gfx::RectF scrollbar_part_rect(GetRectForScrollbarPart(
- scrollbar, autoscroll_state_->pressed_scrollbar_part));
+ const gfx::RectF scrollbar_part_rect(
+ GetRectForScrollbarPart(autoscroll_state_->pressed_scrollbar_part));
if (!scrollbar_part_rect.Contains(scroller_relative_position)) {
// Stop animating if pointer moves outside the rect bounds.
layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort();
@@ -475,17 +485,17 @@ void ScrollbarController::RecomputeAutoscrollStateIfNeeded() {
!layer_tree_host_impl_->mutator_host()->IsElementAnimating(
scrollbar->scroll_element_id())) {
// Start animating if pointer re-enters the bounds.
- StartAutoScrollAnimation(autoscroll_state_->velocity, scrollbar,
+ StartAutoScrollAnimation(autoscroll_state_->velocity,
autoscroll_state_->pressed_scrollbar_part);
}
}
// Helper to calculate the autoscroll velocity.
float ScrollbarController::InitialDeltaToAutoscrollVelocity(
- const ScrollbarLayerImplBase* scrollbar,
gfx::ScrollOffset scroll_offset) const {
+ DCHECK(captured_scrollbar_metadata_.has_value());
const float scroll_delta =
- scrollbar->orientation() == ScrollbarOrientation::VERTICAL
+ ScrollbarLayer()->orientation() == ScrollbarOrientation::VERTICAL
? scroll_offset.y()
: scroll_offset.x();
return scroll_delta * kAutoscrollMultiplier;
@@ -493,15 +503,17 @@ float ScrollbarController::InitialDeltaToAutoscrollVelocity(
void ScrollbarController::StartAutoScrollAnimation(
const float velocity,
- const ScrollbarLayerImplBase* scrollbar,
ScrollbarPart pressed_scrollbar_part) {
// Autoscroll and thumb drag are mutually exclusive. Both can't be active at
// the same time.
DCHECK(!drag_state_.has_value());
+ DCHECK(captured_scrollbar_metadata_.has_value());
DCHECK_NE(velocity, 0);
+ DCHECK(ScrollbarLayer());
// 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.
+ const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
ScrollTree& scroll_tree =
layer_tree_host_impl_->active_tree()->property_trees()->scroll_tree;
ScrollNode* scroll_node =
@@ -552,18 +564,13 @@ InputHandlerPointerResult ScrollbarController::HandlePointerUp(
if (autoscroll_state_.has_value())
layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort();
- if (cancelable_autoscroll_task_) {
- cancelable_autoscroll_task_->Cancel();
- cancelable_autoscroll_task_.reset();
- }
-
ResetState();
return scroll_result;
}
// Returns the layer that is hit by the position_in_widget.
LayerImpl* ScrollbarController::GetLayerHitByPoint(
- const gfx::PointF position_in_widget) {
+ const gfx::PointF position_in_widget) const {
LayerTreeImpl* active_tree = layer_tree_host_impl_->active_tree();
gfx::Point viewport_point(position_in_widget.x(), position_in_widget.y());
@@ -575,8 +582,8 @@ LayerImpl* ScrollbarController::GetLayerHitByPoint(
return layer_impl;
}
-int ScrollbarController::GetViewportLength(
- const ScrollbarLayerImplBase* scrollbar) const {
+int ScrollbarController::GetViewportLength() const {
+ const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
const ScrollNode* scroll_node =
layer_tree_host_impl_->active_tree()
->property_trees()
@@ -587,30 +594,51 @@ int ScrollbarController::GetViewportLength(
: scroll_node->container_bounds.width();
}
+int ScrollbarController::GetScrollDeltaForPercentBasedScroll() const {
+ const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
+
+ const ScrollNode* scroll_node =
+ layer_tree_host_impl_->active_tree()
+ ->property_trees()
+ ->scroll_tree.FindNodeFromElementId(scrollbar->scroll_element_id());
+ DCHECK(scroll_node);
+
+ const gfx::Vector2dF scroll_delta =
+ scrollbar->orientation() == ScrollbarOrientation::VERTICAL
+ ? gfx::Vector2dF(0, kPercentDeltaForDirectionalScroll)
+ : gfx::Vector2dF(kPercentDeltaForDirectionalScroll, 0);
+
+ const gfx::Vector2dF pixel_delta =
+ layer_tree_host_impl_->ResolveScrollGranularityToPixels(
+ *scroll_node, scroll_delta,
+ ui::ScrollGranularity::kScrollByPercentage);
+
+ return scrollbar->orientation() == ScrollbarOrientation::VERTICAL
+ ? pixel_delta.y()
+ : pixel_delta.x();
+}
+
int ScrollbarController::GetScrollDeltaForScrollbarPart(
- const ScrollbarLayerImplBase* scrollbar,
const ScrollbarPart scrollbar_part,
- const bool shift_modifier) {
+ const bool jump_key_modifier) const {
int scroll_delta = 0;
switch (scrollbar_part) {
case ScrollbarPart::BACK_BUTTON:
case ScrollbarPart::FORWARD_BUTTON:
if (layer_tree_host_impl_->settings().percent_based_scrolling) {
- scroll_delta =
- kPercentDeltaForDirectionalScroll * GetViewportLength(scrollbar);
+ scroll_delta = GetScrollDeltaForPercentBasedScroll();
} else {
scroll_delta = kPixelsPerLineStep * ScreenSpaceScaleFactor();
}
break;
case ScrollbarPart::BACK_TRACK:
case ScrollbarPart::FORWARD_TRACK: {
- if (shift_modifier) {
- scroll_delta = GetScrollDeltaForAbsoluteJump(scrollbar);
+ if (jump_key_modifier) {
+ scroll_delta = GetScrollDeltaForAbsoluteJump();
break;
}
- scroll_delta =
- GetViewportLength(scrollbar) * kMinFractionToStepWhenPaging;
+ scroll_delta = GetViewportLength() * kMinFractionToStepWhenPaging;
break;
}
default:
@@ -634,9 +662,8 @@ float ScrollbarController::ScreenSpaceScaleFactor() const {
}
gfx::PointF ScrollbarController::GetScrollbarRelativePosition(
- const ScrollbarLayerImplBase* scrollbar,
const gfx::PointF position_in_widget,
- bool* clipped) {
+ bool* clipped) const {
gfx::Transform inverse_screen_space_transform(
gfx::Transform::kSkipInitialization);
@@ -647,7 +674,7 @@ gfx::PointF ScrollbarController::GetScrollbarRelativePosition(
? 1.f / layer_tree_host_impl_->active_tree()->device_scale_factor()
: 1.f;
gfx::Transform scaled_screen_space_transform(
- scrollbar->ScreenSpaceTransform());
+ ScrollbarLayer()->ScreenSpaceTransform());
scaled_screen_space_transform.PostScale(scale, scale);
if (!scaled_screen_space_transform.GetInverse(
&inverse_screen_space_transform))
@@ -659,14 +686,14 @@ gfx::PointF ScrollbarController::GetScrollbarRelativePosition(
// Determines the ScrollbarPart based on the position_in_widget.
ScrollbarPart ScrollbarController::GetScrollbarPartFromPointerDown(
- const ScrollbarLayerImplBase* scrollbar,
- const gfx::PointF position_in_widget) {
+ const gfx::PointF position_in_widget) const {
// position_in_widget needs to be transformed and made relative to the
// scrollbar layer because hit testing assumes layer relative coordinates.
+ const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
bool clipped = false;
const gfx::PointF scroller_relative_position(
- GetScrollbarRelativePosition(scrollbar, position_in_widget, &clipped));
+ GetScrollbarRelativePosition(position_in_widget, &clipped));
if (clipped)
return ScrollbarPart::NO_PART;
@@ -676,8 +703,8 @@ ScrollbarPart ScrollbarController::GetScrollbarPartFromPointerDown(
// Determines the corresponding rect for the given scrollbar part.
gfx::Rect ScrollbarController::GetRectForScrollbarPart(
- const ScrollbarLayerImplBase* scrollbar,
- const ScrollbarPart scrollbar_part) {
+ const ScrollbarPart scrollbar_part) const {
+ const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
if (scrollbar_part == ScrollbarPart::BACK_BUTTON)
return scrollbar->BackButtonRect();
if (scrollbar_part == ScrollbarPart::FORWARD_BUTTON)
@@ -692,11 +719,11 @@ gfx::Rect ScrollbarController::GetRectForScrollbarPart(
// Determines the scroll offsets based on the ScrollbarPart and the scrollbar
// orientation.
gfx::ScrollOffset ScrollbarController::GetScrollOffsetForScrollbarPart(
- const ScrollbarLayerImplBase* scrollbar,
const ScrollbarPart scrollbar_part,
- const bool shift_modifier) {
+ const bool jump_key_modifier) const {
+ const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
float scroll_delta =
- GetScrollDeltaForScrollbarPart(scrollbar, scrollbar_part, shift_modifier);
+ GetScrollDeltaForScrollbarPart(scrollbar_part, jump_key_modifier);
// See CreateScrollStateForGesture for more information on how these values
// will be interpreted.
diff --git a/chromium/cc/input/scrollbar_controller.h b/chromium/cc/input/scrollbar_controller.h
index 4177c3464e5..ebe4dde9f09 100644
--- a/chromium/cc/input/scrollbar_controller.h
+++ b/chromium/cc/input/scrollbar_controller.h
@@ -119,24 +119,31 @@ class CC_EXPORT ScrollbarController {
explicit ScrollbarController(LayerTreeHostImpl*);
virtual ~ScrollbarController();
+ // On Mac, the "jump to the spot that's clicked" setting can be dynamically
+ // set via System Preferences. When enabled, the expectation is that regular
+ // clicks on the scrollbar should make the scroller "jump" to the clicked
+ // location rather than animated scrolling. Additionally, when this is enabled
+ // and the user does an Option + click on the scrollbar, the scroller should
+ // *not* jump to that spot (i.e it should be treated as a regular track
+ // click). When this setting is disabled on the Mac, Option + click should
+ // make the scroller jump and a regular click should animate the scroll
+ // offset. On all other platforms, the "jump on click" option is available
+ // (via Shift + click) but is not configurable.
InputHandlerPointerResult HandlePointerDown(
const gfx::PointF position_in_widget,
- const bool shift_modifier);
+ const bool jump_key_modifier);
InputHandlerPointerResult HandlePointerMove(
const gfx::PointF position_in_widget);
InputHandlerPointerResult HandlePointerUp(
const gfx::PointF position_in_widget);
- // "velocity" here is calculated based on the initial scroll delta (See
- // InitialDeltaToAutoscrollVelocity). This value carries a "sign" which is
- // needed to determine whether we should set up the autoscrolling in the
- // forwards or the backwards direction.
- void StartAutoScrollAnimation(float velocity,
- const ScrollbarLayerImplBase* scrollbar,
- ScrollbarPart pressed_scrollbar_part);
- bool ScrollbarScrollIsActive() { return scrollbar_scroll_is_active_; }
- void DidUnregisterScrollbar(ElementId element_id);
- ScrollbarLayerImplBase* ScrollbarLayer();
+ bool AutoscrollTaskIsScheduled() const {
+ return cancelable_autoscroll_task_ != nullptr;
+ }
+ bool ScrollbarScrollIsActive() const { return scrollbar_scroll_is_active_; }
+ void DidUnregisterScrollbar(ElementId element_id,
+ ScrollbarOrientation orientation);
+ ScrollbarLayerImplBase* ScrollbarLayer() const;
void WillBeginImplFrame();
void ResetState();
@@ -187,44 +194,42 @@ class CC_EXPORT ScrollbarController {
ScrollbarOrientation orientation;
};
+ // "velocity" here is calculated based on the initial scroll delta (See
+ // InitialDeltaToAutoscrollVelocity). This value carries a "sign" which is
+ // needed to determine whether we should set up the autoscrolling in the
+ // forwards or the backwards direction.
+ void StartAutoScrollAnimation(float velocity,
+ ScrollbarPart pressed_scrollbar_part);
+
// Returns the DSF based on whether use-zoom-for-dsf is enabled.
float ScreenSpaceScaleFactor() const;
// Helper to convert scroll offset to autoscroll velocity.
- float InitialDeltaToAutoscrollVelocity(
- const ScrollbarLayerImplBase* scrollbar,
- gfx::ScrollOffset scroll_offset) const;
+ float InitialDeltaToAutoscrollVelocity(gfx::ScrollOffset scroll_offset) const;
// Returns the hit tested ScrollbarPart based on the position_in_widget.
ScrollbarPart GetScrollbarPartFromPointerDown(
- const ScrollbarLayerImplBase* scrollbar,
- const gfx::PointF position_in_widget);
+ const gfx::PointF position_in_widget) const;
// Returns scroll offsets based on which ScrollbarPart was hit tested.
gfx::ScrollOffset GetScrollOffsetForScrollbarPart(
- const ScrollbarLayerImplBase* scrollbar,
const ScrollbarPart scrollbar_part,
- const bool shift_modifier);
+ const bool jump_key_modifier) const;
// Returns the rect for the ScrollbarPart.
- gfx::Rect GetRectForScrollbarPart(const ScrollbarLayerImplBase* scrollbar,
- const ScrollbarPart scrollbar_part);
+ gfx::Rect GetRectForScrollbarPart(const ScrollbarPart scrollbar_part) const;
- LayerImpl* GetLayerHitByPoint(const gfx::PointF position_in_widget);
- int GetScrollDeltaForScrollbarPart(const ScrollbarLayerImplBase* scrollbar,
- const ScrollbarPart scrollbar_part,
- const bool shift_modifier);
+ LayerImpl* GetLayerHitByPoint(const gfx::PointF position_in_widget) const;
+ int GetScrollDeltaForScrollbarPart(const ScrollbarPart scrollbar_part,
+ const bool jump_key_modifier) const;
// Makes position_in_widget relative to the scrollbar.
- gfx::PointF GetScrollbarRelativePosition(
- const ScrollbarLayerImplBase* scrollbar,
- const gfx::PointF position_in_widget,
- bool* clipped);
+ gfx::PointF GetScrollbarRelativePosition(const gfx::PointF position_in_widget,
+ bool* clipped) const;
// Decides if the scroller should snap to the offset that it was originally at
// (i.e the offset before the thumb drag).
- bool SnapToDragOrigin(const ScrollbarLayerImplBase* scrollbar,
- const gfx::PointF pointer_position_in_widget);
+ bool SnapToDragOrigin(const gfx::PointF pointer_position_in_widget) const;
// Decides whether a track autoscroll should be aborted (or restarted) due to
// the thumb reaching the pointer or the pointer leaving (or re-entering) the
@@ -233,22 +238,24 @@ class CC_EXPORT ScrollbarController {
// Shift (or "Option" in case of Mac) + click is expected to do a non-animated
// jump to a certain offset.
- float GetScrollDeltaForAbsoluteJump(const ScrollbarLayerImplBase* scrollbar);
+ float GetScrollDeltaForAbsoluteJump() const;
// Determines if the delta needs to be animated.
ui::ScrollGranularity Granularity(const ScrollbarPart scrollbar_part,
- bool shift_modifier);
+ bool jump_key_modifier) const;
// Calculates the delta based on position_in_widget and drag_origin.
int GetScrollDeltaForDragPosition(
- const ScrollbarLayerImplBase* scrollbar,
- const gfx::PointF pointer_position_in_widget);
+ const gfx::PointF pointer_position_in_widget) const;
// Returns the ratio of the scroller length to the scrollbar length. This is
// needed to scale the scroll delta for thumb drag.
- float GetScrollerToScrollbarRatio(const ScrollbarLayerImplBase* scrollbar);
+ float GetScrollerToScrollbarRatio() const;
+
+ int GetViewportLength() const;
- int GetViewportLength(const ScrollbarLayerImplBase* scrollbar) const;
+ // Returns the pixel delta for a percent-based scroll of the scrollbar
+ int GetScrollDeltaForPercentBasedScroll() const;
LayerTreeHostImpl* layer_tree_host_impl_;
diff --git a/chromium/cc/input/touch_action.h b/chromium/cc/input/touch_action.h
index f00d263acea..e873bd02d62 100644
--- a/chromium/cc/input/touch_action.h
+++ b/chromium/cc/input/touch_action.h
@@ -8,7 +8,7 @@
#include <cstdlib>
#include <string>
-#include "base/logging.h"
+#include "base/notreached.h"
namespace cc {
diff --git a/chromium/cc/layers/deadline_policy.h b/chromium/cc/layers/deadline_policy.h
index 6c3b0063046..6a461e3c235 100644
--- a/chromium/cc/layers/deadline_policy.h
+++ b/chromium/cc/layers/deadline_policy.h
@@ -7,7 +7,7 @@
#include <cstdint>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/optional.h"
#include "cc/cc_export.h"
diff --git a/chromium/cc/layers/draw_properties.h b/chromium/cc/layers/draw_properties.h
index b241728724c..d5a0a7d84a4 100644
--- a/chromium/cc/layers/draw_properties.h
+++ b/chromium/cc/layers/draw_properties.h
@@ -53,9 +53,9 @@ struct CC_EXPORT DrawProperties {
// the layer's coordinate space.
gfx::Rect visible_layer_rect;
- // In target surface space, the rect that encloses the clipped, drawable
- // content of the layer.
- gfx::Rect drawable_content_rect;
+ // In target surface space, the rect that encloses the clipped, visible,
+ // and drawable content of the layer.
+ gfx::Rect visible_drawable_content_rect;
// In target surface space, the original rect that clipped this layer. This
// value is used to avoid unnecessarily changing GL scissor state.
diff --git a/chromium/cc/layers/heads_up_display_layer_impl.cc b/chromium/cc/layers/heads_up_display_layer_impl.cc
index 6851763975f..b539a5da7c7 100644
--- a/chromium/cc/layers/heads_up_display_layer_impl.cc
+++ b/chromium/cc/layers/heads_up_display_layer_impl.cc
@@ -10,6 +10,7 @@
#include <algorithm>
#include <vector>
+#include "base/logging.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/numerics/safe_conversions.h"
#include "base/optional.h"
@@ -19,6 +20,7 @@
#include "base/trace_event/trace_event.h"
#include "base/trace_event/traced_value.h"
#include "cc/debug/debug_colors.h"
+#include "cc/metrics/dropped_frame_counter.h"
#include "cc/paint/display_item_list.h"
#include "cc/paint/paint_canvas.h"
#include "cc/paint/paint_flags.h"
@@ -27,7 +29,6 @@
#include "cc/paint/skia_paint_canvas.h"
#include "cc/raster/scoped_gpu_raster.h"
#include "cc/resources/memory_history.h"
-#include "cc/trees/frame_rate_counter.h"
#include "cc/trees/layer_tree_frame_sink.h"
#include "cc/trees/layer_tree_host_impl.h"
#include "cc/trees/layer_tree_impl.h"
@@ -88,27 +89,9 @@ class DummyImageProvider : public ImageProvider {
} // namespace
-HeadsUpDisplayLayerImpl::Graph::Graph(double indicator_value,
- double start_upper_bound)
- : value(0.0),
- min(0.0),
- max(0.0),
- current_upper_bound(start_upper_bound),
- default_upper_bound(start_upper_bound),
- indicator(indicator_value) {}
-
-double HeadsUpDisplayLayerImpl::Graph::UpdateUpperBound() {
- double target_upper_bound = std::max(max, default_upper_bound);
- current_upper_bound += (target_upper_bound - current_upper_bound) * 0.5;
- return current_upper_bound;
-}
-
HeadsUpDisplayLayerImpl::HeadsUpDisplayLayerImpl(LayerTreeImpl* tree_impl,
int id)
- : LayerImpl(tree_impl, id),
- internal_contents_scale_(1.f),
- fps_graph_(60.0, 80.0),
- paint_time_graph_(16.0, 48.0) {}
+ : LayerImpl(tree_impl, id) {}
HeadsUpDisplayLayerImpl::~HeadsUpDisplayLayerImpl() {
ReleaseResources();
@@ -550,18 +533,8 @@ void HeadsUpDisplayLayerImpl::UpdateHudContents() {
time_of_last_graph_update_ = now;
if (debug_state.show_fps_counter) {
- FrameRateCounter* fps_counter = layer_tree_impl()->frame_rate_counter();
- fps_graph_.value = fps_counter->GetAverageFPS();
- fps_counter->GetMinAndMaxFPS(&fps_graph_.min, &fps_graph_.max);
- current_throughput_ = layer_tree_impl()->current_universal_throughput();
- if (current_throughput_.has_value()) {
- if (!max_throughput.has_value() ||
- current_throughput_.value() > max_throughput.value())
- max_throughput = current_throughput_;
- if (!min_throughput.has_value() ||
- current_throughput_.value() < min_throughput.value())
- min_throughput = current_throughput_;
- }
+ throughput_value_ =
+ layer_tree_impl()->dropped_frame_counter()->GetAverageThroughput();
}
if (debug_state.ShowMemoryStats()) {
@@ -572,9 +545,6 @@ void HeadsUpDisplayLayerImpl::UpdateHudContents() {
memory_entry_ = MemoryHistory::Entry();
}
}
-
- fps_graph_.UpdateUpperBound();
- paint_time_graph_.UpdateUpperBound();
}
void HeadsUpDisplayLayerImpl::DrawHudContents(PaintCanvas* canvas) {
@@ -597,8 +567,8 @@ void HeadsUpDisplayLayerImpl::DrawHudContents(PaintCanvas* canvas) {
return;
}
- SkRect area =
- DrawFPSDisplay(canvas, layer_tree_impl()->frame_rate_counter(), 0, 0);
+ SkRect area = DrawFrameThroughputDisplay(
+ canvas, layer_tree_impl()->dropped_frame_counter(), 0, 0);
area = DrawGpuRasterizationStatus(canvas, 0, area.bottom(),
std::max<SkScalar>(area.width(), 150));
@@ -652,30 +622,18 @@ void HeadsUpDisplayLayerImpl::DrawGraphBackground(PaintCanvas* canvas,
void HeadsUpDisplayLayerImpl::DrawGraphLines(PaintCanvas* canvas,
PaintFlags* flags,
- const SkRect& bounds,
- const Graph& graph) const {
+ const SkRect& bounds) const {
// Draw top and bottom line.
flags->setColor(DebugColors::HUDSeparatorLineColor());
canvas->drawLine(bounds.left(), bounds.top() - 1, bounds.right(),
bounds.top() - 1, *flags);
canvas->drawLine(bounds.left(), bounds.bottom(), bounds.right(),
bounds.bottom(), *flags);
-
- // Draw indicator line (additive blend mode to increase contrast when drawn on
- // top of graph).
- flags->setColor(DebugColors::HUDIndicatorLineColor());
- flags->setBlendMode(SkBlendMode::kPlus);
- const double indicator_top =
- bounds.height() * (1.0 - graph.indicator / graph.current_upper_bound) -
- 1.0;
- canvas->drawLine(bounds.left(), bounds.top() + indicator_top, bounds.right(),
- bounds.top() + indicator_top, *flags);
- flags->setBlendMode(SkBlendMode::kSrcOver);
}
-SkRect HeadsUpDisplayLayerImpl::DrawFPSDisplay(
+SkRect HeadsUpDisplayLayerImpl::DrawFrameThroughputDisplay(
PaintCanvas* canvas,
- const FrameRateCounter* fps_counter,
+ const DroppedFrameCounter* dropped_frame_counter,
int right,
int top) const {
const int kPadding = 4;
@@ -685,39 +643,35 @@ SkRect HeadsUpDisplayLayerImpl::DrawFPSDisplay(
const int kFontHeight = 12;
const int kGraphWidth =
- base::saturated_cast<int>(fps_counter->time_stamp_history_size()) - 2;
+ base::saturated_cast<int>(dropped_frame_counter->frame_history_size());
const int kGraphHeight = 40;
- const int kHistogramWidth = 37;
-
- int width = kGraphWidth + kHistogramWidth + 4 * kPadding;
- int height =
- 2 * kTitleFontHeight + 2 * kFontHeight + kGraphHeight + 10 * kPadding + 2;
+ int width = kGraphWidth + 4 * kPadding;
+ int height = kTitleFontHeight + kFontHeight + kGraphHeight + 6 * kPadding + 2;
int left = 0;
SkRect area = SkRect::MakeXYWH(left, top, width, height);
PaintFlags flags;
DrawGraphBackground(canvas, &flags, area);
- SkRect title_bounds = SkRect::MakeXYWH(
- left + kPadding, top + kPadding, kGraphWidth + kHistogramWidth + kGap + 2,
- kTitleFontHeight);
+ SkRect title_bounds =
+ SkRect::MakeXYWH(left + kPadding, top + kPadding, kGraphWidth + kGap + 2,
+ kTitleFontHeight);
SkRect text_bounds =
SkRect::MakeXYWH(left + kPadding, title_bounds.bottom() + 2 * kPadding,
- kGraphWidth + kHistogramWidth + kGap + 2, kFontHeight);
+ kGraphWidth + kGap + 2, kFontHeight);
SkRect graph_bounds =
SkRect::MakeXYWH(left + kPadding, text_bounds.bottom() + 2 * kPadding,
kGraphWidth, kGraphHeight);
- SkRect histogram_bounds =
- SkRect::MakeXYWH(graph_bounds.right() + kGap, graph_bounds.top(),
- kHistogramWidth, kGraphHeight);
// Draw the fps meter.
- const std::string title("Frame Rate");
- const std::string value_text =
- base::StringPrintf("%5.1f fps", fps_graph_.value);
- const std::string min_max_text =
- base::StringPrintf("%.0f-%.0f", fps_graph_.min, fps_graph_.max);
+ const std::string title("Frames");
+ const std::string value_text = base::StringPrintf("%d %%", throughput_value_);
+ const std::string dropped_frames_text =
+ base::StringPrintf("%zu (%zu m) dropped of %zu",
+ dropped_frame_counter->total_compositor_dropped(),
+ dropped_frame_counter->total_main_dropped(),
+ dropped_frame_counter->total_frames());
VLOG(1) << value_text;
@@ -728,105 +682,40 @@ SkRect HeadsUpDisplayLayerImpl::DrawFPSDisplay(
flags.setColor(DebugColors::FPSDisplayTextAndGraphColor());
DrawText(canvas, flags, value_text, TextAlign::kLeft, kFontHeight,
text_bounds.left(), text_bounds.bottom());
- DrawText(canvas, flags, min_max_text, TextAlign::kRight, kFontHeight,
+ DrawText(canvas, flags, dropped_frames_text, TextAlign::kRight, kFontHeight,
text_bounds.right(), text_bounds.bottom());
- DrawGraphLines(canvas, &flags, graph_bounds, fps_graph_);
-
- // Draw the throughput meter.
- int current_top = histogram_bounds.bottom() + kPadding;
- const std::string throughput_title("Throughput");
- const std::string throughput_value_text =
- current_throughput_.has_value()
- ? base::StringPrintf("%d %%", current_throughput_.value())
- : base::StringPrintf("n/a");
-
- VLOG(1) << throughput_value_text;
-
- flags.setColor(DebugColors::HUDTitleColor());
- DrawText(canvas, flags, throughput_title, TextAlign::kLeft, kTitleFontHeight,
- title_bounds.left(), title_bounds.bottom() + current_top);
-
- flags.setColor(DebugColors::FPSDisplayTextAndGraphColor());
- DrawText(canvas, flags, throughput_value_text, TextAlign::kLeft, kFontHeight,
- text_bounds.left(), text_bounds.bottom() + current_top);
- if (min_throughput.has_value()) {
- const std::string throughput_min_max_text = base::StringPrintf(
- "%d-%d", min_throughput.value(), max_throughput.value());
- DrawText(canvas, flags, throughput_min_max_text, TextAlign::kRight,
- kFontHeight, text_bounds.right(),
- text_bounds.bottom() + current_top);
- }
-
- // Collect fps graph and histogram data.
- SkPath path;
-
- const int kHistogramSize = 20;
- double histogram[kHistogramSize] = {1.0};
- double max_bucket_value = 1.0;
-
- for (FrameRateCounter::RingBufferType::Iterator it = --fps_counter->end(); it;
- --it) {
- base::TimeDelta delta = fps_counter->RecentFrameInterval(it.index() + 1);
-
- // Skip this particular instantaneous frame rate if it is not likely to have
- // been valid.
- if (!fps_counter->IsBadFrameInterval(delta)) {
- double fps = 1.0 / delta.InSecondsF();
-
- // Clamp the FPS to the range we want to plot visually.
- double p = fps / fps_graph_.current_upper_bound;
- if (p > 1.0)
- p = 1.0;
-
- // Plot this data point.
- SkPoint cur =
- SkPoint::Make(graph_bounds.left() + it.index(),
- graph_bounds.bottom() - p * graph_bounds.height());
- if (path.isEmpty())
- path.moveTo(cur);
- else
- path.lineTo(cur);
-
- // Use the fps value to find the right bucket in the histogram.
- int bucket_index = floor(p * (kHistogramSize - 1));
-
- // Add the delta time to take the time spent at that fps rate into
- // account.
- histogram[bucket_index] += delta.InSecondsF();
- max_bucket_value = std::max(histogram[bucket_index], max_bucket_value);
- }
- }
-
- // Draw FPS histogram.
- flags.setColor(DebugColors::HUDSeparatorLineColor());
- canvas->drawLine(histogram_bounds.left() - 1, histogram_bounds.top() - 1,
- histogram_bounds.left() - 1, histogram_bounds.bottom() + 1,
- flags);
- canvas->drawLine(histogram_bounds.right() + 1, histogram_bounds.top() - 1,
- histogram_bounds.right() + 1, histogram_bounds.bottom() + 1,
- flags);
-
- flags.setColor(DebugColors::FPSDisplayTextAndGraphColor());
- const double bar_height = histogram_bounds.height() / kHistogramSize;
-
- for (int i = kHistogramSize - 1; i >= 0; --i) {
- if (histogram[i] > 0) {
- double bar_width =
- histogram[i] / max_bucket_value * histogram_bounds.width();
- canvas->drawRect(
- SkRect::MakeXYWH(histogram_bounds.left(),
- histogram_bounds.bottom() - (i + 1) * bar_height,
- bar_width, 1),
- flags);
- }
+ DrawGraphLines(canvas, &flags, graph_bounds);
+
+ // Collect the frames graph data.
+ SkPath good_path;
+ SkPath dropped_path;
+ SkPath partial_path;
+ for (auto it = --dropped_frame_counter->end(); it; --it) {
+ const auto state = **it;
+ int x = graph_bounds.left() + it.index();
+ SkPath& path = state == DroppedFrameCounter::kFrameStateDropped
+ ? dropped_path
+ : state == DroppedFrameCounter::kFrameStateComplete
+ ? good_path
+ : partial_path;
+ path.moveTo(x, graph_bounds.top());
+ path.lineTo(x, graph_bounds.bottom());
}
// Draw FPS graph.
flags.setAntiAlias(true);
flags.setStyle(PaintFlags::kStroke_Style);
flags.setStrokeWidth(1);
- canvas->drawPath(path, flags);
+
+ flags.setColor(SkColorSetA(SK_ColorGREEN, 128));
+ canvas->drawPath(good_path, flags);
+
+ flags.setColor(SkColorSetA(SK_ColorRED, 128));
+ canvas->drawPath(dropped_path, flags);
+
+ flags.setColor(SkColorSetA(SK_ColorYELLOW, 255));
+ canvas->drawPath(partial_path, flags);
return area;
}
diff --git a/chromium/cc/layers/heads_up_display_layer_impl.h b/chromium/cc/layers/heads_up_display_layer_impl.h
index 323da1fff87..417d5a0d9b8 100644
--- a/chromium/cc/layers/heads_up_display_layer_impl.h
+++ b/chromium/cc/layers/heads_up_display_layer_impl.h
@@ -26,7 +26,7 @@ class ClientResourceProvider;
}
namespace cc {
-class FrameRateCounter;
+class DroppedFrameCounter;
class LayerTreeFrameSink;
class PaintCanvas;
class PaintFlags;
@@ -76,24 +76,6 @@ class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl {
void PushPropertiesTo(LayerImpl* layer) override;
private:
- class Graph {
- public:
- Graph(double indicator_value, double start_upper_bound);
-
- // Eases the upper bound, which limits what is currently visible in the
- // graph, so that the graph always scales to either it's max or
- // default_upper_bound.
- double UpdateUpperBound();
-
- double value;
- double min;
- double max;
-
- double current_upper_bound;
- const double default_upper_bound;
- const double indicator;
- };
-
HeadsUpDisplayLayerImpl(LayerTreeImpl* tree_impl, int id);
const char* LayerTypeAsString() const override;
@@ -120,13 +102,13 @@ class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl {
const SkRect& bounds) const;
void DrawGraphLines(PaintCanvas* canvas,
PaintFlags* flags,
- const SkRect& bounds,
- const Graph& graph) const;
+ const SkRect& bounds) const;
- SkRect DrawFPSDisplay(PaintCanvas* canvas,
- const FrameRateCounter* fps_counter,
- int right,
- int top) const;
+ SkRect DrawFrameThroughputDisplay(
+ PaintCanvas* canvas,
+ const DroppedFrameCounter* dropped_frame_counter,
+ int right,
+ int top) const;
SkRect DrawMemoryDisplay(PaintCanvas* canvas,
int top,
int right,
@@ -154,21 +136,15 @@ class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl {
sk_sp<SkTypeface> typeface_;
std::vector<gfx::Rect> layout_shift_rects_;
- float internal_contents_scale_;
+ float internal_contents_scale_ = 1.0f;
gfx::Size internal_content_bounds_;
- Graph fps_graph_;
- Graph paint_time_graph_;
+ uint32_t throughput_value_ = 0.0f;
MemoryHistory::Entry memory_entry_;
int paint_rects_fade_step_ = 0;
int layout_shift_rects_fade_step_ = 0;
std::vector<DebugRect> paint_rects_;
std::vector<DebugRect> layout_shift_debug_rects_;
- base::Optional<int> current_throughput_;
- // The worst and best throughput we have seen so far, they either both have no
- // value, or both have value.
- base::Optional<int> min_throughput;
- base::Optional<int> max_throughput;
base::TimeTicks time_of_last_graph_update_;
};
diff --git a/chromium/cc/layers/layer.cc b/chromium/cc/layers/layer.cc
index c354081f17b..7b74ae2da3c 100644
--- a/chromium/cc/layers/layer.cc
+++ b/chromium/cc/layers/layer.cc
@@ -78,6 +78,7 @@ Layer::Inputs::Inputs(int layer_id)
: layer_id(layer_id),
hit_testable(false),
contents_opaque(false),
+ contents_opaque_for_text(false),
is_drawable(false),
double_sided(true),
has_will_change_transform_hint(false),
@@ -804,11 +805,21 @@ void Layer::SetContentsOpaque(bool opaque) {
if (inputs_.contents_opaque == opaque)
return;
inputs_.contents_opaque = opaque;
+ inputs_.contents_opaque_for_text = opaque;
SetNeedsCommit();
SetSubtreePropertyChanged();
SetPropertyTreesNeedRebuild();
}
+void Layer::SetContentsOpaqueForText(bool opaque) {
+ DCHECK(IsPropertyChangeAllowed());
+ if (inputs_.contents_opaque_for_text == opaque)
+ return;
+ DCHECK(!contents_opaque() || opaque);
+ inputs_.contents_opaque_for_text = opaque;
+ SetNeedsCommit();
+}
+
void Layer::SetPosition(const gfx::PointF& position) {
DCHECK(!layer_tree_host_ || !layer_tree_host_->IsUsingLayerLists());
@@ -1321,6 +1332,7 @@ void Layer::PushPropertiesTo(LayerImpl* layer) {
layer->SetWheelEventHandlerRegion(Region());
}
layer->SetContentsOpaque(inputs_.contents_opaque);
+ layer->SetContentsOpaqueForText(inputs_.contents_opaque_for_text);
layer->SetShouldCheckBackfaceVisibility(should_check_backface_visibility_);
layer->UpdateScrollable();
diff --git a/chromium/cc/layers/layer.h b/chromium/cc/layers/layer.h
index 43debb199e8..a2da20587d1 100644
--- a/chromium/cc/layers/layer.h
+++ b/chromium/cc/layers/layer.h
@@ -333,9 +333,24 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> {
// must be opaque or visual errors can occur. This applies only to this layer
// and not to children, and does not imply the layer should be composited
// opaquely, as effects may be applied such as opacity() or filters().
+ // Note that this also calls SetContentsOpaqueForText(opaque) internally.
+ // To override a different contents_opaque_for_text, the client should call
+ // SetContentsOpaqueForText() after SetContentsOpaque().
void SetContentsOpaque(bool opaque);
bool contents_opaque() const { return inputs_.contents_opaque; }
+ // Whether the contents area containing text is known to be opaque.
+ // For example, blink will SetContentsOpaque(false) but
+ // SetContentsOpaqueForText(true) for the following case:
+ // <div style="overflow: hidden; border-radius: 10px; background: white">
+ // TEXT
+ // </div>
+ // See also the note for SetContentsOpaque().
+ void SetContentsOpaqueForText(bool opaque);
+ bool contents_opaque_for_text() const {
+ return inputs_.contents_opaque_for_text;
+ }
+
// Set or get whether this layer should be a hit test target
void SetHitTestable(bool should_hit_test);
virtual bool HitTestable() const;
@@ -829,6 +844,7 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> {
bool hit_testable : 1;
bool contents_opaque : 1;
+ bool contents_opaque_for_text : 1;
bool is_drawable : 1;
bool double_sided : 1;
diff --git a/chromium/cc/layers/layer_impl.cc b/chromium/cc/layers/layer_impl.cc
index bb1c93f2538..318c2cbc1ee 100644
--- a/chromium/cc/layers/layer_impl.cc
+++ b/chromium/cc/layers/layer_impl.cc
@@ -55,6 +55,7 @@ LayerImpl::LayerImpl(LayerTreeImpl* tree_impl,
layer_property_changed_from_property_trees_(false),
may_contain_video_(false),
contents_opaque_(false),
+ contents_opaque_for_text_(false),
should_check_backface_visibility_(false),
draws_content_(false),
contributes_to_drawn_render_surface_(false),
@@ -374,6 +375,7 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) {
layer->has_transform_node_ = has_transform_node_;
layer->offset_to_transform_parent_ = offset_to_transform_parent_;
layer->contents_opaque_ = contents_opaque_;
+ layer->contents_opaque_for_text_ = contents_opaque_for_text_;
layer->may_contain_video_ = may_contain_video_;
layer->should_check_backface_visibility_ = should_check_backface_visibility_;
layer->draws_content_ = draws_content_;
@@ -571,6 +573,12 @@ SkColor LayerImpl::SafeOpaqueBackgroundColor() const {
void LayerImpl::SetContentsOpaque(bool opaque) {
contents_opaque_ = opaque;
+ contents_opaque_for_text_ = opaque;
+}
+
+void LayerImpl::SetContentsOpaqueForText(bool opaque) {
+ DCHECK(!contents_opaque_ || opaque);
+ contents_opaque_for_text_ = opaque;
}
float LayerImpl::Opacity() const {
diff --git a/chromium/cc/layers/layer_impl.h b/chromium/cc/layers/layer_impl.h
index 95eb4828df8..006057b5b12 100644
--- a/chromium/cc/layers/layer_impl.h
+++ b/chromium/cc/layers/layer_impl.h
@@ -14,7 +14,7 @@
#include <string>
#include <vector>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/memory/ptr_util.h"
#include "cc/base/region.h"
#include "cc/base/synced_property.h"
@@ -165,8 +165,12 @@ class CC_EXPORT LayerImpl {
// non-opaque color. Tries to return background_color(), if possible.
SkColor SafeOpaqueBackgroundColor() const;
+ // See Layer::SetContentsOpaque() and SetContentsOpaqueForText() for the
+ // relationship between the two flags.
void SetContentsOpaque(bool opaque);
bool contents_opaque() const { return contents_opaque_; }
+ void SetContentsOpaqueForText(bool opaque);
+ bool contents_opaque_for_text() const { return contents_opaque_for_text_; }
float Opacity() const;
@@ -221,8 +225,8 @@ class CC_EXPORT LayerImpl {
return draw_properties_.screen_space_transform_is_animating;
}
gfx::Rect clip_rect() const { return draw_properties_.clip_rect; }
- gfx::Rect drawable_content_rect() const {
- return draw_properties_.drawable_content_rect;
+ gfx::Rect visible_drawable_content_rect() const {
+ return draw_properties_.visible_drawable_content_rect;
}
gfx::Rect visible_layer_rect() const {
return draw_properties_.visible_layer_rect;
@@ -475,6 +479,7 @@ class CC_EXPORT LayerImpl {
bool may_contain_video_ : 1;
bool contents_opaque_ : 1;
+ bool contents_opaque_for_text_ : 1;
bool should_check_backface_visibility_ : 1;
bool draws_content_ : 1;
bool contributes_to_drawn_render_surface_ : 1;
diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc
index c49c99b106f..1d975284495 100644
--- a/chromium/cc/layers/layer_unittest.cc
+++ b/chromium/cc/layers/layer_unittest.cc
@@ -17,6 +17,7 @@
#include "cc/layers/picture_layer.h"
#include "cc/layers/solid_color_scrollbar_layer.h"
#include "cc/test/animation_test_common.h"
+#include "cc/test/cc_test_suite.h"
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_impl_task_runner_provider.h"
#include "cc/test/fake_layer_tree_host.h"
@@ -1388,15 +1389,23 @@ TEST_F(LayerTest, DedupesCopyOutputRequestsBySource) {
viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
base::BindOnce(&ReceiveCopyOutputResult, &result_count));
layer->RequestCopyOfOutput(std::move(request));
+ // Because RequestCopyOfOutput could run as a PostTask to return results
+ // RunUntilIdle() to ensure that the result is not returned yet.
+ CCTestSuite::RunUntilIdle();
EXPECT_EQ(0, result_count);
request = std::make_unique<viz::CopyOutputRequest>(
viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
base::BindOnce(&ReceiveCopyOutputResult, &result_count));
layer->RequestCopyOfOutput(std::move(request));
+ // Because RequestCopyOfOutput could run as a PostTask to return results
+ // RunUntilIdle() to ensure that the result is not returned yet.
+ CCTestSuite::RunUntilIdle();
EXPECT_EQ(0, result_count);
// When the layer is destroyed, expect both requests to be aborted.
layer = nullptr;
+ // Wait for any posted tasks to run so the results will be returned.
+ CCTestSuite::RunUntilIdle();
EXPECT_EQ(2, result_count);
layer = Layer::Create();
@@ -1412,6 +1421,9 @@ TEST_F(LayerTest, DedupesCopyOutputRequestsBySource) {
&did_receive_first_result_from_this_source));
request->set_source(kArbitrarySourceId1);
layer->RequestCopyOfOutput(std::move(request));
+ // Because RequestCopyOfOutput could run as a PostTask to return results
+ // RunUntilIdle() to ensure that the result is not returned yet.
+ CCTestSuite::RunUntilIdle();
EXPECT_EQ(0, did_receive_first_result_from_this_source);
// Make a request from a different source.
int did_receive_result_from_different_source = 0;
@@ -1421,6 +1433,9 @@ TEST_F(LayerTest, DedupesCopyOutputRequestsBySource) {
&did_receive_result_from_different_source));
request->set_source(kArbitrarySourceId2);
layer->RequestCopyOfOutput(std::move(request));
+ // Because RequestCopyOfOutput could run as a PostTask to return results
+ // RunUntilIdle() to ensure that the result is not returned yet.
+ CCTestSuite::RunUntilIdle();
EXPECT_EQ(0, did_receive_result_from_different_source);
// Make a request without specifying the source.
int did_receive_result_from_anonymous_source = 0;
@@ -1429,6 +1444,9 @@ TEST_F(LayerTest, DedupesCopyOutputRequestsBySource) {
base::BindOnce(&ReceiveCopyOutputResult,
&did_receive_result_from_anonymous_source));
layer->RequestCopyOfOutput(std::move(request));
+ // Because RequestCopyOfOutput could run as a PostTask to return results
+ // RunUntilIdle() to ensure that the result is not returned yet.
+ CCTestSuite::RunUntilIdle();
EXPECT_EQ(0, did_receive_result_from_anonymous_source);
// Make the second request from |kArbitrarySourceId1|.
int did_receive_second_result_from_this_source = 0;
@@ -1439,6 +1457,8 @@ TEST_F(LayerTest, DedupesCopyOutputRequestsBySource) {
request->set_source(kArbitrarySourceId1);
layer->RequestCopyOfOutput(
std::move(request)); // First request to be aborted.
+ // Wait for any posted tasks to run so the results will be returned.
+ CCTestSuite::RunUntilIdle();
EXPECT_EQ(1, did_receive_first_result_from_this_source);
EXPECT_EQ(0, did_receive_result_from_different_source);
EXPECT_EQ(0, did_receive_result_from_anonymous_source);
@@ -1446,6 +1466,8 @@ TEST_F(LayerTest, DedupesCopyOutputRequestsBySource) {
// When the layer is destroyed, the other three requests should be aborted.
layer = nullptr;
+ // Wait for any posted tasks to run so the results will be returned.
+ CCTestSuite::RunUntilIdle();
EXPECT_EQ(1, did_receive_first_result_from_this_source);
EXPECT_EQ(1, did_receive_result_from_different_source);
EXPECT_EQ(1, did_receive_result_from_anonymous_source);
@@ -1507,7 +1529,7 @@ TEST_F(LayerTest, SetLayerTreeHostNotUsingLayerListsManagesElementId) {
// Expect additional calls due to has-animation check and initialization
// of keyframes.
- EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(7);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(3);
scoped_refptr<AnimationTimeline> timeline =
AnimationTimeline::Create(AnimationIdProvider::NextTimelineId());
animation_host_->AddAnimationTimeline(timeline);
@@ -1526,6 +1548,19 @@ TEST_F(LayerTest, SetLayerTreeHostNotUsingLayerListsManagesElementId) {
EXPECT_EQ(nullptr, layer_tree_host_->LayerByElementId(element_id));
}
+// Triggering a commit to push animation counts and raf presence to the
+// compositor is expensive and updated counts can wait until the next
+// commit to be pushed. See https://crbug.com/1083244.
+TEST_F(LayerTest, PushAnimationCountsLazily) {
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(0);
+ animation_host_->SetAnimationCounts(0, /* current_frame_had_raf = */ true,
+ /* next_frame_has_pending_raf = */ true);
+ EXPECT_FALSE(host_impl_.animation_host()->CurrentFrameHadRAF());
+ EXPECT_FALSE(animation_host_->needs_push_properties());
+ animation_host_->PushPropertiesTo(host_impl_.animation_host());
+ EXPECT_TRUE(host_impl_.animation_host()->CurrentFrameHadRAF());
+}
+
TEST_F(LayerTest, SetElementIdNotUsingLayerLists) {
scoped_refptr<Layer> test_layer = Layer::Create();
test_layer->SetLayerTreeHost(layer_tree_host_.get());
diff --git a/chromium/cc/layers/mirror_layer_impl.cc b/chromium/cc/layers/mirror_layer_impl.cc
index 7a916bf5d69..5751ccb53b2 100644
--- a/chromium/cc/layers/mirror_layer_impl.cc
+++ b/chromium/cc/layers/mirror_layer_impl.cc
@@ -56,8 +56,7 @@ void MirrorLayerImpl::AppendQuads(viz::RenderPass* render_pass,
quad->SetNew(shared_quad_state, content_rect, unoccluded_content_rect,
mirrored_layer_id_, mask_resource_id, mask_uv_rect,
mask_texture_size, mirrored_effect_node->surface_contents_scale,
- mirrored_effect_node->filters_origin,
- gfx::RectF(gfx::Rect(content_rect.size())),
+ gfx::PointF(), gfx::RectF(gfx::Rect(content_rect.size())),
!layer_tree_impl()->settings().enable_edge_anti_aliasing, 0.f);
}
diff --git a/chromium/cc/layers/painted_scrollbar_layer.cc b/chromium/cc/layers/painted_scrollbar_layer.cc
index 84f6b872302..2be635c50e2 100644
--- a/chromium/cc/layers/painted_scrollbar_layer.cc
+++ b/chromium/cc/layers/painted_scrollbar_layer.cc
@@ -40,6 +40,7 @@ PaintedScrollbarLayer::PaintedScrollbarLayer(scoped_refptr<Scrollbar> scrollbar)
internal_contents_scale_(1.f),
painted_opacity_(scrollbar_->Opacity()),
has_thumb_(scrollbar_->HasThumb()),
+ jump_on_track_click_(scrollbar_->JumpOnTrackClick()),
supports_drag_snap_back_(scrollbar_->SupportsDragSnapBack()),
is_overlay_(scrollbar_->IsOverlay()) {}
@@ -58,6 +59,7 @@ void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
scrollbar_layer->set_internal_contents_scale_and_bounds(
internal_contents_scale_, internal_content_bounds_);
+ scrollbar_layer->SetJumpOnTrackClick(jump_on_track_click_);
scrollbar_layer->SetSupportsDragSnapBack(supports_drag_snap_back_);
scrollbar_layer->SetBackButtonRect(back_button_rect_);
scrollbar_layer->SetForwardButtonRect(forward_button_rect_);
@@ -112,6 +114,7 @@ void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() {
DCHECK_EQ(is_overlay_, scrollbar_->IsOverlay());
DCHECK_EQ(orientation(), scrollbar_->Orientation());
+ UpdateProperty(scrollbar_->JumpOnTrackClick(), &jump_on_track_click_);
UpdateProperty(scrollbar_->TrackRect(), &track_rect_);
UpdateProperty(scrollbar_->BackButtonRect(), &back_button_rect_);
UpdateProperty(scrollbar_->ForwardButtonRect(), &forward_button_rect_);
diff --git a/chromium/cc/layers/painted_scrollbar_layer.h b/chromium/cc/layers/painted_scrollbar_layer.h
index 191c37d8e5a..930c614071e 100644
--- a/chromium/cc/layers/painted_scrollbar_layer.h
+++ b/chromium/cc/layers/painted_scrollbar_layer.h
@@ -86,6 +86,7 @@ class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerBase {
gfx::Rect forward_button_rect_;
float painted_opacity_;
bool has_thumb_;
+ bool jump_on_track_click_;
const bool supports_drag_snap_back_;
const bool is_overlay_;
diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.cc b/chromium/cc/layers/painted_scrollbar_layer_impl.cc
index 70da5b1be8f..6d36154b803 100644
--- a/chromium/cc/layers/painted_scrollbar_layer_impl.cc
+++ b/chromium/cc/layers/painted_scrollbar_layer_impl.cc
@@ -43,6 +43,7 @@ PaintedScrollbarLayerImpl::PaintedScrollbarLayerImpl(
thumb_ui_resource_id_(0),
painted_opacity_(1.f),
internal_contents_scale_(1.f),
+ jump_on_track_click_(false),
supports_drag_snap_back_(false),
thumb_thickness_(0),
thumb_length_(0) {}
@@ -65,6 +66,7 @@ void PaintedScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) {
scrollbar_layer->set_internal_contents_scale_and_bounds(
internal_contents_scale_, internal_content_bounds_);
+ scrollbar_layer->SetJumpOnTrackClick(jump_on_track_click_);
scrollbar_layer->SetSupportsDragSnapBack(supports_drag_snap_back_);
scrollbar_layer->SetThumbThickness(thumb_thickness_);
scrollbar_layer->SetThumbLength(thumb_length_);
@@ -162,6 +164,17 @@ gfx::Rect PaintedScrollbarLayerImpl::GetEnclosingRectInTargetSpace() const {
return GetScaledEnclosingRectInTargetSpace(internal_contents_scale_);
}
+void PaintedScrollbarLayerImpl::SetJumpOnTrackClick(bool jump_on_track_click) {
+ if (jump_on_track_click_ == jump_on_track_click)
+ return;
+ jump_on_track_click_ = jump_on_track_click;
+ NoteLayerPropertyChanged();
+}
+
+bool PaintedScrollbarLayerImpl::JumpOnTrackClick() const {
+ return jump_on_track_click_;
+}
+
void PaintedScrollbarLayerImpl::SetSupportsDragSnapBack(
bool supports_drag_snap_back) {
if (supports_drag_snap_back_ == supports_drag_snap_back)
diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.h b/chromium/cc/layers/painted_scrollbar_layer_impl.h
index e5035e1dc3b..eecf7c9aefd 100644
--- a/chromium/cc/layers/painted_scrollbar_layer_impl.h
+++ b/chromium/cc/layers/painted_scrollbar_layer_impl.h
@@ -38,6 +38,7 @@ class CC_EXPORT PaintedScrollbarLayerImpl : public ScrollbarLayerImplBase {
AppendQuadsData* append_quads_data) override;
gfx::Rect GetEnclosingRectInTargetSpace() const override;
+ void SetJumpOnTrackClick(bool jump_on_track_click);
void SetSupportsDragSnapBack(bool supports_drag_snap_back);
void SetBackButtonRect(gfx::Rect back_button_rect);
void SetForwardButtonRect(gfx::Rect forward_button_rect);
@@ -63,6 +64,7 @@ class CC_EXPORT PaintedScrollbarLayerImpl : public ScrollbarLayerImplBase {
internal_content_bounds_ = content_bounds;
}
+ bool JumpOnTrackClick() const override;
bool SupportsDragSnapBack() const override;
gfx::Rect BackButtonRect() const override;
gfx::Rect ForwardButtonRect() const override;
@@ -98,6 +100,7 @@ class CC_EXPORT PaintedScrollbarLayerImpl : public ScrollbarLayerImplBase {
float internal_contents_scale_;
gfx::Size internal_content_bounds_;
+ bool jump_on_track_click_;
bool supports_drag_snap_back_;
int thumb_thickness_;
int thumb_length_;
diff --git a/chromium/cc/layers/picture_image_layer.cc b/chromium/cc/layers/picture_image_layer.cc
deleted file mode 100644
index 2208b11c6a1..00000000000
--- a/chromium/cc/layers/picture_image_layer.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2010 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/picture_image_layer.h"
-
-#include <stddef.h>
-
-#include "cc/base/math_util.h"
-#include "cc/layers/picture_layer_impl.h"
-#include "cc/paint/paint_op_buffer.h"
-#include "cc/trees/layer_tree_host.h"
-#include "cc/trees/layer_tree_settings.h"
-
-namespace cc {
-
-scoped_refptr<PictureImageLayer> PictureImageLayer::Create() {
- return base::WrapRefCounted(new PictureImageLayer());
-}
-
-PictureImageLayer::PictureImageLayer() : PictureLayer(this) {}
-
-PictureImageLayer::~PictureImageLayer() {
- ClearClient();
-}
-
-bool PictureImageLayer::HasDrawableContent() const {
- return image_ && PictureLayer::HasDrawableContent();
-}
-
-void PictureImageLayer::SetImage(PaintImage image,
- const SkMatrix& matrix,
- bool uses_width_as_height) {
- // SetImage() currently gets called whenever there is any
- // style change that affects the layer even if that change doesn't
- // affect the actual contents of the image (e.g. a CSS animation).
- // With this check in place we avoid unecessary texture uploads.
- if (image_ == image && matrix_ == matrix &&
- uses_width_as_height_ == uses_width_as_height) {
- return;
- }
-
- image_ = std::move(image);
- matrix_ = matrix;
- uses_width_as_height_ = uses_width_as_height;
-
- int width = uses_width_as_height_ ? image_.height() : image_.width();
- int height = uses_width_as_height_ ? image_.width() : image_.height();
- picture_layer_inputs_.directly_composited_image_size =
- gfx::Size(width, height);
-
- UpdateDrawsContent(HasDrawableContent());
- SetNeedsDisplay();
-}
-
-gfx::Rect PictureImageLayer::PaintableRegion() {
- return gfx::Rect(bounds());
-}
-
-scoped_refptr<DisplayItemList> PictureImageLayer::PaintContentsToDisplayList(
- ContentLayerClient::PaintingControlSetting painting_control) {
- DCHECK(image_);
- DCHECK_GT(image_.width(), 0);
- DCHECK_GT(image_.height(), 0);
- DCHECK(layer_tree_host());
-
- gfx::Size image_size =
- picture_layer_inputs_.directly_composited_image_size.value();
- float content_to_layer_scale_x =
- static_cast<float>(bounds().width()) / image_size.width();
- float content_to_layer_scale_y =
- static_cast<float>(bounds().height()) / image_size.height();
-
- bool has_scale = !MathUtil::IsWithinEpsilon(content_to_layer_scale_x, 1.f) ||
- !MathUtil::IsWithinEpsilon(content_to_layer_scale_y, 1.f);
- DCHECK(!has_scale);
- bool needs_save = has_scale || !matrix_.isIdentity();
-
- auto display_list = base::MakeRefCounted<DisplayItemList>();
-
- display_list->StartPaint();
-
- if (needs_save)
- display_list->push<SaveOp>();
-
- if (has_scale) {
- display_list->push<ScaleOp>(content_to_layer_scale_x,
- content_to_layer_scale_y);
- }
-
- if (!matrix_.isIdentity())
- display_list->push<ConcatOp>(matrix_);
-
- // Because Android WebView resourceless software draw mode rasters directly
- // to the root canvas, this draw must use the SkBlendMode::kSrcOver so that
- // transparent images blend correctly.
- display_list->push<DrawImageOp>(image_, 0.f, 0.f, nullptr);
-
- if (needs_save)
- display_list->push<RestoreOp>();
-
- display_list->EndPaintOfUnpaired(PaintableRegion());
- display_list->Finalize();
-
- return display_list;
-}
-
-bool PictureImageLayer::FillsBoundsCompletely() const {
- return false;
-}
-
-size_t PictureImageLayer::GetApproximateUnsharedMemoryUsage() const {
- return 0;
-}
-
-} // namespace cc
diff --git a/chromium/cc/layers/picture_image_layer.h b/chromium/cc/layers/picture_image_layer.h
deleted file mode 100644
index 92fddfad360..00000000000
--- a/chromium/cc/layers/picture_image_layer.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2010 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_PICTURE_IMAGE_LAYER_H_
-#define CC_LAYERS_PICTURE_IMAGE_LAYER_H_
-
-#include <stddef.h>
-
-#include "cc/cc_export.h"
-#include "cc/layers/content_layer_client.h"
-#include "cc/layers/picture_layer.h"
-#include "cc/paint/paint_image.h"
-#include "third_party/skia/include/core/SkRefCnt.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace cc {
-
-class CC_EXPORT PictureImageLayer : public PictureLayer, ContentLayerClient {
- public:
- static scoped_refptr<PictureImageLayer> Create();
-
- PictureImageLayer(const PictureImageLayer&) = delete;
- PictureImageLayer& operator=(const PictureImageLayer&) = delete;
-
- void SetImage(PaintImage image,
- const SkMatrix& matrix,
- bool uses_width_as_height);
-
- gfx::Rect PaintableRegion() override;
-
- // ContentLayerClient implementation.
- scoped_refptr<DisplayItemList> PaintContentsToDisplayList(
- ContentLayerClient::PaintingControlSetting painting_control) override;
- bool FillsBoundsCompletely() const override;
- size_t GetApproximateUnsharedMemoryUsage() const override;
-
- protected:
- bool HasDrawableContent() const override;
-
- private:
- PictureImageLayer();
- ~PictureImageLayer() override;
-
- PaintImage image_;
- SkMatrix matrix_;
- bool uses_width_as_height_;
-};
-
-} // namespace cc
-
-#endif // CC_LAYERS_PICTURE_IMAGE_LAYER_H_
diff --git a/chromium/cc/layers/picture_image_layer_unittest.cc b/chromium/cc/layers/picture_image_layer_unittest.cc
deleted file mode 100644
index f02c127aa37..00000000000
--- a/chromium/cc/layers/picture_image_layer_unittest.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2015 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/picture_image_layer.h"
-
-#include "cc/animation/animation_host.h"
-#include "cc/paint/paint_image_builder.h"
-#include "cc/test/fake_layer_tree_host.h"
-#include "cc/test/skia_common.h"
-#include "cc/test/test_task_graph_runner.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/core/SkImage.h"
-#include "third_party/skia/include/core/SkSurface.h"
-
-namespace cc {
-namespace {
-
-TEST(PictureImageLayerTest, PaintContentsToDisplayList) {
- scoped_refptr<PictureImageLayer> layer = PictureImageLayer::Create();
- FakeLayerTreeHostClient client;
- TestTaskGraphRunner task_graph_runner;
- auto animation_host = AnimationHost::CreateForTesting(ThreadInstance::MAIN);
- std::unique_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(
- &client, &task_graph_runner, animation_host.get());
- layer->SetLayerTreeHost(host.get());
- gfx::Rect layer_rect(200, 200);
-
- unsigned char image_pixels[4 * 200 * 200] = {0};
- SkImageInfo info =
- SkImageInfo::MakeN32Premul(layer_rect.width(), layer_rect.height());
- sk_sp<SkSurface> image_surface =
- SkSurface::MakeRasterDirect(info, image_pixels, info.minRowBytes());
- SkCanvas* image_canvas = image_surface->getCanvas();
- image_canvas->clear(SK_ColorRED);
- SkPaint blue_paint;
- blue_paint.setColor(SK_ColorBLUE);
- image_canvas->drawRect(SkRect::MakeWH(100, 100), blue_paint);
- image_canvas->drawRect(SkRect::MakeLTRB(100, 100, 200, 200), blue_paint);
-
- layer->SetImage(PaintImageBuilder::WithDefault()
- .set_id(PaintImage::GetNextId())
- .set_image(image_surface->makeImageSnapshot(),
- PaintImage::GetNextContentId())
- .TakePaintImage(),
- SkMatrix::I(), false);
- layer->SetBounds(gfx::Size(layer_rect.width(), layer_rect.height()));
-
- scoped_refptr<DisplayItemList> display_list =
- layer->PaintContentsToDisplayList(
- ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
- unsigned char actual_pixels[4 * 200 * 200] = {0};
- DrawDisplayList(actual_pixels, layer_rect, display_list);
-
- EXPECT_EQ(0, memcmp(actual_pixels, image_pixels, 4 * 200 * 200));
-
- layer->SetLayerTreeHost(nullptr);
-}
-
-} // namespace
-} // namespace cc
diff --git a/chromium/cc/layers/picture_layer.cc b/chromium/cc/layers/picture_layer.cc
index b7244152c31..0baee75a961 100644
--- a/chromium/cc/layers/picture_layer.cc
+++ b/chromium/cc/layers/picture_layer.cc
@@ -137,9 +137,17 @@ bool PictureLayer::Update() {
&last_updated_invalidation_, layer_size, recorded_viewport);
if (updated) {
- picture_layer_inputs_.display_list =
- picture_layer_inputs_.client->PaintContentsToDisplayList(
- ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+ {
+ auto old_display_list = std::move(picture_layer_inputs_.display_list);
+ picture_layer_inputs_.display_list =
+ picture_layer_inputs_.client->PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+ if (old_display_list &&
+ picture_layer_inputs_.display_list
+ ->NeedsAdditionalInvalidationForLCDText(*old_display_list)) {
+ last_updated_invalidation_ = gfx::Rect(bounds());
+ }
+ }
// Clear out previous directly composited image state - if the layer
// qualifies we'll set up the state below.
@@ -282,14 +290,6 @@ bool PictureLayer::ShouldUseTransformedRasterization() const {
if (!picture_layer_inputs_.transformed_rasterization_allowed)
return false;
- // Background color overfill is undesirable with transformed rasterization.
- // However, without background overfill, the tiles will be non-opaque on
- // external edges, and layer opaque region can't be computed in layer space
- // due to rounding under extreme scaling. This defeats many opaque layer
- // optimization. Prefer optimization over quality for this particular case.
- if (contents_opaque())
- return false;
-
const TransformTree& transform_tree =
layer_tree_host()->property_trees()->transform_tree;
DCHECK(!transform_tree.needs_update());
diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc
index 00e845e2b3f..0f030bd4ff3 100644
--- a/chromium/cc/layers/picture_layer_impl.cc
+++ b/chromium/cc/layers/picture_layer_impl.cc
@@ -258,12 +258,16 @@ void PictureLayerImpl::AppendQuads(viz::RenderPass* render_pass,
// |PictureLayerTiling::EnclosingContentsRectFromLayer()| will
// create a tiling that, when scaled by |max_contents_scale| above, is
// larger than the layer bounds by a fraction of a pixel.
- gfx::Rect clip_rect = draw_properties().drawable_content_rect;
+ gfx::Rect bounds_in_target_space = MathUtil::MapEnclosingClippedRect(
+ draw_properties().target_space_transform, gfx::Rect(bounds()));
+ if (is_clipped())
+ bounds_in_target_space.Intersect(draw_properties().clip_rect);
+
if (shared_quad_state->is_clipped)
- clip_rect.Intersect(shared_quad_state->clip_rect);
+ bounds_in_target_space.Intersect(shared_quad_state->clip_rect);
shared_quad_state->is_clipped = true;
- shared_quad_state->clip_rect = clip_rect;
+ shared_quad_state->clip_rect = bounds_in_target_space;
#if DCHECK_IS_ON()
// Validate that the tile and bounds size are always within one pixel.
@@ -836,22 +840,24 @@ LCDTextDisallowedReason PictureLayerImpl::ComputeLCDTextDisallowedReason()
return LCDTextDisallowedReason::kNone;
if (!layer_tree_impl()->settings().can_use_lcd_text)
return LCDTextDisallowedReason::kSetting;
- if (!contents_opaque()) {
+ if (!contents_opaque_for_text()) {
if (SkColorGetA(background_color()) != SK_AlphaOPAQUE)
return LCDTextDisallowedReason::kBackgroundColorNotOpaque;
return LCDTextDisallowedReason::kContentsNotOpaque;
}
- if (!GetTransformTree()
- .Node(transform_tree_index())
- ->node_and_ancestors_have_only_integer_translation)
- return LCDTextDisallowedReason::kNonIntegralTranslation;
- if (static_cast<int>(offset_to_transform_parent().x()) !=
- offset_to_transform_parent().x())
- return LCDTextDisallowedReason::kNonIntegralXOffset;
- if (static_cast<int>(offset_to_transform_parent().y()) !=
- offset_to_transform_parent().y())
- return LCDTextDisallowedReason::kNonIntegralYOffset;
+ if (!use_transformed_rasterization_) {
+ if (!GetTransformTree()
+ .Node(transform_tree_index())
+ ->node_and_ancestors_have_only_integer_translation)
+ return LCDTextDisallowedReason::kNonIntegralTranslation;
+ if (static_cast<int>(offset_to_transform_parent().x()) !=
+ offset_to_transform_parent().x())
+ return LCDTextDisallowedReason::kNonIntegralXOffset;
+ if (static_cast<int>(offset_to_transform_parent().y()) !=
+ offset_to_transform_parent().y())
+ return LCDTextDisallowedReason::kNonIntegralYOffset;
+ }
if (has_will_change_transform_hint())
return LCDTextDisallowedReason::kWillChangeTransform;
@@ -1066,6 +1072,11 @@ void PictureLayerImpl::SetNearestNeighbor(bool nearest_neighbor) {
}
void PictureLayerImpl::SetUseTransformedRasterization(bool use) {
+ // With transformed rasterization, the pixels along the edge of the layer may
+ // become translucent, so clear contents_opaque.
+ if (use)
+ SetContentsOpaque(false);
+
if (use_transformed_rasterization_ == use)
return;
@@ -1561,6 +1572,8 @@ gfx::Vector2dF PictureLayerImpl::CalculateRasterTranslation(
if (!use_transformed_rasterization_)
return gfx::Vector2dF();
+ DCHECK(!contents_opaque());
+
gfx::Transform draw_transform = DrawTransform();
// TODO(enne): for performance reasons, we should only have a raster
// translation when the screen space transform is not animating. We try to
diff --git a/chromium/cc/layers/picture_layer_impl_unittest.cc b/chromium/cc/layers/picture_layer_impl_unittest.cc
index 25fff7cb2ae..38609980105 100644
--- a/chromium/cc/layers/picture_layer_impl_unittest.cc
+++ b/chromium/cc/layers/picture_layer_impl_unittest.cc
@@ -5981,5 +5981,33 @@ TEST_F(LegacySWPictureLayerImplTest, NoTilingsUsesScaleOne) {
EXPECT_RECT_EQ(gfx::Rect(1000, 10000), shared_quad_state->quad_layer_rect);
EXPECT_TRUE(shared_quad_state->quad_to_target_transform.IsIdentity());
}
+
+TEST_F(LegacySWPictureLayerImplTest,
+ TransformedRasterizationAndContentsOpaqueAndLCDText) {
+ SetupDefaultTreesWithInvalidation(gfx::Size(200, 200), Region());
+
+ pending_layer()->SetBackgroundColor(SK_ColorWHITE);
+ pending_layer()->SetContentsOpaque(true);
+ pending_layer()->SetOffsetToTransformParent(gfx::Vector2dF(0.2, 0.3));
+ EXPECT_TRUE(pending_layer()->contents_opaque());
+ EXPECT_TRUE(pending_layer()->contents_opaque_for_text());
+ EXPECT_EQ(LCDTextDisallowedReason::kNonIntegralXOffset,
+ pending_layer()->ComputeLCDTextDisallowedReasonForTesting());
+
+ pending_layer()->SetUseTransformedRasterization(true);
+ EXPECT_FALSE(pending_layer()->contents_opaque());
+ EXPECT_FALSE(pending_layer()->contents_opaque_for_text());
+ EXPECT_EQ(LCDTextDisallowedReason::kContentsNotOpaque,
+ pending_layer()->ComputeLCDTextDisallowedReasonForTesting());
+
+ // Simulate another push from main-thread with the same values.
+ pending_layer()->SetContentsOpaque(true);
+ pending_layer()->SetUseTransformedRasterization(true);
+ EXPECT_FALSE(pending_layer()->contents_opaque());
+ EXPECT_FALSE(pending_layer()->contents_opaque_for_text());
+ EXPECT_EQ(LCDTextDisallowedReason::kContentsNotOpaque,
+ pending_layer()->ComputeLCDTextDisallowedReasonForTesting());
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/layers/render_surface_impl.cc b/chromium/cc/layers/render_surface_impl.cc
index 3d60067a835..f3cd766fdd5 100644
--- a/chromium/cc/layers/render_surface_impl.cc
+++ b/chromium/cc/layers/render_surface_impl.cc
@@ -134,10 +134,6 @@ const FilterOperations& RenderSurfaceImpl::Filters() const {
return OwningEffectNode()->filters;
}
-gfx::PointF RenderSurfaceImpl::FiltersOrigin() const {
- return OwningEffectNode()->filters_origin;
-}
-
gfx::Transform RenderSurfaceImpl::SurfaceScale() const {
gfx::Transform surface_scale;
surface_scale.Scale(OwningEffectNode()->surface_contents_scale.x(),
@@ -303,7 +299,7 @@ void RenderSurfaceImpl::AccumulateContentRectFromContributingLayer(
if (render_target() == this)
return;
- accumulated_content_rect_.Union(layer->drawable_content_rect());
+ accumulated_content_rect_.Union(layer->visible_drawable_content_rect());
}
void RenderSurfaceImpl::AccumulateContentRectFromContributingRenderSurface(
@@ -456,7 +452,7 @@ void RenderSurfaceImpl::AppendQuads(DrawMode draw_mode,
auto* quad = render_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>();
quad->SetAll(shared_quad_state, content_rect(), unoccluded_content_rect,
/*needs_blending=*/true, id(), mask_resource_id, mask_uv_rect,
- mask_texture_size, surface_contents_scale, FiltersOrigin(),
+ mask_texture_size, surface_contents_scale, gfx::PointF(),
tex_coord_rect,
!layer_tree_impl_->settings().enable_edge_anti_aliasing,
OwningEffectNode()->backdrop_filter_quality,
diff --git a/chromium/cc/layers/render_surface_impl.h b/chromium/cc/layers/render_surface_impl.h
index 72587093ba7..fac0274a79d 100644
--- a/chromium/cc/layers/render_surface_impl.h
+++ b/chromium/cc/layers/render_surface_impl.h
@@ -168,7 +168,6 @@ class CC_EXPORT RenderSurfaceImpl {
const FilterOperations& BackdropFilters() const;
base::Optional<gfx::RRectF> BackdropFilterBounds() const;
LayerImpl* BackdropMaskLayer() const;
- gfx::PointF FiltersOrigin() const;
gfx::Transform SurfaceScale() const;
bool TrilinearFiltering() const;
diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.cc b/chromium/cc/layers/scrollbar_layer_impl_base.cc
index 2d9e885c030..787f339fb49 100644
--- a/chromium/cc/layers/scrollbar_layer_impl_base.cc
+++ b/chromium/cc/layers/scrollbar_layer_impl_base.cc
@@ -291,6 +291,10 @@ bool ScrollbarLayerImplBase::SupportsDragSnapBack() const {
return false;
}
+bool ScrollbarLayerImplBase::JumpOnTrackClick() const {
+ return false;
+}
+
gfx::Rect ScrollbarLayerImplBase::BackButtonRect() const {
return gfx::Rect(0, 0);
}
diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.h b/chromium/cc/layers/scrollbar_layer_impl_base.h
index 3b508f3c17a..a887ab47ed2 100644
--- a/chromium/cc/layers/scrollbar_layer_impl_base.h
+++ b/chromium/cc/layers/scrollbar_layer_impl_base.h
@@ -72,6 +72,7 @@ class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl {
virtual gfx::Rect BackTrackRect() const;
virtual gfx::Rect ForwardTrackRect() const;
virtual bool SupportsDragSnapBack() const;
+ virtual bool JumpOnTrackClick() const;
virtual ScrollbarPart IdentifyScrollbarPart(
const gfx::PointF position_in_widget) const;
// Only PaintedOverlayScrollbar(Aura Overlay Scrollbar) need to know
diff --git a/chromium/cc/layers/solid_color_layer_impl.cc b/chromium/cc/layers/solid_color_layer_impl.cc
index 48c728cfde6..242f76415f9 100644
--- a/chromium/cc/layers/solid_color_layer_impl.cc
+++ b/chromium/cc/layers/solid_color_layer_impl.cc
@@ -53,6 +53,9 @@ void SolidColorLayerImpl::AppendSolidQuads(
gfx::Rect visible_quad_rect =
occlusion_in_layer_space.GetUnoccludedContentRect(visible_layer_rect);
+ if (visible_quad_rect.IsEmpty())
+ return;
+
auto* quad = render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
quad->SetNew(shared_quad_state, visible_layer_rect, visible_quad_rect, color,
force_anti_aliasing_off);
diff --git a/chromium/cc/layers/texture_layer_impl.cc b/chromium/cc/layers/texture_layer_impl.cc
index 5b4229280eb..544676a563b 100644
--- a/chromium/cc/layers/texture_layer_impl.cc
+++ b/chromium/cc/layers/texture_layer_impl.cc
@@ -9,6 +9,7 @@
#include <vector>
+#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "cc/trees/layer_tree_frame_sink.h"
#include "cc/trees/layer_tree_impl.h"
diff --git a/chromium/cc/layers/video_layer_impl_unittest.cc b/chromium/cc/layers/video_layer_impl_unittest.cc
index 8a22f392e56..06197477989 100644
--- a/chromium/cc/layers/video_layer_impl_unittest.cc
+++ b/chromium/cc/layers/video_layer_impl_unittest.cc
@@ -395,8 +395,7 @@ TEST(VideoLayerImplTest, NativeYUVFrameGeneratesYUVQuad) {
gfx::Size(10, 10), gfx::Rect(10, 10), gfx::Size(10, 10),
base::TimeDelta());
ASSERT_TRUE(video_frame);
- video_frame->metadata()->SetBoolean(media::VideoFrameMetadata::ALLOW_OVERLAY,
- true);
+ video_frame->metadata()->allow_overlay = true;
FakeVideoFrameProvider provider;
provider.set_frame(video_frame);
@@ -439,8 +438,7 @@ TEST(VideoLayerImplTest, NativeARGBFrameGeneratesTextureQuad) {
media::PIXEL_FORMAT_ARGB, mailbox_holders, base::DoNothing(),
resource_size, gfx::Rect(10, 10), resource_size, base::TimeDelta());
ASSERT_TRUE(video_frame);
- video_frame->metadata()->SetBoolean(media::VideoFrameMetadata::ALLOW_OVERLAY,
- true);
+ video_frame->metadata()->allow_overlay = true;
FakeVideoFrameProvider provider;
provider.set_frame(video_frame);
diff --git a/chromium/cc/metrics/compositor_frame_reporter.cc b/chromium/cc/metrics/compositor_frame_reporter.cc
index 13c15d9b1dd..c5b037edf4d 100644
--- a/chromium/cc/metrics/compositor_frame_reporter.cc
+++ b/chromium/cc/metrics/compositor_frame_reporter.cc
@@ -4,6 +4,7 @@
#include "cc/metrics/compositor_frame_reporter.h"
+#include <algorithm>
#include <memory>
#include <string>
#include <utility>
@@ -11,10 +12,14 @@
#include "base/metrics/histogram_macros.h"
#include "base/stl_util.h"
#include "base/strings/strcat.h"
+#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "cc/base/rolling_time_delta_history.h"
+#include "cc/metrics/dropped_frame_counter.h"
#include "cc/metrics/frame_sequence_tracker.h"
#include "cc/metrics/latency_ukm_reporter.h"
+#include "services/tracing/public/cpp/perfetto/macros.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_frame_reporter.pbzero.h"
#include "ui/events/types/event_type.h"
namespace cc {
@@ -42,7 +47,8 @@ constexpr int kFrameSequenceTrackerTypeCount =
static_cast<int>(FrameSequenceTrackerType::kMaxType) + 1;
// Names for the viz breakdowns that are shown in trace as substages under
-// PipelineReporter -> SubmitCompositorFrameToPresentationCompositorFrame
+// PipelineReporter -> SubmitCompositorFrameToPresentationCompositorFrame or
+// EventLatency -> SubmitCompositorFrameToPresentationCompositorFrame.
constexpr const char* GetVizBreakdownName(VizBreakdown stage) {
switch (stage) {
case VizBreakdown::kSubmitToReceiveCompositorFrame:
@@ -55,6 +61,14 @@ constexpr const char* GetVizBreakdownName(VizBreakdown stage) {
return "Swap";
case VizBreakdown::kSwapEndToPresentationCompositorFrame:
return "SwapEndToPresentationCompositorFrame";
+ case VizBreakdown::kSwapStartToBufferAvailable:
+ return "SwapStartToBufferAvailable";
+ case VizBreakdown::kBufferAvailableToBufferReady:
+ return "BufferAvailableToBufferReady";
+ case VizBreakdown::kBufferReadyToLatch:
+ return "BufferReadyToLatch";
+ case VizBreakdown::kLatchToSwapEnd:
+ return "LatchToSwapEnd";
case VizBreakdown::kBreakdownCount:
NOTREACHED();
return "";
@@ -165,9 +179,9 @@ constexpr int kCompositorLatencyHistogramMax = 350000;
constexpr int kCompositorLatencyHistogramBucketCount = 50;
constexpr int kEventLatencyEventTypeCount =
- static_cast<int>(ui::EventType::ET_LAST);
+ static_cast<int>(EventMetrics::EventType::kMaxValue) + 1;
constexpr int kEventLatencyScrollTypeCount =
- static_cast<int>(ui::ScrollInputType::kMaxValue) + 1;
+ static_cast<int>(EventMetrics::ScrollType::kMaxValue) + 1;
constexpr int kMaxEventLatencyHistogramBaseIndex =
kEventLatencyEventTypeCount * kEventLatencyScrollTypeCount;
constexpr int kMaxEventLatencyHistogramIndex =
@@ -203,37 +217,81 @@ std::string GetCompositorLatencyHistogramName(
std::string GetEventLatencyHistogramBaseName(
const EventMetrics& event_metrics) {
- const bool is_scroll = event_metrics.scroll_input_type().has_value();
+ const bool is_scroll = event_metrics.scroll_type().has_value();
return base::StrCat(
{"EventLatency.", event_metrics.GetTypeName(), is_scroll ? "." : nullptr,
is_scroll ? event_metrics.GetScrollTypeName() : nullptr});
}
+base::TimeTicks ComputeSafeDeadlineForFrame(const viz::BeginFrameArgs& args) {
+ return args.frame_time + (args.interval * 1.5);
+}
+
+void ReportOffsetBetweenDeadlineAndPresentationTime(
+ const viz::BeginFrameArgs& args,
+ base::TimeTicks presentation_time) {
+ const base::TimeTicks strict_deadline = args.frame_time + args.interval;
+ const base::TimeDelta offset = presentation_time - strict_deadline;
+ // |strict_deadline| and |presentation_time| should normally be pretty close.
+ // Measure how close they are.
+ UMA_HISTOGRAM_CUSTOM_TIMES(
+ "CompositorLatency.Diagnostic.PresentationTimeDeltaFromDeadline", offset,
+ base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMilliseconds(32), /*bucket_count=*/16);
+}
+
+#define REPORT_VIZ_TRACE_EVENT(start_time, end_time, index, trace_func) \
+ if (start_time <= end_time) { \
+ const char* substage_name = \
+ GetVizBreakdownName(static_cast<VizBreakdown>(index)); \
+ trace_func(start_time, end_time, substage_name); \
+ }
+
+#define REPORT_VIZ_BREAKDOWN_TRACES(trace_func) \
+ size_t start_to_buffer_available = \
+ static_cast<size_t>(VizBreakdown::kSwapStartToBufferAvailable); \
+ bool has_ready_timings = !!viz_breakdown_list_[start_to_buffer_available]; \
+ for (size_t i = 0; i < start_to_buffer_available; i++) { \
+ if (!viz_breakdown_list_[i]) \
+ break; \
+ \
+ if (i == static_cast<size_t>(VizBreakdown::kSwapStartToSwapEnd) && \
+ has_ready_timings) { \
+ size_t latch_to_swap_end = \
+ static_cast<size_t>(VizBreakdown::kLatchToSwapEnd); \
+ for (size_t j = start_to_buffer_available; j <= latch_to_swap_end; \
+ j++) { \
+ REPORT_VIZ_TRACE_EVENT(viz_breakdown_list_[j]->first, \
+ viz_breakdown_list_[j]->second, j, trace_func); \
+ } \
+ } else { \
+ REPORT_VIZ_TRACE_EVENT(viz_breakdown_list_[i]->first, \
+ viz_breakdown_list_[i]->second, i, trace_func); \
+ } \
+ }
+
} // namespace
CompositorFrameReporter::CompositorFrameReporter(
const ActiveTrackers& active_trackers,
- const viz::BeginFrameId& id,
- const base::TimeTicks frame_deadline,
+ const viz::BeginFrameArgs& args,
LatencyUkmReporter* latency_ukm_reporter,
bool should_report_metrics)
- : frame_id_(id),
- should_report_metrics_(should_report_metrics),
+ : should_report_metrics_(should_report_metrics),
+ args_(args),
active_trackers_(active_trackers),
- latency_ukm_reporter_(latency_ukm_reporter),
- frame_deadline_(frame_deadline) {}
+ latency_ukm_reporter_(latency_ukm_reporter) {}
std::unique_ptr<CompositorFrameReporter>
CompositorFrameReporter::CopyReporterAtBeginImplStage() const {
if (stage_history_.empty() ||
stage_history_.front().stage_type !=
StageType::kBeginImplFrameToSendBeginMainFrame ||
- !did_finish_impl_frame()) {
+ (!did_finish_impl_frame() && !did_not_produce_frame_time_.has_value())) {
return nullptr;
}
auto new_reporter = std::make_unique<CompositorFrameReporter>(
- active_trackers_, frame_id_, frame_deadline_, latency_ukm_reporter_,
- should_report_metrics_);
+ active_trackers_, args_, latency_ukm_reporter_, should_report_metrics_);
new_reporter->did_finish_impl_frame_ = did_finish_impl_frame_;
new_reporter->impl_frame_finish_time_ = impl_frame_finish_time_;
new_reporter->main_frame_abort_time_ = main_frame_abort_time_;
@@ -241,6 +299,7 @@ CompositorFrameReporter::CopyReporterAtBeginImplStage() const {
StageType::kBeginImplFrameToSendBeginMainFrame;
new_reporter->current_stage_.start_time = stage_history_.front().start_time;
new_reporter->set_tick_clock(tick_clock_);
+ new_reporter->SetDroppedFrameCounter(dropped_frame_counter_);
return new_reporter;
}
@@ -349,36 +408,33 @@ void CompositorFrameReporter::TerminateReporter() {
PopulateVizBreakdownList();
DCHECK_EQ(current_stage_.start_time, base::TimeTicks());
- const char* termination_status_str = nullptr;
switch (frame_termination_status_) {
case FrameTerminationStatus::kPresentedFrame:
EnableReportType(FrameReportType::kNonDroppedFrame);
- termination_status_str = "presented_frame";
- if (frame_deadline_ < frame_termination_time_)
+ ReportOffsetBetweenDeadlineAndPresentationTime(args_,
+ frame_termination_time_);
+ if (ComputeSafeDeadlineForFrame(args_) < frame_termination_time_)
EnableReportType(FrameReportType::kMissedDeadlineFrame);
break;
case FrameTerminationStatus::kDidNotPresentFrame:
EnableReportType(FrameReportType::kDroppedFrame);
- termination_status_str = "did_not_present_frame";
break;
case FrameTerminationStatus::kReplacedByNewReporter:
EnableReportType(FrameReportType::kDroppedFrame);
- termination_status_str = "replaced_by_new_reporter_at_same_stage";
break;
case FrameTerminationStatus::kDidNotProduceFrame:
- termination_status_str = "did_not_produce_frame";
if (!frame_skip_reason_.has_value() ||
frame_skip_reason() != FrameSkippedReason::kNoDamage) {
EnableReportType(FrameReportType::kDroppedFrame);
- termination_status_str = "dropped_frame";
}
break;
case FrameTerminationStatus::kUnknown:
- termination_status_str = "terminated_before_ending";
break;
}
- ReportAllTraceEvents(termination_status_str);
+ ReportCompositorLatencyTraceEvents();
+ if (TestReportType(FrameReportType::kNonDroppedFrame))
+ ReportEventLatencyTraceEvents();
// Only report compositor latency histograms if the frame was produced.
if (should_report_metrics_ && report_types_.any()) {
@@ -394,6 +450,17 @@ void CompositorFrameReporter::TerminateReporter() {
if (TestReportType(FrameReportType::kNonDroppedFrame))
ReportEventLatencyHistograms();
}
+
+ if (dropped_frame_counter_) {
+ if (TestReportType(FrameReportType::kDroppedFrame)) {
+ dropped_frame_counter_->AddDroppedFrame();
+ } else {
+ if (has_partial_update_)
+ dropped_frame_counter_->AddPartialFrame();
+ else
+ dropped_frame_counter_->AddGoodFrame();
+ }
+ }
}
void CompositorFrameReporter::EndCurrentStage(base::TimeTicks end_time) {
@@ -421,8 +488,8 @@ void CompositorFrameReporter::ReportCompositorLatencyHistograms() const {
FrameReportType report_type = static_cast<FrameReportType>(type);
UMA_HISTOGRAM_ENUMERATION("CompositorLatency.Type", report_type);
if (latency_ukm_reporter_) {
- latency_ukm_reporter_->ReportLatencyUkm(report_type, stage_history_,
- active_trackers_, viz_breakdown_);
+ latency_ukm_reporter_->ReportCompositorLatencyUkm(
+ report_type, stage_history_, active_trackers_, viz_breakdown_);
}
for (size_t fst_type = 0; fst_type < active_trackers_.size(); ++fst_type) {
if (!active_trackers_.test(fst_type)) {
@@ -510,9 +577,11 @@ void CompositorFrameReporter::ReportCompositorLatencyVizBreakdowns(
#endif
break;
}
+ const base::TimeTicks start_time = viz_breakdown_list_[i]->first;
+ const base::TimeTicks end_time = viz_breakdown_list_[i]->second;
ReportCompositorLatencyHistogram(frame_sequence_tracker_type,
kVizBreakdownInitialIndex + i,
- *viz_breakdown_list_[i]);
+ end_time - start_time);
}
}
@@ -561,8 +630,8 @@ void CompositorFrameReporter::ReportEventLatencyHistograms() const {
GetEventLatencyHistogramBaseName(event_metrics);
const int event_type_index = static_cast<int>(event_metrics.type());
const int scroll_type_index =
- event_metrics.scroll_input_type()
- ? static_cast<int>(*event_metrics.scroll_input_type())
+ event_metrics.scroll_type()
+ ? static_cast<int>(*event_metrics.scroll_type())
: 0;
const int histogram_base_index =
event_type_index * kEventLatencyScrollTypeCount + scroll_type_index;
@@ -570,8 +639,7 @@ void CompositorFrameReporter::ReportEventLatencyHistograms() const {
// For scroll events, report total latency up to gpu-swap-end. This is
// useful in comparing new EventLatency metrics with LatencyInfo-based
// scroll event latency metrics.
- if (event_metrics.scroll_input_type() &&
- !viz_breakdown_.swap_timings.is_null()) {
+ if (event_metrics.scroll_type() && !viz_breakdown_.swap_timings.is_null()) {
const base::TimeDelta swap_end_latency =
viz_breakdown_.swap_timings.swap_end - event_metrics.time_stamp();
const std::string swap_end_histogram_name =
@@ -586,57 +654,51 @@ void CompositorFrameReporter::ReportEventLatencyHistograms() const {
base::HistogramBase::kUmaTargetedHistogramFlag));
}
- const auto trace_id = TRACE_ID_LOCAL(&event_metrics);
- TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1(
- "cc,input", "EventLatency", trace_id, event_metrics.time_stamp(),
- "event", event_metrics.GetTypeName());
-
// It is possible for an event to arrive in the compositor in the middle of
// a frame (e.g. the browser received the event *after* renderer received a
// begin-impl, and the event reached the compositor before that frame
// ended). To handle such cases, find the first stage that happens after the
// event's arrival in the browser.
- size_t index = 0;
- for (; index < stage_history_.size(); ++index) {
- const auto& stage = stage_history_[index];
- if (stage.start_time > event_metrics.time_stamp()) {
- const char stage_type_name[] = "BrowserToRendererCompositor";
-
- const base::TimeDelta latency =
- stage.start_time - event_metrics.time_stamp();
- const std::string histogram_name =
- base::StrCat({histogram_base_name, ".", stage_type_name});
- STATIC_HISTOGRAM_POINTER_GROUP(
- histogram_name, histogram_base_index,
- kMaxEventLatencyHistogramBaseIndex,
- AddTimeMicrosecondsGranularity(latency),
- base::Histogram::FactoryGet(
- histogram_name, kEventLatencyHistogramMin,
- kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount,
- base::HistogramBase::kUmaTargetedHistogramFlag));
-
- TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
- "cc,input", stage_type_name, trace_id, event_metrics.time_stamp());
- TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
- "cc,input", stage_type_name, trace_id, stage.start_time);
- break;
- }
- }
+ auto stage_it =
+ std::find_if(stage_history_.begin(), stage_history_.end(),
+ [&event_metrics](const StageData& stage) {
+ return stage.start_time > event_metrics.time_stamp();
+ });
+ // TODO(crbug.com/1079116): Ideally, at least the start time of
+ // SubmitCompositorFrameToPresentationCompositorFrame stage should be
+ // greater than the event time stamp, but apparently, this is not always the
+ // case (see crbug.com/1093698). For now, skip to the next event in such
+ // cases. Hopefully, the work to reduce discrepancies between the new
+ // EventLatency and the old Event.Latency metrics would fix this issue. If
+ // not, we need to reconsider investigating this issue.
+ if (stage_it == stage_history_.end())
+ continue;
- for (; index < stage_history_.size(); ++index) {
- const auto& stage = stage_history_[index];
+ const base::TimeDelta b2r_latency =
+ stage_it->start_time - event_metrics.time_stamp();
+ const std::string b2r_histogram_name =
+ histogram_base_name + ".BrowserToRendererCompositor";
+ STATIC_HISTOGRAM_POINTER_GROUP(
+ b2r_histogram_name, histogram_base_index,
+ kMaxEventLatencyHistogramBaseIndex,
+ AddTimeMicrosecondsGranularity(b2r_latency),
+ base::Histogram::FactoryGet(
+ b2r_histogram_name, kEventLatencyHistogramMin,
+ kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount,
+ base::HistogramBase::kUmaTargetedHistogramFlag));
+ for (; stage_it != stage_history_.end(); ++stage_it) {
// Total latency is calculated since the event timestamp.
const base::TimeTicks start_time =
- stage.stage_type == StageType::kTotalLatency
+ stage_it->stage_type == StageType::kTotalLatency
? event_metrics.time_stamp()
- : stage.start_time;
- const base::TimeDelta latency = stage.end_time - start_time;
- const int stage_type_index = static_cast<int>(stage.stage_type);
+ : stage_it->start_time;
+ const base::TimeDelta latency = stage_it->end_time - start_time;
+ const int stage_type_index = static_cast<int>(stage_it->stage_type);
ReportEventLatencyHistogram(histogram_base_index, histogram_base_name,
stage_type_index, latency);
- switch (stage.stage_type) {
+ switch (stage_it->stage_type) {
case StageType::kSendBeginMainFrameToCommit:
ReportEventLatencyBlinkBreakdowns(histogram_base_index,
histogram_base_name);
@@ -648,18 +710,12 @@ void CompositorFrameReporter::ReportEventLatencyHistograms() const {
default:
break;
}
-
- if (stage.stage_type != StageType::kTotalLatency) {
- TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
- "cc,input", GetStageName(stage_type_index), trace_id,
- stage.start_time);
- TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
- "cc,input", GetStageName(stage_type_index), trace_id,
- stage.end_time);
- }
}
- TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
- "cc,input", "EventLatency", trace_id, frame_termination_time_);
+ }
+
+ if (latency_ukm_reporter_) {
+ latency_ukm_reporter_->ReportEventLatencyUkm(
+ events_metrics_, stage_history_, viz_breakdown_);
}
}
@@ -685,9 +741,11 @@ void CompositorFrameReporter::ReportEventLatencyVizBreakdowns(
#endif
break;
}
+ const base::TimeTicks start_time = viz_breakdown_list_[i]->first;
+ const base::TimeTicks end_time = viz_breakdown_list_[i]->second;
ReportEventLatencyHistogram(histogram_base_index, histogram_base_name,
kVizBreakdownInitialIndex + i,
- *viz_breakdown_list_[i]);
+ end_time - start_time);
}
}
@@ -710,32 +768,31 @@ void CompositorFrameReporter::ReportEventLatencyHistogram(
base::HistogramBase::kUmaTargetedHistogramFlag));
}
-void CompositorFrameReporter::ReportVizBreakdownTrace(
- VizBreakdown substage,
- const base::TimeTicks start_time,
- const base::TimeTicks end_time) const {
- // Do not report events with negative time span.
- if (end_time < start_time)
- return;
-
- const char* stage_name = GetVizBreakdownName(substage);
- TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
- "cc,benchmark", stage_name, TRACE_ID_LOCAL(this), start_time);
-
- TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
- "cc,benchmark", stage_name, TRACE_ID_LOCAL(this), end_time);
-}
-
-void CompositorFrameReporter::ReportAllTraceEvents(
- const char* termination_status_str) const {
+void CompositorFrameReporter::ReportCompositorLatencyTraceEvents() const {
if (stage_history_.empty())
return;
- const auto trace_id = TRACE_ID_LOCAL(this);
- const auto& startstage = stage_history_.front();
- TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1(
- "cc,benchmark", "PipelineReporter", trace_id, startstage.start_time,
- "frame_id", frame_id_.ToString());
+ const auto trace_track = perfetto::Track(reinterpret_cast<uint64_t>(this));
+ TRACE_EVENT_BEGIN(
+ "cc,benchmark", "PipelineReporter", trace_track,
+ stage_history_.front().start_time, [&](perfetto::EventContext context) {
+ using perfetto::protos::pbzero::ChromeFrameReporter;
+ bool frame_dropped = TestReportType(FrameReportType::kDroppedFrame);
+ ChromeFrameReporter::State state;
+ if (frame_dropped) {
+ state = ChromeFrameReporter::STATE_DROPPED;
+ } else if (frame_termination_status_ ==
+ FrameTerminationStatus::kDidNotProduceFrame) {
+ state = ChromeFrameReporter::STATE_NO_UPDATE_DESIRED;
+ } else {
+ state = ChromeFrameReporter::STATE_PRESENTED_ALL;
+ }
+ auto* reporter = context.event()->set_chrome_frame_reporter();
+ reporter->set_state(state);
+ reporter->set_frame_source(args_.frame_id.source_id);
+ reporter->set_frame_sequence(args_.frame_id.sequence_number);
+ // TODO(crbug.com/1086974): Set 'drop reason' if applicable.
+ });
// The trace-viewer cannot seem to handle a single child-event that has the
// same start/end timestamps as the parent-event. So avoid adding the
@@ -750,50 +807,89 @@ void CompositorFrameReporter::ReportAllTraceEvents(
DCHECK_GE(stage.end_time, stage.start_time);
if (stage.start_time == stage.end_time)
continue;
- const char* name = GetStageName(stage_type_index);
+ const char* stage_name = GetStageName(stage_type_index);
+ TRACE_EVENT_BEGIN("cc,benchmark", stage_name, trace_track,
+ stage.start_time);
+ if (stage.stage_type ==
+ StageType::kSubmitCompositorFrameToPresentationCompositorFrame) {
+ REPORT_VIZ_BREAKDOWN_TRACES([&](base::TimeTicks start_time,
+ base::TimeTicks end_time,
+ const char* substage_name) {
+ TRACE_EVENT_BEGIN("cc,benchmark", substage_name, trace_track,
+ start_time);
+ TRACE_EVENT_END("cc,benchmark", trace_track, end_time);
+ });
+ }
+ TRACE_EVENT_END("cc,benchmark", trace_track, stage.end_time);
+ }
+ }
+
+ TRACE_EVENT_END("cc,benchmark", trace_track, frame_termination_time_);
+}
+
+void CompositorFrameReporter::ReportEventLatencyTraceEvents() const {
+ for (const EventMetrics& event_metrics : events_metrics_) {
+ const auto trace_id = TRACE_ID_LOCAL(&event_metrics);
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1(
+ "cc,input", "EventLatency", trace_id, event_metrics.time_stamp(),
+ "event", event_metrics.GetTypeName());
+
+ // Find the first stage that happens after the event's arrival in the
+ // browser.
+ auto stage_it =
+ std::find_if(stage_history_.begin(), stage_history_.end(),
+ [&event_metrics](const StageData& stage) {
+ return stage.start_time > event_metrics.time_stamp();
+ });
+ // TODO(crbug.com/1079116): Ideally, at least the start time of
+ // SubmitCompositorFrameToPresentationCompositorFrame stage should be
+ // greater than the event time stamp, but apparently, this is not always the
+ // case (see crbug.com/1093698). For now, skip to the next event in such
+ // cases. Hopefully, the work to reduce discrepancies between the new
+ // EventLatency and the old Event.Latency metrics would fix this issue. If
+ // not, we need to reconsider investigating this issue.
+ if (stage_it == stage_history_.end())
+ continue;
+
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
+ "cc,input", "BrowserToRendererCompositor", trace_id,
+ event_metrics.time_stamp());
+ TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
+ "cc,input", "BrowserToRendererCompositor", trace_id,
+ stage_it->start_time);
+
+ for (; stage_it != stage_history_.end(); ++stage_it) {
+ const int stage_type_index = static_cast<int>(stage_it->stage_type);
+ CHECK_LT(stage_type_index, static_cast<int>(StageType::kStageTypeCount));
+ CHECK_GE(stage_type_index, 0);
+ if (stage_it->start_time >= frame_termination_time_)
+ break;
+ DCHECK_GE(stage_it->end_time, stage_it->start_time);
+ if (stage_it->start_time == stage_it->end_time)
+ continue;
+ const char* stage_name = GetStageName(stage_type_index);
+
TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
- "cc,benchmark", name, trace_id, stage.start_time);
+ "cc,input", stage_name, trace_id, stage_it->start_time);
- if (stage.stage_type ==
+ if (stage_it->stage_type ==
StageType::kSubmitCompositorFrameToPresentationCompositorFrame) {
- const struct {
- VizBreakdown stage;
- base::TimeTicks start_time;
- base::TimeTicks end_time;
- } sub_stages[] = {
- {VizBreakdown::kSubmitToReceiveCompositorFrame, stage.start_time,
- viz_breakdown_.received_compositor_frame_timestamp},
- {VizBreakdown::kReceivedCompositorFrameToStartDraw,
- viz_breakdown_.received_compositor_frame_timestamp,
- viz_breakdown_.draw_start_timestamp},
- {VizBreakdown::kStartDrawToSwapStart,
- viz_breakdown_.draw_start_timestamp,
- viz_breakdown_.swap_timings.swap_start},
- {VizBreakdown::kSwapStartToSwapEnd,
- viz_breakdown_.swap_timings.swap_start,
- viz_breakdown_.swap_timings.swap_end},
- {VizBreakdown::kSwapEndToPresentationCompositorFrame,
- viz_breakdown_.swap_timings.swap_end,
- viz_breakdown_.presentation_feedback.timestamp}};
- for (const auto& sub : sub_stages) {
- if (sub.start_time.is_null() || sub.end_time.is_null())
- break;
- ReportVizBreakdownTrace(sub.stage, sub.start_time, sub.end_time);
- }
+ REPORT_VIZ_BREAKDOWN_TRACES([&](base::TimeTicks start_time,
+ base::TimeTicks end_time,
+ const char* substage_name) {
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
+ "cc,input", substage_name, trace_id, start_time);
+ TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
+ "cc,input", substage_name, trace_id, end_time);
+ });
}
- TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0("cc,benchmark", name,
- trace_id, stage.end_time);
+ TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
+ "cc,input", stage_name, trace_id, stage_it->end_time);
}
+ TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
+ "cc,input", "EventLatency", trace_id, frame_termination_time_);
}
-
- const char* submission_status_str =
- TestReportType(FrameReportType::kDroppedFrame) ? "dropped_frame"
- : "non_dropped_frame";
- TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP2(
- "cc,benchmark", "PipelineReporter", trace_id, frame_termination_time_,
- "termination_status", termination_status_str,
- "compositor_frame_submission_status", submission_status_str);
}
void CompositorFrameReporter::PopulateBlinkBreakdownList() {
@@ -839,29 +935,47 @@ void CompositorFrameReporter::PopulateVizBreakdownList() {
}
viz_breakdown_list_[static_cast<int>(
VizBreakdown::kSubmitToReceiveCompositorFrame)] =
- viz_breakdown_.received_compositor_frame_timestamp - viz_start_time_;
+ std::make_pair(viz_start_time_,
+ viz_breakdown_.received_compositor_frame_timestamp);
if (viz_breakdown_.draw_start_timestamp.is_null())
return;
viz_breakdown_list_[static_cast<int>(
VizBreakdown::kReceivedCompositorFrameToStartDraw)] =
- viz_breakdown_.draw_start_timestamp -
- viz_breakdown_.received_compositor_frame_timestamp;
+ std::make_pair(viz_breakdown_.received_compositor_frame_timestamp,
+ viz_breakdown_.draw_start_timestamp);
if (viz_breakdown_.swap_timings.is_null())
return;
viz_breakdown_list_[static_cast<int>(VizBreakdown::kStartDrawToSwapStart)] =
- viz_breakdown_.swap_timings.swap_start -
- viz_breakdown_.draw_start_timestamp;
+ std::make_pair(viz_breakdown_.draw_start_timestamp,
+ viz_breakdown_.swap_timings.swap_start);
viz_breakdown_list_[static_cast<int>(VizBreakdown::kSwapStartToSwapEnd)] =
- viz_breakdown_.swap_timings.swap_end -
- viz_breakdown_.swap_timings.swap_start;
+ std::make_pair(viz_breakdown_.swap_timings.swap_start,
+ viz_breakdown_.swap_timings.swap_end);
viz_breakdown_list_[static_cast<int>(
VizBreakdown::kSwapEndToPresentationCompositorFrame)] =
- viz_breakdown_.presentation_feedback.timestamp -
- viz_breakdown_.swap_timings.swap_end;
+ std::make_pair(viz_breakdown_.swap_timings.swap_end,
+ viz_breakdown_.presentation_feedback.timestamp);
+
+ if (viz_breakdown_.presentation_feedback.ready_timestamp.is_null())
+ return;
+ viz_breakdown_list_[static_cast<int>(
+ VizBreakdown::kSwapStartToBufferAvailable)] =
+ std::make_pair(viz_breakdown_.swap_timings.swap_start,
+ viz_breakdown_.presentation_feedback.available_timestamp);
+ viz_breakdown_list_[static_cast<int>(
+ VizBreakdown::kBufferAvailableToBufferReady)] =
+ std::make_pair(viz_breakdown_.presentation_feedback.available_timestamp,
+ viz_breakdown_.presentation_feedback.ready_timestamp);
+ viz_breakdown_list_[static_cast<int>(VizBreakdown::kBufferReadyToLatch)] =
+ std::make_pair(viz_breakdown_.presentation_feedback.ready_timestamp,
+ viz_breakdown_.presentation_feedback.latch_timestamp);
+ viz_breakdown_list_[static_cast<int>(VizBreakdown::kLatchToSwapEnd)] =
+ std::make_pair(viz_breakdown_.presentation_feedback.latch_timestamp,
+ viz_breakdown_.swap_timings.swap_end);
}
base::TimeDelta CompositorFrameReporter::SumOfStageHistory() const {
diff --git a/chromium/cc/metrics/compositor_frame_reporter.h b/chromium/cc/metrics/compositor_frame_reporter.h
index 9cae550ba76..2ff3b986489 100644
--- a/chromium/cc/metrics/compositor_frame_reporter.h
+++ b/chromium/cc/metrics/compositor_frame_reporter.h
@@ -7,6 +7,7 @@
#include <bitset>
#include <memory>
+#include <utility>
#include <vector>
#include "base/optional.h"
@@ -26,6 +27,7 @@ struct FrameTimingDetails;
}
namespace cc {
+class DroppedFrameCounter;
class LatencyUkmReporter;
// This is used for tracing and reporting the duration of pipeline stages within
@@ -93,6 +95,13 @@ class CC_EXPORT CompositorFrameReporter {
kStartDrawToSwapStart = 2,
kSwapStartToSwapEnd = 3,
kSwapEndToPresentationCompositorFrame = 4,
+
+ // This is a breakdown of SwapStartToSwapEnd stage which is optionally
+ // recorded if querying these timestamps is supported by the platform.
+ kSwapStartToBufferAvailable = 5,
+ kBufferAvailableToBufferReady = 6,
+ kBufferReadyToLatch = 7,
+ kLatchToSwapEnd = 8,
kBreakdownCount
};
@@ -111,7 +120,7 @@ class CC_EXPORT CompositorFrameReporter {
kBreakdownCount
};
- struct StageData {
+ struct CC_EXPORT StageData {
StageType stage_type;
base::TimeTicks start_time;
base::TimeTicks end_time;
@@ -127,8 +136,7 @@ class CC_EXPORT CompositorFrameReporter {
std::bitset<static_cast<size_t>(FrameSequenceTrackerType::kMaxType)>;
CompositorFrameReporter(const ActiveTrackers& active_trackers,
- const viz::BeginFrameId& id,
- const base::TimeTicks frame_deadline,
+ const viz::BeginFrameArgs& args,
LatencyUkmReporter* latency_ukm_reporter,
bool should_report_metrics);
~CompositorFrameReporter();
@@ -139,8 +147,6 @@ class CC_EXPORT CompositorFrameReporter {
std::unique_ptr<CompositorFrameReporter> CopyReporterAtBeginImplStage() const;
- const viz::BeginFrameId frame_id_;
-
// 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);
@@ -183,6 +189,13 @@ class CC_EXPORT CompositorFrameReporter {
tick_clock_ = tick_clock;
}
+ void SetDroppedFrameCounter(DroppedFrameCounter* counter) {
+ dropped_frame_counter_ = counter;
+ }
+ void SetHasPartialUpdate() { has_partial_update_ = true; }
+
+ const viz::BeginFrameId& frame_id() const { return args_.frame_id; }
+
private:
void TerminateReporter();
void EndCurrentStage(base::TimeTicks end_time);
@@ -213,15 +226,8 @@ class CC_EXPORT CompositorFrameReporter {
int stage_type_index,
base::TimeDelta latency) const;
- // Generate a trace event corresponding to a Viz breakdown under
- // SubmitCompositorFrameToPresentationCompositorFrame stage in
- // PipelineReporter. This function only generates trace events and does not
- // report histograms.
- void ReportVizBreakdownTrace(VizBreakdown substage,
- const base::TimeTicks start_time,
- const base::TimeTicks end_time) const;
-
- void ReportAllTraceEvents(const char* termination_status_str) const;
+ void ReportCompositorLatencyTraceEvents() const;
+ void ReportEventLatencyTraceEvents() const;
void EnableReportType(FrameReportType report_type) {
report_types_.set(static_cast<size_t>(report_type));
@@ -239,6 +245,7 @@ class CC_EXPORT CompositorFrameReporter {
base::TimeTicks Now() const;
const bool should_report_metrics_;
+ const viz::BeginFrameArgs args_;
StageData current_stage_;
@@ -249,7 +256,7 @@ class CC_EXPORT CompositorFrameReporter {
viz::FrameTimingDetails viz_breakdown_;
base::TimeTicks viz_start_time_;
- base::Optional<base::TimeDelta>
+ base::Optional<std::pair<base::TimeTicks, base::TimeTicks>>
viz_breakdown_list_[static_cast<int>(VizBreakdown::kBreakdownCount)];
// Stage data is recorded here. On destruction these stages will be reported
@@ -277,7 +284,6 @@ class CC_EXPORT CompositorFrameReporter {
// The time that work on Impl frame is finished. It's only valid if the
// reporter is in a stage other than begin impl frame.
base::TimeTicks impl_frame_finish_time_;
- base::TimeTicks frame_deadline_;
// The timestamp of when the frame was marked as not having produced a frame
// (through a call to DidNotProduceFrame()).
@@ -286,7 +292,11 @@ class CC_EXPORT CompositorFrameReporter {
base::Optional<base::TimeTicks> main_frame_abort_time_;
const base::TickClock* tick_clock_ = base::DefaultTickClock::GetInstance();
+
+ DroppedFrameCounter* dropped_frame_counter_ = nullptr;
+ bool has_partial_update_ = false;
};
+
} // namespace cc
#endif // CC_METRICS_COMPOSITOR_FRAME_REPORTER_H_"
diff --git a/chromium/cc/metrics/compositor_frame_reporter_unittest.cc b/chromium/cc/metrics/compositor_frame_reporter_unittest.cc
index afd8830e3e3..414655412dd 100644
--- a/chromium/cc/metrics/compositor_frame_reporter_unittest.cc
+++ b/chromium/cc/metrics/compositor_frame_reporter_unittest.cc
@@ -30,8 +30,7 @@ class CompositorFrameReporterTest : public testing::Test {
CompositorFrameReporterTest()
: pipeline_reporter_(std::make_unique<CompositorFrameReporter>(
CompositorFrameReporter::ActiveTrackers(),
- viz::BeginFrameId(),
- base::TimeTicks() + base::TimeDelta::FromMilliseconds(16),
+ viz::BeginFrameArgs(),
nullptr,
/*should_report_metrics=*/true)) {
pipeline_reporter_->set_tick_clock(&test_tick_clock_);
diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller.cc b/chromium/cc/metrics/compositor_frame_reporting_controller.cc
index cd0ae991606..b19d869c968 100644
--- a/chromium/cc/metrics/compositor_frame_reporting_controller.cc
+++ b/chromium/cc/metrics/compositor_frame_reporting_controller.cc
@@ -68,11 +68,12 @@ void CompositorFrameReportingController::WillBeginImplFrame(
reporter->did_not_produce_frame_time());
}
auto reporter = std::make_unique<CompositorFrameReporter>(
- active_trackers_, args.frame_id, args.frame_time + (args.interval * 1.5),
- latency_ukm_reporter_.get(), should_report_metrics_);
+ active_trackers_, args, latency_ukm_reporter_.get(),
+ should_report_metrics_);
reporter->set_tick_clock(tick_clock_);
reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame,
begin_time);
+ reporter->SetDroppedFrameCounter(dropped_frame_counter_);
reporters_[PipelineStage::kBeginImplFrame] = std::move(reporter);
}
@@ -83,7 +84,7 @@ void CompositorFrameReportingController::WillBeginMainFrame(
// C++20 feature.
DCHECK_NE(reporters_[PipelineStage::kBeginMainFrame].get(),
reporters_[PipelineStage::kBeginImplFrame].get());
- DCHECK_EQ(reporters_[PipelineStage::kBeginImplFrame]->frame_id_,
+ DCHECK_EQ(reporters_[PipelineStage::kBeginImplFrame]->frame_id(),
args.frame_id);
reporters_[PipelineStage::kBeginImplFrame]->StartStage(
StageType::kSendBeginMainFrameToCommit, Now());
@@ -94,11 +95,11 @@ void CompositorFrameReportingController::WillBeginMainFrame(
// beginMain frame before next BeginImplFrame (Not reached the ImplFrame
// deadline yet). So will start a new reporter at BeginMainFrame.
auto reporter = std::make_unique<CompositorFrameReporter>(
- active_trackers_, args.frame_id,
- args.frame_time + (args.interval * 1.5), latency_ukm_reporter_.get(),
+ active_trackers_, args, latency_ukm_reporter_.get(),
should_report_metrics_);
reporter->set_tick_clock(tick_clock_);
reporter->StartStage(StageType::kSendBeginMainFrameToCommit, Now());
+ reporter->SetDroppedFrameCounter(dropped_frame_counter_);
reporters_[PipelineStage::kBeginMainFrame] = std::move(reporter);
}
}
@@ -107,7 +108,7 @@ void CompositorFrameReportingController::BeginMainFrameAborted(
const viz::BeginFrameId& id) {
auto& reporter = reporters_[PipelineStage::kBeginMainFrame];
DCHECK(reporter);
- DCHECK_EQ(reporter->frame_id_, id);
+ DCHECK_EQ(reporter->frame_id(), id);
reporter->OnAbortBeginMainFrame(Now());
}
@@ -169,7 +170,7 @@ void CompositorFrameReportingController::DidSubmitCompositorFrame(
// |impl_reporter| is guaranteed to be set, and |main_reporter| will not be
// set.
if (is_activated_frame_new) {
- DCHECK_EQ(reporters_[PipelineStage::kActivate]->frame_id_,
+ DCHECK_EQ(reporters_[PipelineStage::kActivate]->frame_id(),
last_activated_frame_id);
// The reporter in activate state can be submitted
main_reporter = std::move(reporters_[PipelineStage::kActivate]);
@@ -205,6 +206,7 @@ void CompositorFrameReportingController::DidSubmitCompositorFrame(
if (reporter) {
reporter->StartStage(StageType::kEndActivateToSubmitCompositorFrame,
reporter->impl_frame_finish_time());
+ reporter->SetHasPartialUpdate();
impl_reporter = std::move(reporter);
}
}
@@ -220,7 +222,7 @@ void CompositorFrameReportingController::DidSubmitCompositorFrame(
DCHECK(main_reporter);
// If there are impl events, there must be a reporter with
// |current_frame_id|.
- DCHECK_EQ(main_reporter->frame_id_, current_frame_id);
+ DCHECK_EQ(main_reporter->frame_id(), current_frame_id);
events_metrics.main_event_metrics.reserve(
events_metrics.main_event_metrics.size() +
events_metrics.impl_event_metrics.size());
@@ -255,7 +257,7 @@ void CompositorFrameReportingController::DidNotProduceFrame(
const viz::BeginFrameId& id,
FrameSkippedReason skip_reason) {
for (auto& stage_reporter : reporters_) {
- if (stage_reporter && stage_reporter->frame_id_ == id) {
+ if (stage_reporter && stage_reporter->frame_id() == id) {
// The reporter will be flagged and terminated when replaced by another
// reporter. The reporter is not terminated immediately here because it
// can still end up producing a frame afterwards. For example, if the
@@ -264,7 +266,20 @@ void CompositorFrameReportingController::DidNotProduceFrame(
// BeginMain stage, but the main-thread can make updates, which can be
// submitted with the next frame.
stage_reporter->OnDidNotProduceFrame(skip_reason);
- return;
+ break;
+ }
+ }
+
+ // If the compositor has no updates, and the main-thread has not responded to
+ // the begin-main-frame yet, then it is essentially a dropped frame. To handle
+ // this case, keep the reporter for the main-thread, but recreate a reporter
+ // for the dropped-frame.
+ if (skip_reason == FrameSkippedReason::kWaitingOnMain) {
+ auto reporter = RestoreReporterAtBeginImpl(id);
+ if (reporter) {
+ reporter->OnDidNotProduceFrame(skip_reason);
+ reporter->TerminateFrame(FrameTerminationStatus::kDidNotProduceFrame,
+ Now());
}
}
}
@@ -272,7 +287,7 @@ void CompositorFrameReportingController::DidNotProduceFrame(
void CompositorFrameReportingController::OnFinishImplFrame(
const viz::BeginFrameId& id) {
for (auto& reporter : reporters_) {
- if (reporter && reporter->frame_id_ == id) {
+ if (reporter && reporter->frame_id() == id) {
reporter->OnFinishImplFrame(Now());
return;
}
@@ -288,8 +303,10 @@ void CompositorFrameReportingController::DidPresentCompositorFrame(
break;
auto termination_status = FrameTerminationStatus::kPresentedFrame;
- if (submitted_frame->frame_token != frame_token)
+ if (submitted_frame->frame_token != frame_token ||
+ details.presentation_feedback.failed()) {
termination_status = FrameTerminationStatus::kDidNotPresentFrame;
+ }
submitted_frame->reporter->SetVizBreakdown(details);
submitted_frame->reporter->TerminateFrame(
@@ -356,7 +373,7 @@ bool CompositorFrameReportingController::CanSubmitImplFrame(
if (!reporters_[PipelineStage::kBeginImplFrame])
return false;
auto& reporter = reporters_[PipelineStage::kBeginImplFrame];
- return (reporter->frame_id_ == id && reporter->did_finish_impl_frame());
+ return (reporter->frame_id() == id && reporter->did_finish_impl_frame());
}
bool CompositorFrameReportingController::CanSubmitMainFrame(
@@ -364,7 +381,7 @@ bool CompositorFrameReportingController::CanSubmitMainFrame(
if (!reporters_[PipelineStage::kBeginMainFrame])
return false;
auto& reporter = reporters_[PipelineStage::kBeginMainFrame];
- return (reporter->frame_id_ == id && reporter->did_finish_impl_frame() &&
+ return (reporter->frame_id() == id && reporter->did_finish_impl_frame() &&
reporter->did_abort_main_frame());
}
@@ -373,15 +390,15 @@ CompositorFrameReportingController::RestoreReporterAtBeginImpl(
const viz::BeginFrameId& id) {
auto& main_reporter = reporters_[PipelineStage::kBeginMainFrame];
auto& commit_reporter = reporters_[PipelineStage::kCommit];
- if (main_reporter && main_reporter->frame_id_ == id)
+ if (main_reporter && main_reporter->frame_id() == id)
return main_reporter->CopyReporterAtBeginImplStage();
- if (commit_reporter && commit_reporter->frame_id_ == id)
+ if (commit_reporter && commit_reporter->frame_id() == id)
return commit_reporter->CopyReporterAtBeginImplStage();
return nullptr;
}
void CompositorFrameReportingController::SetUkmManager(UkmManager* manager) {
- latency_ukm_reporter_->SetUkmManager(manager);
+ latency_ukm_reporter_->set_ukm_manager(manager);
}
} // namespace cc
diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller.h b/chromium/cc/metrics/compositor_frame_reporting_controller.h
index fb6b219ce27..2659e92b59d 100644
--- a/chromium/cc/metrics/compositor_frame_reporting_controller.h
+++ b/chromium/cc/metrics/compositor_frame_reporting_controller.h
@@ -20,6 +20,7 @@ struct FrameTimingDetails;
}
namespace cc {
+class DroppedFrameCounter;
class UkmManager;
struct BeginMainFrameMetrics;
@@ -85,6 +86,10 @@ class CC_EXPORT CompositorFrameReportingController {
std::unique_ptr<CompositorFrameReporter>* reporters() { return reporters_; }
+ void SetDroppedFrameCounter(DroppedFrameCounter* counter) {
+ dropped_frame_counter_ = counter;
+ }
+
protected:
struct SubmittedCompositorFrame {
uint32_t frame_token;
@@ -98,6 +103,9 @@ class CC_EXPORT CompositorFrameReportingController {
base::TimeTicks Now() const;
bool HasReporterAt(PipelineStage stage) const;
+ bool next_activate_has_invalidation() const {
+ return next_activate_has_invalidation_;
+ }
private:
void AdvanceReporterStage(PipelineStage start, PipelineStage target);
@@ -128,6 +136,8 @@ class CC_EXPORT CompositorFrameReportingController {
base::circular_deque<SubmittedCompositorFrame> submitted_compositor_frames_;
const base::TickClock* tick_clock_ = base::DefaultTickClock::GetInstance();
+
+ DroppedFrameCounter* dropped_frame_counter_ = nullptr;
};
} // namespace cc
diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc
index ca70577d732..68bab0a45c1 100644
--- a/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc
+++ b/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc
@@ -524,13 +524,13 @@ TEST_F(CompositorFrameReportingControllerTest,
args_3.interval, 0};
reporting_controller_.DidPresentCompositorFrame(1, details);
- // Frame for |args_2| was dropped waiting on the main-thread.
+ // Frames for |args_1| and |args_2| were dropped waiting on the main-thread.
histogram_tester.ExpectBucketCount(
"CompositorLatency.Type",
- CompositorFrameReporter::FrameReportType::kDroppedFrame, 1);
+ CompositorFrameReporter::FrameReportType::kDroppedFrame, 2);
- // Frames for |args_1| and |args_3| were presented, although |args_1| missed
- // its deadline.
+ // Frames for |args_1| and |args_3| were presented with |args_3|, and |args_1|
+ // missed its deadline.
histogram_tester.ExpectBucketCount(
"CompositorLatency.Type",
CompositorFrameReporter::FrameReportType::kNonDroppedFrame, 2);
diff --git a/chromium/cc/metrics/compositor_timing_history.cc b/chromium/cc/metrics/compositor_timing_history.cc
index d0cc7d44262..99873a0152f 100644
--- a/chromium/cc/metrics/compositor_timing_history.cc
+++ b/chromium/cc/metrics/compositor_timing_history.cc
@@ -46,10 +46,6 @@ class CompositorTimingHistory::UMAReporter {
// crbug.com/758439: the following functions are used to report timing in
// certain conditions targeting blink / compositor animations.
// Only the renderer would get the meaningful data.
- virtual void AddDrawIntervalWithCompositedAnimations(
- base::TimeDelta duration) = 0;
- virtual void AddDrawIntervalWithMainThreadAnimations(
- base::TimeDelta duration) = 0;
virtual void AddDrawIntervalWithCustomPropertyAnimations(
base::TimeDelta duration) = 0;
};
@@ -310,18 +306,6 @@ class RendererUMAReporter : public CompositorTimingHistory::UMAReporter {
interval);
}
- void AddDrawIntervalWithCompositedAnimations(
- base::TimeDelta interval) override {
- UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED(
- "Scheduling.Renderer.DrawIntervalWithCompositedAnimations", interval);
- }
-
- void AddDrawIntervalWithMainThreadAnimations(
- base::TimeDelta interval) override {
- UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED(
- "Scheduling.Renderer.DrawIntervalWithMainThreadAnimations", interval);
- }
-
void AddDrawIntervalWithCustomPropertyAnimations(
base::TimeDelta interval) override {
UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED(
@@ -396,12 +380,6 @@ class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter {
// browser rendering fps is not at 60.
void AddDrawInterval(base::TimeDelta interval) override {}
- void AddDrawIntervalWithCompositedAnimations(
- base::TimeDelta interval) override {}
-
- void AddDrawIntervalWithMainThreadAnimations(
- base::TimeDelta interval) override {}
-
void AddDrawIntervalWithCustomPropertyAnimations(
base::TimeDelta interval) override {}
@@ -456,10 +434,6 @@ class NullUMAReporter : public CompositorTimingHistory::UMAReporter {
~NullUMAReporter() override = default;
void AddBeginMainFrameIntervalCritical(base::TimeDelta interval) override {}
void AddDrawInterval(base::TimeDelta interval) override {}
- void AddDrawIntervalWithCompositedAnimations(
- base::TimeDelta inverval) override {}
- void AddDrawIntervalWithMainThreadAnimations(
- base::TimeDelta inverval) override {}
void AddDrawIntervalWithCustomPropertyAnimations(
base::TimeDelta inverval) override {}
void AddBeginImplFrameLatency(base::TimeDelta delta) override {}
@@ -861,10 +835,6 @@ void CompositorTimingHistory::WillDraw() {
}
void CompositorTimingHistory::DidDraw(bool used_new_active_tree,
- size_t composited_animations_count,
- size_t main_thread_animations_count,
- bool current_frame_had_raf,
- bool next_frame_has_pending_raf,
bool has_custom_property_animations) {
DCHECK_NE(base::TimeTicks(), draw_start_time_);
base::TimeTicks draw_end_time = Now();
@@ -898,42 +868,16 @@ void CompositorTimingHistory::DidDraw(bool used_new_active_tree,
TRACE_ID_LOCAL(g_num_long_draw_intervals), draw_end_time);
g_num_long_draw_intervals++;
}
- if (composited_animations_count > 0 &&
- previous_frame_had_composited_animations_)
- uma_reporter_->AddDrawIntervalWithCompositedAnimations(draw_interval);
if (has_custom_property_animations &&
previous_frame_had_custom_property_animations_)
uma_reporter_->AddDrawIntervalWithCustomPropertyAnimations(draw_interval);
}
- previous_frame_had_composited_animations_ = composited_animations_count > 0;
previous_frame_had_custom_property_animations_ =
has_custom_property_animations;
draw_end_time_prev_ = draw_end_time;
- if (used_new_active_tree) {
- bool current_main_frame_had_visual_update =
- main_thread_animations_count > 0 || current_frame_had_raf;
- bool previous_main_frame_had_visual_update =
- previous_frame_had_main_thread_animations_ || previous_frame_had_raf_;
- if (current_main_frame_had_visual_update &&
- previous_main_frame_had_visual_update) {
- base::TimeDelta draw_interval =
- draw_end_time - new_active_tree_draw_end_time_prev_;
- uma_reporter_->AddDrawIntervalWithMainThreadAnimations(draw_interval);
- }
- previous_frame_had_main_thread_animations_ =
- main_thread_animations_count > 0;
- // It's possible that two consecutive main frames both run a rAF but are
- // separated by idle time (for example: calling requestAnimationFrame from a
- // setInterval function, with nothing else producing a main frame
- // in-between). To avoid incorrectly counting those cases as long draw
- // intervals, we only update previous_frame_had_raf_ if the current frame
- // also already has a future raf scheduled.
- previous_frame_had_raf_ =
- current_frame_had_raf && next_frame_has_pending_raf;
-
+ if (used_new_active_tree)
new_active_tree_draw_end_time_prev_ = draw_end_time;
- }
draw_start_time_ = base::TimeTicks();
}
diff --git a/chromium/cc/metrics/compositor_timing_history.h b/chromium/cc/metrics/compositor_timing_history.h
index 1d265a8419a..a4ff2503787 100644
--- a/chromium/cc/metrics/compositor_timing_history.h
+++ b/chromium/cc/metrics/compositor_timing_history.h
@@ -91,10 +91,6 @@ class CC_EXPORT CompositorTimingHistory {
void DidActivate();
void WillDraw();
void DidDraw(bool used_new_active_tree,
- size_t composited_animations_count,
- size_t main_thread_animations_count,
- bool current_frame_had_raf,
- bool next_frame_has_pending_raf,
bool has_custom_property_animations);
void DidSubmitCompositorFrame(
uint32_t frame_token,
@@ -140,7 +136,6 @@ class CC_EXPORT CompositorTimingHistory {
bool compositor_drawing_continuously_;
base::TimeTicks begin_main_frame_end_time_prev_;
base::TimeTicks new_active_tree_draw_end_time_prev_;
- base::TimeTicks new_active_tree_draw_end_time_prev_committing_continuously_;
base::TimeTicks draw_end_time_prev_;
// If you add any history here, please remember to reset it in
@@ -179,10 +174,7 @@ class CC_EXPORT CompositorTimingHistory {
CompositorFrameReportingController* compositor_frame_reporting_controller_;
// Used only for reporting animation targeted UMA.
- bool previous_frame_had_composited_animations_ = false;
- bool previous_frame_had_main_thread_animations_ = false;
bool previous_frame_had_custom_property_animations_ = false;
- bool previous_frame_had_raf_ = false;
TreePriority tree_priority_ = SAME_PRIORITY_FOR_BOTH_TREES;
};
diff --git a/chromium/cc/metrics/compositor_timing_history_unittest.cc b/chromium/cc/metrics/compositor_timing_history_unittest.cc
index b5b713355d2..82c35dcad08 100644
--- a/chromium/cc/metrics/compositor_timing_history_unittest.cc
+++ b/chromium/cc/metrics/compositor_timing_history_unittest.cc
@@ -4,7 +4,6 @@
#include "cc/metrics/compositor_timing_history.h"
-#include "base/test/metrics/histogram_tester.h"
#include "cc/debug/rendering_stats_instrumentation.h"
#include "cc/test/fake_compositor_frame_reporting_controller.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -53,43 +52,6 @@ class CompositorTimingHistoryTest : public testing::Test {
base::TimeTicks Now() { return now_; }
- // TODO(xidachen): the composited_animations_count should just be 0.
- void DrawMainFrame(int advance_ms,
- int composited_animations_count,
- int main_thread_animations_count,
- bool current_frame_had_raf = false,
- bool next_frame_has_pending_raf = false) {
- timing_history_.WillBeginMainFrame(getFakeBeginFrameArg());
- timing_history_.BeginMainFrameStarted(Now());
- timing_history_.WillCommit();
- timing_history_.DidCommit();
- timing_history_.ReadyToActivate();
- timing_history_.WillActivate();
- timing_history_.DidActivate();
- timing_history_.WillDraw();
- AdvanceNowBy(base::TimeDelta::FromMicroseconds(advance_ms));
- timing_history_.DidDraw(true, composited_animations_count,
- main_thread_animations_count, current_frame_had_raf,
- next_frame_has_pending_raf, false);
- }
-
- void DrawImplFrame(int advance_ms,
- int composited_animations_count,
- int main_thread_animations_count,
- bool has_custom_property_animation) {
- viz::BeginFrameArgs args_ = getFakeBeginFrameArg();
- timing_history_.WillBeginMainFrame(args_);
- timing_history_.BeginMainFrameStarted(Now());
- timing_history_.BeginMainFrameAborted(args_.frame_id);
- timing_history_.WillActivate();
- timing_history_.DidActivate();
- timing_history_.WillDraw();
- AdvanceNowBy(base::TimeDelta::FromMicroseconds(advance_ms));
- timing_history_.DidDraw(false, composited_animations_count,
- main_thread_animations_count, false, false,
- has_custom_property_animation);
- }
-
protected:
std::unique_ptr<RenderingStatsInstrumentation> rendering_stats_;
std::unique_ptr<CompositorFrameReportingController> reporting_controller_;
@@ -148,7 +110,7 @@ TEST_F(CompositorTimingHistoryTest, AllSequential_Commit) {
AdvanceNowBy(one_second);
timing_history_.WillDraw();
AdvanceNowBy(draw_duration);
- timing_history_.DidDraw(true, 0, 0, false, false, false);
+ timing_history_.DidDraw(true, false);
EXPECT_EQ(begin_main_frame_queue_duration,
timing_history_.BeginMainFrameQueueDurationCriticalEstimate());
@@ -200,7 +162,7 @@ TEST_F(CompositorTimingHistoryTest, AllSequential_BeginMainFrameAborted) {
AdvanceNowBy(one_second);
timing_history_.WillDraw();
AdvanceNowBy(draw_duration);
- timing_history_.DidDraw(false, 0, 0, false, false, false);
+ timing_history_.DidDraw(false, false);
EXPECT_EQ(base::TimeDelta(),
timing_history_.BeginMainFrameQueueDurationCriticalEstimate());
@@ -320,179 +282,5 @@ TEST_F(CompositorTimingHistoryTest, BeginMainFrames_NewCriticalSlower) {
timing_history_.BeginMainFrameQueueDurationNotCriticalEstimate());
}
-void TestAnimationUMA(const base::HistogramTester& histogram_tester,
- base::HistogramBase::Count composited_animation_frames,
- base::HistogramBase::Count main_thread_animation_frames) {
- histogram_tester.ExpectTotalCount(
- "Scheduling.Renderer.DrawIntervalWithCompositedAnimations2",
- composited_animation_frames);
- histogram_tester.ExpectTotalCount(
- "Scheduling.Renderer.DrawIntervalWithMainThreadAnimations2",
- main_thread_animation_frames);
-}
-
-TEST_F(CompositorTimingHistoryTest, AnimationNotReported) {
- base::HistogramTester histogram_tester;
-
- // Initial frame has no main-thread animations or rAF.
- DrawMainFrame(123, 0, 0);
- TestAnimationUMA(histogram_tester, 0, 0);
-
- // The next frame has one composited and one main thread animation running,
- // but as the previous frame had no animation we shouldn't report anything.
- DrawMainFrame(456, 1, 1);
- TestAnimationUMA(histogram_tester, 0, 0);
-
- DrawMainFrame(123, 0, 0);
- TestAnimationUMA(histogram_tester, 0, 0);
-
- // The next frame has just one main thread animation running, but again as the
- // previous frame had no animation we shouldn't report anything.
- DrawMainFrame(456, 0, 1);
- TestAnimationUMA(histogram_tester, 0, 0);
-
- DrawMainFrame(123, 0, 0);
- TestAnimationUMA(histogram_tester, 0, 0);
-
- // The next frame has no main thread animations but did have a rAF callback.
- // Again as the previous frame had no visual change we shouldn't report.
- DrawMainFrame(123, 0, 0, true);
- TestAnimationUMA(histogram_tester, 0, 0);
-
- DrawMainFrame(123, 0, 0);
- TestAnimationUMA(histogram_tester, 0, 0);
-
- // Finally, test the combination of both main thread animations and rAF
- // callbacks being called.
- DrawMainFrame(123, 1, 2, true);
- TestAnimationUMA(histogram_tester, 0, 0);
-}
-
-TEST_F(CompositorTimingHistoryTest, ConsecutiveFramesAnimationsReported) {
- base::HistogramTester histogram_tester;
-
- DrawMainFrame(123, 1, 0);
- TestAnimationUMA(histogram_tester, 0, 0);
-
- DrawMainFrame(456, 1, 0);
- TestAnimationUMA(histogram_tester, 1, 0);
- histogram_tester.ExpectBucketCount(
- "Scheduling.Renderer.DrawIntervalWithCompositedAnimations2", 456, 1);
-
- DrawMainFrame(321, 0, 1);
- TestAnimationUMA(histogram_tester, 1, 0);
-
- DrawMainFrame(654, 0, 1);
- TestAnimationUMA(histogram_tester, 1, 1);
- histogram_tester.ExpectBucketCount(
- "Scheduling.Renderer.DrawIntervalWithMainThreadAnimations2", 654, 1);
-
- DrawMainFrame(123, 0, 1);
- TestAnimationUMA(histogram_tester, 1, 2);
-
- DrawMainFrame(456, 0, 1);
- TestAnimationUMA(histogram_tester, 1, 3);
-
- // Main thread and rAF animations are both considered to be part of the same
- // animation type.
- DrawMainFrame(789, 0, 0, true, true);
- TestAnimationUMA(histogram_tester, 1, 4);
-
- DrawMainFrame(987, 0, 1, false);
- TestAnimationUMA(histogram_tester, 1, 5);
-
- // However if there is no pending rAF for a frame, we don't count the one
- // after it as being linked.
- DrawMainFrame(789, 0, 0, true, false);
- TestAnimationUMA(histogram_tester, 1, 6);
-
- DrawMainFrame(987, 0, 0, true, true);
- TestAnimationUMA(histogram_tester, 1, 6);
-}
-
-TEST_F(CompositorTimingHistoryTest, InterFrameAnimationsNotReported) {
- base::HistogramTester histogram_tester;
-
- DrawMainFrame(123, 0, 1);
- TestAnimationUMA(histogram_tester, 0, 0);
-
- // The previous frame had a main thread animation, where the current one is
- // main thread compositable animation, we don't measure the timing from a
- // different animation type.
- DrawMainFrame(456, 0, 1);
- TestAnimationUMA(histogram_tester, 0, 1);
-
- DrawMainFrame(321, 1, 0);
- TestAnimationUMA(histogram_tester, 0, 1);
-
- DrawMainFrame(654, 0, 1);
- TestAnimationUMA(histogram_tester, 0, 1);
-
- DrawMainFrame(123, 1, 0);
- TestAnimationUMA(histogram_tester, 0, 1);
-}
-
-TEST_F(CompositorTimingHistoryTest, AnimationsWithNewActiveTreeNotUsed) {
- base::HistogramTester histogram_tester;
-
- DrawImplFrame(123, 1, 1, false);
- TestAnimationUMA(histogram_tester, 0, 0);
-
- DrawImplFrame(456, 1, 0, false);
- TestAnimationUMA(histogram_tester, 1, 0);
- histogram_tester.ExpectBucketCount(
- "Scheduling.Renderer.DrawIntervalWithCompositedAnimations2", 456, 1);
-
- DrawMainFrame(321, 0, 1);
- TestAnimationUMA(histogram_tester, 1, 0);
-
- // This frame verifies that we record that there is a composited animation,
- // so in the next frame when there is a composited animation, we report it.
- DrawImplFrame(234, 1, 1, false);
- TestAnimationUMA(histogram_tester, 1, 0);
-
- // Even though the previous frame had no main thread animation, we report it
- // in this frame because the previous main frame had a main thread animation
- // with the time between main frame draws.
- DrawMainFrame(654, 1, 1);
- TestAnimationUMA(histogram_tester, 2, 1);
- histogram_tester.ExpectBucketCount(
- "Scheduling.Renderer.DrawIntervalWithCompositedAnimations2", 654, 1);
- // The recorded time for this main thread animation should be the total time
- // between the two new tree activations, which is 234 + 654 = 888.
- histogram_tester.ExpectBucketCount(
- "Scheduling.Renderer.DrawIntervalWithMainThreadAnimations2", 888, 1);
-
- DrawImplFrame(123, 1, 0, false);
- TestAnimationUMA(histogram_tester, 3, 1);
- histogram_tester.ExpectBucketCount(
- "Scheduling.Renderer.DrawIntervalWithCompositedAnimations2", 123, 1);
-}
-
-TEST_F(CompositorTimingHistoryTest, CustomPropertyAnimations) {
- base::HistogramTester histogram_tester;
-
- DrawImplFrame(123, 1, 0, true);
- TestAnimationUMA(histogram_tester, 0, 0);
-
- DrawImplFrame(456, 1, 0, true);
- TestAnimationUMA(histogram_tester, 1, 0);
-
- histogram_tester.ExpectBucketCount(
- "Scheduling.Renderer.DrawIntervalWithCompositedAnimations2", 456, 1);
- histogram_tester.ExpectBucketCount(
- "Scheduling.Renderer.DrawIntervalWithCustomPropertyAnimations2", 456, 1);
-
- DrawImplFrame(1234, 1, 0, false);
- DrawImplFrame(2345, 1, 0, true);
- TestAnimationUMA(histogram_tester, 3, 0);
- histogram_tester.ExpectBucketCount(
- "Scheduling.Renderer.DrawIntervalWithCompositedAnimations2", 2345, 1);
- // This impl frame does have custom property animation, but the previous impl
- // frame doesn't, so we won't report it.
- histogram_tester.ExpectBucketCount(
- "Scheduling.Renderer.DrawIntervalWithCustomPropertyAnimations2", 2345, 0);
-}
-
} // namespace
} // namespace cc
diff --git a/chromium/cc/metrics/dropped_frame_counter.cc b/chromium/cc/metrics/dropped_frame_counter.cc
new file mode 100644
index 00000000000..56564a2415f
--- /dev/null
+++ b/chromium/cc/metrics/dropped_frame_counter.cc
@@ -0,0 +1,45 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/metrics/dropped_frame_counter.h"
+
+#include <stddef.h>
+
+#include <limits>
+
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+
+namespace cc {
+
+DroppedFrameCounter::DroppedFrameCounter() = default;
+
+uint32_t DroppedFrameCounter::GetAverageThroughput() const {
+ size_t good_frames = 0;
+ for (auto it = --end(); it; --it) {
+ if (**it == kFrameStateComplete)
+ ++good_frames;
+ }
+ double throughput = 100. * good_frames / ring_buffer_.BufferSize();
+ return static_cast<uint32_t>(throughput);
+}
+
+void DroppedFrameCounter::AddGoodFrame() {
+ ring_buffer_.SaveToBuffer(kFrameStateComplete);
+ ++total_frames_;
+}
+
+void DroppedFrameCounter::AddPartialFrame() {
+ ring_buffer_.SaveToBuffer(kFrameStatePartial);
+ ++total_frames_;
+ ++total_partial_;
+}
+
+void DroppedFrameCounter::AddDroppedFrame() {
+ ring_buffer_.SaveToBuffer(kFrameStateDropped);
+ ++total_frames_;
+ ++total_dropped_;
+}
+
+} // namespace cc
diff --git a/chromium/cc/metrics/dropped_frame_counter.h b/chromium/cc/metrics/dropped_frame_counter.h
new file mode 100644
index 00000000000..4929295ddce
--- /dev/null
+++ b/chromium/cc/metrics/dropped_frame_counter.h
@@ -0,0 +1,55 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_METRICS_DROPPED_FRAME_COUNTER_H_
+#define CC_METRICS_DROPPED_FRAME_COUNTER_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/containers/ring_buffer.h"
+#include "base/time/time.h"
+
+namespace cc {
+
+// This class maintains a counter for produced/dropped frames, and can be used
+// to estimate the recent throughput.
+class DroppedFrameCounter {
+ public:
+ enum FrameState {
+ kFrameStateDropped,
+ kFrameStatePartial,
+ kFrameStateComplete
+ };
+ DroppedFrameCounter();
+
+ DroppedFrameCounter(const DroppedFrameCounter&) = delete;
+ DroppedFrameCounter& operator=(const DroppedFrameCounter&) = delete;
+
+ size_t frame_history_size() const { return ring_buffer_.BufferSize(); }
+ size_t total_frames() const { return total_frames_; }
+ size_t total_compositor_dropped() const { return total_dropped_; }
+ size_t total_main_dropped() const { return total_partial_; }
+
+ uint32_t GetAverageThroughput() const;
+
+ typedef base::RingBuffer<FrameState, 180> RingBufferType;
+ RingBufferType::Iterator begin() const { return ring_buffer_.Begin(); }
+ RingBufferType::Iterator end() const { return ring_buffer_.End(); }
+
+ void AddGoodFrame();
+ void AddPartialFrame();
+ void AddDroppedFrame();
+
+ private:
+ RingBufferType ring_buffer_;
+ size_t total_frames_ = 0;
+ size_t total_partial_ = 0;
+ size_t total_dropped_ = 0;
+};
+
+} // namespace cc
+
+#endif // CC_METRICS_DROPPED_FRAME_COUNTER_H_
diff --git a/chromium/cc/metrics/event_metrics.cc b/chromium/cc/metrics/event_metrics.cc
index 326eaee70a1..951cdfc2ceb 100644
--- a/chromium/cc/metrics/event_metrics.cc
+++ b/chromium/cc/metrics/event_metrics.cc
@@ -4,98 +4,130 @@
#include "cc/metrics/event_metrics.h"
+#include <utility>
+
#include "base/check.h"
#include "base/memory/ptr_util.h"
#include "base/notreached.h"
#include "base/stl_util.h"
namespace cc {
+namespace {
+
+constexpr struct {
+ EventMetrics::EventType metrics_event_type;
+ ui::EventType ui_event_type;
+ const char* name;
+} kWhitelistedEvents[] = {
+#define EVENT_TYPE(name, ui_type) \
+ { EventMetrics::EventType::k##name, ui_type, #name }
+ EVENT_TYPE(MousePressed, ui::ET_MOUSE_PRESSED),
+ EVENT_TYPE(MouseReleased, ui::ET_MOUSE_RELEASED),
+ EVENT_TYPE(MouseWheel, ui::ET_MOUSEWHEEL),
+ EVENT_TYPE(KeyPressed, ui::ET_KEY_PRESSED),
+ EVENT_TYPE(KeyReleased, ui::ET_KEY_RELEASED),
+ EVENT_TYPE(TouchPressed, ui::ET_TOUCH_PRESSED),
+ EVENT_TYPE(TouchReleased, ui::ET_TOUCH_RELEASED),
+ EVENT_TYPE(TouchMoved, ui::ET_TOUCH_MOVED),
+ EVENT_TYPE(GestureScrollBegin, ui::ET_GESTURE_SCROLL_BEGIN),
+ EVENT_TYPE(GestureScrollUpdate, ui::ET_GESTURE_SCROLL_UPDATE),
+ EVENT_TYPE(GestureScrollEnd, ui::ET_GESTURE_SCROLL_END),
+ EVENT_TYPE(GestureDoubleTap, ui::ET_GESTURE_DOUBLE_TAP),
+ EVENT_TYPE(GestureLongPress, ui::ET_GESTURE_LONG_PRESS),
+ EVENT_TYPE(GestureLongTap, ui::ET_GESTURE_LONG_TAP),
+ EVENT_TYPE(GestureShowPress, ui::ET_GESTURE_SHOW_PRESS),
+ EVENT_TYPE(GestureTap, ui::ET_GESTURE_TAP),
+ EVENT_TYPE(GestureTapCancel, ui::ET_GESTURE_TAP_CANCEL),
+ EVENT_TYPE(GestureTapDown, ui::ET_GESTURE_TAP_DOWN),
+ EVENT_TYPE(GestureTapUnconfirmed, ui::ET_GESTURE_TAP_UNCONFIRMED),
+ EVENT_TYPE(GestureTwoFingerTap, ui::ET_GESTURE_TWO_FINGER_TAP),
+#undef EVENT_TYPE
+};
+static_assert(base::size(kWhitelistedEvents) ==
+ static_cast<int>(EventMetrics::EventType::kMaxValue) + 1,
+ "EventMetrics::EventType has changed.");
+
+constexpr struct {
+ EventMetrics::ScrollType metrics_scroll_type;
+ ui::ScrollInputType ui_scroll_type;
+ const char* name;
+} kScrollTypes[] = {
+#define SCROLL_TYPE(name, ui_type) \
+ { EventMetrics::ScrollType::k##name, ui_type, #name }
+ SCROLL_TYPE(Autoscroll, ui::ScrollInputType::kAutoscroll),
+ SCROLL_TYPE(Scrollbar, ui::ScrollInputType::kScrollbar),
+ SCROLL_TYPE(Touchscreen, ui::ScrollInputType::kTouchscreen),
+ SCROLL_TYPE(Wheel, ui::ScrollInputType::kWheel),
+#undef SCROLL_TYPE
+};
+static_assert(base::size(kScrollTypes) ==
+ static_cast<int>(EventMetrics::ScrollType::kMaxValue) + 1,
+ "EventMetrics::ScrollType has changed.");
+
+base::Optional<EventMetrics::EventType> ToWhitelistedEventType(
+ ui::EventType ui_event_type) {
+ for (size_t i = 0; i < base::size(kWhitelistedEvents); i++) {
+ if (ui_event_type == kWhitelistedEvents[i].ui_event_type) {
+ EventMetrics::EventType metrics_event_type =
+ static_cast<EventMetrics::EventType>(i);
+ DCHECK_EQ(metrics_event_type, kWhitelistedEvents[i].metrics_event_type);
+ return metrics_event_type;
+ }
+ }
+ return base::nullopt;
+}
+
+base::Optional<EventMetrics::ScrollType> ToScrollType(
+ const base::Optional<ui::ScrollInputType>& scroll_input_type) {
+ if (!scroll_input_type)
+ return base::nullopt;
+
+ for (size_t i = 0; i < base::size(kScrollTypes); i++) {
+ if (*scroll_input_type == kScrollTypes[i].ui_scroll_type) {
+ EventMetrics::ScrollType metrics_scroll_type =
+ static_cast<EventMetrics::ScrollType>(i);
+ DCHECK_EQ(metrics_scroll_type, kScrollTypes[i].metrics_scroll_type);
+ return metrics_scroll_type;
+ }
+ }
+ NOTREACHED();
+ return base::nullopt;
+}
+
+} // namespace
std::unique_ptr<EventMetrics> EventMetrics::Create(
ui::EventType type,
base::TimeTicks time_stamp,
base::Optional<ui::ScrollInputType> scroll_input_type) {
- switch (type) {
- case ui::ET_MOUSE_PRESSED:
- case ui::ET_MOUSE_RELEASED:
- case ui::ET_MOUSEWHEEL:
- case ui::ET_KEY_PRESSED:
- case ui::ET_KEY_RELEASED:
- case ui::ET_TOUCH_PRESSED:
- case ui::ET_TOUCH_RELEASED:
- case ui::ET_TOUCH_MOVED:
- case ui::ET_GESTURE_SCROLL_BEGIN:
- case ui::ET_GESTURE_SCROLL_UPDATE:
- case ui::ET_GESTURE_SCROLL_END:
- return base::WrapUnique(
- new EventMetrics(type, time_stamp, scroll_input_type));
- default:
- return nullptr;
- }
+ base::Optional<EventType> whitelisted_type = ToWhitelistedEventType(type);
+ if (!whitelisted_type)
+ return nullptr;
+ return base::WrapUnique(new EventMetrics(*whitelisted_type, time_stamp,
+ ToScrollType(scroll_input_type)));
}
-EventMetrics::EventMetrics(
- ui::EventType type,
- base::TimeTicks time_stamp,
- base::Optional<ui::ScrollInputType> scroll_input_type)
- : type_(type),
- time_stamp_(time_stamp),
- scroll_input_type_(scroll_input_type) {}
+EventMetrics::EventMetrics(EventType type,
+ base::TimeTicks time_stamp,
+ base::Optional<ScrollType> scroll_type)
+ : type_(type), time_stamp_(time_stamp), scroll_type_(scroll_type) {}
EventMetrics::EventMetrics(const EventMetrics&) = default;
EventMetrics& EventMetrics::operator=(const EventMetrics&) = default;
const char* EventMetrics::GetTypeName() const {
- switch (type_) {
- case ui::ET_MOUSE_PRESSED:
- return "MousePressed";
- case ui::ET_MOUSE_RELEASED:
- return "MouseReleased";
- case ui::ET_MOUSEWHEEL:
- return "MouseWheel";
- case ui::ET_KEY_PRESSED:
- // TODO(crbug/1071645): Currently, all ET_KEY_PRESSED events are reported
- // under EventLatency.KeyPressed histogram. This includes both key-down
- // and key-char events. Consider reporting them separately.
- return "KeyPressed";
- case ui::ET_KEY_RELEASED:
- return "KeyReleased";
- case ui::ET_TOUCH_PRESSED:
- return "TouchPressed";
- case ui::ET_TOUCH_RELEASED:
- return "TouchReleased";
- case ui::ET_TOUCH_MOVED:
- return "TouchMoved";
- case ui::ET_GESTURE_SCROLL_BEGIN:
- return "GestureScrollBegin";
- case ui::ET_GESTURE_SCROLL_UPDATE:
- return "GestureScrollUpdate";
- case ui::ET_GESTURE_SCROLL_END:
- return "GestureScrollEnd";
- default:
- NOTREACHED();
- return nullptr;
- }
+ return kWhitelistedEvents[static_cast<int>(type_)].name;
}
const char* EventMetrics::GetScrollTypeName() const {
- DCHECK(scroll_input_type_) << "Event is not a scroll event";
-
- switch (*scroll_input_type_) {
- case ui::ScrollInputType::kTouchscreen:
- return "Touchscreen";
- case ui::ScrollInputType::kWheel:
- return "Wheel";
- case ui::ScrollInputType::kAutoscroll:
- return "Autoscroll";
- case ui::ScrollInputType::kScrollbar:
- return "Scrollbar";
- }
+ DCHECK(scroll_type_) << "Event is not a scroll event.";
+
+ return kScrollTypes[static_cast<int>(*scroll_type_)].name;
}
bool EventMetrics::operator==(const EventMetrics& other) const {
- return std::tie(type_, time_stamp_, scroll_input_type_) ==
- std::tie(other.type_, other.time_stamp_, other.scroll_input_type_);
+ return std::tie(type_, time_stamp_, scroll_type_) ==
+ std::tie(other.type_, other.time_stamp_, other.scroll_type_);
}
// EventMetricsSet
diff --git a/chromium/cc/metrics/event_metrics.h b/chromium/cc/metrics/event_metrics.h
index 8de2fc58be9..df37365b7ed 100644
--- a/chromium/cc/metrics/event_metrics.h
+++ b/chromium/cc/metrics/event_metrics.h
@@ -6,6 +6,7 @@
#define CC_METRICS_EVENT_METRICS_H_
#include <memory>
+#include <vector>
#include "base/optional.h"
#include "base/time/time.h"
@@ -19,6 +20,45 @@ namespace cc {
// latency metrics.
class CC_EXPORT EventMetrics {
public:
+ // Whitelisted event types. This list should be in the same order as values of
+ // EventLatencyEventType enum from enums.xml file.
+ enum class EventType {
+ kMousePressed,
+ kMouseReleased,
+ kMouseWheel,
+ // TODO(crbug/1071645): Currently, all ET_KEY_PRESSED events are reported
+ // under EventLatency.KeyPressed histogram. This includes both key-down and
+ // key-char events. Consider reporting them separately.
+ kKeyPressed,
+ kKeyReleased,
+ kTouchPressed,
+ kTouchReleased,
+ kTouchMoved,
+ kGestureScrollBegin,
+ kGestureScrollUpdate,
+ kGestureScrollEnd,
+ kGestureDoubleTap,
+ kGestureLongPress,
+ kGestureLongTap,
+ kGestureShowPress,
+ kGestureTap,
+ kGestureTapCancel,
+ kGestureTapDown,
+ kGestureTapUnconfirmed,
+ kGestureTwoFingerTap,
+ kMaxValue = kGestureTwoFingerTap,
+ };
+
+ // Type of scroll events. This list should be in the same order as values of
+ // EventLatencyScrollInputType enum from enums.xml file.
+ enum class ScrollType {
+ kAutoscroll,
+ kScrollbar,
+ kTouchscreen,
+ kWheel,
+ kMaxValue = kWheel,
+ };
+
// Returns a new instance if |type| is a whitelisted event type. Otherwise,
// returns nullptr.
static std::unique_ptr<EventMetrics> Create(
@@ -29,36 +69,33 @@ class CC_EXPORT EventMetrics {
EventMetrics(const EventMetrics&);
EventMetrics& operator=(const EventMetrics&);
- // Returns a string representing event type. Should only be called for
- // whitelisted event types.
- const char* GetTypeName() const;
-
- // Returns a string representing scroll input type. Should only be called for
- // scroll events.
- const char* GetScrollTypeName() const;
+ EventType type() const { return type_; }
- ui::EventType type() const { return type_; }
+ // Returns a string representing event type.
+ const char* GetTypeName() const;
base::TimeTicks time_stamp() const { return time_stamp_; }
- const base::Optional<ui::ScrollInputType>& scroll_input_type() const {
- return scroll_input_type_;
- }
+ const base::Optional<ScrollType>& scroll_type() const { return scroll_type_; }
+
+ // Returns a string representing input type for a scroll event. Should only be
+ // called for scroll events.
+ const char* GetScrollTypeName() const;
// Used in tests to check expectations on EventMetrics objects.
bool operator==(const EventMetrics& other) const;
private:
- EventMetrics(ui::EventType type,
+ EventMetrics(EventType type,
base::TimeTicks time_stamp,
- base::Optional<ui::ScrollInputType> scroll_input_type);
+ base::Optional<ScrollType> scroll_type);
- ui::EventType type_;
+ EventType type_;
base::TimeTicks time_stamp_;
// Only available for scroll events and represents the type of input device
// for the event.
- base::Optional<ui::ScrollInputType> scroll_input_type_;
+ base::Optional<ScrollType> scroll_type_;
};
// Struct storing event metrics from both main and impl threads.
diff --git a/chromium/cc/metrics/frame_sequence_metrics.cc b/chromium/cc/metrics/frame_sequence_metrics.cc
index 0cefe55fd51..5933096b855 100644
--- a/chromium/cc/metrics/frame_sequence_metrics.cc
+++ b/chromium/cc/metrics/frame_sequence_metrics.cc
@@ -4,6 +4,10 @@
#include "cc/metrics/frame_sequence_metrics.h"
+#include <memory>
+#include <string>
+#include <utility>
+
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/strcat.h"
@@ -91,13 +95,20 @@ bool IsInteractionType(FrameSequenceTrackerType sequence_type) {
FrameSequenceMetrics::FrameSequenceMetrics(FrameSequenceTrackerType type,
ThroughputUkmReporter* ukm_reporter)
: type_(type), throughput_ukm_reporter_(ukm_reporter) {
- TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
- "cc,benchmark", "FrameSequenceTracker", TRACE_ID_LOCAL(this), "name",
- FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type_));
}
-FrameSequenceMetrics::~FrameSequenceMetrics() {
+FrameSequenceMetrics::~FrameSequenceMetrics() = default;
+
+void FrameSequenceMetrics::ReportLeftoverData() {
if (HasDataLeftForReporting()) {
+ // Do this before ReportMetrics() which clears the throughput data.
+ // TODO(xidachen): Find a way to make ThroughputUkmReporter to directly talk
+ // to LayerTreeHostClient, and submit throughput data. Instead of storing
+ // the values in ThroughputUkmReporter.
+ if (type_ == FrameSequenceTrackerType::kUniversal &&
+ HasEnoughDataForReporting()) {
+ throughput_ukm_reporter_->ComputeUniversalThroughput(this);
+ }
ReportMetrics();
}
}
@@ -136,6 +147,8 @@ FrameSequenceMetrics::ThreadType FrameSequenceMetrics::GetEffectiveThread()
return ThreadType::kSlower;
case FrameSequenceTrackerType::kCustom:
+ return ThreadType::kMain;
+
case FrameSequenceTrackerType::kMaxType:
NOTREACHED();
}
@@ -144,6 +157,8 @@ FrameSequenceMetrics::ThreadType FrameSequenceMetrics::GetEffectiveThread()
void FrameSequenceMetrics::Merge(
std::unique_ptr<FrameSequenceMetrics> metrics) {
+ // Merging custom trackers are not supported.
+ DCHECK_NE(type_, FrameSequenceTrackerType::kCustom);
DCHECK_EQ(type_, metrics->type_);
DCHECK_EQ(GetEffectiveThread(), metrics->GetEffectiveThread());
impl_throughput_.Merge(metrics->impl_throughput_);
@@ -169,6 +184,16 @@ bool FrameSequenceMetrics::HasDataLeftForReporting() const {
main_throughput_.frames_expected > 0;
}
+void FrameSequenceMetrics::AdoptTrace(FrameSequenceMetrics* adopt_from) {
+ DCHECK(!trace_data_.trace_id);
+ trace_data_.trace_id = adopt_from->trace_data_.trace_id;
+ adopt_from->trace_data_.trace_id = nullptr;
+}
+
+void FrameSequenceMetrics::AdvanceTrace(base::TimeTicks timestamp) {
+ trace_data_.Advance(timestamp);
+}
+
void FrameSequenceMetrics::ComputeAggregatedThroughput() {
// Whenever we are expecting and producing main frames, we are expecting and
// producing impl frames as well. As an example, if we expect one main frame
@@ -183,10 +208,11 @@ void FrameSequenceMetrics::ComputeAggregatedThroughput() {
void FrameSequenceMetrics::ReportMetrics() {
DCHECK_LE(impl_throughput_.frames_produced, impl_throughput_.frames_expected);
DCHECK_LE(main_throughput_.frames_produced, main_throughput_.frames_expected);
- TRACE_EVENT_NESTABLE_ASYNC_END2(
- "cc,benchmark", "FrameSequenceTracker", TRACE_ID_LOCAL(this), "args",
- ThroughputData::ToTracedValue(impl_throughput_, main_throughput_),
- "checkerboard", frames_checkerboarded_);
+ DCHECK_LE(aggregated_throughput_.frames_produced,
+ aggregated_throughput_.frames_expected);
+
+ // Terminates |trace_data_| for all types of FrameSequenceTracker.
+ trace_data_.Terminate();
if (type_ == FrameSequenceTrackerType::kCustom) {
DCHECK(!custom_reporter_.is_null());
@@ -199,8 +225,6 @@ void FrameSequenceMetrics::ReportMetrics() {
return;
}
- ComputeAggregatedThroughput();
-
// Report the throughput metrics.
base::Optional<int> impl_throughput_percent = ThroughputData::ReportHistogram(
this, ThreadType::kCompositor,
@@ -220,7 +244,8 @@ void FrameSequenceMetrics::ReportMetrics() {
this, ThreadType::kSlower,
GetIndexForMetric(FrameSequenceMetrics::ThreadType::kSlower, type_),
aggregated_throughput_);
- if (aggregated_throughput_percent.has_value() && throughput_ukm_reporter_) {
+ if (aggregated_throughput_percent.has_value() && throughput_ukm_reporter_ &&
+ type_ != FrameSequenceTrackerType::kUniversal) {
throughput_ukm_reporter_->ReportThroughputUkm(
aggregated_throughput_percent, impl_throughput_percent,
main_throughput_percent, type_);
@@ -312,9 +337,7 @@ base::Optional<int> FrameSequenceMetrics::ThroughputData::ReportHistogram(
// Throughput means the percent of frames that was expected to show on the
// screen but didn't. In other words, the lower the throughput is, the
// smoother user experience.
- const int percent =
- std::ceil(100 * (data.frames_expected - data.frames_produced) /
- static_cast<double>(data.frames_expected));
+ const int percent = data.DroppedFramePercent();
const bool is_animation =
ShouldReportForAnimation(sequence_type, thread_type);
@@ -323,6 +346,11 @@ base::Optional<int> FrameSequenceMetrics::ThroughputData::ReportHistogram(
ThroughputUkmReporter* const ukm_reporter = metrics->ukm_reporter();
if (is_animation) {
+ TRACE_EVENT_INSTANT2("cc,benchmark", "PercentDroppedFrames.AllAnimations",
+ TRACE_EVENT_SCOPE_THREAD, "frames_expected",
+ data.frames_expected, "frames_produced",
+ data.frames_produced);
+
UMA_HISTOGRAM_PERCENTAGE(
"Graphics.Smoothness.PercentDroppedFrames.AllAnimations", percent);
if (ukm_reporter) {
@@ -332,6 +360,10 @@ base::Optional<int> FrameSequenceMetrics::ThroughputData::ReportHistogram(
}
if (is_interaction) {
+ TRACE_EVENT_INSTANT2("cc,benchmark", "PercentDroppedFrames.AllInteractions",
+ TRACE_EVENT_SCOPE_THREAD, "frames_expected",
+ data.frames_expected, "frames_produced",
+ data.frames_produced);
UMA_HISTOGRAM_PERCENTAGE(
"Graphics.Smoothness.PercentDroppedFrames.AllInteractions", percent);
if (ukm_reporter) {
@@ -341,6 +373,10 @@ base::Optional<int> FrameSequenceMetrics::ThroughputData::ReportHistogram(
}
if (is_animation || is_interaction) {
+ TRACE_EVENT_INSTANT2("cc,benchmark", "PercentDroppedFrames.AllSequences",
+ TRACE_EVENT_SCOPE_THREAD, "frames_expected",
+ data.frames_expected, "frames_produced",
+ data.frames_produced);
UMA_HISTOGRAM_PERCENTAGE(
"Graphics.Smoothness.PercentDroppedFrames.AllSequences", percent);
if (ukm_reporter) {
@@ -368,4 +404,62 @@ base::Optional<int> FrameSequenceMetrics::ThroughputData::ReportHistogram(
return percent;
}
+std::unique_ptr<base::trace_event::TracedValue>
+FrameSequenceMetrics::ThroughputData::ToTracedValue(
+ const ThroughputData& impl,
+ const ThroughputData& main,
+ ThreadType effective_thread) {
+ auto dict = std::make_unique<base::trace_event::TracedValue>();
+ if (effective_thread == ThreadType::kMain) {
+ dict->SetInteger("main-frames-produced", main.frames_produced);
+ dict->SetInteger("main-frames-expected", main.frames_expected);
+ } else {
+ dict->SetInteger("impl-frames-produced", impl.frames_produced);
+ dict->SetInteger("impl-frames-expected", impl.frames_expected);
+ }
+ return dict;
+}
+
+FrameSequenceMetrics::TraceData::TraceData(FrameSequenceMetrics* m)
+ : metrics(m) {
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED("cc,benchmark", &enabled);
+}
+
+FrameSequenceMetrics::TraceData::~TraceData() = default;
+
+void FrameSequenceMetrics::TraceData::Terminate() {
+ if (!enabled || !trace_id)
+ return;
+ TRACE_EVENT_NESTABLE_ASYNC_END2(
+ "cc,benchmark", "FrameSequenceTracker", TRACE_ID_LOCAL(trace_id), "args",
+ ThroughputData::ToTracedValue(metrics->impl_throughput(),
+ metrics->main_throughput(),
+ metrics->GetEffectiveThread()),
+ "checkerboard", metrics->frames_checkerboarded());
+ trace_id = nullptr;
+}
+
+void FrameSequenceMetrics::TraceData::Advance(base::TimeTicks new_timestamp) {
+ if (!enabled)
+ return;
+ if (!trace_id) {
+ trace_id = this;
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1(
+ "cc,benchmark", "FrameSequenceTracker", TRACE_ID_LOCAL(trace_id),
+ this->last_timestamp, "name",
+ FrameSequenceTracker::GetFrameSequenceTrackerTypeName(metrics->type()));
+ }
+ // Use different names, because otherwise the trace-viewer shows the slices in
+ // the same color, and that makes it difficult to tell the traces apart from
+ // each other.
+ const char* trace_names[] = {"Frame", "Frame ", "Frame "};
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
+ "cc,benchmark", trace_names[++this->frame_count % 3],
+ TRACE_ID_LOCAL(trace_id), this->last_timestamp);
+ TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
+ "cc,benchmark", trace_names[this->frame_count % 3],
+ TRACE_ID_LOCAL(trace_id), new_timestamp);
+ this->last_timestamp = new_timestamp;
+}
+
} // namespace cc
diff --git a/chromium/cc/metrics/frame_sequence_metrics.h b/chromium/cc/metrics/frame_sequence_metrics.h
index 0417899c3a9..3a77fc57775 100644
--- a/chromium/cc/metrics/frame_sequence_metrics.h
+++ b/chromium/cc/metrics/frame_sequence_metrics.h
@@ -5,6 +5,8 @@
#ifndef CC_METRICS_FRAME_SEQUENCE_METRICS_H_
#define CC_METRICS_FRAME_SEQUENCE_METRICS_H_
+#include <memory>
+
#include "base/callback.h"
#include "base/optional.h"
#include "base/trace_event/traced_value.h"
@@ -44,7 +46,8 @@ class CC_EXPORT FrameSequenceMetrics {
struct ThroughputData {
static std::unique_ptr<base::trace_event::TracedValue> ToTracedValue(
const ThroughputData& impl,
- const ThroughputData& main);
+ const ThroughputData& main,
+ ThreadType effective_thred);
// Returns the throughput in percent, a return value of base::nullopt
// indicates that no throughput metric is reported.
@@ -62,6 +65,13 @@ class CC_EXPORT FrameSequenceMetrics {
#endif
}
+ int DroppedFramePercent() const {
+ if (frames_expected == 0)
+ return 0;
+ return std::ceil(100 * (frames_expected - frames_produced) /
+ static_cast<double>(frames_expected));
+ }
+
// Tracks the number of frames that were expected to be shown during this
// frame-sequence.
uint32_t frames_expected = 0;
@@ -113,10 +123,31 @@ class CC_EXPORT FrameSequenceMetrics {
return throughput_ukm_reporter_;
}
+ // Must be called before destructor.
+ void ReportLeftoverData();
+
+ void AdoptTrace(FrameSequenceMetrics* adopt_from);
+ void AdvanceTrace(base::TimeTicks timestamp);
+
private:
void ComputeAggregatedThroughput();
+
const FrameSequenceTrackerType type_;
+ // Tracks some data to generate useful trace events.
+ struct TraceData {
+ explicit TraceData(FrameSequenceMetrics* metrics);
+ ~TraceData();
+ FrameSequenceMetrics* metrics;
+ base::TimeTicks last_timestamp = base::TimeTicks::Now();
+ int frame_count = 0;
+ bool enabled = false;
+ void* trace_id = nullptr;
+
+ void Advance(base::TimeTicks new_timestamp);
+ void Terminate();
+ } trace_data_{this};
+
// Pointer to the reporter owned by the FrameSequenceTrackerCollection.
ThroughputUkmReporter* const throughput_ukm_reporter_;
diff --git a/chromium/cc/metrics/frame_sequence_metrics_unittest.cc b/chromium/cc/metrics/frame_sequence_metrics_unittest.cc
index ae43da19a24..c2a1f76babb 100644
--- a/chromium/cc/metrics/frame_sequence_metrics_unittest.cc
+++ b/chromium/cc/metrics/frame_sequence_metrics_unittest.cc
@@ -6,6 +6,9 @@
#include "base/macros.h"
#include "base/test/metrics/histogram_tester.h"
+#include "cc/metrics/throughput_ukm_reporter.h"
+#include "cc/trees/ukm_manager.h"
+#include "components/ukm/test_ukm_recorder.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -28,6 +31,7 @@ TEST(FrameSequenceMetricsTest, AggregatedThroughputClearedAfterReport) {
nullptr);
first.impl_throughput().frames_expected = 200u;
first.impl_throughput().frames_produced = 190u;
+ first.aggregated_throughput().frames_expected = 170u;
first.aggregated_throughput().frames_produced = 150u;
first.ReportMetrics();
@@ -35,6 +39,28 @@ TEST(FrameSequenceMetricsTest, AggregatedThroughputClearedAfterReport) {
EXPECT_EQ(first.aggregated_throughput().frames_produced, 0u);
}
+// Test that ThroughputUkmReporter::ReportThroughputUkm isn't called for the
+// kUniversal tracker.
+TEST(FrameSequenceMetricsTest, UniversalNotReportUkmAtRenderer) {
+ auto recorder = std::make_unique<ukm::TestUkmRecorder>();
+ auto ukm_manager = std::make_unique<UkmManager>(std::move(recorder));
+ ThroughputUkmReporter reporter(ukm_manager.get());
+ auto metric = std::make_unique<FrameSequenceMetrics>(
+ FrameSequenceTrackerType::kUniversal, &reporter);
+
+ metric->impl_throughput().frames_expected = 200u;
+ metric->impl_throughput().frames_produced = 190u;
+ metric->aggregated_throughput().frames_expected = 170u;
+ metric->aggregated_throughput().frames_produced = 150u;
+ metric->ReportMetrics();
+
+ // The corresponding |samples_to_next_event_| element is 0 if the
+ // ReportThroughputUkm isn't called.
+ EXPECT_EQ(reporter.GetSamplesToNextEventForTesting(
+ static_cast<int>(FrameSequenceTrackerType::kUniversal)),
+ 1u);
+}
+
TEST(FrameSequenceMetricsTest, MergeMetrics) {
// Create a metric with only a small number of frames. It shouldn't report any
// metrics.
diff --git a/chromium/cc/metrics/frame_sequence_tracker.cc b/chromium/cc/metrics/frame_sequence_tracker.cc
index f1a1fd9b995..dec83686b65 100644
--- a/chromium/cc/metrics/frame_sequence_tracker.cc
+++ b/chromium/cc/metrics/frame_sequence_tracker.cc
@@ -4,7 +4,13 @@
#include "cc/metrics/frame_sequence_tracker.h"
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+
#include "base/bind.h"
+#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_macros.h"
#include "base/trace_event/trace_event.h"
@@ -27,6 +33,8 @@
namespace cc {
+using ThreadType = FrameSequenceMetrics::ThreadType;
+
// In the |TRACKER_TRACE_STREAM|, we mod the numbers such as frame sequence
// number, or frame token, such that the debug string is not too long.
constexpr int kDebugStrMod = 1000;
@@ -63,9 +71,9 @@ FrameSequenceTracker::FrameSequenceTracker(
FrameSequenceTrackerType type,
ThroughputUkmReporter* throughput_ukm_reporter)
: custom_sequence_id_(-1),
- metrics_(std::make_unique<FrameSequenceMetrics>(type,
- throughput_ukm_reporter)),
- trace_data_(metrics_.get()) {
+ metrics_(
+ std::make_unique<FrameSequenceMetrics>(type,
+ throughput_ukm_reporter)) {
DCHECK_LT(type, FrameSequenceTrackerType::kMaxType);
DCHECK(type != FrameSequenceTrackerType::kCustom);
}
@@ -76,13 +84,14 @@ FrameSequenceTracker::FrameSequenceTracker(
: custom_sequence_id_(custom_sequence_id),
metrics_(std::make_unique<FrameSequenceMetrics>(
FrameSequenceTrackerType::kCustom,
- /*ukm_reporter=*/nullptr)),
- trace_data_(metrics_.get()) {
+ /*ukm_reporter=*/nullptr)) {
DCHECK_GT(custom_sequence_id_, 0);
metrics_->SetCustomReporter(std::move(custom_reporter));
}
-FrameSequenceTracker::~FrameSequenceTracker() = default;
+FrameSequenceTracker::~FrameSequenceTracker() {
+ CleanUp();
+}
void FrameSequenceTracker::ScheduleTerminate() {
// If the last frame has ended and there is no frame awaiting presentation,
@@ -126,6 +135,8 @@ void FrameSequenceTracker::ReportBeginImplFrame(
args.frame_id.sequence_number);
impl_throughput().frames_expected +=
begin_impl_frame_data_.previous_sequence_delta;
+ aggregated_throughput().frames_expected +=
+ begin_impl_frame_data_.previous_sequence_delta;
#if DCHECK_IS_ON()
++impl_throughput().frames_received;
#endif
@@ -363,6 +374,8 @@ void FrameSequenceTracker::ReportFrameEnd(
NOTREACHED() << TRACKER_DCHECK_MSG;
#endif
begin_impl_frame_data_.previous_sequence = 0;
+ if (!IsExpectingMainFrame())
+ --aggregated_throughput().frames_expected;
}
// last_submitted_frame_ == 0 means the last impl frame has been presented.
if (termination_status_ == TerminationStatus::kScheduledForTermination &&
@@ -423,15 +436,17 @@ void FrameSequenceTracker::ReportFramePresented(
uint32_t impl_frames_produced = 0;
uint32_t main_frames_produced = 0;
- trace_data_.Advance(feedback.timestamp);
- const bool was_presented = !feedback.timestamp.is_null();
+ const bool was_presented = !feedback.failed();
if (was_presented && submitted_frame_since_last_presentation) {
DCHECK_LT(impl_throughput().frames_produced,
impl_throughput().frames_expected)
<< TRACKER_DCHECK_MSG;
++impl_throughput().frames_produced;
++impl_frames_produced;
+ if (metrics()->GetEffectiveThread() == ThreadType::kCompositor) {
+ metrics()->AdvanceTrace(feedback.timestamp);
+ }
}
if (was_presented) {
@@ -448,6 +463,9 @@ void FrameSequenceTracker::ReportFramePresented(
<< TRACKER_DCHECK_MSG;
++main_throughput().frames_produced;
++main_frames_produced;
+ if (metrics()->GetEffectiveThread() == ThreadType::kMain) {
+ metrics()->AdvanceTrace(feedback.timestamp);
+ }
}
if (impl_frames_produced > 0) {
@@ -647,16 +665,11 @@ bool FrameSequenceTracker::ShouldIgnoreSequence(
return sequence_number != begin_impl_frame_data_.previous_sequence;
}
-std::unique_ptr<base::trace_event::TracedValue>
-FrameSequenceMetrics::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;
+bool FrameSequenceTracker::IsExpectingMainFrame() const {
+ bool last_main_not_processed =
+ begin_main_frame_data_.previous_sequence != 0 &&
+ begin_main_frame_data_.previous_sequence != last_processed_main_sequence_;
+ return !main_frames_.empty() || last_main_not_processed;
}
bool FrameSequenceTracker::ShouldReportMetricsNow(
@@ -675,22 +688,12 @@ std::unique_ptr<FrameSequenceMetrics> FrameSequenceTracker::TakeMetrics() {
return std::move(metrics_);
}
+void FrameSequenceTracker::CleanUp() {
+ if (metrics_)
+ metrics_->ReportLeftoverData();
+}
+
FrameSequenceTracker::CheckerboardingData::CheckerboardingData() = default;
FrameSequenceTracker::CheckerboardingData::~CheckerboardingData() = default;
-FrameSequenceTracker::TraceData::TraceData(const void* id) : trace_id(id) {}
-void FrameSequenceTracker::TraceData::Advance(base::TimeTicks new_timestamp) {
- // Use different names, because otherwise the trace-viewer shows the slices in
- // the same color, and that makes it difficult to tell the traces apart from
- // each other.
- const char* trace_names[] = {"Frame", "Frame ", "Frame "};
- TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(
- "cc,benchmark", trace_names[++this->frame_count % 3],
- TRACE_ID_LOCAL(this->trace_id), this->last_timestamp);
- TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(
- "cc,benchmark", trace_names[this->frame_count % 3],
- TRACE_ID_LOCAL(this->trace_id), new_timestamp);
- this->last_timestamp = new_timestamp;
-}
-
} // namespace cc
diff --git a/chromium/cc/metrics/frame_sequence_tracker.h b/chromium/cc/metrics/frame_sequence_tracker.h
index 680b2a85ad9..86deb91b3bd 100644
--- a/chromium/cc/metrics/frame_sequence_tracker.h
+++ b/chromium/cc/metrics/frame_sequence_tracker.h
@@ -5,6 +5,9 @@
#ifndef CC_METRICS_FRAME_SEQUENCE_TRACKER_H_
#define CC_METRICS_FRAME_SEQUENCE_TRACKER_H_
+#include <memory>
+#include <sstream>
+
#include "base/containers/circular_deque.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
@@ -101,6 +104,10 @@ class CC_EXPORT FrameSequenceTracker {
std::unique_ptr<FrameSequenceMetrics> TakeMetrics();
+ // Called by the destructor of FrameSequenceTrackerCollection, asking its
+ // |metrics_| to report.
+ void CleanUp();
+
private:
friend class FrameSequenceTrackerCollection;
friend class FrameSequenceTrackerTest;
@@ -158,6 +165,8 @@ class CC_EXPORT FrameSequenceTracker {
bool ShouldIgnoreSequence(uint64_t sequence_number) const;
+ bool IsExpectingMainFrame() const;
+
const int custom_sequence_id_;
TerminationStatus termination_status_ = TerminationStatus::kActive;
@@ -247,16 +256,6 @@ class CC_EXPORT FrameSequenceTracker {
// TODO(xidachen): remove this one.
uint64_t current_begin_main_sequence_ = 0;
- // Tracks some data to generate useful trace events.
- struct TraceData {
- explicit TraceData(const void* trace_id);
- const void* trace_id;
- base::TimeTicks last_timestamp = base::TimeTicks::Now();
- int frame_count = 0;
-
- void Advance(base::TimeTicks new_timestamp);
- } trace_data_;
-
// True when an impl-impl is not ended. A tracker is ready for termination
// only when the last impl-frame is ended (ReportFrameEnd).
bool is_inside_frame_ = false;
diff --git a/chromium/cc/metrics/frame_sequence_tracker_collection.cc b/chromium/cc/metrics/frame_sequence_tracker_collection.cc
index 8dd78ca0ccd..1efd3a37159 100644
--- a/chromium/cc/metrics/frame_sequence_tracker_collection.cc
+++ b/chromium/cc/metrics/frame_sequence_tracker_collection.cc
@@ -11,6 +11,18 @@
namespace cc {
+namespace {
+
+using ThreadType = FrameSequenceMetrics::ThreadType;
+
+bool IsScrollType(FrameSequenceTrackerType type) {
+ return type == FrameSequenceTrackerType::kTouchScroll ||
+ type == FrameSequenceTrackerType::kWheelScroll ||
+ type == FrameSequenceTrackerType::kScrollbarScroll;
+}
+
+} // namespace
+
FrameSequenceTrackerCollection::FrameSequenceTrackerCollection(
bool is_single_threaded,
CompositorFrameReportingController* compositor_frame_reporting_controller)
@@ -19,42 +31,86 @@ FrameSequenceTrackerCollection::FrameSequenceTrackerCollection(
compositor_frame_reporting_controller) {}
FrameSequenceTrackerCollection::~FrameSequenceTrackerCollection() {
+ CleanUp();
frame_trackers_.clear();
removal_trackers_.clear();
}
-FrameSequenceMetrics* FrameSequenceTrackerCollection::StartSequence(
- FrameSequenceTrackerType type) {
+FrameSequenceTracker* FrameSequenceTrackerCollection::StartSequenceInternal(
+ FrameSequenceTrackerType type,
+ FrameSequenceMetrics::ThreadType scrolling_thread) {
DCHECK_NE(FrameSequenceTrackerType::kCustom, type);
-
if (is_single_threaded_)
return nullptr;
- if (frame_trackers_.contains(type))
- return frame_trackers_[type]->metrics();
+ auto key = std::make_pair(type, scrolling_thread);
+ if (frame_trackers_.contains(key))
+ return frame_trackers_[key].get();
+
auto tracker = base::WrapUnique(
new FrameSequenceTracker(type, throughput_ukm_reporter_.get()));
- frame_trackers_[type] = std::move(tracker);
+ frame_trackers_[key] = std::move(tracker);
if (compositor_frame_reporting_controller_)
compositor_frame_reporting_controller_->AddActiveTracker(type);
- return frame_trackers_[type]->metrics();
+
+ auto* metrics = frame_trackers_[key]->metrics();
+ if (accumulated_metrics_.contains(key)) {
+ metrics->AdoptTrace(accumulated_metrics_[key].get());
+ }
+ if (IsScrollType(type)) {
+ DCHECK_NE(scrolling_thread, ThreadType::kUnknown);
+ metrics->SetScrollingThread(scrolling_thread);
+ }
+ return frame_trackers_[key].get();
+}
+
+FrameSequenceTracker* FrameSequenceTrackerCollection::StartSequence(
+ FrameSequenceTrackerType type) {
+ DCHECK(!IsScrollType(type));
+ return StartSequenceInternal(type, ThreadType::kUnknown);
+}
+
+FrameSequenceTracker* FrameSequenceTrackerCollection::StartScrollSequence(
+ FrameSequenceTrackerType type,
+ FrameSequenceMetrics::ThreadType scrolling_thread) {
+ DCHECK(IsScrollType(type));
+ return StartSequenceInternal(type, scrolling_thread);
+}
+
+void FrameSequenceTrackerCollection::CleanUp() {
+ for (auto& tracker : frame_trackers_)
+ tracker.second->CleanUp();
+ for (auto& tracker : custom_frame_trackers_)
+ tracker.second->CleanUp();
+ for (auto& tracker : removal_trackers_)
+ tracker->CleanUp();
+ for (auto& metric : accumulated_metrics_)
+ metric.second->ReportLeftoverData();
+ throughput_ukm_reporter_ = nullptr;
}
void FrameSequenceTrackerCollection::StopSequence(
FrameSequenceTrackerType type) {
DCHECK_NE(FrameSequenceTrackerType::kCustom, type);
- if (!frame_trackers_.contains(type))
+ auto key = std::make_pair(type, ThreadType::kUnknown);
+ if (IsScrollType(type)) {
+ key = std::make_pair(type, ThreadType::kCompositor);
+ if (!frame_trackers_.contains(key))
+ key = std::make_pair(type, ThreadType::kMain);
+ }
+
+ if (!frame_trackers_.contains(key))
return;
std::unique_ptr<FrameSequenceTracker> tracker =
- std::move(frame_trackers_[type]);
+ std::move(frame_trackers_[key]);
if (compositor_frame_reporting_controller_)
compositor_frame_reporting_controller_->RemoveActiveTracker(
tracker->type());
- frame_trackers_.erase(type);
+ frame_trackers_.erase(key);
tracker->ScheduleTerminate();
removal_trackers_.push_back(std::move(tracker));
DestroyTrackers();
@@ -193,10 +249,42 @@ void FrameSequenceTrackerCollection::NotifyFramePresented(
tracker.second->ReportFramePresented(frame_token, feedback);
for (auto& tracker : custom_frame_trackers_)
tracker.second->ReportFramePresented(frame_token, feedback);
-
for (auto& tracker : removal_trackers_)
tracker->ReportFramePresented(frame_token, feedback);
+ DestroyTrackers();
+}
+
+bool FrameSequenceTrackerCollection::HasThroughputData() const {
+ return throughput_ukm_reporter_ &&
+ throughput_ukm_reporter_->HasThroughputData();
+}
+
+int FrameSequenceTrackerCollection::TakeLastAggregatedPercent() {
+ DCHECK(throughput_ukm_reporter_);
+ return throughput_ukm_reporter_->TakeLastAggregatedPercent();
+}
+
+int FrameSequenceTrackerCollection::TakeLastImplPercent() {
+ DCHECK(throughput_ukm_reporter_);
+ return throughput_ukm_reporter_->TakeLastImplPercent();
+}
+
+base::Optional<int> FrameSequenceTrackerCollection::TakeLastMainPercent() {
+ DCHECK(throughput_ukm_reporter_);
+ return throughput_ukm_reporter_->TakeLastMainPercent();
+}
+
+void FrameSequenceTrackerCollection::ComputeUniversalThroughputForTesting() {
+ DCHECK(throughput_ukm_reporter_);
+ const auto type = FrameSequenceTrackerType::kUniversal;
+ auto key = std::make_pair(type, ThreadType::kUnknown);
+ DCHECK(frame_trackers_.contains(key));
+ throughput_ukm_reporter_->ComputeUniversalThroughput(
+ frame_trackers_[key]->metrics());
+}
+
+void FrameSequenceTrackerCollection::DestroyTrackers() {
for (auto& tracker : removal_trackers_) {
if (tracker->termination_status() ==
FrameSequenceTracker::TerminationStatus::kReadyForTermination) {
@@ -209,9 +297,10 @@ void FrameSequenceTrackerCollection::NotifyFramePresented(
// For kCustom typed trackers, |metrics| invokes AddCustomTrackerResult
// on its destruction, which add its data to |custom_tracker_results_|
// to be picked up by caller.
- auto metrics = tracker->TakeMetrics();
- if (metrics->type() == FrameSequenceTrackerType::kCustom)
+ if (tracker->metrics() &&
+ tracker->type() == FrameSequenceTrackerType::kCustom)
continue;
+ auto metrics = tracker->TakeMetrics();
auto key = std::make_pair(metrics->type(), metrics->GetEffectiveThread());
if (accumulated_metrics_.contains(key)) {
@@ -220,24 +309,17 @@ void FrameSequenceTrackerCollection::NotifyFramePresented(
}
if (metrics->HasEnoughDataForReporting()) {
- if (metrics->type() == FrameSequenceTrackerType::kUniversal) {
- uint32_t frames_expected = metrics->impl_throughput().frames_expected;
- uint32_t frames_produced =
- metrics->aggregated_throughput().frames_produced;
- current_universal_throughput_ = std::floor(
- 100 * frames_produced / static_cast<float>(frames_expected));
- }
+ // Do this before ReportMetrics() which clears the throughput data.
+ if (metrics->type() == FrameSequenceTrackerType::kUniversal)
+ throughput_ukm_reporter_->ComputeUniversalThroughput(metrics.get());
metrics->ReportMetrics();
}
- if (metrics->HasDataLeftForReporting())
+ if (metrics->HasDataLeftForReporting()) {
accumulated_metrics_[key] = std::move(metrics);
+ }
}
}
- DestroyTrackers();
-}
-
-void FrameSequenceTrackerCollection::DestroyTrackers() {
base::EraseIf(
removal_trackers_,
[](const std::unique_ptr<FrameSequenceTracker>& tracker) {
@@ -248,28 +330,40 @@ void FrameSequenceTrackerCollection::DestroyTrackers() {
void FrameSequenceTrackerCollection::RecreateTrackers(
const viz::BeginFrameArgs& args) {
- std::vector<FrameSequenceTrackerType> recreate_trackers;
+ std::vector<std::pair<FrameSequenceTrackerType, ThreadType>>
+ recreate_trackers;
for (const auto& tracker : frame_trackers_) {
if (tracker.second->ShouldReportMetricsNow(args))
recreate_trackers.push_back(tracker.first);
}
- for (const auto& tracker_type : recreate_trackers) {
+ for (const auto& key : recreate_trackers) {
+ DCHECK(frame_trackers_[key]);
+ auto tracker_type = key.first;
+ ThreadType thread_type = key.second;
+
// StopSequence put the tracker in the |removal_trackers_|, which will
// report its throughput data when its frame is presented.
StopSequence(tracker_type);
+
// The frame sequence is still active, so create a new tracker to keep
// tracking this sequence.
- StartSequence(tracker_type);
+ if (thread_type != FrameSequenceMetrics::ThreadType::kUnknown) {
+ DCHECK(IsScrollType(tracker_type));
+ StartScrollSequence(tracker_type, thread_type);
+ } else {
+ StartSequence(tracker_type);
+ }
}
}
ActiveFrameSequenceTrackers
FrameSequenceTrackerCollection::FrameSequenceTrackerActiveTypes() {
ActiveFrameSequenceTrackers encoded_types = 0;
- for (const auto& tracker : frame_trackers_) {
+ for (const auto& key : frame_trackers_) {
+ auto thread_type = key.first.first;
encoded_types |= static_cast<ActiveFrameSequenceTrackers>(
- 1 << static_cast<unsigned>(tracker.first));
+ 1 << static_cast<unsigned>(thread_type));
}
return encoded_types;
}
@@ -281,13 +375,6 @@ FrameSequenceTrackerCollection::TakeCustomTrackerResults() {
return results;
}
-FrameSequenceTracker* FrameSequenceTrackerCollection::GetTrackerForTesting(
- FrameSequenceTrackerType type) {
- if (!frame_trackers_.contains(type))
- return nullptr;
- return frame_trackers_[type].get();
-}
-
FrameSequenceTracker*
FrameSequenceTrackerCollection::GetRemovalTrackerForTesting(
FrameSequenceTrackerType type) {
diff --git a/chromium/cc/metrics/frame_sequence_tracker_collection.h b/chromium/cc/metrics/frame_sequence_tracker_collection.h
index 9eaf1efb6b2..0fecfce7895 100644
--- a/chromium/cc/metrics/frame_sequence_tracker_collection.h
+++ b/chromium/cc/metrics/frame_sequence_tracker_collection.h
@@ -6,6 +6,8 @@
#define CC_METRICS_FRAME_SEQUENCE_TRACKER_COLLECTION_H_
#include <memory>
+#include <utility>
+#include <vector>
#include "base/containers/flat_map.h"
#include "base/optional.h"
@@ -47,8 +49,12 @@ class CC_EXPORT FrameSequenceTrackerCollection {
FrameSequenceTrackerCollection& operator=(
const FrameSequenceTrackerCollection&) = delete;
- // Creates a tracker for the specified sequence-type.
- FrameSequenceMetrics* StartSequence(FrameSequenceTrackerType type);
+ // Creates a new tracker for the specified sequence-type if one doesn't
+ // already exist. Returns the associated FrameSequenceTracker instance.
+ FrameSequenceTracker* StartSequence(FrameSequenceTrackerType type);
+ FrameSequenceTracker* StartScrollSequence(
+ FrameSequenceTrackerType type,
+ FrameSequenceMetrics::ThreadType scrolling_thread);
// Schedules |tracker| for destruction. This is preferred instead of outright
// desrtruction of the tracker, since this ensures that the actual tracker
@@ -103,23 +109,35 @@ class CC_EXPORT FrameSequenceTrackerCollection {
// Reports the accumulated kCustom tracker results and clears it.
CustomTrackerResults TakeCustomTrackerResults();
- FrameSequenceTracker* GetTrackerForTesting(FrameSequenceTrackerType type);
FrameSequenceTracker* GetRemovalTrackerForTesting(
FrameSequenceTrackerType type);
void SetUkmManager(UkmManager* manager);
- base::Optional<int> current_universal_throughput() {
- return current_universal_throughput_;
- }
+ // These methods directly calls corresponding APIs in ThroughputUkmReporter,
+ // please refer to the ThroughputUkmReporter for details.
+ bool HasThroughputData() const;
+ int TakeLastAggregatedPercent();
+ int TakeLastImplPercent();
+ base::Optional<int> TakeLastMainPercent();
+
+ void ComputeUniversalThroughputForTesting();
private:
friend class FrameSequenceTrackerTest;
+ FrameSequenceTracker* StartSequenceInternal(
+ FrameSequenceTrackerType type,
+ FrameSequenceMetrics::ThreadType scrolling_thread);
+
void RecreateTrackers(const viz::BeginFrameArgs& args);
// Destroy the trackers that are ready to be terminated.
void DestroyTrackers();
+ // Ask all trackers to report their metrics if there is any, must be the first
+ // thing in the destructor.
+ void CleanUp();
+
// Adds collected metrics data for |custom_sequence_id| to be picked up via
// TakeCustomTrackerResults() below.
void AddCustomTrackerResult(
@@ -127,9 +145,19 @@ class CC_EXPORT FrameSequenceTrackerCollection {
FrameSequenceMetrics::ThroughputData throughput_data);
const bool is_single_threaded_;
+ // The reporter takes throughput data and connect to UkmManager to report it.
+ // Note: this has to be before the frame_trackers_. The reason is that a
+ // FrameSequenceTracker owners a FrameSequenceMetrics, so the destructor of
+ // the former calls the destructor of the later. FrameSequenceMetrics's
+ // destructor calls its ReportMetrics() which requires
+ // |throughput_ukm_reporter_| to be alive. So putting it before
+ // |frame_trackers_| to ensure that it is destroyed after the tracker.
+ std::unique_ptr<ThroughputUkmReporter> throughput_ukm_reporter_;
+
// The callsite can use the type to manipulate the tracker.
- base::flat_map<FrameSequenceTrackerType,
- std::unique_ptr<FrameSequenceTracker>>
+ base::flat_map<
+ std::pair<FrameSequenceTrackerType, FrameSequenceMetrics::ThreadType>,
+ std::unique_ptr<FrameSequenceTracker>>
frame_trackers_;
// Custom trackers are keyed by a custom sequence id.
@@ -141,14 +169,10 @@ class CC_EXPORT FrameSequenceTrackerCollection {
CompositorFrameReportingController* const
compositor_frame_reporting_controller_;
- // The reporter takes throughput data and connect to UkmManager to report it.
- std::unique_ptr<ThroughputUkmReporter> throughput_ukm_reporter_;
-
base::flat_map<
std::pair<FrameSequenceTrackerType, FrameSequenceMetrics::ThreadType>,
std::unique_ptr<FrameSequenceMetrics>>
accumulated_metrics_;
- base::Optional<int> current_universal_throughput_;
};
} // namespace cc
diff --git a/chromium/cc/metrics/frame_sequence_tracker_unittest.cc b/chromium/cc/metrics/frame_sequence_tracker_unittest.cc
index 2a6b8cd2b82..963cf1045f4 100644
--- a/chromium/cc/metrics/frame_sequence_tracker_unittest.cc
+++ b/chromium/cc/metrics/frame_sequence_tracker_unittest.cc
@@ -8,6 +8,9 @@
#include "base/test/metrics/histogram_tester.h"
#include "cc/metrics/compositor_frame_reporting_controller.h"
#include "cc/metrics/frame_sequence_tracker_collection.h"
+#include "cc/metrics/throughput_ukm_reporter.h"
+#include "cc/trees/ukm_manager.h"
+#include "components/ukm/test_ukm_recorder.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -40,16 +43,16 @@ class FrameSequenceTrackerTest : public testing::Test {
/*should_report_metrics=*/true)),
collection_(/*is_single_threaded=*/false,
compositor_frame_reporting_controller_.get()) {
- collection_.StartSequence(FrameSequenceTrackerType::kTouchScroll);
- tracker_ = collection_.GetTrackerForTesting(
- FrameSequenceTrackerType::kTouchScroll);
+ tracker_ = collection_.StartScrollSequence(
+ FrameSequenceTrackerType::kTouchScroll,
+ FrameSequenceMetrics::ThreadType::kCompositor);
}
~FrameSequenceTrackerTest() override = default;
- void CreateNewTracker() {
- collection_.StartSequence(FrameSequenceTrackerType::kTouchScroll);
- tracker_ = collection_.GetTrackerForTesting(
- FrameSequenceTrackerType::kTouchScroll);
+ void CreateNewTracker(FrameSequenceMetrics::ThreadType thread_type =
+ FrameSequenceMetrics::ThreadType::kCompositor) {
+ tracker_ = collection_.StartScrollSequence(
+ FrameSequenceTrackerType::kTouchScroll, thread_type);
}
viz::BeginFrameArgs CreateBeginFrameArgs(
@@ -100,7 +103,15 @@ class FrameSequenceTrackerTest : public testing::Test {
// Check whether a type of tracker exists in |frame_trackers_| or not.
bool TrackerExists(FrameSequenceTrackerType type) const {
- return collection_.frame_trackers_.contains(type);
+ auto key = std::make_pair(type, FrameSequenceMetrics::ThreadType::kUnknown);
+ if (type == FrameSequenceTrackerType::kTouchScroll ||
+ type == FrameSequenceTrackerType::kWheelScroll ||
+ type == FrameSequenceTrackerType::kScrollbarScroll) {
+ key = std::make_pair(type, FrameSequenceMetrics::ThreadType::kCompositor);
+ if (!collection_.frame_trackers_.contains(key))
+ key = std::make_pair(type, FrameSequenceMetrics::ThreadType::kMain);
+ }
+ return collection_.frame_trackers_.contains(key);
}
bool RemovalTrackerExists(unsigned index,
@@ -254,14 +265,29 @@ class FrameSequenceTrackerTest : public testing::Test {
return tracker_->ignored_frame_tokens_;
}
+ FrameSequenceMetrics::ThroughputData& ImplThroughput(
+ FrameSequenceTracker* tracker) const {
+ return tracker->impl_throughput();
+ }
+
FrameSequenceMetrics::ThroughputData& ImplThroughput() const {
return tracker_->impl_throughput();
}
+ FrameSequenceMetrics::ThroughputData& MainThroughput(
+ FrameSequenceTracker* tracker) const {
+ return tracker->main_throughput();
+ }
+
FrameSequenceMetrics::ThroughputData& MainThroughput() const {
return tracker_->main_throughput();
}
+ FrameSequenceMetrics::ThroughputData& AggregatedThroughput(
+ FrameSequenceTracker* tracker) const {
+ return tracker->aggregated_throughput();
+ }
+
FrameSequenceMetrics::ThroughputData& AggregatedThroughput() const {
return tracker_->aggregated_throughput();
}
@@ -464,8 +490,6 @@ TEST_F(FrameSequenceTrackerTest, ReportMetrics) {
1u);
histogram_tester.ExpectTotalCount(
"Graphics.Smoothness.PercentDroppedFrames.MainThread.TouchScroll", 0u);
- histogram_tester.ExpectTotalCount(
- "Graphics.Smoothness.PercentDroppedFrames.SlowerThread.TouchScroll", 1u);
// Test that both are reported.
ImplThroughput().frames_expected = 100u;
@@ -478,8 +502,6 @@ TEST_F(FrameSequenceTrackerTest, ReportMetrics) {
2u);
histogram_tester.ExpectTotalCount(
"Graphics.Smoothness.PercentDroppedFrames.MainThread.TouchScroll", 1u);
- histogram_tester.ExpectTotalCount(
- "Graphics.Smoothness.PercentDroppedFrames.SlowerThread.TouchScroll", 2u);
// Test that none is reported.
MainThroughput().frames_expected = 2u;
@@ -492,8 +514,6 @@ TEST_F(FrameSequenceTrackerTest, ReportMetrics) {
2u);
histogram_tester.ExpectTotalCount(
"Graphics.Smoothness.PercentDroppedFrames.MainThread.TouchScroll", 1u);
- histogram_tester.ExpectTotalCount(
- "Graphics.Smoothness.PercentDroppedFrames.SlowerThread.TouchScroll", 2u);
// Test the case where compositor and main thread have the same throughput.
ImplThroughput().frames_expected = 120u;
@@ -506,8 +526,6 @@ TEST_F(FrameSequenceTrackerTest, ReportMetrics) {
3u);
histogram_tester.ExpectTotalCount(
"Graphics.Smoothness.PercentDroppedFrames.MainThread.TouchScroll", 2u);
- histogram_tester.ExpectTotalCount(
- "Graphics.Smoothness.PercentDroppedFrames.SlowerThread.TouchScroll", 3u);
}
TEST_F(FrameSequenceTrackerTest, ReportMetricsAtFixedInterval) {
@@ -638,9 +656,6 @@ TEST_F(FrameSequenceTrackerTest, BeginMainFrameSubmit) {
}
TEST_F(FrameSequenceTrackerTest, ScrollingThreadMetricCompositorThread) {
- tracker_->metrics()->SetScrollingThread(
- FrameSequenceMetrics::ThreadType::kCompositor);
-
// Start with a bunch of frames so that the metric does get reported at the
// end of the test.
ImplThroughput().frames_expected = 100u;
@@ -659,8 +674,7 @@ TEST_F(FrameSequenceTrackerTest, ScrollingThreadMetricCompositorThread) {
}
TEST_F(FrameSequenceTrackerTest, ScrollingThreadMetricMainThread) {
- tracker_->metrics()->SetScrollingThread(
- FrameSequenceMetrics::ThreadType::kMain);
+ CreateNewTracker(FrameSequenceMetrics::ThreadType::kMain);
// Start with a bunch of frames so that the metric does get reported at the
// end of the test.
@@ -1104,8 +1118,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame2) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1132,8 +1146,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame3) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1160,8 +1174,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame4) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1229,8 +1243,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame7) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1257,8 +1271,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame8) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1285,8 +1299,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame9) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1346,8 +1360,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame12) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1374,8 +1388,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame13) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1395,8 +1409,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame14) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1430,8 +1444,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame15) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1458,8 +1472,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame16) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1486,8 +1500,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame17) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1515,8 +1529,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame18) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1543,8 +1557,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame19) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1571,8 +1585,8 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame20) {
EXPECT_EQ(NumberOfRemovalTrackers(), 0u);
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
- // Both impl and slower threads reports 101 frames expected.
- EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 2);
+ // Impl thread reports 101 frames expected.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 101), 1);
// The main thread reports 0 frames expected.
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
metric =
@@ -1851,7 +1865,8 @@ TEST_F(FrameSequenceTrackerTest, MainThreadPresentWithNullTimeStamp) {
const char sequence[] = "b(1)B(0,1)E(1)s(1)S(1)e(1,1)";
GenerateSequence(sequence);
collection_.NotifyFramePresented(
- 1, {base::TimeTicks(), viz::BeginFrameArgs::DefaultInterval(), 0});
+ 1, {base::TimeTicks(), viz::BeginFrameArgs::DefaultInterval(),
+ gfx::PresentationFeedback::kFailure});
EXPECT_EQ(MainThroughput().frames_expected, 1u);
// No presentation, no main frame produced.
EXPECT_EQ(MainThroughput().frames_produced, 0u);
@@ -1869,6 +1884,29 @@ TEST_F(FrameSequenceTrackerTest, TrackerTypeEncoding) {
EXPECT_EQ(active_encoded, 16); // 1 << 4
}
+TEST_F(FrameSequenceTrackerTest, UniversalTrackerSubmitThroughput) {
+ auto recorder = std::make_unique<ukm::TestUkmRecorder>();
+ auto ukm_manager = std::make_unique<UkmManager>(std::move(recorder));
+
+ collection_.ClearAll();
+ collection_.SetUkmManager(ukm_manager.get());
+ auto* tracker =
+ collection_.StartSequence(FrameSequenceTrackerType::kUniversal);
+ ImplThroughput(tracker).frames_expected = 200u;
+ ImplThroughput(tracker).frames_produced = 190u;
+ MainThroughput(tracker).frames_expected = 100u;
+ MainThroughput(tracker).frames_produced = 50u;
+ AggregatedThroughput(tracker).frames_expected = 200u;
+ AggregatedThroughput(tracker).frames_produced = 150u;
+
+ collection_.ComputeUniversalThroughputForTesting();
+ DCHECK(collection_.HasThroughputData());
+ EXPECT_EQ(collection_.TakeLastAggregatedPercent(), 25);
+ EXPECT_EQ(collection_.TakeLastImplPercent(), 5);
+ EXPECT_EQ(collection_.TakeLastMainPercent().value(), 50);
+ EXPECT_FALSE(collection_.HasThroughputData());
+}
+
TEST_F(FrameSequenceTrackerTest, CustomTrackers) {
// Start custom tracker 1.
collection_.StartCustomSequence(1);
@@ -1918,4 +1956,104 @@ TEST_F(FrameSequenceTrackerTest, CustomTrackers) {
EXPECT_EQ(1u, results[3].frames_expected);
}
+TEST_F(FrameSequenceTrackerTest, MergeTrackers) {
+ // Generate two sequences of scrolls: first with only 1 frame, and then with
+ // 99 frames. Verify that the two scrolls are merged to report a single
+ // metric.
+ base::HistogramTester histogram_tester;
+ const char first_sequence[] = "b(1)s(1)e(1,0)P(1)";
+ GenerateSequence(first_sequence);
+ EXPECT_EQ(ImplThroughput().frames_expected, 1u);
+ EXPECT_EQ(ImplThroughput().frames_produced, 1u);
+ collection_.StopSequence(FrameSequenceTrackerType::kTouchScroll);
+
+ const char metric[] =
+ "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
+ histogram_tester.ExpectTotalCount(metric, 0u);
+ EXPECT_FALSE(TrackerExists(FrameSequenceTrackerType::kTouchScroll));
+
+ CreateNewTracker();
+ const char second_sequence[] = "b(2)s(2)e(2,0)P(2)b(100)s(3)e(100,0)P(3)";
+ GenerateSequence(second_sequence);
+ EXPECT_EQ(ImplThroughput().frames_expected, 99u);
+ EXPECT_EQ(ImplThroughput().frames_produced, 2u);
+ collection_.StopSequence(FrameSequenceTrackerType::kTouchScroll);
+ EXPECT_FALSE(TrackerExists(FrameSequenceTrackerType::kTouchScroll));
+ histogram_tester.ExpectTotalCount(metric, 1u);
+ EXPECT_THAT(histogram_tester.GetAllSamples(metric),
+ testing::ElementsAre(base::Bucket(97, 1)));
+}
+
+TEST_F(FrameSequenceTrackerTest, MergeTrackersPresentAfterStopSequence) {
+ // Generate two sequences of scrolls: first with only 1 frame, and then with
+ // 99 frames. Verify that the two scrolls are merged to report a single
+ // metric. For the second sequence, the last frame is presented after the
+ // sequence ends.
+ base::HistogramTester histogram_tester;
+ const char first_sequence[] = "b(1)s(1)e(1,0)P(1)";
+ GenerateSequence(first_sequence);
+ EXPECT_EQ(ImplThroughput().frames_expected, 1u);
+ EXPECT_EQ(ImplThroughput().frames_produced, 1u);
+ collection_.StopSequence(FrameSequenceTrackerType::kTouchScroll);
+
+ const char metric[] =
+ "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
+ histogram_tester.ExpectTotalCount(metric, 0u);
+ EXPECT_FALSE(TrackerExists(FrameSequenceTrackerType::kTouchScroll));
+
+ CreateNewTracker();
+ const char second_sequence[] = "b(2)s(2)e(2,0)P(2)b(100)s(3)e(100,0)";
+ GenerateSequence(second_sequence);
+ EXPECT_EQ(ImplThroughput().frames_expected, 99u);
+ EXPECT_EQ(ImplThroughput().frames_produced, 1u);
+ collection_.StopSequence(FrameSequenceTrackerType::kTouchScroll);
+ GenerateSequence("P(3)");
+ histogram_tester.ExpectTotalCount(metric, 1u);
+ EXPECT_THAT(histogram_tester.GetAllSamples(metric),
+ testing::ElementsAre(base::Bucket(97, 1)));
+}
+
+TEST_F(FrameSequenceTrackerTest, MergeTrackersScrollOnSameThread) {
+ // Do a short scroll on the compositor thread, then do another short scroll on
+ // the compositor thread. Make sure these are merged.
+ base::HistogramTester histogram_tester;
+ const char first_sequence[] = "b(1)s(1)e(1,0)P(1)b(80)s(2)e(80,0)P(2)";
+ GenerateSequence(first_sequence);
+ collection_.StopSequence(FrameSequenceTrackerType::kTouchScroll);
+
+ CreateNewTracker(FrameSequenceMetrics::ThreadType::kCompositor);
+ const char second_sequence[] = "b(81)s(3)e(81,0)P(3)b(101)s(4)e(101,0)P(4)";
+ GenerateSequence(second_sequence);
+ collection_.StopSequence(FrameSequenceTrackerType::kTouchScroll);
+
+ const char comp_metric[] =
+ "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
+ const char main_metric[] =
+ "Graphics.Smoothness.PercentDroppedFrames.MainThread.TouchScroll";
+ histogram_tester.ExpectTotalCount(comp_metric, 1u);
+ histogram_tester.ExpectTotalCount(main_metric, 0u);
+}
+
+TEST_F(FrameSequenceTrackerTest, MergeTrackersScrollOnDifferentThreads) {
+ // Do a short scroll on the compositor thread, then do another short scroll on
+ // the main-thread. Make sure these are not merged.
+ base::HistogramTester histogram_tester;
+ const char compscroll_sequence[] = "b(1)s(1)e(1,0)P(1)b(80)s(2)e(80,0)P(2)";
+ GenerateSequence(compscroll_sequence);
+ collection_.StopSequence(FrameSequenceTrackerType::kTouchScroll);
+
+ CreateNewTracker(FrameSequenceMetrics::ThreadType::kMain);
+ const char mainscroll_sequence[] =
+ "b(81)s(3)e(81,0)P(3)b(101)s(4)e(101,0)P(4)";
+ GenerateSequence(mainscroll_sequence);
+ collection_.StopSequence(FrameSequenceTrackerType::kTouchScroll);
+
+ const char comp_metric[] =
+ "Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
+ const char main_metric[] =
+ "Graphics.Smoothness.PercentDroppedFrames.MainThread.TouchScroll";
+ histogram_tester.ExpectTotalCount(comp_metric, 0u);
+ histogram_tester.ExpectTotalCount(main_metric, 0u);
+}
+
} // namespace cc
diff --git a/chromium/cc/metrics/latency_ukm_reporter.cc b/chromium/cc/metrics/latency_ukm_reporter.cc
index 072ccb31daf..5155ee6513c 100644
--- a/chromium/cc/metrics/latency_ukm_reporter.cc
+++ b/chromium/cc/metrics/latency_ukm_reporter.cc
@@ -4,57 +4,108 @@
#include "cc/metrics/latency_ukm_reporter.h"
+#include <climits>
+#include <memory>
+
#include "base/rand_util.h"
#include "cc/trees/ukm_manager.h"
namespace cc {
-void LatencyUkmReporter::ReportLatencyUkm(
+// We use a Poisson process with an exponential decay multiplier. The goal is to
+// get many randomly distributed samples early during page load and initial
+// interaction, then samples at an exponentially decreasing rate to effectively
+// cap the number of samples. The particular parameters chosen here give roughly
+// 5-10 samples in the first 100 frames, decaying to several hours between
+// samples by the 40th sample. The multiplier value should be tuned to achieve a
+// total sample count that avoids throttling by the UKM system.
+class LatencyUkmReporter::SamplingController {
+ public:
+ SamplingController() = default;
+ ~SamplingController() = default;
+
+ // When a new UKM event is issued, this function should be called (once and
+ // only once) by the client to determine whether that event should be recorded
+ // or ignored, according to the sampling parameters. The sampling state will
+ // be updated to be ready for the next UKM event.
+ bool ShouldRecordNextEvent() {
+ bool should_record = false;
+ if (!frames_to_next_event_) {
+ should_record = true;
+ frames_to_next_event_ = SampleFramesToNextEvent();
+ }
+ DCHECK_GT(frames_to_next_event_, 0);
+ --frames_to_next_event_;
+ return should_record;
+ }
+
+ private:
+ // The |kSampleRateMultiplier| and |kSampleDecayRate| have been set to meet
+ // UKM goals for data volume.
+ const double kSampleDecayRate = 1.0;
+ const double kSampleRateMultiplier = 2.0;
+
+ int SampleFramesToNextEvent() {
+ // Sample from an exponential distribution to give a Poisson distribution
+ // of samples per time unit, then weigh it with an exponential multiplier
+ // to give a few samples in rapid succession (for frames early in the
+ // page's life) then exponentially fewer as the page lives longer.
+ // RandDouble() returns [0,1), but we need (0,1]. If RandDouble() is
+ // uniformly random, so is 1-RandDouble(), so use it to adjust the range.
+ // When RandDouble() returns 0.0, as it could, we will get a float_sample
+ // of 0, causing underflow. So rejection sample until we get a positive
+ // count.
+ double float_sample = 0.0;
+ do {
+ float_sample = -(kSampleRateMultiplier *
+ std::exp(samples_so_far_ * kSampleDecayRate) *
+ std::log(1.0 - base::RandDouble()));
+ } while (float_sample == 0.0);
+ // float_sample is positive, so we don't need to worry about underflow.
+ // After around 30 samples we will end up with a super high sample. That's
+ // OK because it just means we'll stop reporting metrics for that session,
+ // but we do need to be careful about overflow and NaN.
+ samples_so_far_++;
+ int integer_sample =
+ std::isnan(float_sample)
+ ? INT_MAX
+ : base::saturated_cast<int>(std::ceil(float_sample));
+ return integer_sample;
+ }
+
+ int samples_so_far_ = 0;
+ int frames_to_next_event_ = 0;
+};
+
+LatencyUkmReporter::LatencyUkmReporter()
+ : compositor_latency_sampling_controller_(
+ std::make_unique<SamplingController>()),
+ event_latency_sampling_controller_(
+ std::make_unique<SamplingController>()) {}
+
+LatencyUkmReporter::~LatencyUkmReporter() = default;
+
+void LatencyUkmReporter::ReportCompositorLatencyUkm(
CompositorFrameReporter::FrameReportType report_type,
const std::vector<CompositorFrameReporter::StageData>& stage_history,
const CompositorFrameReporter::ActiveTrackers& active_trackers,
const viz::FrameTimingDetails& viz_breakdown) {
- if (!ukm_manager_)
- return;
- if (!frames_to_next_event_) {
- ukm_manager_->RecordLatencyUKM(report_type, stage_history, active_trackers,
- viz_breakdown);
- frames_to_next_event_ += SampleFramesToNextEvent();
+ if (ukm_manager_ &&
+ compositor_latency_sampling_controller_->ShouldRecordNextEvent()) {
+ ukm_manager_->RecordCompositorLatencyUKM(report_type, stage_history,
+ active_trackers, viz_breakdown);
}
- DCHECK_GT(frames_to_next_event_, 0u);
- --frames_to_next_event_;
}
-unsigned LatencyUkmReporter::SampleFramesToNextEvent() {
- // Return the test interval if set
- if (frames_to_next_event_for_test_)
- return frames_to_next_event_for_test_;
-
- // Sample from an exponential distribution to give a poisson distribution
- // of samples per time unit, then weigh it with an exponential multiplier to
- // give a few samples in rapid succession (for frames early in the page's
- // life) then exponentially fewer as the page lives longer.
- // RandDouble() returns [0,1), but we need (0,1]. If RandDouble() is
- // uniformly random, so is 1-RandDouble(), so use it to adjust the range.
- // When RandDouble returns 0.0, as it could, we will get a float_sample of
- // 0, causing underflow in UpdateEventTimeAndRecordIfNeeded. So rejection
- // sample until we get a positive count.
- double float_sample = 0;
- do {
- float_sample = -(sample_rate_multiplier_ *
- std::exp(samples_so_far_ * sample_decay_rate_) *
- std::log(1.0 - base::RandDouble()));
- } while (float_sample == 0);
- // float_sample is positive, so we don't need to worry about underflow.
- // After around 30 samples we will end up with a super high
- // sample. That's OK because it just means we'll stop reporting metrics
- // for that session, but we do need to be careful about overflow and NaN.
- samples_so_far_++;
- unsigned unsigned_sample =
- std::isnan(float_sample)
- ? UINT_MAX
- : base::saturated_cast<unsigned>(std::ceil(float_sample));
- return unsigned_sample;
+void LatencyUkmReporter::ReportEventLatencyUkm(
+ const std::vector<EventMetrics>& events_metrics,
+ const std::vector<CompositorFrameReporter::StageData>& stage_history,
+ const viz::FrameTimingDetails& viz_breakdown) {
+ if (ukm_manager_ &&
+ event_latency_sampling_controller_->ShouldRecordNextEvent()) {
+ ukm_manager_->RecordEventLatencyUKM(events_metrics, stage_history,
+ viz_breakdown);
+ }
}
} // namespace cc
diff --git a/chromium/cc/metrics/latency_ukm_reporter.h b/chromium/cc/metrics/latency_ukm_reporter.h
index 3ee27823a4b..e5b0a8ac273 100644
--- a/chromium/cc/metrics/latency_ukm_reporter.h
+++ b/chromium/cc/metrics/latency_ukm_reporter.h
@@ -5,9 +5,12 @@
#ifndef CC_METRICS_LATENCY_UKM_REPORTER_H_
#define CC_METRICS_LATENCY_UKM_REPORTER_H_
-#include "base/optional.h"
+#include <memory>
+#include <vector>
+
#include "cc/cc_export.h"
#include "cc/metrics/compositor_frame_reporter.h"
+#include "cc/metrics/event_metrics.h"
#include "components/viz/common/frame_timing_details.h"
namespace cc {
@@ -17,25 +20,27 @@ class UkmManager;
// talks to UkmManager to report it.
class CC_EXPORT LatencyUkmReporter {
public:
- LatencyUkmReporter() = default;
- ~LatencyUkmReporter() = default;
+ LatencyUkmReporter();
+ ~LatencyUkmReporter();
+
+ LatencyUkmReporter(const LatencyUkmReporter&) = delete;
+ LatencyUkmReporter& operator=(const LatencyUkmReporter&) = delete;
- void ReportLatencyUkm(
+ void ReportCompositorLatencyUkm(
CompositorFrameReporter::FrameReportType report_type,
const std::vector<CompositorFrameReporter::StageData>& stage_history,
const CompositorFrameReporter::ActiveTrackers& active_trackers,
const viz::FrameTimingDetails& viz_breakdown);
- void SetUkmManager(UkmManager* manager) { ukm_manager_ = manager; }
+ void ReportEventLatencyUkm(
+ const std::vector<EventMetrics>& events_metrics,
+ const std::vector<CompositorFrameReporter::StageData>& stage_history,
+ const viz::FrameTimingDetails& viz_breakdown);
- private:
- unsigned SampleFramesToNextEvent();
+ void set_ukm_manager(UkmManager* manager) { ukm_manager_ = manager; }
- // To test event sampling. This and all future intervals will be the given
- // frame count, until this is called again.
- void FramesToNextEventForTest(unsigned num_frames) {
- frames_to_next_event_for_test_ = num_frames;
- }
+ private:
+ class SamplingController;
// This is pointing to the LayerTreeHostImpl::ukm_manager_, which is
// initialized right after the LayerTreeHostImpl is created. So when this
@@ -44,23 +49,8 @@ class CC_EXPORT LatencyUkmReporter {
// this pointer should never be null as long as LayerTreeHostImpl is alive.
UkmManager* ukm_manager_ = nullptr;
- // Sampling control. We use a Poisson process with an exponential decay
- // multiplier. The goal is to get many randomly distributed samples early
- // during page load and initial interaction, then samples at an exponentially
- // decreasing rate to effectively cap the number of samples. The particular
- // parameters chosen here give roughly 5-10 samples in the first 100 frames,
- // decaying to several hours between samples by the 40th sample. The
- // multiplier value should be tuned to achieve a total sample count that
- // avoids throttling by the UKM system.
- // The sample_rate_multiplier_ and sample_decay_rate_ have been set to meet
- // UKM goals for data volume.
- double sample_decay_rate_ = 1;
- double sample_rate_multiplier_ = 2;
- unsigned samples_so_far_ = 0;
- unsigned frames_to_next_event_ = 0;
-
- // Test data, used for SampleFramesToNextEvent if present
- unsigned frames_to_next_event_for_test_ = 0;
+ std::unique_ptr<SamplingController> compositor_latency_sampling_controller_;
+ std::unique_ptr<SamplingController> event_latency_sampling_controller_;
};
} // namespace cc
diff --git a/chromium/cc/metrics/throughput_ukm_reporter.cc b/chromium/cc/metrics/throughput_ukm_reporter.cc
index 08369a64156..39f1a33f7ce 100644
--- a/chromium/cc/metrics/throughput_ukm_reporter.cc
+++ b/chromium/cc/metrics/throughput_ukm_reporter.cc
@@ -30,6 +30,11 @@ void ThroughputUkmReporter::ReportThroughputUkm(
const base::Optional<int>& impl_throughput_percent,
const base::Optional<int>& main_throughput_percent,
FrameSequenceTrackerType type) {
+ // It is possible that when a tab shuts down, the ukm_manager_ owned by the
+ // LayerTreeHostImpl is cleared, and yet we try to report to UKM here. In this
+ // case, the |ukm_manager_| here is null.
+ if (!ukm_manager_)
+ return;
if (samples_to_next_event_[static_cast<int>(type)] == 0) {
// Sample every 100 events. Using the Universal tracker as an example
// which reports UMA every 5s, then the system collects UKM once per
@@ -65,4 +70,42 @@ void ThroughputUkmReporter::ReportAggregateThroughput(
--samples_for_aggregated_report_;
}
+void ThroughputUkmReporter::ComputeUniversalThroughput(
+ FrameSequenceMetrics* metrics) {
+ last_impl_percent_ = metrics->impl_throughput().DroppedFramePercent();
+ last_main_percent_ = metrics->main_throughput().DroppedFramePercent();
+ last_aggregated_percent_ =
+ metrics->aggregated_throughput().DroppedFramePercent();
+}
+
+bool ThroughputUkmReporter::HasThroughputData() const {
+ return last_aggregated_percent_.has_value();
+}
+
+int ThroughputUkmReporter::TakeLastAggregatedPercent() {
+ int ret_value = last_aggregated_percent_.value();
+ DCHECK(ret_value >= 0 && ret_value <= 100);
+ last_aggregated_percent_ = base::nullopt;
+ return ret_value;
+}
+
+int ThroughputUkmReporter::TakeLastImplPercent() {
+ int ret_value = last_impl_percent_.value();
+ DCHECK(ret_value >= 0 && ret_value <= 100);
+ last_impl_percent_ = base::nullopt;
+ return ret_value;
+}
+
+base::Optional<int> ThroughputUkmReporter::TakeLastMainPercent() {
+ base::Optional<int> ret_value = last_main_percent_;
+ DCHECK(!ret_value || (ret_value.value() >= 0 && ret_value.value() <= 100));
+ last_main_percent_ = base::nullopt;
+ return ret_value;
+}
+
+uint32_t ThroughputUkmReporter::GetSamplesToNextEventForTesting(int index) {
+ DCHECK_LT(index, static_cast<int>(FrameSequenceTrackerType::kMaxType));
+ return samples_to_next_event_[index];
+}
+
} // namespace cc
diff --git a/chromium/cc/metrics/throughput_ukm_reporter.h b/chromium/cc/metrics/throughput_ukm_reporter.h
index 441be89f88a..c92fdfe33b4 100644
--- a/chromium/cc/metrics/throughput_ukm_reporter.h
+++ b/chromium/cc/metrics/throughput_ukm_reporter.h
@@ -36,6 +36,23 @@ class CC_EXPORT ThroughputUkmReporter {
void ReportAggregateThroughput(AggregationType aggregation_type,
int throughput);
+ void ComputeUniversalThroughput(FrameSequenceMetrics* metrics);
+
+ // Once the kUniversal tracker reported its throughput to UMA, this returns
+ // true. In this case, the |last_aggregated_percent_| and |last_impl_percent_|
+ // must have value.
+ bool HasThroughputData() const;
+ // These functions are called only when HasThroughputData is true. They return
+ // the throughput value of the corresponding thread, and reset them to nullopt
+ // to indicate the value has been reported.
+ int TakeLastAggregatedPercent();
+ int TakeLastImplPercent();
+ // This could be nullopt even if the HasThroughputData() is true, because it
+ // could happen that all the frames are generated from the compositor thread.
+ base::Optional<int> TakeLastMainPercent();
+
+ uint32_t GetSamplesToNextEventForTesting(int index);
+
private:
// Sampling control. We sample the event here to not throttle the UKM system.
// Currently, the same sampling rate is applied to all existing trackers. We
@@ -46,10 +63,20 @@ class CC_EXPORT ThroughputUkmReporter {
// This is pointing to the LayerTreeHostImpl::ukm_manager_, which is
// initialized right after the LayerTreeHostImpl is created. So when this
- // pointer is initialized, there should be no trackers yet. Moreover, the
- // LayerTreeHostImpl::ukm_manager_ lives as long as the LayerTreeHostImpl, so
- // this pointer should never be null as long as LayerTreeHostImpl is alive.
+ // pointer is initialized, there should be no trackers yet.
UkmManager* const ukm_manager_;
+
+ // The last "PercentDroppedFrames" reported to UMA. LayerTreeHostImpl will
+ // read this number and send to the GPU process. When this page is done, we
+ // will report to UKM using these numbers. Currently only meaningful to the
+ // kUniversal tracker.
+ // Possible values:
+ // 1. A non-negative int value which is the percent of frames dropped.
+ // 2. base::nullopt: when they are fetched by LayerTreeHostImpl, so that it
+ // knows that the last value has been reported.
+ base::Optional<int> last_aggregated_percent_;
+ base::Optional<int> last_main_percent_;
+ base::Optional<int> last_impl_percent_;
};
} // namespace cc
diff --git a/chromium/cc/metrics/video_playback_roughness_reporter.cc b/chromium/cc/metrics/video_playback_roughness_reporter.cc
index 8e76345e537..a7b77e99a65 100644
--- a/chromium/cc/metrics/video_playback_roughness_reporter.cc
+++ b/chromium/cc/metrics/video_playback_roughness_reporter.cc
@@ -4,6 +4,8 @@
#include "cc/metrics/video_playback_roughness_reporter.h"
+#include <algorithm>
+
#include "base/bind_helpers.h"
#include "base/metrics/histogram_macros.h"
#include "components/viz/common/quads/compositor_frame_metadata.h"
@@ -28,6 +30,7 @@ constexpr int VideoPlaybackRoughnessReporter::kMaxWindowSize;
constexpr int VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit;
constexpr int VideoPlaybackRoughnessReporter::kMinWindowsBeforeSubmit;
constexpr int VideoPlaybackRoughnessReporter::kPercentileToSubmit;
+constexpr double VideoPlaybackRoughnessReporter::kDesiredWindowDuration;
VideoPlaybackRoughnessReporter::VideoPlaybackRoughnessReporter(
PlaybackRoughnessReportingCallback reporting_cb)
@@ -37,8 +40,7 @@ VideoPlaybackRoughnessReporter::~VideoPlaybackRoughnessReporter() = default;
double VideoPlaybackRoughnessReporter::ConsecutiveFramesWindow::roughness()
const {
- return root_mean_square_error.InMicrosecondsF() /
- intended_duration.InMicrosecondsF();
+ return root_mean_square_error.InMillisecondsF();
}
VideoPlaybackRoughnessReporter::FrameInfo::FrameInfo() = default;
@@ -56,17 +58,10 @@ void VideoPlaybackRoughnessReporter::FrameSubmitted(
FrameInfo info;
info.token = token;
- info.decode_time.emplace();
- if (!frame.metadata()->GetTimeTicks(
- media::VideoFrameMetadata::DECODE_END_TIME,
- &info.decode_time.value())) {
- info.decode_time.reset();
- }
+ info.decode_time = frame.metadata()->decode_end_time;
- info.intended_duration.emplace();
- if (frame.metadata()->GetTimeDelta(
- media::VideoFrameMetadata::WALLCLOCK_FRAME_DURATION,
- &info.intended_duration.value())) {
+ info.intended_duration = frame.metadata()->wallclock_frame_duration;
+ if (info.intended_duration) {
if (render_interval > info.intended_duration.value()) {
// In videos with FPS higher than display refresh rate we acknowledge
// the fact that some frames will be dropped upstream and frame's intended
@@ -74,27 +69,28 @@ void VideoPlaybackRoughnessReporter::FrameSubmitted(
info.intended_duration = render_interval;
}
- // Adjust frame window size to fit about 0.5 seconds of playback
- double win_size =
- std::round(0.5 / info.intended_duration.value().InSecondsF());
+ // Adjust frame window size to fit about 1 second of playback
+ double win_size = std::round(kDesiredWindowDuration /
+ info.intended_duration.value().InSecondsF());
frames_window_size_ = std::max(kMinWindowSize, static_cast<int>(win_size));
frames_window_size_ = std::min(frames_window_size_, kMaxWindowSize);
- } else {
- info.intended_duration.reset();
}
+
frames_.push_back(info);
}
void VideoPlaybackRoughnessReporter::FramePresented(TokenType token,
- base::TimeTicks timestamp) {
+ base::TimeTicks timestamp,
+ bool reliable_timestamp) {
for (auto it = frames_.rbegin(); it != frames_.rend(); it++) {
FrameInfo& info = *it;
if (token == it->token) {
- info.presentation_time = timestamp;
if (info.decode_time.has_value()) {
auto time_since_decode = timestamp - info.decode_time.value();
UMA_HISTOGRAM_TIMES("Media.VideoFrameSubmitter", time_since_decode);
}
+ if (reliable_timestamp)
+ info.presentation_time = timestamp;
break;
}
if (viz::FrameTokenGT(token, it->token))
diff --git a/chromium/cc/metrics/video_playback_roughness_reporter.h b/chromium/cc/metrics/video_playback_roughness_reporter.h
index caddf6a3922..d001df91d7c 100644
--- a/chromium/cc/metrics/video_playback_roughness_reporter.h
+++ b/chromium/cc/metrics/video_playback_roughness_reporter.h
@@ -25,7 +25,7 @@ using PlaybackRoughnessReportingCallback = base::RepeatingCallback<
// This class tracks moments when each frame was submitted
// and when it was displayed. Then series of frames split into groups
-// of consecutive frames, where each group takes about 1/2 second of playback.
+// of consecutive frames, where each group takes about one second of playback.
// Such groups also called 'frame windows'. Each windows is assigned a roughness
// score that measures how far playback smoothness was from the ideal playback.
// Information about several windows and their roughness score is aggregated
@@ -46,7 +46,9 @@ class CC_EXPORT VideoPlaybackRoughnessReporter {
void FrameSubmitted(TokenType token,
const media::VideoFrame& frame,
base::TimeDelta render_interval);
- void FramePresented(TokenType token, base::TimeTicks timestamp);
+ void FramePresented(TokenType token,
+ base::TimeTicks timestamp,
+ bool reliable_timestamp);
void ProcessFrameWindow();
void Reset();
@@ -54,13 +56,13 @@ class CC_EXPORT VideoPlaybackRoughnessReporter {
static constexpr int kMinWindowSize = 6;
// An upper bund on how many frames can be in ConsecutiveFramesWindow
- static constexpr int kMaxWindowSize = 40;
+ static constexpr int kMaxWindowSize = 60;
// How many frame windows should be observed before reporting smoothness
// due to playback time.
- // 1/2 a second per window, 200 windows. It means smoothness will be reported
+ // 1 second per window, 100 windows. It means smoothness will be reported
// for every 100 seconds of playback.
- static constexpr int kMaxWindowsBeforeSubmit = 200;
+ static constexpr int kMaxWindowsBeforeSubmit = 100;
// How many frame windows should be observed to report soothness on last
// time before the destruction of the reporter.
@@ -72,6 +74,13 @@ class CC_EXPORT VideoPlaybackRoughnessReporter {
static_assert(kPercentileToSubmit > 0 && kPercentileToSubmit < 100,
"invalid percentile value");
+ // Desired duration of ConsecutiveFramesWindow in seconds.
+ // This value and the video FPS are being used when calculating actual
+ // number of frames in the ConsecutiveFramesWindow.
+ // kMinWindowSize and kMaxWindowSize put bounds to the window length
+ // and superseed this value.
+ static constexpr double kDesiredWindowDuration = 1.0;
+
private:
friend class VideoPlaybackRoughnessReporterTest;
struct FrameInfo {
diff --git a/chromium/cc/metrics/video_playback_roughness_reporter_unittest.cc b/chromium/cc/metrics/video_playback_roughness_reporter_unittest.cc
index e93cbfc3d61..053c1e77d35 100644
--- a/chromium/cc/metrics/video_playback_roughness_reporter_unittest.cc
+++ b/chromium/cc/metrics/video_playback_roughness_reporter_unittest.cc
@@ -5,6 +5,7 @@
#include "cc/metrics/video_playback_roughness_reporter.h"
#include <algorithm>
+#include <memory>
#include <random>
#include <vector>
@@ -36,16 +37,21 @@ class VideoPlaybackRoughnessReporterTest : public ::testing::Test {
scoped_refptr<VideoFrame> MakeFrame(base::TimeDelta duration) {
scoped_refptr<VideoFrame> result = media::VideoFrame::CreateColorFrame(
gfx::Size(4, 4), 0x80, 0x80, 0x80, base::TimeDelta());
- result->metadata()->SetTimeDelta(
- VideoFrameMetadata::WALLCLOCK_FRAME_DURATION, duration);
+ result->metadata()->wallclock_frame_duration = duration;
return result;
}
::testing::AssertionResult CheckSizes() {
- if (reporter()->frames_.size() > 80u)
- return ::testing::AssertionFailure()
- << "frames " << reporter()->frames_.size();
- if (reporter()->worst_windows_.size() > 20u)
+ size_t max_frames =
+ 2 * size_t{VideoPlaybackRoughnessReporter::kMaxWindowSize};
+ if (reporter()->frames_.size() > max_frames)
+ return ::testing::AssertionFailure();
+
+ constexpr int max_worst_windows_size =
+ 1 + VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit *
+ (100 - VideoPlaybackRoughnessReporter::kPercentileToSubmit) /
+ 100;
+ if (reporter()->worst_windows_.size() > max_worst_windows_size)
return ::testing::AssertionFailure()
<< "windows " << reporter()->worst_windows_.size();
return ::testing::AssertionSuccess();
@@ -60,7 +66,7 @@ class VideoPlaybackRoughnessReporterTest : public ::testing::Test {
base::TimeDelta duration = vsync * frame_cadence;
auto frame = MakeFrame(ideal_duration);
reporter()->FrameSubmitted(idx, *frame, vsync);
- reporter()->FramePresented(idx, time);
+ reporter()->FramePresented(idx, time, true);
reporter()->ProcessFrameWindow();
time += duration;
}
@@ -83,7 +89,7 @@ class VideoPlaybackRoughnessReporterTest : public ::testing::Test {
int presented_idx = idx - i;
int frame_cadence = cadence[presented_idx % cadence.size()];
base::TimeDelta duration = vsync * frame_cadence;
- reporter()->FramePresented(presented_idx, time);
+ reporter()->FramePresented(presented_idx, time, true);
time += duration;
}
}
@@ -98,13 +104,13 @@ TEST_F(VideoPlaybackRoughnessReporterTest, BestCase24fps) {
int fps = 24;
SetReportingCallabck(
[&](int size, base::TimeDelta duration, double roughness) {
- ASSERT_EQ(size, fps / 2);
- ASSERT_NEAR(duration.InMillisecondsF(), 500.0, 1.0);
- ASSERT_NEAR(roughness, 0.0118, 0.0001);
+ ASSERT_EQ(size, fps);
+ ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+ ASSERT_NEAR(roughness, 5.9, 0.1);
call_count++;
});
int frames_to_run =
- VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps / 2 + 10;
+ VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 10;
NormalRun(fps, 60, {2, 3}, frames_to_run);
EXPECT_EQ(call_count, 1);
}
@@ -114,29 +120,90 @@ TEST_F(VideoPlaybackRoughnessReporterTest, BestCase24fpsOn120Hz) {
int fps = 24;
SetReportingCallabck(
[&](int size, base::TimeDelta duration, double roughness) {
- ASSERT_EQ(size, fps / 2);
- ASSERT_NEAR(duration.InMillisecondsF(), 500.0, 1.0);
- ASSERT_NEAR(roughness, 0.0, 0.0001);
+ ASSERT_EQ(size, fps);
+ ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+ ASSERT_NEAR(roughness, 0.0, 0.1);
call_count++;
});
int frames_to_run =
- VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps / 2 + 10;
+ VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 10;
NormalRun(fps, 120, {5}, frames_to_run);
EXPECT_EQ(call_count, 1);
}
+TEST_F(VideoPlaybackRoughnessReporterTest, BestCase30fps) {
+ int call_count = 0;
+ int fps = 30;
+ SetReportingCallabck(
+ [&](int size, base::TimeDelta duration, double roughness) {
+ ASSERT_EQ(size, fps);
+ ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+ ASSERT_NEAR(roughness, 0.0, 0.1);
+ call_count++;
+ });
+ int frames_to_run =
+ VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 1;
+ NormalRun(fps, 60, {2}, frames_to_run);
+ EXPECT_EQ(call_count, 1);
+}
+
+// This cadence pattern was used in the small user study and was found
+// to be perceived by participants as not as good as ideal 30fps playback but
+// better than the pattern from UserStudyBad.
+// The main characteristic of this test is that cadence breaks by having a frame
+// shown only once, but the very next frame is being shown 3 times thus
+// fixing the synchronization.
+TEST_F(VideoPlaybackRoughnessReporterTest, UserStudyOkay) {
+ int call_count = 0;
+ int fps = 30;
+ SetReportingCallabck(
+ [&](int size, base::TimeDelta duration, double roughness) {
+ ASSERT_EQ(size, fps);
+ ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+ ASSERT_NEAR(roughness, 4.3, 0.1);
+ call_count++;
+ });
+ int frames_to_run =
+ VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 1;
+ NormalRun(fps, 60, {2, 2, 2, 2, 2, 2, 1, 3, 2, 2, 2, 2, 2, 2, 2},
+ frames_to_run);
+ EXPECT_EQ(call_count, 1);
+}
+
+// This cadence pattern was used in the small user study and was found
+// to be perceived as worst of all options in the study.
+// The main characteristic of this test is that cadence breaks by having a frame
+// shown only once, and it takes 2 more frames for a frame that is shown 3 times
+// thus fixing the synchronization.
+TEST_F(VideoPlaybackRoughnessReporterTest, UserStudyBad) {
+ int call_count = 0;
+ int fps = 30;
+ SetReportingCallabck(
+ [&](int size, base::TimeDelta duration, double roughness) {
+ ASSERT_EQ(size, fps);
+ ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+ ASSERT_NEAR(roughness, 7.46, 0.1);
+ call_count++;
+ });
+ int frames_to_run =
+ VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 1;
+ NormalRun(fps, 60, {2, 2, 2, 2, 2, 1, 2, 2, 3, 2, 2, 2, 2, 2, 2},
+ frames_to_run);
+ EXPECT_EQ(call_count, 1);
+}
+
TEST_F(VideoPlaybackRoughnessReporterTest, Glitchy24fps) {
int call_count = 0;
int fps = 24;
SetReportingCallabck(
[&](int size, base::TimeDelta duration, double roughness) {
- ASSERT_EQ(size, fps / 2);
- ASSERT_NEAR(duration.InMillisecondsF(), 500.0, 1.0);
- ASSERT_NEAR(roughness, 0.0264, 0.0001);
+ ASSERT_EQ(size, fps);
+ ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+ ASSERT_NEAR(roughness, 14.8, 0.1);
call_count++;
});
int frames_to_run =
- VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps / 2 + 1;
+ VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 1;
NormalRun(fps, 60, {2, 3, 1, 3, 2, 4, 2, 3, 2, 3, 3, 3}, frames_to_run);
EXPECT_EQ(call_count, 1);
}
@@ -146,13 +213,13 @@ TEST_F(VideoPlaybackRoughnessReporterTest, BestCase60fps) {
int fps = 60;
SetReportingCallabck(
[&](int size, base::TimeDelta duration, double roughness) {
- ASSERT_EQ(size, fps / 2);
- ASSERT_NEAR(duration.InMillisecondsF(), 500.0, 1.0);
- ASSERT_NEAR(roughness, 0.0, 0.0001);
+ ASSERT_EQ(size, fps);
+ ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+ ASSERT_NEAR(roughness, 0.0, 0.1);
call_count++;
});
int frames_to_run =
- VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps / 2 + 1;
+ VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 1;
NormalRun(fps, 60, {1}, frames_to_run);
EXPECT_EQ(call_count, 1);
}
@@ -162,13 +229,13 @@ TEST_F(VideoPlaybackRoughnessReporterTest, BestCase50fps) {
int fps = 50;
SetReportingCallabck(
[&](int size, base::TimeDelta duration, double roughness) {
- ASSERT_EQ(size, fps / 2);
- ASSERT_NEAR(duration.InMillisecondsF(), 500.0, 1.0);
- ASSERT_NEAR(roughness, 0.0163, 0.0001);
+ ASSERT_EQ(size, fps);
+ ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+ ASSERT_NEAR(roughness, 8.1, 01);
call_count++;
});
int frames_to_run =
- VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps / 2 + 1;
+ VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 1;
NormalRun(fps, 60, {1, 1, 1, 1, 2}, frames_to_run);
EXPECT_EQ(call_count, 1);
}
@@ -177,19 +244,18 @@ TEST_F(VideoPlaybackRoughnessReporterTest, BestCase50fps) {
// get any result we need.
TEST_F(VideoPlaybackRoughnessReporterTest, PredictableRoughnessValue) {
int fps = 12;
- int frames_in_window = fps / 2;
+ int frames_in_window = fps;
int call_count = 0;
- double intended_roughness = 0.042;
+ double intended_roughness = 4.2;
base::TimeDelta vsync = base::TimeDelta::FromSecondsD(1.0 / fps);
// Calculating the error value that needs to be injected into one frame
// in order to get desired roughness.
- base::TimeDelta error =
- std::sqrt(intended_roughness * intended_roughness * frames_in_window) *
- frames_in_window * vsync;
+ base::TimeDelta error = base::TimeDelta::FromMillisecondsD(
+ std::sqrt(intended_roughness * intended_roughness * frames_in_window));
auto callback = [&](int size, base::TimeDelta duration, double roughness) {
ASSERT_EQ(frames_in_window, size);
- ASSERT_NEAR(roughness, intended_roughness, 0.0001);
+ ASSERT_NEAR(roughness, intended_roughness, 0.1);
call_count++;
};
SetReportingCallabck(callback);
@@ -204,7 +270,7 @@ TEST_F(VideoPlaybackRoughnessReporterTest, PredictableRoughnessValue) {
auto frame = MakeFrame(vsync);
reporter()->FrameSubmitted(token, *frame, vsync);
- reporter()->FramePresented(token++, time);
+ reporter()->FramePresented(token++, time, true);
reporter()->ProcessFrameWindow();
}
}
@@ -216,22 +282,22 @@ TEST_F(VideoPlaybackRoughnessReporterTest, PredictableRoughnessValue) {
TEST_F(VideoPlaybackRoughnessReporterTest, TakingPercentile) {
int token = 0;
int fps = 12;
- int frames_in_window = fps / 2;
+ int frames_in_window = fps;
int call_count = 0;
int win_count = 100;
base::TimeDelta vsync = base::TimeDelta::FromSecondsD(1.0 / fps);
std::vector<double> targets;
targets.reserve(win_count);
for (int i = 0; i < win_count; i++)
- targets.push_back(i * 0.0001);
+ targets.push_back(i * 0.1);
double expected_roughness =
- VideoPlaybackRoughnessReporter::kPercentileToSubmit * 0.0001;
+ VideoPlaybackRoughnessReporter::kPercentileToSubmit * 0.1;
std::mt19937 rnd(1);
std::shuffle(targets.begin(), targets.end(), rnd);
auto callback = [&](int size, base::TimeDelta duration, double roughness) {
ASSERT_EQ(frames_in_window, size);
- ASSERT_NEAR(roughness, expected_roughness, 0.00001);
+ ASSERT_NEAR(roughness, expected_roughness, 0.05);
call_count++;
};
SetReportingCallabck(callback);
@@ -240,9 +306,8 @@ TEST_F(VideoPlaybackRoughnessReporterTest, TakingPercentile) {
double roughness = targets[win_idx];
// Calculating the error value that needs to be injected into one frame
// in order to get desired roughness.
- base::TimeDelta error =
- std::sqrt(roughness * roughness * frames_in_window) * frames_in_window *
- vsync;
+ base::TimeDelta error = base::TimeDelta::FromMillisecondsD(
+ std::sqrt(roughness * roughness * frames_in_window));
for (int frame_idx = 0; frame_idx < frames_in_window; frame_idx++) {
base::TimeTicks time;
@@ -252,7 +317,7 @@ TEST_F(VideoPlaybackRoughnessReporterTest, TakingPercentile) {
auto frame = MakeFrame(vsync);
reporter()->FrameSubmitted(token, *frame, vsync);
- reporter()->FramePresented(token++, time);
+ reporter()->FramePresented(token++, time, true);
reporter()->ProcessFrameWindow();
}
}
@@ -271,7 +336,7 @@ TEST_F(VideoPlaybackRoughnessReporterTest, LongRunWithoutWindows) {
auto frame = MakeFrame(vsync);
reporter()->FrameSubmitted(i, *frame, vsync);
if (i % 2 == 0)
- reporter()->FramePresented(i, base::TimeTicks() + i * vsync);
+ reporter()->FramePresented(i, base::TimeTicks() + i * vsync, true);
reporter()->ProcessFrameWindow();
ASSERT_TRUE(CheckSizes());
}
@@ -288,7 +353,24 @@ TEST_F(VideoPlaybackRoughnessReporterTest, PresentingUnknownFrames) {
for (int i = 0; i < 10000; i++) {
auto frame = MakeFrame(vsync);
reporter()->FrameSubmitted(i, *frame, vsync);
- reporter()->FramePresented(i + 100000, base::TimeTicks() + i * vsync);
+ reporter()->FramePresented(i + 100000, base::TimeTicks() + i * vsync, true);
+ reporter()->ProcessFrameWindow();
+ ASSERT_TRUE(CheckSizes());
+ }
+ EXPECT_EQ(call_count, 0);
+}
+
+// Test that the reporter is ignoring frames with unreliable
+// presentation timestamp.
+TEST_F(VideoPlaybackRoughnessReporterTest, IgnoringUnreliableTimings) {
+ int call_count = 0;
+ base::TimeDelta vsync = base::TimeDelta::FromMilliseconds(1);
+ SetReportingCallabck([&](int size, base::TimeDelta duration,
+ double roughness) { call_count++; });
+ for (int i = 0; i < 10000; i++) {
+ auto frame = MakeFrame(vsync);
+ reporter()->FrameSubmitted(i, *frame, vsync);
+ reporter()->FramePresented(i, base::TimeTicks() + i * vsync, false);
reporter()->ProcessFrameWindow();
ASSERT_TRUE(CheckSizes());
}
@@ -307,7 +389,7 @@ TEST_F(VideoPlaybackRoughnessReporterTest, ReportingInReset) {
// Set number of frames insufficient for reporting in Reset()
int frames_to_run =
- VideoPlaybackRoughnessReporter::kMinWindowsBeforeSubmit * fps / 2 - 1;
+ VideoPlaybackRoughnessReporter::kMinWindowsBeforeSubmit * fps - 1;
NormalRun(fps, 60, {1}, frames_to_run);
// No calls since, not enough windows were reported
EXPECT_EQ(call_count, 0);
@@ -318,7 +400,7 @@ TEST_F(VideoPlaybackRoughnessReporterTest, ReportingInReset) {
// Set number of frames sufficient for reporting in Reset()
frames_to_run =
- VideoPlaybackRoughnessReporter::kMinWindowsBeforeSubmit * fps / 2 + 1;
+ VideoPlaybackRoughnessReporter::kMinWindowsBeforeSubmit * fps + 1;
NormalRun(fps, 60, {1}, frames_to_run);
// No calls since, not enough windows were reported
@@ -338,27 +420,27 @@ TEST_F(VideoPlaybackRoughnessReporterTest, BatchPresentation) {
// Try 60 fps
SetReportingCallabck(
[&](int size, base::TimeDelta duration, double roughness) {
- ASSERT_EQ(size, fps / 2);
- ASSERT_NEAR(duration.InMillisecondsF(), 500.0, 1.0);
- ASSERT_NEAR(roughness, 0.0, 0.0001);
+ ASSERT_EQ(size, fps);
+ ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+ ASSERT_NEAR(roughness, 0.0, 0.1);
call_count++;
});
int frames_to_run =
- VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps / 2 + 10;
+ VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 10;
BatchPresentationRun(fps, 60, {1}, frames_to_run);
EXPECT_EQ(call_count, 1);
// Try 24fps
SetReportingCallabck(
[&](int size, base::TimeDelta duration, double roughness) {
- ASSERT_EQ(size, fps / 2);
- ASSERT_NEAR(duration.InMillisecondsF(), 500.0, 1.0);
- ASSERT_NEAR(roughness, 0.0118, 0.0001);
+ ASSERT_EQ(size, fps);
+ ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+ ASSERT_NEAR(roughness, 5.9, 0.1);
call_count++;
});
fps = 24;
frames_to_run =
- VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps / 2 + 10;
+ VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 10;
BatchPresentationRun(fps, 60, {2, 3}, frames_to_run);
EXPECT_EQ(call_count, 2);
}
diff --git a/chromium/cc/paint/BUILD.gn b/chromium/cc/paint/BUILD.gn
index 4c3776c0411..0a9161d4e66 100644
--- a/chromium/cc/paint/BUILD.gn
+++ b/chromium/cc/paint/BUILD.gn
@@ -107,6 +107,7 @@ cc_component("paint") {
deps = [
"//base",
+ "//gpu/command_buffer/common:mailbox",
"//ui/gfx/animation",
"//ui/gfx/ipc/color",
]
diff --git a/chromium/cc/paint/decoded_draw_image.cc b/chromium/cc/paint/decoded_draw_image.cc
index 2c66554d449..69f195f2bb4 100644
--- a/chromium/cc/paint/decoded_draw_image.cc
+++ b/chromium/cc/paint/decoded_draw_image.cc
@@ -17,6 +17,14 @@ DecodedDrawImage::DecodedDrawImage(sk_sp<const SkImage> image,
filter_quality_(filter_quality),
is_budgeted_(is_budgeted) {}
+DecodedDrawImage::DecodedDrawImage(const gpu::Mailbox& mailbox,
+ SkFilterQuality filter_quality)
+ : mailbox_(mailbox),
+ src_rect_offset_(SkSize::MakeEmpty()),
+ scale_adjustment_(SkSize::Make(1.f, 1.f)),
+ filter_quality_(filter_quality),
+ is_budgeted_(true) {}
+
DecodedDrawImage::DecodedDrawImage(
base::Optional<uint32_t> transfer_cache_entry_id,
const SkSize& src_rect_offset,
diff --git a/chromium/cc/paint/decoded_draw_image.h b/chromium/cc/paint/decoded_draw_image.h
index 02b3ad1092c..a3e47d30fc0 100644
--- a/chromium/cc/paint/decoded_draw_image.h
+++ b/chromium/cc/paint/decoded_draw_image.h
@@ -10,6 +10,7 @@
#include "base/optional.h"
#include "cc/paint/paint_export.h"
+#include "gpu/command_buffer/common/mailbox.h"
#include "third_party/skia/include/core/SkFilterQuality.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkRefCnt.h"
@@ -29,6 +30,7 @@ class CC_PAINT_EXPORT DecodedDrawImage {
const SkSize& scale_adjustment,
SkFilterQuality filter_quality,
bool is_budgeted);
+ DecodedDrawImage(const gpu::Mailbox& mailbox, SkFilterQuality filter_quality);
DecodedDrawImage(base::Optional<uint32_t> transfer_cache_entry_id,
const SkSize& src_rect_offset,
const SkSize& scale_adjustment,
@@ -62,6 +64,7 @@ class CC_PAINT_EXPORT DecodedDrawImage {
private:
sk_sp<const SkImage> image_;
+ gpu::Mailbox mailbox_;
base::Optional<uint32_t> transfer_cache_entry_id_;
SkSize src_rect_offset_;
SkSize scale_adjustment_;
diff --git a/chromium/cc/paint/discardable_image_map_unittest.cc b/chromium/cc/paint/discardable_image_map_unittest.cc
index fd52a353fcc..380ee6d6ae2 100644
--- a/chromium/cc/paint/discardable_image_map_unittest.cc
+++ b/chromium/cc/paint/discardable_image_map_unittest.cc
@@ -626,8 +626,8 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInShader) {
.set_paint_image_generator(
CreatePaintImageGenerator(gfx::Size(500, 500)))
.TakePaintImage();
- SkMatrix scale = SkMatrix::MakeScale(std::max(x * 0.5f, kMinScale),
- std::max(y * 0.5f, kMinScale));
+ SkMatrix scale = SkMatrix::Scale(std::max(x * 0.5f, kMinScale),
+ std::max(y * 0.5f, kMinScale));
PaintFlags flags;
flags.setShader(PaintShader::MakeImage(discardable_image[y][x],
SkTileMode::kClamp,
diff --git a/chromium/cc/paint/display_item_list.cc b/chromium/cc/paint/display_item_list.cc
index 52a3aac9f07..071f57b646f 100644
--- a/chromium/cc/paint/display_item_list.cc
+++ b/chromium/cc/paint/display_item_list.cc
@@ -296,7 +296,6 @@ void DisplayItemList::Reset() {
offsets_.shrink_to_fit();
paired_begin_stack_.clear();
paired_begin_stack_.shrink_to_fit();
- has_draw_ops_ = false;
}
sk_sp<PaintRecord> DisplayItemList::ReleaseAsRecord() {
diff --git a/chromium/cc/paint/display_item_list.h b/chromium/cc/paint/display_item_list.h
index b518d67715a..c065c33e60a 100644
--- a/chromium/cc/paint/display_item_list.h
+++ b/chromium/cc/paint/display_item_list.h
@@ -91,8 +91,6 @@ class CC_PAINT_EXPORT DisplayItemList
if (usage_hint_ == kTopLevelDisplayItemList)
offsets_.push_back(offset);
const T* op = paint_op_buffer_.push<T>(std::forward<Args>(args)...);
- if (op->IsDrawOp())
- has_draw_ops_ = true;
DCHECK(op->IsValid());
return offset;
}
@@ -181,11 +179,17 @@ class CC_PAINT_EXPORT DisplayItemList
int max_ops_to_analyze = 1);
std::string ToString() const;
- bool has_draw_ops() const { return has_draw_ops_; }
+ bool has_draw_ops() const { return paint_op_buffer_.has_draw_ops(); }
// Ops with nested paint ops are considered as a single op.
size_t num_paint_ops() const { return paint_op_buffer_.size(); }
+ bool NeedsAdditionalInvalidationForLCDText(
+ const DisplayItemList& old_list) const {
+ return paint_op_buffer_.NeedsAdditionalInvalidationForLCDText(
+ old_list.paint_op_buffer_);
+ }
+
private:
friend class DisplayItemListTest;
friend gpu::raster::RasterImplementation;
@@ -238,7 +242,6 @@ class CC_PAINT_EXPORT DisplayItemList
#endif
UsageHint usage_hint_;
- bool has_draw_ops_ = false;
friend class base::RefCountedThreadSafe<DisplayItemList>;
FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, BytesUsed);
diff --git a/chromium/cc/paint/display_item_list_unittest.cc b/chromium/cc/paint/display_item_list_unittest.cc
index a4dea57bbfb..a8230788bbb 100644
--- a/chromium/cc/paint/display_item_list_unittest.cc
+++ b/chromium/cc/paint/display_item_list_unittest.cc
@@ -8,6 +8,7 @@
#include <vector>
+#include "base/logging.h"
#include "base/trace_event/traced_value.h"
#include "base/values.h"
#include "cc/paint/filter_operation.h"
diff --git a/chromium/cc/paint/element_id.cc b/chromium/cc/paint/element_id.cc
index 3465eb1117a..a5b11cb2da2 100644
--- a/chromium/cc/paint/element_id.cc
+++ b/chromium/cc/paint/element_id.cc
@@ -15,6 +15,11 @@ namespace cc {
const ElementIdType ElementId::kInvalidElementId = 0;
+// static
+bool ElementId::IsValid(ElementIdType id) {
+ return id != kInvalidElementId;
+}
+
ElementId LayerIdToElementIdForTesting(int layer_id) {
return ElementId(std::numeric_limits<int>::max() - layer_id);
}
diff --git a/chromium/cc/paint/element_id.h b/chromium/cc/paint/element_id.h
index a4108a13b37..fcdebf565b7 100644
--- a/chromium/cc/paint/element_id.h
+++ b/chromium/cc/paint/element_id.h
@@ -63,6 +63,8 @@ struct CC_PAINT_EXPORT ElementId {
std::string ToString() const;
+ static bool IsValid(ElementIdType id);
+
private:
friend struct ElementIdHash;
static const ElementIdType kInvalidElementId;
diff --git a/chromium/cc/paint/filter_operation.h b/chromium/cc/paint/filter_operation.h
index e1c018b9be3..8971192195f 100644
--- a/chromium/cc/paint/filter_operation.h
+++ b/chromium/cc/paint/filter_operation.h
@@ -8,7 +8,7 @@
#include <memory>
#include <vector>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "cc/paint/paint_export.h"
#include "cc/paint/paint_filter.h"
#include "third_party/skia/include/core/SkColor.h"
diff --git a/chromium/cc/paint/filter_operations.h b/chromium/cc/paint/filter_operations.h
index 5bc51ad8ec8..1186796cd1a 100644
--- a/chromium/cc/paint/filter_operations.h
+++ b/chromium/cc/paint/filter_operations.h
@@ -10,7 +10,7 @@
#include <string>
#include <vector>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "cc/paint/filter_operation.h"
namespace base {
diff --git a/chromium/cc/paint/filter_operations_unittest.cc b/chromium/cc/paint/filter_operations_unittest.cc
index 93b3dbb0eed..7e4fdc4834d 100644
--- a/chromium/cc/paint/filter_operations_unittest.cc
+++ b/chromium/cc/paint/filter_operations_unittest.cc
@@ -23,9 +23,9 @@ TEST(FilterOperationsTest, MapRectBlur) {
EXPECT_EQ(gfx::Rect(-60, -60, 130, 130),
ops.MapRect(gfx::Rect(0, 0, 10, 10), SkMatrix::I()));
EXPECT_EQ(gfx::Rect(-120, -120, 260, 260),
- ops.MapRect(gfx::Rect(0, 0, 20, 20), SkMatrix::MakeScale(2, 2)));
+ ops.MapRect(gfx::Rect(0, 0, 20, 20), SkMatrix::Scale(2, 2)));
EXPECT_EQ(gfx::Rect(-60, -70, 130, 130),
- ops.MapRect(gfx::Rect(0, -10, 10, 10), SkMatrix::MakeScale(1, -1)));
+ ops.MapRect(gfx::Rect(0, -10, 10, 10), SkMatrix::Scale(1, -1)));
}
TEST(FilterOperationsTest, MapRectBlurOverflow) {
@@ -43,10 +43,10 @@ TEST(FilterOperationsTest, MapRectReverseBlur) {
ops.MapRectReverse(gfx::Rect(0, 0, 150, 150), SkMatrix::I()));
EXPECT_EQ(
gfx::Rect(120, 120, 60, 60),
- ops.MapRectReverse(gfx::Rect(0, 0, 300, 300), SkMatrix::MakeScale(2, 2)));
- EXPECT_EQ(gfx::Rect(60, 50, 30, 30),
- ops.MapRectReverse(gfx::Rect(0, -10, 150, 150),
- SkMatrix::MakeScale(1, -1)));
+ ops.MapRectReverse(gfx::Rect(0, 0, 300, 300), SkMatrix::Scale(2, 2)));
+ EXPECT_EQ(
+ gfx::Rect(60, 50, 30, 30),
+ ops.MapRectReverse(gfx::Rect(0, -10, 150, 150), SkMatrix::Scale(1, -1)));
}
TEST(FilterOperationsTest, MapRectDropShadowReferenceFilter) {
@@ -60,9 +60,9 @@ TEST(FilterOperationsTest, MapRectDropShadowReferenceFilter) {
EXPECT_EQ(gfx::Rect(-9, -19, 34, 64),
ops.MapRect(gfx::Rect(0, 0, 10, 10), SkMatrix::I()));
EXPECT_EQ(gfx::Rect(-18, -38, 68, 128),
- ops.MapRect(gfx::Rect(0, 0, 20, 20), SkMatrix::MakeScale(2, 2)));
+ ops.MapRect(gfx::Rect(0, 0, 20, 20), SkMatrix::Scale(2, 2)));
EXPECT_EQ(gfx::Rect(-9, -45, 34, 64),
- ops.MapRect(gfx::Rect(0, -10, 10, 10), SkMatrix::MakeScale(1, -1)));
+ ops.MapRect(gfx::Rect(0, -10, 10, 10), SkMatrix::Scale(1, -1)));
}
TEST(FilterOperationsTest, MapRectReverseDropShadowReferenceFilter) {
@@ -75,12 +75,11 @@ TEST(FilterOperationsTest, MapRectReverseDropShadowReferenceFilter) {
nullptr)));
EXPECT_EQ(gfx::Rect(-15, -35, 34, 64),
ops.MapRectReverse(gfx::Rect(0, 0, 10, 10), SkMatrix::I()));
+ EXPECT_EQ(gfx::Rect(-30, -70, 68, 128),
+ ops.MapRectReverse(gfx::Rect(0, 0, 20, 20), SkMatrix::Scale(2, 2)));
EXPECT_EQ(
- gfx::Rect(-30, -70, 68, 128),
- ops.MapRectReverse(gfx::Rect(0, 0, 20, 20), SkMatrix::MakeScale(2, 2)));
- EXPECT_EQ(gfx::Rect(-15, -29, 34, 64),
- ops.MapRectReverse(gfx::Rect(0, -10, 10, 10),
- SkMatrix::MakeScale(1, -1)));
+ gfx::Rect(-15, -29, 34, 64),
+ ops.MapRectReverse(gfx::Rect(0, -10, 10, 10), SkMatrix::Scale(1, -1)));
}
TEST(FilterOperationsTest, MapRectOffsetReferenceFilter) {
@@ -90,9 +89,9 @@ TEST(FilterOperationsTest, MapRectOffsetReferenceFilter) {
EXPECT_EQ(gfx::Rect(30, 40, 10, 10),
ops.MapRect(gfx::Rect(0, 0, 10, 10), SkMatrix::I()));
EXPECT_EQ(gfx::Rect(60, 80, 20, 20),
- ops.MapRect(gfx::Rect(0, 0, 20, 20), SkMatrix::MakeScale(2, 2)));
+ ops.MapRect(gfx::Rect(0, 0, 20, 20), SkMatrix::Scale(2, 2)));
EXPECT_EQ(gfx::Rect(30, -50, 10, 10),
- ops.MapRect(gfx::Rect(0, -10, 10, 10), SkMatrix::MakeScale(1, -1)));
+ ops.MapRect(gfx::Rect(0, -10, 10, 10), SkMatrix::Scale(1, -1)));
}
TEST(FilterOperationsTest, MapRectReverseOffsetReferenceFilter) {
@@ -101,12 +100,11 @@ TEST(FilterOperationsTest, MapRectReverseOffsetReferenceFilter) {
ops.Append(FilterOperation::CreateReferenceFilter(std::move(filter)));
EXPECT_EQ(gfx::Rect(-30, -40, 10, 10),
ops.MapRectReverse(gfx::Rect(0, 0, 10, 10), SkMatrix::I()));
+ EXPECT_EQ(gfx::Rect(-60, -80, 20, 20),
+ ops.MapRectReverse(gfx::Rect(0, 0, 20, 20), SkMatrix::Scale(2, 2)));
EXPECT_EQ(
- gfx::Rect(-60, -80, 20, 20),
- ops.MapRectReverse(gfx::Rect(0, 0, 20, 20), SkMatrix::MakeScale(2, 2)));
- EXPECT_EQ(gfx::Rect(-30, 30, 10, 10),
- ops.MapRectReverse(gfx::Rect(0, -10, 10, 10),
- SkMatrix::MakeScale(1, -1)));
+ gfx::Rect(-30, 30, 10, 10),
+ ops.MapRectReverse(gfx::Rect(0, -10, 10, 10), SkMatrix::Scale(1, -1)));
}
TEST(FilterOperationsTest, MapRectCombineNonCommutative) {
@@ -123,9 +121,9 @@ TEST(FilterOperationsTest, MapRectCombineNonCommutative) {
EXPECT_EQ(gfx::Rect(200, 200, 20, 20),
ops.MapRect(gfx::Rect(10, 10), SkMatrix::I()));
EXPECT_EQ(gfx::Rect(400, 400, 40, 40),
- ops.MapRect(gfx::Rect(20, 20), SkMatrix::MakeScale(2, 2)));
+ ops.MapRect(gfx::Rect(20, 20), SkMatrix::Scale(2, 2)));
EXPECT_EQ(gfx::Rect(200, -220, 20, 20),
- ops.MapRect(gfx::Rect(0, -10, 10, 10), SkMatrix::MakeScale(1, -1)));
+ ops.MapRect(gfx::Rect(0, -10, 10, 10), SkMatrix::Scale(1, -1)));
}
TEST(FilterOperationsTest, MapRectReverseCombineNonCommutative) {
@@ -142,10 +140,10 @@ TEST(FilterOperationsTest, MapRectReverseCombineNonCommutative) {
EXPECT_EQ(gfx::Rect(10, 10),
ops.MapRectReverse(gfx::Rect(200, 200, 20, 20), SkMatrix::I()));
EXPECT_EQ(gfx::Rect(20, 20), ops.MapRectReverse(gfx::Rect(400, 400, 40, 40),
- SkMatrix::MakeScale(2, 2)));
- EXPECT_EQ(gfx::Rect(0, -10, 10, 10),
- ops.MapRectReverse(gfx::Rect(200, -220, 20, 20),
- SkMatrix::MakeScale(1, -1)));
+ SkMatrix::Scale(2, 2)));
+ EXPECT_EQ(
+ gfx::Rect(0, -10, 10, 10),
+ ops.MapRectReverse(gfx::Rect(200, -220, 20, 20), SkMatrix::Scale(1, -1)));
}
TEST(FilterOperationsTest, MapRectNullReferenceFilter) {
@@ -154,9 +152,9 @@ TEST(FilterOperationsTest, MapRectNullReferenceFilter) {
EXPECT_EQ(gfx::Rect(0, 0, 10, 10),
ops.MapRect(gfx::Rect(0, 0, 10, 10), SkMatrix::I()));
EXPECT_EQ(gfx::Rect(0, 0, 20, 20),
- ops.MapRect(gfx::Rect(0, 0, 20, 20), SkMatrix::MakeScale(2, 2)));
+ ops.MapRect(gfx::Rect(0, 0, 20, 20), SkMatrix::Scale(2, 2)));
EXPECT_EQ(gfx::Rect(0, -10, 10, 10),
- ops.MapRect(gfx::Rect(0, -10, 10, 10), SkMatrix::MakeScale(1, -1)));
+ ops.MapRect(gfx::Rect(0, -10, 10, 10), SkMatrix::Scale(1, -1)));
}
TEST(FilterOperationsTest, MapRectReverseNullReferenceFilter) {
@@ -164,12 +162,11 @@ TEST(FilterOperationsTest, MapRectReverseNullReferenceFilter) {
ops.Append(FilterOperation::CreateReferenceFilter(nullptr));
EXPECT_EQ(gfx::Rect(0, 0, 10, 10),
ops.MapRectReverse(gfx::Rect(0, 0, 10, 10), SkMatrix::I()));
+ EXPECT_EQ(gfx::Rect(0, 0, 20, 20),
+ ops.MapRectReverse(gfx::Rect(0, 0, 20, 20), SkMatrix::Scale(2, 2)));
EXPECT_EQ(
- gfx::Rect(0, 0, 20, 20),
- ops.MapRectReverse(gfx::Rect(0, 0, 20, 20), SkMatrix::MakeScale(2, 2)));
- EXPECT_EQ(gfx::Rect(0, -10, 10, 10),
- ops.MapRectReverse(gfx::Rect(0, -10, 10, 10),
- SkMatrix::MakeScale(1, -1)));
+ gfx::Rect(0, -10, 10, 10),
+ ops.MapRectReverse(gfx::Rect(0, -10, 10, 10), SkMatrix::Scale(1, -1)));
}
TEST(FilterOperationsTest, MapRectDropShadow) {
@@ -178,9 +175,9 @@ TEST(FilterOperationsTest, MapRectDropShadow) {
EXPECT_EQ(gfx::Rect(-57, -52, 130, 130),
ops.MapRect(gfx::Rect(0, 0, 10, 10), SkMatrix::I()));
EXPECT_EQ(gfx::Rect(-114, -104, 260, 260),
- ops.MapRect(gfx::Rect(0, 0, 20, 20), SkMatrix::MakeScale(2, 2)));
+ ops.MapRect(gfx::Rect(0, 0, 20, 20), SkMatrix::Scale(2, 2)));
EXPECT_EQ(gfx::Rect(-57, -78, 130, 130),
- ops.MapRect(gfx::Rect(0, -10, 10, 10), SkMatrix::MakeScale(1, -1)));
+ ops.MapRect(gfx::Rect(0, -10, 10, 10), SkMatrix::Scale(1, -1)));
}
TEST(FilterOperationsTest, MapRectReverseDropShadow) {
@@ -188,12 +185,11 @@ TEST(FilterOperationsTest, MapRectReverseDropShadow) {
ops.Append(FilterOperation::CreateDropShadowFilter(gfx::Point(3, 8), 20, 0));
EXPECT_EQ(gfx::Rect(-63, -68, 130, 130),
ops.MapRectReverse(gfx::Rect(0, 0, 10, 10), SkMatrix::I()));
+ EXPECT_EQ(gfx::Rect(-126, -136, 260, 260),
+ ops.MapRectReverse(gfx::Rect(0, 0, 20, 20), SkMatrix::Scale(2, 2)));
EXPECT_EQ(
- gfx::Rect(-126, -136, 260, 260),
- ops.MapRectReverse(gfx::Rect(0, 0, 20, 20), SkMatrix::MakeScale(2, 2)));
- EXPECT_EQ(gfx::Rect(-63, -62, 130, 130),
- ops.MapRectReverse(gfx::Rect(0, -10, 10, 10),
- SkMatrix::MakeScale(1, -1)));
+ gfx::Rect(-63, -62, 130, 130),
+ ops.MapRectReverse(gfx::Rect(0, -10, 10, 10), SkMatrix::Scale(1, -1)));
}
TEST(FilterOperationsTest, MapRectDropShadowDoesNotContract) {
diff --git a/chromium/cc/paint/image_transfer_cache_entry.cc b/chromium/cc/paint/image_transfer_cache_entry.cc
index 155d95f1e3b..1f669ebefb4 100644
--- a/chromium/cc/paint/image_transfer_cache_entry.cc
+++ b/chromium/cc/paint/image_transfer_cache_entry.cc
@@ -24,6 +24,14 @@
namespace cc {
namespace {
+struct Context {
+ const std::vector<sk_sp<SkImage>> sk_planes_;
+};
+
+void ReleaseContext(SkImage::ReleaseContext context) {
+ auto* texture_context = static_cast<Context*>(context);
+ delete texture_context;
+}
// Creates a SkImage backed by the YUV textures corresponding to |plane_images|.
// The layout is specified by |plane_images_format|). The backend textures are
@@ -81,10 +89,11 @@ sk_sp<SkImage> MakeYUVImageFromUploadedPlanes(
return nullptr;
}
plane_indices[SkYUVAIndex::kA_Index] = {-1, SkColorChannel::kR};
+ Context* ctx = new Context{plane_images};
sk_sp<SkImage> image = SkImage::MakeFromYUVATextures(
context, yuv_color_space, plane_backend_textures.data(), plane_indices,
plane_images[0]->dimensions(), kTopLeft_GrSurfaceOrigin,
- std::move(image_color_space));
+ std::move(image_color_space), ReleaseContext, ctx);
if (!image) {
DLOG(ERROR) << "Could not create YUV image";
return nullptr;
diff --git a/chromium/cc/paint/image_transfer_cache_entry_unittest.cc b/chromium/cc/paint/image_transfer_cache_entry_unittest.cc
index 24a334eedbb..0da5db2f946 100644
--- a/chromium/cc/paint/image_transfer_cache_entry_unittest.cc
+++ b/chromium/cc/paint/image_transfer_cache_entry_unittest.cc
@@ -144,7 +144,7 @@ class ImageTransferCacheEntryTest
if (texture.isValid())
gr_context_->deleteBackendTexture(texture);
}
- gr_context_->flush();
+ gr_context_->flushAndSubmit();
textures_to_free_.clear();
}
diff --git a/chromium/cc/paint/oop_pixeltest.cc b/chromium/cc/paint/oop_pixeltest.cc
index 6c9f1c1e192..ffc090ebee1 100644
--- a/chromium/cc/paint/oop_pixeltest.cc
+++ b/chromium/cc/paint/oop_pixeltest.cc
@@ -332,7 +332,7 @@ class OopPixelTest : public testing::Test,
raster_source->PlaybackToCanvas(
canvas, options.content_size, options.full_raster_rect,
options.playback_rect, raster_transform, settings);
- surface->flush();
+ surface->flushAndSubmit();
EXPECT_EQ(gles2_context_provider_->ContextGL()->GetError(),
static_cast<unsigned>(GL_NO_ERROR));
@@ -852,7 +852,7 @@ TEST_P(OopImagePixelTest, DrawImageWithSetMatrix) {
display_item_list->StartPaint();
PaintFlags flags;
flags.setFilterQuality(FilterQuality());
- display_item_list->push<SetMatrixOp>(SkMatrix::MakeScale(0.5f, 0.5f));
+ display_item_list->push<SetMatrixOp>(SkMatrix::Scale(0.5f, 0.5f));
display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, &flags);
display_item_list->EndPaintOfUnpaired(rect);
display_item_list->Finalize();
@@ -1358,6 +1358,39 @@ TEST_F(OopPixelTest, DrawRectScaleTransformOptions) {
ExpectEquals(actual, expected);
}
+TEST_F(OopPixelTest, DrawRectTransformOptionsFullRaster) {
+ PaintFlags flags;
+ // Use powers of two here to make floating point blending consistent.
+ flags.setColor(SkColorSetRGB(64, 128, 32));
+ flags.setAntiAlias(true);
+ gfx::Rect draw_rect(0, 0, 19, 19);
+
+ auto display_item_list = base::MakeRefCounted<DisplayItemList>();
+ display_item_list->StartPaint();
+ display_item_list->push<DrawRectOp>(gfx::RectToSkRect(draw_rect), flags);
+ display_item_list->EndPaintOfUnpaired(draw_rect);
+ display_item_list->Finalize();
+
+ // The opaque rect above is 1px smaller than the canvas. With the subpixel
+ // translation, the rect fills the whole canvas, but the pixels at the edges
+ // are translucent. We should clear the canvas before drawing the rect, so
+ // the translucent pixels at the edges should not expose the preclear color,
+ // even if requires_clear is not true.
+ RasterOptions options;
+ options.resource_size = {20, 20};
+ options.content_size = {25, 25};
+ options.full_raster_rect = {5, 5, 20, 20};
+ options.playback_rect = {5, 5, 20, 20};
+ options.preclear = true;
+ options.preclear_color = SK_ColorRED;
+ options.post_translate = {0.5f, 0.25f};
+ options.post_scale = 2.f;
+
+ auto actual = Raster(display_item_list, options);
+ auto expected = RasterExpectedBitmap(display_item_list, options);
+ ExpectEquals(actual, expected);
+}
+
TEST_F(OopPixelTest, DrawRectQueryMiddleOfDisplayList) {
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
std::vector<SkColor> colors = {
@@ -1546,12 +1579,12 @@ class OopRecordFilterPixelTest : public OopPixelTest,
};
TEST_P(OopRecordFilterPixelTest, FilterWithTextScaled) {
- SkMatrix mat = SkMatrix::MakeScale(2.f, 2.f);
+ SkMatrix mat = SkMatrix::Scale(2.f, 2.f);
RunTest(mat);
}
TEST_P(OopRecordFilterPixelTest, FilterWithTextAndComplexCTM) {
- SkMatrix mat = SkMatrix::MakeScale(2.f, 2.f);
+ SkMatrix mat = SkMatrix::Scale(2.f, 2.f);
mat.preSkew(2.f, 2.f);
RunTest(mat);
}
diff --git a/chromium/cc/paint/paint_canvas.h b/chromium/cc/paint/paint_canvas.h
index 8a0883b7fc8..2b9c34a2336 100644
--- a/chromium/cc/paint/paint_canvas.h
+++ b/chromium/cc/paint/paint_canvas.h
@@ -146,16 +146,11 @@ class CC_PAINT_EXPORT PaintCanvas {
drawImage(image, left, top, nullptr);
}
- enum SrcRectConstraint {
- kStrict_SrcRectConstraint = SkCanvas::kStrict_SrcRectConstraint,
- kFast_SrcRectConstraint = SkCanvas::kFast_SrcRectConstraint,
- };
-
virtual void drawImageRect(const PaintImage& image,
const SkRect& src,
const SkRect& dst,
const PaintFlags* flags,
- SrcRectConstraint constraint) = 0;
+ SkCanvas::SrcRectConstraint constraint) = 0;
// Draws the frame of the |skottie| animation specified by the normalized time
// t [0->first frame..1->last frame] at the destination bounds given by |dst|
diff --git a/chromium/cc/paint/paint_filter.cc b/chromium/cc/paint/paint_filter.cc
index 279258b26b7..cf868e21167 100644
--- a/chromium/cc/paint/paint_filter.cc
+++ b/chromium/cc/paint/paint_filter.cc
@@ -701,7 +701,7 @@ sk_sp<PaintFilter> ImagePaintFilter::SnapshotWithImagesInternal(
PaintImage decoded_paint_image =
PaintImageBuilder::WithDefault()
.set_id(image_.stable_id())
- .set_image(decoded_sk_image, PaintImage::GetNextContentId())
+ .set_texture_image(decoded_sk_image, PaintImage::GetNextContentId())
.TakePaintImage();
return sk_make_sp<ImagePaintFilter>(std::move(decoded_paint_image), src_rect_,
diff --git a/chromium/cc/paint/paint_filter.h b/chromium/cc/paint/paint_filter.h
index cec36ef7c6a..556ab9a91e8 100644
--- a/chromium/cc/paint/paint_filter.h
+++ b/chromium/cc/paint/paint_filter.h
@@ -5,8 +5,8 @@
#ifndef CC_PAINT_PAINT_FILTER_H_
#define CC_PAINT_PAINT_FILTER_H_
+#include "base/check_op.h"
#include "base/containers/stack_container.h"
-#include "base/logging.h"
#include "base/optional.h"
#include "base/stl_util.h"
#include "cc/paint/paint_export.h"
diff --git a/chromium/cc/paint/paint_flags.h b/chromium/cc/paint/paint_flags.h
index c1f1cb5ce2f..e28e68929c9 100644
--- a/chromium/cc/paint/paint_flags.h
+++ b/chromium/cc/paint/paint_flags.h
@@ -33,7 +33,6 @@ class CC_PAINT_EXPORT PaintFlags {
enum Style {
kFill_Style = SkPaint::kFill_Style,
kStroke_Style = SkPaint::kStroke_Style,
- kStrokeAndFill_Style = SkPaint::kStrokeAndFill_Style,
};
bool nothingToDraw() const;
ALWAYS_INLINE Style getStyle() const {
@@ -141,9 +140,13 @@ class CC_PAINT_EXPORT PaintFlags {
draw_looper_ = std::move(looper);
}
- // Returns true if this just represents an opacity blend when
- // used as saveLayer flags.
+ // Returns true if this just represents an opacity blend when used as
+ // saveLayer flags, thus the saveLayer can be converted to a saveLayerAlpha.
bool IsSimpleOpacity() const;
+
+ // Returns true if this (of a drawOp) allows the sequence
+ // saveLayerAlpha/drawOp/restore to be folded into a single drawOp by baking
+ // the alpha in the saveLayerAlpha into the flags of the drawOp.
bool SupportsFoldingAlpha() const;
// SkPaint does not support loopers, so callers of SkToPaint need
diff --git a/chromium/cc/paint/paint_image.cc b/chromium/cc/paint/paint_image.cc
index 86deeddd70f..529718556d9 100644
--- a/chromium/cc/paint/paint_image.cc
+++ b/chromium/cc/paint/paint_image.cc
@@ -5,6 +5,7 @@
#include "cc/paint/paint_image.h"
#include <memory>
+#include <sstream>
#include "base/atomic_sequence_num.h"
#include "base/hash/hash.h"
@@ -113,6 +114,15 @@ const sk_sp<SkImage>& PaintImage::GetSkImage() const {
return cached_sk_image_;
}
+const sk_sp<SkImage>& PaintImage::GetRasterSkImage() const {
+ return cached_sk_image_;
+}
+
+gpu::Mailbox PaintImage::GetMailbox() const {
+ DCHECK(texture_backing_);
+ return texture_backing_->GetMailbox();
+}
+
PaintImage PaintImage::MakeSubset(const gfx::Rect& subset) const {
DCHECK(!subset.IsEmpty());
@@ -133,7 +143,7 @@ PaintImage PaintImage::MakeSubset(const gfx::Rect& subset) const {
// PaintImage is an optimization to allow re-use of the original decode for
// image subsets in skia, for cases that rely on skia's image decode cache.
result.cached_sk_image_ =
- GetSkImage()->makeSubset(gfx::RectToSkIRect(subset));
+ GetRasterSkImage()->makeSubset(gfx::RectToSkIRect(subset));
return result;
}
@@ -288,6 +298,14 @@ SkAlphaType PaintImage::GetAlphaType() const {
return kUnknown_SkAlphaType;
}
+bool PaintImage::IsTextureBacked() const {
+ if (texture_backing_)
+ return true;
+ if (cached_sk_image_)
+ return cached_sk_image_->isTextureBacked();
+ return false;
+}
+
int PaintImage::width() const {
return paint_worklet_input_
? static_cast<int>(paint_worklet_input_->GetSize().width())
@@ -361,18 +379,19 @@ sk_sp<SkImage> PaintImage::GetSkImageForFrame(
size_t index,
GeneratorClientId client_id) const {
DCHECK_LT(index, FrameCount());
+ DCHECK(!IsTextureBacked());
// |client_id| and |index| are only relevant for generator backed images which
// perform lazy decoding and can be multi-frame.
if (!paint_image_generator_) {
DCHECK_EQ(index, kDefaultFrameIndex);
- return GetSkImage();
+ return GetRasterSkImage();
}
// The internally cached SkImage is constructed using the default frame index
// and GeneratorClientId. Avoid creating a new SkImage.
if (index == kDefaultFrameIndex && client_id == kDefaultGeneratorClientId)
- return GetSkImage();
+ return GetRasterSkImage();
sk_sp<SkImage> image =
SkImage::MakeFromGenerator(std::make_unique<SkiaPaintImageGenerator>(
diff --git a/chromium/cc/paint/paint_image.h b/chromium/cc/paint/paint_image.h
index 95881204e0a..1d7503a2d04 100644
--- a/chromium/cc/paint/paint_image.h
+++ b/chromium/cc/paint/paint_image.h
@@ -8,12 +8,12 @@
#include <vector>
#include "base/gtest_prod_util.h"
-#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "cc/paint/frame_metadata.h"
#include "cc/paint/image_animation_count.h"
#include "cc/paint/paint_export.h"
+#include "gpu/command_buffer/common/mailbox.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkYUVAIndex.h"
#include "third_party/skia/include/core/SkYUVASizeInfo.h"
@@ -229,8 +229,16 @@ class CC_PAINT_EXPORT PaintImage {
const SkYUVASizeInfo& yuva_size_info,
SkYUVAIndex* plane_indices) const;
+ // Returns the SkImage associated with this PaintImage. If PaintImage is
+ // texture backed, this API may do a readback from GPU to CPU memory.
+ // Avoid using this API unless actual pixels are needed.
+ // For other cases, prefer using PaintImage APIs directly or use
+ // GetSkImageInfo() for metadata about the SkImage.
+ const sk_sp<SkImage>& GetRasterSkImage() const;
+
Id stable_id() const { return id_; }
const sk_sp<SkImage>& GetSkImage() const;
+ gpu::Mailbox GetMailbox() const;
AnimationType animation_type() const { return animation_type_; }
CompletionState completion_state() const { return completion_state_; }
bool is_multipart() const { return is_multipart_; }
@@ -253,9 +261,7 @@ class CC_PAINT_EXPORT PaintImage {
return paint_worklet_input_ ? false : GetSkImage()->isLazyGenerated();
}
bool IsPaintWorklet() const { return !!paint_worklet_input_; }
- bool IsTextureBacked() const {
- return paint_worklet_input_ ? false : GetSkImage()->isTextureBacked();
- }
+ bool IsTextureBacked() const;
int width() const;
int height() const;
SkColorSpace* color_space() const {
diff --git a/chromium/cc/paint/paint_image_builder.h b/chromium/cc/paint/paint_image_builder.h
index 25a20bf1a9c..8820b078f5e 100644
--- a/chromium/cc/paint/paint_image_builder.h
+++ b/chromium/cc/paint/paint_image_builder.h
@@ -43,10 +43,17 @@ class CC_PAINT_EXPORT PaintImageBuilder {
PaintImageBuilder&& set_image(sk_sp<SkImage> sk_image,
PaintImage::ContentId content_id) {
+ DCHECK(!sk_image->isTextureBacked());
paint_image_.sk_image_ = std::move(sk_image);
paint_image_.content_id_ = content_id;
return std::move(*this);
}
+ PaintImageBuilder&& set_texture_backing(sk_sp<TextureBacking> texture_backing,
+ PaintImage::ContentId content_id) {
+ paint_image_.texture_backing_ = std::move(texture_backing);
+ paint_image_.content_id_ = content_id;
+ return std::move(*this);
+ }
PaintImageBuilder&& set_paint_record(sk_sp<PaintRecord> paint_record,
const gfx::Rect& rect,
PaintImage::ContentId content_id) {
@@ -62,7 +69,6 @@ class CC_PAINT_EXPORT PaintImageBuilder {
paint_image_.paint_image_generator_ = std::move(generator);
return std::move(*this);
}
-
PaintImageBuilder&& set_animation_type(PaintImage::AnimationType type) {
paint_image_.animation_type_ = type;
return std::move(*this);
@@ -105,19 +111,26 @@ class CC_PAINT_EXPORT PaintImageBuilder {
paint_image_.paint_worklet_input_ = std::move(input);
return std::move(*this);
}
- PaintImageBuilder&& set_texture_backing(sk_sp<TextureBacking> texture_backing,
- PaintImage::ContentId content_id) {
- paint_image_.texture_backing_ = std::move(texture_backing);
- paint_image_.content_id_ = content_id;
- return std::move(*this);
- }
PaintImage TakePaintImage();
private:
+ friend class PaintOpReader;
+ friend class PaintShader;
+ friend class ImagePaintFilter;
+ friend PaintImage CreateNonDiscardablePaintImage(const gfx::Size& size);
+
PaintImageBuilder();
PaintImageBuilder(PaintImage starting_image, bool clear_contents);
+ // For GPU process callers using a texture backed SkImage.
+ PaintImageBuilder&& set_texture_image(sk_sp<SkImage> sk_image,
+ PaintImage::ContentId content_id) {
+ paint_image_.sk_image_ = std::move(sk_image);
+ paint_image_.content_id_ = content_id;
+ return std::move(*this);
+ }
+
PaintImage paint_image_;
#if DCHECK_IS_ON()
bool id_set_ = false;
diff --git a/chromium/cc/paint/paint_op_buffer.cc b/chromium/cc/paint/paint_op_buffer.cc
index 51955bcf32f..04963e4d4b4 100644
--- a/chromium/cc/paint/paint_op_buffer.cc
+++ b/chromium/cc/paint/paint_op_buffer.cc
@@ -315,10 +315,7 @@ size_t SimpleSerialize(const PaintOp* op, void* memory, size_t size) {
}
PlaybackParams::PlaybackParams(ImageProvider* image_provider)
- : image_provider(image_provider),
- original_ctm(SkMatrix::I()),
- custom_callback(CustomDataRasterCallback()),
- did_draw_op_callback(DidDrawOpCallback()) {}
+ : PlaybackParams(image_provider, SkMatrix::I()) {}
PlaybackParams::PlaybackParams(ImageProvider* image_provider,
const SkMatrix& original_ctm,
@@ -1275,7 +1272,8 @@ void DrawImageOp::RasterWithFlags(const DrawImageOp* op,
canvas->scale(1.f / op->scale_adjustment.width(),
1.f / op->scale_adjustment.height());
}
- canvas->drawImage(op->image.GetSkImage().get(), op->left, op->top, &paint);
+ canvas->drawImage(op->image.GetRasterSkImage().get(), op->left, op->top,
+ &paint);
return;
}
@@ -1342,16 +1340,11 @@ void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op,
return;
}
- // TODO(enne): Probably PaintCanvas should just use the skia enum directly.
- SkCanvas::SrcRectConstraint skconstraint =
- static_cast<SkCanvas::SrcRectConstraint>(op->constraint);
-
if (!params.image_provider) {
SkRect adjusted_src = AdjustSrcRectForScale(op->src, op->scale_adjustment);
- flags->DrawToSk(canvas, [op, adjusted_src, skconstraint](SkCanvas* c,
- const SkPaint& p) {
- c->drawImageRect(op->image.GetSkImage().get(), adjusted_src, op->dst, &p,
- skconstraint);
+ flags->DrawToSk(canvas, [op, adjusted_src](SkCanvas* c, const SkPaint& p) {
+ c->drawImageRect(op->image.GetRasterSkImage().get(), adjusted_src,
+ op->dst, &p, op->constraint);
});
return;
}
@@ -1381,12 +1374,12 @@ void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op,
op->src.makeOffset(decoded_image.src_rect_offset().width(),
decoded_image.src_rect_offset().height());
adjusted_src = AdjustSrcRectForScale(adjusted_src, scale_adjustment);
- flags->DrawToSk(canvas, [op, &decoded_image, adjusted_src, skconstraint](
- SkCanvas* c, const SkPaint& p) {
+ flags->DrawToSk(canvas, [op, &decoded_image, adjusted_src](SkCanvas* c,
+ const SkPaint& p) {
SkPaint paint_with_filter_quality(p);
paint_with_filter_quality.setFilterQuality(decoded_image.filter_quality());
c->drawImageRect(decoded_image.image().get(), adjusted_src, op->dst,
- &paint_with_filter_quality, skconstraint);
+ &paint_with_filter_quality, op->constraint);
});
}
@@ -1506,7 +1499,19 @@ void SaveLayerAlphaOp::Raster(const SaveLayerAlphaOp* op,
const PlaybackParams& params) {
// See PaintOp::kUnsetRect
bool unset = op->bounds.left() == SK_ScalarInfinity;
- canvas->saveLayerAlpha(unset ? nullptr : &op->bounds, op->alpha);
+ base::Optional<SkPaint> paint;
+ if (op->alpha != 0xFF) {
+ paint.emplace();
+ paint->setAlpha(op->alpha);
+ }
+ SkCanvas::SaveLayerRec rec(unset ? nullptr : &op->bounds,
+ base::OptionalOrNullptr(paint));
+ if (params.save_layer_alpha_should_preserve_lcd_text.has_value() &&
+ *params.save_layer_alpha_should_preserve_lcd_text) {
+ rec.fSaveLayerFlags = SkCanvas::kPreserveLCDText_SaveLayerFlag |
+ SkCanvas::kInitWithPrevious_SaveLayerFlag;
+ }
+ canvas->saveLayer(rec);
}
void ScaleOp::Raster(const ScaleOp* op,
@@ -2278,6 +2283,18 @@ bool DrawRecordOp::HasNonAAPaint() const {
return record->HasNonAAPaint();
}
+bool DrawRecordOp::HasDrawTextOps() const {
+ return record->has_draw_text_ops();
+}
+
+bool DrawRecordOp::HasSaveLayerAlphaOps() const {
+ return record->has_save_layer_alpha_ops();
+}
+
+bool DrawRecordOp::HasEffectsPreventingLCDTextForSaveLayerAlpha() const {
+ return record->has_effects_preventing_lcd_text_for_save_layer_alpha();
+}
+
AnnotateOp::AnnotateOp() : PaintOp(kType) {}
AnnotateOp::AnnotateOp(PaintCanvas::AnnotationType annotation_type,
@@ -2313,7 +2330,7 @@ DrawImageRectOp::DrawImageRectOp(const PaintImage& image,
const SkRect& src,
const SkRect& dst,
const PaintFlags* flags,
- PaintCanvas::SrcRectConstraint constraint)
+ SkCanvas::SrcRectConstraint constraint)
: PaintOpWithFlags(kType, flags ? *flags : PaintFlags()),
image(image),
src(src),
@@ -2389,7 +2406,12 @@ 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_draw_ops_(false),
+ has_draw_text_ops_(false),
+ has_save_layer_alpha_ops_(false),
+ has_effects_preventing_lcd_text_for_save_layer_alpha_(false) {}
PaintOpBuffer::PaintOpBuffer(PaintOpBuffer&& other) {
*this = std::move(other);
@@ -2409,6 +2431,11 @@ 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_draw_ops_ = other.has_draw_ops_;
+ has_draw_text_ops_ = other.has_draw_text_ops_;
+ has_save_layer_alpha_ops_ = other.has_save_layer_alpha_ops_;
+ has_effects_preventing_lcd_text_for_save_layer_alpha_ =
+ other.has_effects_preventing_lcd_text_for_save_layer_alpha_;
// Make sure the other pob can destruct safely.
other.used_ = 0;
@@ -2430,6 +2457,10 @@ void PaintOpBuffer::Reset() {
subrecord_bytes_used_ = 0;
subrecord_op_count_ = 0;
has_discardable_images_ = false;
+ has_draw_ops_ = false;
+ has_draw_text_ops_ = false;
+ has_save_layer_alpha_ops_ = false;
+ has_effects_preventing_lcd_text_for_save_layer_alpha_ = false;
}
// When |op| is a nested PaintOpBuffer, this returns the PaintOp inside
@@ -2556,10 +2587,22 @@ void PaintOpBuffer::Playback(SkCanvas* canvas,
return;
if (offsets && offsets->empty())
return;
-
// Prevent PaintOpBuffers from having side effects back into the canvas.
SkAutoCanvasRestore save_restore(canvas, true);
+ bool save_layer_alpha_should_preserve_lcd_text =
+ (!params.save_layer_alpha_should_preserve_lcd_text.has_value() ||
+ *params.save_layer_alpha_should_preserve_lcd_text) &&
+ has_draw_text_ops_ &&
+ !has_effects_preventing_lcd_text_for_save_layer_alpha_;
+ if (save_layer_alpha_should_preserve_lcd_text) {
+ // Check if the canvas supports LCD text.
+ SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
+ canvas->getProps(&props);
+ if (props.pixelGeometry() == kUnknown_SkPixelGeometry)
+ save_layer_alpha_should_preserve_lcd_text = false;
+ }
+
// TODO(enne): a PaintRecord that contains a SetMatrix assumes that the
// SetMatrix is local to that PaintRecord itself. Said differently, if you
// translate(x, y), then draw a paint record with a SetMatrix(identity),
@@ -2568,6 +2611,8 @@ void PaintOpBuffer::Playback(SkCanvas* canvas,
PlaybackParams new_params(params.image_provider, canvas->getTotalMatrix(),
params.custom_callback,
params.did_draw_op_callback);
+ new_params.save_layer_alpha_should_preserve_lcd_text =
+ save_layer_alpha_should_preserve_lcd_text;
for (PlaybackFoldingIterator iter(this, offsets); iter; ++iter) {
const PaintOp* op = *iter;
@@ -2591,10 +2636,6 @@ void PaintOpBuffer::Playback(SkCanvas* canvas,
if (const auto* raster_flags = scoped_flags.flags())
flags_op->RasterWithFlags(canvas, raster_flags, new_params);
} else {
- // TODO(enne): skip SaveLayer followed by restore with nothing in
- // between, however SaveLayer with image filters on it (or maybe
- // other PaintFlags options) are not a noop. Figure out what these
- // are so we can skip them correctly.
DCHECK_EQ(iter.alpha(), 255);
op->Raster(canvas, new_params);
}
@@ -2719,4 +2760,18 @@ bool PaintOpBuffer::operator==(const PaintOpBuffer& other) const {
return true;
}
+bool PaintOpBuffer::NeedsAdditionalInvalidationForLCDText(
+ const PaintOpBuffer& old_buffer) const {
+ // We need this in addition to blink's raster invalidation because change of
+ // has_effects_preventing_lcd_text_for_save_layer_alpha() can affect
+ // all SaveLayerAlphaOps of the PaintOpBuffer, not just the area that the
+ // changed effects affected.
+ if (!has_draw_text_ops() || !has_save_layer_alpha_ops())
+ return false;
+ if (!old_buffer.has_draw_text_ops() || !old_buffer.has_save_layer_alpha_ops())
+ return false;
+ return has_effects_preventing_lcd_text_for_save_layer_alpha() !=
+ old_buffer.has_effects_preventing_lcd_text_for_save_layer_alpha();
+}
+
} // namespace cc
diff --git a/chromium/cc/paint/paint_op_buffer.h b/chromium/cc/paint/paint_op_buffer.h
index ef564407da9..24459b52dd3 100644
--- a/chromium/cc/paint/paint_op_buffer.h
+++ b/chromium/cc/paint/paint_op_buffer.h
@@ -12,10 +12,11 @@
#include <type_traits>
#include "base/callback.h"
+#include "base/check_op.h"
#include "base/containers/stack_container.h"
#include "base/debug/alias.h"
-#include "base/logging.h"
#include "base/memory/aligned_memory.h"
+#include "base/notreached.h"
#include "base/optional.h"
#include "cc/base/math_util.h"
#include "cc/paint/node_id.h"
@@ -124,6 +125,7 @@ struct CC_PAINT_EXPORT PlaybackParams {
SkMatrix original_ctm;
CustomDataRasterCallback custom_callback;
DidDrawOpCallback did_draw_op_callback;
+ base::Optional<bool> save_layer_alpha_should_preserve_lcd_text;
};
class CC_PAINT_EXPORT PaintOp {
@@ -252,6 +254,11 @@ class CC_PAINT_EXPORT PaintOp {
int CountSlowPathsFromFlags() const { return 0; }
bool HasNonAAPaint() const { return false; }
+ bool HasDrawTextOps() const { return false; }
+ bool HasSaveLayerAlphaOps() const { return false; }
+ // Returns true if effects are present that would break LCD text or be broken
+ // by the flags for SaveLayerAlpha to preserving LCD text.
+ bool HasEffectsPreventingLCDTextForSaveLayerAlpha() const { return false; }
bool HasDiscardableImages() const { return false; }
bool HasDiscardableImagesFromFlags() const { return false; }
@@ -534,7 +541,7 @@ class CC_PAINT_EXPORT DrawImageRectOp final : public PaintOpWithFlags {
const SkRect& src,
const SkRect& dst,
const PaintFlags* flags,
- PaintCanvas::SrcRectConstraint constraint);
+ SkCanvas::SrcRectConstraint constraint);
~DrawImageRectOp();
static void RasterWithFlags(const DrawImageRectOp* op,
const PaintFlags* flags,
@@ -552,7 +559,7 @@ class CC_PAINT_EXPORT DrawImageRectOp final : public PaintOpWithFlags {
PaintImage image;
SkRect src;
SkRect dst;
- PaintCanvas::SrcRectConstraint constraint;
+ SkCanvas::SrcRectConstraint constraint;
private:
DrawImageRectOp();
@@ -672,6 +679,9 @@ class CC_PAINT_EXPORT DrawRecordOp final : public PaintOp {
bool HasDiscardableImages() const;
int CountSlowPaths() const;
bool HasNonAAPaint() const;
+ bool HasDrawTextOps() const;
+ bool HasSaveLayerAlphaOps() const;
+ bool HasEffectsPreventingLCDTextForSaveLayerAlpha() const;
HAS_SERIALIZATION_FUNCTIONS();
sk_sp<const PaintRecord> record;
@@ -759,6 +769,7 @@ class CC_PAINT_EXPORT DrawTextBlobOp final : public PaintOpWithFlags {
SkCanvas* canvas,
const PlaybackParams& params);
bool IsValid() const { return flags.IsValid(); }
+ bool HasDrawTextOps() const { return true; }
static bool AreEqual(const PaintOp* left, const PaintOp* right);
HAS_SERIALIZATION_FUNCTIONS();
@@ -835,6 +846,10 @@ class CC_PAINT_EXPORT SaveLayerOp final : public PaintOpWithFlags {
bool IsValid() const { return flags.IsValid() && IsValidOrUnsetRect(bounds); }
static bool AreEqual(const PaintOp* left, const PaintOp* right);
bool HasNonAAPaint() const { return false; }
+ // We simply assume any effects (or even no effects -- just starting an empty
+ // transparent layer) would break LCD text or be broken by the flags for
+ // SaveLayerAlpha to preserve LCD text.
+ bool HasEffectsPreventingLCDTextForSaveLayerAlpha() const { return true; }
HAS_SERIALIZATION_FUNCTIONS();
SkRect bounds;
@@ -853,6 +868,7 @@ class CC_PAINT_EXPORT SaveLayerAlphaOp final : public PaintOp {
const PlaybackParams& params);
bool IsValid() const { return IsValidOrUnsetRect(bounds); }
static bool AreEqual(const PaintOp* left, const PaintOp* right);
+ bool HasSaveLayerAlphaOps() const { return true; }
HAS_SERIALIZATION_FUNCTIONS();
SkRect bounds;
@@ -985,6 +1001,16 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt {
bool HasNonAAPaint() const { return has_non_aa_paint_; }
bool HasDiscardableImages() const { return has_discardable_images_; }
+ bool has_draw_ops() const { return has_draw_ops_; }
+ bool has_draw_text_ops() const { return has_draw_text_ops_; }
+ bool has_save_layer_alpha_ops() const { return has_save_layer_alpha_ops_; }
+ bool has_effects_preventing_lcd_text_for_save_layer_alpha() const {
+ return has_effects_preventing_lcd_text_for_save_layer_alpha_;
+ }
+
+ bool NeedsAdditionalInvalidationForLCDText(
+ const PaintOpBuffer& old_buffer) const;
+
bool operator==(const PaintOpBuffer& other) const;
bool operator!=(const PaintOpBuffer& other) const {
return !(*this == other);
@@ -1047,6 +1073,12 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt {
subrecord_bytes_used_ += op->AdditionalBytesUsed();
subrecord_op_count_ += op->AdditionalOpCount();
+
+ has_draw_ops_ |= op->IsDrawOp();
+ has_draw_text_ops_ |= op->HasDrawTextOps();
+ has_save_layer_alpha_ops_ |= op->HasSaveLayerAlphaOps();
+ has_effects_preventing_lcd_text_for_save_layer_alpha_ |=
+ op->HasEffectsPreventingLCDTextForSaveLayerAlpha();
}
template <typename T>
@@ -1263,15 +1295,19 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt {
size_t reserved_ = 0;
size_t op_count_ = 0;
- // Record paths for veto-to-msaa for gpu raster.
- int num_slow_paths_ = 0;
// Record additional bytes used by referenced sub-records and display lists.
size_t subrecord_bytes_used_ = 0;
// Record total op count of referenced sub-record and display lists.
size_t subrecord_op_count_ = 0;
+ // Record paths for veto-to-msaa for gpu raster.
+ int num_slow_paths_ = 0;
bool has_non_aa_paint_ : 1;
bool has_discardable_images_ : 1;
+ bool has_draw_ops_ : 1;
+ bool has_draw_text_ops_ : 1;
+ bool has_save_layer_alpha_ops_ : 1;
+ bool has_effects_preventing_lcd_text_for_save_layer_alpha_ : 1;
};
} // namespace cc
diff --git a/chromium/cc/paint/paint_op_buffer_fuzzer.cc b/chromium/cc/paint/paint_op_buffer_fuzzer.cc
index 48c7b99140d..3f28f0e8d29 100644
--- a/chromium/cc/paint/paint_op_buffer_fuzzer.cc
+++ b/chromium/cc/paint/paint_op_buffer_fuzzer.cc
@@ -6,6 +6,7 @@
#include <stdint.h>
#include "base/command_line.h"
+#include "base/logging.h"
#include "base/process/memory.h"
#include "base/test/test_discardable_memory_allocator.h"
#include "cc/paint/paint_cache.h"
diff --git a/chromium/cc/paint/paint_op_buffer_serializer.cc b/chromium/cc/paint/paint_op_buffer_serializer.cc
index 7601f978325..3f59290175c 100644
--- a/chromium/cc/paint/paint_op_buffer_serializer.cc
+++ b/chromium/cc/paint/paint_op_buffer_serializer.cc
@@ -4,13 +4,16 @@
#include "cc/paint/paint_op_buffer_serializer.h"
+#include <limits>
+#include <memory>
+#include <utility>
+#include <vector>
+
#include "base/bind.h"
#include "base/trace_event/trace_event.h"
#include "cc/paint/scoped_raster_flags.h"
#include "ui/gfx/skia_util.h"
-#include <utility>
-
namespace cc {
namespace {
@@ -72,12 +75,16 @@ PaintOpBufferSerializer::PaintOpBufferSerializer(
context_supports_distance_field_text_(
context_supports_distance_field_text),
max_texture_size_(max_texture_size),
- text_blob_canvas_(kMaxExtent,
- kMaxExtent,
- ComputeSurfaceProps(can_use_lcd_text),
- strike_server,
- std::move(color_space),
- context_supports_distance_field_text) {
+ text_blob_canvas_(
+ strike_server
+ ? std::make_unique<SkTextBlobCacheDiffCanvas>(
+ kMaxExtent,
+ kMaxExtent,
+ ComputeSurfaceProps(can_use_lcd_text),
+ strike_server,
+ std::move(color_space),
+ context_supports_distance_field_text)
+ : std::make_unique<SkNoDrawCanvas>(kMaxExtent, kMaxExtent)) {
DCHECK(serialize_cb_);
}
@@ -86,16 +93,16 @@ PaintOpBufferSerializer::~PaintOpBufferSerializer() = default;
void PaintOpBufferSerializer::Serialize(const PaintOpBuffer* buffer,
const std::vector<size_t>* offsets,
const Preamble& preamble) {
- DCHECK(text_blob_canvas_.getTotalMatrix().isIdentity());
+ DCHECK(text_blob_canvas_->getTotalMatrix().isIdentity());
static const int kInitialSaveCount = 1;
- DCHECK_EQ(kInitialSaveCount, text_blob_canvas_.getSaveCount());
+ DCHECK_EQ(kInitialSaveCount, text_blob_canvas_->getSaveCount());
// These SerializeOptions and PlaybackParams use the initial (identity) canvas
// matrix, as they are only used for serializing the preamble and the initial
// save / final restore. SerializeBuffer will create its own SerializeOptions
// and PlaybackParams based on the post-preamble canvas.
PaintOp::SerializeOptions options = MakeSerializeOptions();
- PlaybackParams params = MakeParams(&text_blob_canvas_);
+ PlaybackParams params = MakeParams(text_blob_canvas_.get());
Save(options, params);
SerializePreamble(preamble, options, params);
@@ -104,7 +111,7 @@ void PaintOpBufferSerializer::Serialize(const PaintOpBuffer* buffer,
}
void PaintOpBufferSerializer::Serialize(const PaintOpBuffer* buffer) {
- DCHECK(text_blob_canvas_.getTotalMatrix().isIdentity());
+ DCHECK(text_blob_canvas_->getTotalMatrix().isIdentity());
SerializeBuffer(buffer, nullptr);
}
@@ -114,10 +121,10 @@ void PaintOpBufferSerializer::Serialize(
const gfx::Rect& playback_rect,
const gfx::SizeF& post_scale,
const SkMatrix& post_matrix_for_analysis) {
- DCHECK(text_blob_canvas_.getTotalMatrix().isIdentity());
+ DCHECK(text_blob_canvas_->getTotalMatrix().isIdentity());
PaintOp::SerializeOptions options = MakeSerializeOptions();
- PlaybackParams params = MakeParams(&text_blob_canvas_);
+ PlaybackParams params = MakeParams(text_blob_canvas_.get());
// TODO(khushalsagar): remove this clip rect if it's not needed.
if (!playback_rect.IsEmpty()) {
@@ -131,7 +138,7 @@ void PaintOpBufferSerializer::Serialize(
SerializeOp(&scale_op, options, params);
}
- text_blob_canvas_.concat(post_matrix_for_analysis);
+ text_blob_canvas_->concat(post_matrix_for_analysis);
SerializeBuffer(buffer, nullptr);
}
@@ -208,8 +215,10 @@ void PaintOpBufferSerializer::SerializePreamble(
<< "full: " << preamble.full_raster_rect.ToString()
<< ", playback: " << preamble.playback_rect.ToString();
+ // NOTE: The following code should be kept consistent with
+ // RasterSource::PlaybackToCanvas().
bool is_partial_raster = preamble.full_raster_rect != preamble.playback_rect;
- if (!preamble.requires_clear) {
+ if (!preamble.requires_clear && preamble.post_translation.IsZero()) {
ClearForOpaqueRaster(preamble, options, params);
} else if (!is_partial_raster) {
// If rastering the entire tile, clear to transparent pre-clip. This is so
@@ -259,7 +268,7 @@ void PaintOpBufferSerializer::SerializeBuffer(
const std::vector<size_t>* offsets) {
DCHECK(buffer);
PaintOp::SerializeOptions options = MakeSerializeOptions();
- PlaybackParams params = MakeParams(&text_blob_canvas_);
+ PlaybackParams params = MakeParams(text_blob_canvas_.get());
for (PaintOpBuffer::PlaybackFoldingIterator iter(buffer, offsets); iter;
++iter) {
@@ -267,13 +276,16 @@ void PaintOpBufferSerializer::SerializeBuffer(
// Skip ops outside the current clip if they have images. This saves
// performing an unnecessary expensive decode.
- const bool skip_op = PaintOp::OpHasDiscardableImages(op) &&
- PaintOp::QuickRejectDraw(op, &text_blob_canvas_);
+ bool skip_op = PaintOp::OpHasDiscardableImages(op) &&
+ PaintOp::QuickRejectDraw(op, text_blob_canvas_.get());
+ // Skip text ops if there is no SkStrikeServer.
+ skip_op |=
+ op->GetType() == PaintOpType::DrawTextBlob && !options.strike_server;
if (skip_op)
continue;
if (op->GetType() == PaintOpType::DrawRecord) {
- int save_count = text_blob_canvas_.getSaveCount();
+ int save_count = text_blob_canvas_->getSaveCount();
Save(options, params);
SerializeBuffer(static_cast<const DrawRecordOp*>(op)->record.get(),
nullptr);
@@ -290,7 +302,7 @@ void PaintOpBufferSerializer::SerializeBuffer(
if (!result || !result.paint_record())
continue;
- int save_count = text_blob_canvas_.getSaveCount();
+ int save_count = text_blob_canvas_->getSaveCount();
Save(options, params);
// The following ops are copying the canvas's ops from
// DrawImageRectOp::RasterWithFlags.
@@ -387,9 +399,9 @@ void PaintOpBufferSerializer::PlaybackOnAnalysisCanvas(
if (op->IsPaintOpWithFlags() && options.flags_to_serialize) {
static_cast<const PaintOpWithFlags*>(op)->RasterWithFlags(
- &text_blob_canvas_, options.flags_to_serialize, params);
+ text_blob_canvas_.get(), options.flags_to_serialize, params);
} else {
- op->Raster(&text_blob_canvas_, params);
+ op->Raster(text_blob_canvas_.get(), params);
}
}
@@ -404,7 +416,7 @@ void PaintOpBufferSerializer::RestoreToCount(
const PaintOp::SerializeOptions& options,
const PlaybackParams& params) {
RestoreOp restore_op;
- while (text_blob_canvas_.getSaveCount() > count) {
+ while (text_blob_canvas_->getSaveCount() > count) {
if (!SerializeOp(&restore_op, options, params))
return;
}
@@ -412,10 +424,10 @@ void PaintOpBufferSerializer::RestoreToCount(
PaintOp::SerializeOptions PaintOpBufferSerializer::MakeSerializeOptions() {
return PaintOp::SerializeOptions(
- image_provider_, transfer_cache_, paint_cache_, &text_blob_canvas_,
+ image_provider_, transfer_cache_, paint_cache_, text_blob_canvas_.get(),
strike_server_, color_space_, can_use_lcd_text_,
context_supports_distance_field_text_, max_texture_size_,
- text_blob_canvas_.getTotalMatrix());
+ text_blob_canvas_->getTotalMatrix());
}
SimpleBufferSerializer::SimpleBufferSerializer(
diff --git a/chromium/cc/paint/paint_op_buffer_serializer.h b/chromium/cc/paint/paint_op_buffer_serializer.h
index 88eb914af4f..3a57a8039cb 100644
--- a/chromium/cc/paint/paint_op_buffer_serializer.h
+++ b/chromium/cc/paint/paint_op_buffer_serializer.h
@@ -114,7 +114,7 @@ class CC_PAINT_EXPORT PaintOpBufferSerializer {
bool context_supports_distance_field_text_;
int max_texture_size_;
- SkTextBlobCacheDiffCanvas text_blob_canvas_;
+ std::unique_ptr<SkNoDrawCanvas> text_blob_canvas_;
bool valid_ = true;
};
diff --git a/chromium/cc/paint/paint_op_buffer_unittest.cc b/chromium/cc/paint/paint_op_buffer_unittest.cc
index 7a2861e76a7..fe73fe73b6b 100644
--- a/chromium/cc/paint/paint_op_buffer_unittest.cc
+++ b/chromium/cc/paint/paint_op_buffer_unittest.cc
@@ -503,16 +503,16 @@ TEST(PaintOpBufferTest, DiscardableImagesTracking_PaintWorkletImageRect) {
SkRect src = SkRect::MakeEmpty();
SkRect dst = SkRect::MakeEmpty();
buffer.push<DrawImageRectOp>(image, src, dst, nullptr,
- PaintCanvas::kStrict_SrcRectConstraint);
+ SkCanvas::kStrict_SrcRectConstraint);
EXPECT_TRUE(buffer.HasDiscardableImages());
}
TEST(PaintOpBufferTest, DiscardableImagesTracking_DrawImageRect) {
PaintOpBuffer buffer;
PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100));
- buffer.push<DrawImageRectOp>(
- image, SkRect::MakeWH(100, 100), SkRect::MakeWH(100, 100), nullptr,
- PaintCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
+ buffer.push<DrawImageRectOp>(image, SkRect::MakeWH(100, 100),
+ SkRect::MakeWH(100, 100), nullptr,
+ SkCanvas::kFast_SrcRectConstraint);
EXPECT_TRUE(buffer.HasDiscardableImages());
}
@@ -1081,8 +1081,8 @@ std::vector<uint32_t> test_ids = {0, 1, 56, 0xFFFFFFFF, 0xFFFFFFFE, 0x10001};
std::vector<SkMatrix> test_matrices = {
SkMatrix::I(),
- SkMatrix::MakeScale(3.91f, 4.31f),
- SkMatrix::MakeTrans(-5.2f, 8.7f),
+ SkMatrix::Scale(3.91f, 4.31f),
+ SkMatrix::Translate(-5.2f, 8.7f),
[] {
SkMatrix matrix;
SkScalar buffer[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
@@ -1126,7 +1126,7 @@ std::vector<PaintFlags> test_flags = {
flags.setBlendMode(SkBlendMode::kDst);
flags.setStrokeCap(PaintFlags::kSquare_Cap);
flags.setStrokeJoin(PaintFlags::kBevel_Join);
- flags.setStyle(PaintFlags::kStrokeAndFill_Style);
+ flags.setStyle(PaintFlags::kStroke_Style);
flags.setFilterQuality(SkFilterQuality::kMedium_SkFilterQuality);
flags.setShader(PaintShader::MakeColor(SkColorSetARGB(1, 2, 3, 4)));
return flags;
@@ -1467,9 +1467,9 @@ void PushDrawImageRectOps(PaintOpBuffer* buffer) {
size_t len =
std::min({test_images.size(), test_flags.size(), test_rects.size() - 1});
for (size_t i = 0; i < len; ++i) {
- PaintCanvas::SrcRectConstraint constraint =
- i % 2 ? PaintCanvas::kStrict_SrcRectConstraint
- : PaintCanvas::kFast_SrcRectConstraint;
+ SkCanvas::SrcRectConstraint constraint =
+ i % 2 ? SkCanvas::kStrict_SrcRectConstraint
+ : SkCanvas::kFast_SrcRectConstraint;
buffer->push<DrawImageRectOp>(test_images[i], test_rects[i],
test_rects[i + 1], &test_flags[i],
constraint);
@@ -1477,8 +1477,7 @@ void PushDrawImageRectOps(PaintOpBuffer* buffer) {
// Test optional flags.
buffer->push<DrawImageRectOp>(test_images[0], test_rects[0], test_rects[1],
- nullptr,
- PaintCanvas::kStrict_SrcRectConstraint);
+ nullptr, SkCanvas::kStrict_SrcRectConstraint);
ValidateOps<DrawImageRectOp>(buffer);
}
@@ -2602,9 +2601,9 @@ TEST(PaintOpBufferTest, ValidateRects) {
buffer.push<ClipRectOp>(bad_rect, SkClipOp::kDifference, true);
buffer.push<DrawImageRectOp>(test_images[0], bad_rect, test_rects[1], nullptr,
- PaintCanvas::kStrict_SrcRectConstraint);
+ SkCanvas::kStrict_SrcRectConstraint);
buffer.push<DrawImageRectOp>(test_images[0], test_rects[0], bad_rect, nullptr,
- PaintCanvas::kStrict_SrcRectConstraint);
+ SkCanvas::kStrict_SrcRectConstraint);
buffer.push<DrawOvalOp>(bad_rect, test_flags[0]);
buffer.push<DrawRectOp>(bad_rect, test_flags[0]);
buffer.push<SaveLayerOp>(&bad_rect, nullptr);
@@ -2925,7 +2924,7 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectBasicCase) {
SkRect src = SkRect::MakeXYWH(0, 0, 100, 100);
SkRect dst = SkRect::MakeXYWH(0, 0, 100, 100);
blink_buffer.push<DrawImageRectOp>(image, src, dst, nullptr,
- PaintCanvas::kStrict_SrcRectConstraint);
+ SkCanvas::kStrict_SrcRectConstraint);
testing::StrictMock<MockCanvas> canvas;
testing::Sequence s;
@@ -2967,7 +2966,7 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectTranslated) {
SkRect src = SkRect::MakeXYWH(0, 0, 100, 100);
SkRect dst = SkRect::MakeXYWH(5, 7, 100, 100);
blink_buffer.push<DrawImageRectOp>(image, src, dst, nullptr,
- PaintCanvas::kStrict_SrcRectConstraint);
+ SkCanvas::kStrict_SrcRectConstraint);
testing::StrictMock<MockCanvas> canvas;
testing::Sequence s;
@@ -2975,7 +2974,7 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectTranslated) {
EXPECT_CALL(canvas, willSave()).InSequence(s);
EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, didConcat(SkMatrix::MakeTrans(5.0f, 7.0f)));
+ EXPECT_CALL(canvas, didConcat(SkMatrix::Translate(5.0f, 7.0f)));
EXPECT_CALL(canvas, willSave()).InSequence(s);
EXPECT_CALL(canvas, didScale(1.0f / scale_adjustment[0].width(),
1.0f / scale_adjustment[0].height()));
@@ -3012,7 +3011,7 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectScaled) {
SkRect src = SkRect::MakeXYWH(0, 0, 100, 100);
SkRect dst = SkRect::MakeXYWH(0, 0, 200, 150);
blink_buffer.push<DrawImageRectOp>(image, src, dst, nullptr,
- PaintCanvas::kStrict_SrcRectConstraint);
+ SkCanvas::kStrict_SrcRectConstraint);
testing::StrictMock<MockCanvas> canvas;
testing::Sequence s;
@@ -3020,7 +3019,7 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectScaled) {
EXPECT_CALL(canvas, willSave()).InSequence(s);
EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
EXPECT_CALL(canvas, OnSaveLayer()).InSequence(s);
- EXPECT_CALL(canvas, didConcat(SkMatrix::MakeScale(2.f, 1.5f)));
+ EXPECT_CALL(canvas, didConcat(SkMatrix::Scale(2.f, 1.5f)));
EXPECT_CALL(canvas, willSave()).InSequence(s);
EXPECT_CALL(canvas, didScale(1.0f / scale_adjustment[0].width(),
1.0f / scale_adjustment[0].height()));
@@ -3060,7 +3059,7 @@ TEST(PaintOpBufferTest, RasterPaintWorkletImageRectClipped) {
SkRect src = SkRect::MakeXYWH(0, 0, 20, 20);
SkRect dst = SkRect::MakeXYWH(0, 0, 20, 20);
blink_buffer.push<DrawImageRectOp>(image, src, dst, nullptr,
- PaintCanvas::kStrict_SrcRectConstraint);
+ SkCanvas::kStrict_SrcRectConstraint);
testing::StrictMock<MockCanvas> canvas;
testing::Sequence s;
@@ -3098,9 +3097,8 @@ TEST(PaintOpBufferTest, ReplacesImagesFromProvider) {
flags.setFilterQuality(kLow_SkFilterQuality);
PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
buffer.push<DrawImageOp>(paint_image, 0.0f, 0.0f, &flags);
- buffer.push<DrawImageRectOp>(
- paint_image, rect, rect, &flags,
- PaintCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
+ buffer.push<DrawImageRectOp>(paint_image, rect, rect, &flags,
+ SkCanvas::kFast_SrcRectConstraint);
flags.setShader(PaintShader::MakeImage(paint_image, SkTileMode::kRepeat,
SkTileMode::kRepeat, nullptr));
buffer.push<DrawOvalOp>(SkRect::MakeWH(10, 10), flags);
@@ -3142,9 +3140,9 @@ TEST(PaintOpBufferTest, DrawImageRectOpWithLooperNoImageProvider) {
PaintFlags paint_flags;
paint_flags.setLooper(sk_draw_looper_builder.detach());
- buffer.push<DrawImageRectOp>(
- image, SkRect::MakeWH(100, 100), SkRect::MakeWH(100, 100), &paint_flags,
- PaintCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
+ buffer.push<DrawImageRectOp>(image, SkRect::MakeWH(100, 100),
+ SkRect::MakeWH(100, 100), &paint_flags,
+ SkCanvas::kFast_SrcRectConstraint);
testing::StrictMock<MockCanvas> canvas;
EXPECT_CALL(canvas, willSave);
@@ -3165,9 +3163,9 @@ TEST(PaintOpBufferTest, DrawImageRectOpWithLooperWithImageProvider) {
PaintFlags paint_flags;
paint_flags.setLooper(sk_draw_looper_builder.detach());
- buffer.push<DrawImageRectOp>(
- image, SkRect::MakeWH(100, 100), SkRect::MakeWH(100, 100), &paint_flags,
- PaintCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
+ buffer.push<DrawImageRectOp>(image, SkRect::MakeWH(100, 100),
+ SkRect::MakeWH(100, 100), &paint_flags,
+ SkCanvas::kFast_SrcRectConstraint);
testing::StrictMock<MockCanvas> canvas;
EXPECT_CALL(canvas, willSave);
@@ -3192,9 +3190,8 @@ TEST(PaintOpBufferTest, ReplacesImagesFromProviderOOP) {
PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
buffer.push<ScaleOp>(expected_scale.width(), expected_scale.height());
buffer.push<DrawImageOp>(paint_image, 0.0f, 0.0f, &flags);
- buffer.push<DrawImageRectOp>(
- paint_image, rect, rect, &flags,
- PaintCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
+ buffer.push<DrawImageRectOp>(paint_image, rect, rect, &flags,
+ SkCanvas::kFast_SrcRectConstraint);
flags.setShader(PaintShader::MakeImage(paint_image, SkTileMode::kRepeat,
SkTileMode::kRepeat, nullptr));
buffer.push<DrawOvalOp>(SkRect::MakeWH(10, 10), flags);
@@ -3558,9 +3555,9 @@ TEST(PaintOpBufferTest, DrawImageRectSerializeScaledImages) {
// translations here are arbitrary
SkRect src = SkRect::MakeXYWH(3, 4, 20, 6);
SkRect dst = SkRect::MakeXYWH(20, 38, 5, 30);
- buffer->push<DrawImageRectOp>(
- CreateDiscardablePaintImage(gfx::Size(32, 16)), src, dst, nullptr,
- PaintCanvas::SrcRectConstraint::kStrict_SrcRectConstraint);
+ buffer->push<DrawImageRectOp>(CreateDiscardablePaintImage(gfx::Size(32, 16)),
+ src, dst, nullptr,
+ SkCanvas::kStrict_SrcRectConstraint);
std::unique_ptr<char, base::AlignedFreeDeleter> memory(
static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
@@ -3829,4 +3826,108 @@ TEST(PaintOpBufferTest, NullImages) {
PaintOpType::DrawImage);
}
+TEST(PaintOpBufferTest, HasDrawOpsAndHasDrawTextOps) {
+ auto buffer1 = sk_make_sp<PaintOpBuffer>();
+ EXPECT_FALSE(buffer1->has_draw_ops());
+ EXPECT_FALSE(buffer1->has_draw_text_ops());
+ buffer1->push<DrawRectOp>(SkRect::MakeWH(3, 4), PaintFlags());
+ PushDrawRectOps(buffer1.get());
+ EXPECT_TRUE(buffer1->has_draw_ops());
+ EXPECT_FALSE(buffer1->has_draw_text_ops());
+
+ auto buffer2 = sk_make_sp<PaintOpBuffer>();
+ EXPECT_FALSE(buffer2->has_draw_ops());
+ EXPECT_FALSE(buffer2->has_draw_text_ops());
+ buffer2->push<DrawRecordOp>(std::move(buffer1));
+ EXPECT_TRUE(buffer2->has_draw_ops());
+ EXPECT_FALSE(buffer2->has_draw_text_ops());
+ buffer2->push<DrawTextBlobOp>(SkTextBlob::MakeFromString("abc", SkFont()), 0,
+ 0, PaintFlags());
+ EXPECT_TRUE(buffer2->has_draw_ops());
+ EXPECT_TRUE(buffer2->has_draw_text_ops());
+ buffer2->push<DrawRectOp>(SkRect::MakeWH(4, 5), PaintFlags());
+ EXPECT_TRUE(buffer2->has_draw_ops());
+ EXPECT_TRUE(buffer2->has_draw_text_ops());
+
+ auto buffer3 = sk_make_sp<PaintOpBuffer>();
+ EXPECT_FALSE(buffer3->has_draw_text_ops());
+ EXPECT_FALSE(buffer3->has_draw_ops());
+ buffer3->push<DrawRecordOp>(std::move(buffer2));
+ EXPECT_TRUE(buffer3->has_draw_ops());
+ EXPECT_TRUE(buffer3->has_draw_text_ops());
+}
+
+TEST(PaintOpBufferTest, HasEffectsPreventingLCDTextForSaveLayerAlpha) {
+ auto buffer1 = sk_make_sp<PaintOpBuffer>();
+ EXPECT_FALSE(buffer1->has_effects_preventing_lcd_text_for_save_layer_alpha());
+ buffer1->push<DrawRectOp>(SkRect::MakeWH(3, 4), PaintFlags());
+ EXPECT_FALSE(buffer1->has_effects_preventing_lcd_text_for_save_layer_alpha());
+
+ auto buffer2 = sk_make_sp<PaintOpBuffer>();
+ EXPECT_FALSE(buffer2->has_effects_preventing_lcd_text_for_save_layer_alpha());
+ buffer2->push<DrawRecordOp>(std::move(buffer1));
+ EXPECT_FALSE(buffer2->has_effects_preventing_lcd_text_for_save_layer_alpha());
+ buffer2->push<SaveLayerOp>(nullptr, nullptr);
+ EXPECT_TRUE(buffer2->has_effects_preventing_lcd_text_for_save_layer_alpha());
+ buffer2->push<DrawRectOp>(SkRect::MakeWH(4, 5), PaintFlags());
+ EXPECT_TRUE(buffer2->has_effects_preventing_lcd_text_for_save_layer_alpha());
+
+ auto buffer3 = sk_make_sp<PaintOpBuffer>();
+ EXPECT_FALSE(buffer3->has_effects_preventing_lcd_text_for_save_layer_alpha());
+ buffer3->push<DrawRecordOp>(std::move(buffer2));
+ EXPECT_TRUE(buffer3->has_effects_preventing_lcd_text_for_save_layer_alpha());
+}
+
+TEST(PaintOpBufferTest, NeedsAdditionalInvalidationForLCDText) {
+ auto buffer1 = sk_make_sp<PaintOpBuffer>();
+ buffer1->push<SaveLayerAlphaOp>(nullptr, 100);
+ EXPECT_FALSE(buffer1->has_draw_text_ops());
+ EXPECT_TRUE(buffer1->has_save_layer_alpha_ops());
+ EXPECT_FALSE(buffer1->has_effects_preventing_lcd_text_for_save_layer_alpha());
+
+ auto buffer2 = sk_make_sp<PaintOpBuffer>();
+ buffer2->push<DrawTextBlobOp>(SkTextBlob::MakeFromString("abc", SkFont()), 0,
+ 0, PaintFlags());
+ buffer2->push<SaveLayerOp>(nullptr, nullptr);
+ EXPECT_TRUE(buffer2->has_draw_ops());
+ EXPECT_FALSE(buffer2->has_save_layer_alpha_ops());
+ EXPECT_TRUE(buffer2->has_effects_preventing_lcd_text_for_save_layer_alpha());
+
+ // Neither buffer has effects preventing lcd text for SaveLayerAlpha.
+ EXPECT_FALSE(buffer1->NeedsAdditionalInvalidationForLCDText(*buffer2));
+ EXPECT_FALSE(buffer2->NeedsAdditionalInvalidationForLCDText(*buffer1));
+
+ {
+ auto buffer3 = sk_make_sp<PaintOpBuffer>();
+ buffer3->push<DrawRecordOp>(buffer2);
+ EXPECT_TRUE(
+ buffer3->has_effects_preventing_lcd_text_for_save_layer_alpha());
+ // Neither buffer has both DrawText and SaveLayerAlpha.
+ EXPECT_FALSE(buffer1->NeedsAdditionalInvalidationForLCDText(*buffer3));
+ EXPECT_FALSE(buffer3->NeedsAdditionalInvalidationForLCDText(*buffer1));
+ EXPECT_FALSE(buffer2->NeedsAdditionalInvalidationForLCDText(*buffer3));
+ EXPECT_FALSE(buffer3->NeedsAdditionalInvalidationForLCDText(*buffer2));
+ }
+ {
+ buffer1->push<DrawTextBlobOp>(SkTextBlob::MakeFromString("abc", SkFont()),
+ 0, 0, PaintFlags());
+ EXPECT_TRUE(buffer1->has_draw_text_ops());
+ EXPECT_TRUE(buffer1->has_save_layer_alpha_ops());
+ EXPECT_FALSE(
+ buffer1->has_effects_preventing_lcd_text_for_save_layer_alpha());
+ auto buffer3 = sk_make_sp<PaintOpBuffer>();
+ buffer3->push<DrawRecordOp>(buffer1);
+ buffer3->push<DrawRecordOp>(buffer2);
+ EXPECT_TRUE(buffer3->has_draw_text_ops());
+ EXPECT_TRUE(buffer3->has_save_layer_alpha_ops());
+ EXPECT_TRUE(
+ buffer3->has_effects_preventing_lcd_text_for_save_layer_alpha());
+ // Both have DrawText and SaveLayerAlpha, and have different
+ // has_effects_preventing_lcd_text_for_save_layer_alpha().
+ EXPECT_TRUE(buffer1->NeedsAdditionalInvalidationForLCDText(*buffer3));
+ EXPECT_TRUE(buffer3->NeedsAdditionalInvalidationForLCDText(*buffer1));
+ EXPECT_FALSE(buffer3->NeedsAdditionalInvalidationForLCDText(*buffer3));
+ }
+}
+
} // namespace cc
diff --git a/chromium/cc/paint/paint_op_helper_unittest.cc b/chromium/cc/paint/paint_op_helper_unittest.cc
index c2682d039b4..3d5db9a8710 100644
--- a/chromium/cc/paint/paint_op_helper_unittest.cc
+++ b/chromium/cc/paint/paint_op_helper_unittest.cc
@@ -93,7 +93,7 @@ TEST(PaintOpHelper, DrawImageToString) {
TEST(PaintOpHelper, DrawImageRectToString) {
DrawImageRectOp op(PaintImage(), SkRect::MakeXYWH(1, 2, 3, 4),
SkRect::MakeXYWH(5, 6, 7, 8), nullptr,
- PaintCanvas::kStrict_SrcRectConstraint);
+ SkCanvas::kStrict_SrcRectConstraint);
std::string str = PaintOpHelper::ToString(&op);
EXPECT_EQ(
str,
diff --git a/chromium/cc/paint/paint_op_reader.cc b/chromium/cc/paint/paint_op_reader.cc
index 5118c0f814a..20a06327cb2 100644
--- a/chromium/cc/paint/paint_op_reader.cc
+++ b/chromium/cc/paint/paint_op_reader.cc
@@ -326,8 +326,8 @@ void PaintOpReader::Read(PaintImage* image) {
*image = PaintImageBuilder::WithDefault()
.set_id(PaintImage::GetNextId())
- .set_image(SkImage::MakeRasterCopy(pixmap),
- PaintImage::kNonLazyStableId)
+ .set_texture_image(SkImage::MakeRasterCopy(pixmap),
+ PaintImage::kNonLazyStableId)
.TakePaintImage();
}
return;
@@ -366,10 +366,11 @@ void PaintOpReader::Read(PaintImage* image) {
transfer_cache_entry_id)) {
if (needs_mips)
entry->EnsureMips();
- *image = PaintImageBuilder::WithDefault()
- .set_id(PaintImage::GetNextId())
- .set_image(entry->image(), PaintImage::kNonLazyStableId)
- .TakePaintImage();
+ *image =
+ PaintImageBuilder::WithDefault()
+ .set_id(PaintImage::GetNextId())
+ .set_texture_image(entry->image(), PaintImage::kNonLazyStableId)
+ .TakePaintImage();
}
}
diff --git a/chromium/cc/paint/paint_op_reader.h b/chromium/cc/paint/paint_op_reader.h
index 9de213ec7f0..03d9a241c0f 100644
--- a/chromium/cc/paint/paint_op_reader.h
+++ b/chromium/cc/paint/paint_op_reader.h
@@ -85,10 +85,10 @@ class CC_PAINT_EXPORT PaintOpReader {
Read(&value);
*type = static_cast<PaintCanvas::AnnotationType>(value);
}
- void Read(PaintCanvas::SrcRectConstraint* constraint) {
+ void Read(SkCanvas::SrcRectConstraint* constraint) {
uint8_t value = 0u;
Read(&value);
- *constraint = static_cast<PaintCanvas::SrcRectConstraint>(value);
+ *constraint = static_cast<SkCanvas::SrcRectConstraint>(value);
}
void Read(SkFilterQuality* quality) {
uint8_t value = 0u;
diff --git a/chromium/cc/paint/paint_op_writer.cc b/chromium/cc/paint/paint_op_writer.cc
index a24b5c9ffb5..bb59e6feaff 100644
--- a/chromium/cc/paint/paint_op_writer.cc
+++ b/chromium/cc/paint/paint_op_writer.cc
@@ -243,7 +243,7 @@ void PaintOpWriter::Write(const DrawImage& draw_image,
// Security constrained serialization inlines the image bitmap.
if (enable_security_constraints_) {
SkBitmap bm;
- if (!draw_image.paint_image().GetSkImage()->asLegacyBitmap(&bm)) {
+ if (!draw_image.paint_image().GetRasterSkImage()->asLegacyBitmap(&bm)) {
Write(static_cast<uint8_t>(PaintOp::SerializedImageType::kNoImage));
return;
}
@@ -741,7 +741,7 @@ void PaintOpWriter::Write(const RecordPaintFilter& filter) {
SkMatrix mat = options_.canvas->getTotalMatrix();
SkSize scale;
if (!mat.isScaleTranslate() && mat.decomposeScale(&scale))
- mat = SkMatrix::MakeScale(scale.width(), scale.height());
+ mat = SkMatrix::Scale(scale.width(), scale.height());
Write(filter.record().get(), gfx::Rect(), gfx::SizeF(1.f, 1.f), mat);
}
diff --git a/chromium/cc/paint/paint_op_writer.h b/chromium/cc/paint/paint_op_writer.h
index 187d697796b..cddf8752d2c 100644
--- a/chromium/cc/paint/paint_op_writer.h
+++ b/chromium/cc/paint/paint_op_writer.h
@@ -69,7 +69,7 @@ class CC_PAINT_EXPORT PaintOpWriter {
void Write(PaintCanvas::AnnotationType type) {
Write(static_cast<uint8_t>(type));
}
- void Write(PaintCanvas::SrcRectConstraint constraint) {
+ void Write(SkCanvas::SrcRectConstraint constraint) {
Write(static_cast<uint8_t>(constraint));
}
void Write(SkFilterQuality filter_quality) {
diff --git a/chromium/cc/paint/paint_shader.cc b/chromium/cc/paint/paint_shader.cc
index 26761e78266..7b55359a9ab 100644
--- a/chromium/cc/paint/paint_shader.cc
+++ b/chromium/cc/paint/paint_shader.cc
@@ -395,7 +395,8 @@ sk_sp<PaintShader> PaintShader::CreateDecodedImage(
decoded_paint_image =
PaintImageBuilder::WithDefault()
.set_id(image_.stable_id())
- .set_image(std::move(sk_image), image_.GetContentIdForFrame(0u))
+ .set_texture_image(std::move(sk_image),
+ image_.GetContentIdForFrame(0u))
.TakePaintImage();
}
diff --git a/chromium/cc/paint/paint_shader_unittest.cc b/chromium/cc/paint/paint_shader_unittest.cc
index f18200efb32..66e8768a083 100644
--- a/chromium/cc/paint/paint_shader_unittest.cc
+++ b/chromium/cc/paint/paint_shader_unittest.cc
@@ -60,13 +60,13 @@ class MockImageProvider : public ImageProvider {
} // namespace
TEST(PaintShaderTest, RasterizationRectForRecordShaders) {
- SkMatrix local_matrix = SkMatrix::MakeScale(0.5f, 0.5f);
+ SkMatrix local_matrix = SkMatrix::Scale(0.5f, 0.5f);
auto record_shader = PaintShader::MakePaintRecord(
sk_make_sp<PaintOpBuffer>(), SkRect::MakeWH(100, 100), SkTileMode::kClamp,
SkTileMode::kClamp, &local_matrix);
SkRect tile_rect;
- SkMatrix ctm = SkMatrix::MakeScale(0.5f, 0.5f);
+ SkMatrix ctm = SkMatrix::Scale(0.5f, 0.5f);
EXPECT_TRUE(record_shader->GetRasterizationTileRect(ctm, &tile_rect));
EXPECT_EQ(tile_rect, SkRect::MakeWH(25, 25));
}
@@ -85,7 +85,7 @@ TEST(PaintShaderTest, DecodePaintRecord) {
.TakePaintImage();
record->push<DrawImageOp>(paint_image, 0.f, 0.f, nullptr);
- SkMatrix local_matrix = SkMatrix::MakeScale(0.5f, 0.5f);
+ SkMatrix local_matrix = SkMatrix::Scale(0.5f, 0.5f);
auto record_shader = PaintShader::MakePaintRecord(
record, SkRect::MakeWH(100, 100), SkTileMode::kClamp, SkTileMode::kClamp,
&local_matrix);
@@ -111,7 +111,7 @@ TEST(PaintShaderTest, DecodePaintRecord) {
EXPECT_TRUE(skia_image->isLazyGenerated());
EXPECT_EQ(xy[0], record_shader->tx());
EXPECT_EQ(xy[1], record_shader->ty());
- EXPECT_EQ(decoded_local_matrix, SkMatrix::MakeScale(2.f, 2.f));
+ EXPECT_EQ(decoded_local_matrix, SkMatrix::Scale(2.f, 2.f));
// The rasterization of the shader is internal to skia, so use a raster canvas
// to verify that the decoded paint does not have the encoded image.
diff --git a/chromium/cc/paint/record_paint_canvas.cc b/chromium/cc/paint/record_paint_canvas.cc
index 09728c8d7de..e8758c76972 100644
--- a/chromium/cc/paint/record_paint_canvas.cc
+++ b/chromium/cc/paint/record_paint_canvas.cc
@@ -261,7 +261,7 @@ void RecordPaintCanvas::drawImageRect(const PaintImage& image,
const SkRect& src,
const SkRect& dst,
const PaintFlags* flags,
- SrcRectConstraint constraint) {
+ SkCanvas::SrcRectConstraint constraint) {
list_->push<DrawImageRectOp>(image, src, dst, flags, constraint);
}
diff --git a/chromium/cc/paint/record_paint_canvas.h b/chromium/cc/paint/record_paint_canvas.h
index cb1396b3b74..4917088a76e 100644
--- a/chromium/cc/paint/record_paint_canvas.h
+++ b/chromium/cc/paint/record_paint_canvas.h
@@ -8,7 +8,6 @@
#include <memory>
#include "base/compiler_specific.h"
-#include "base/logging.h"
#include "base/optional.h"
#include "build/build_config.h"
#include "cc/paint/paint_canvas.h"
@@ -85,7 +84,7 @@ class CC_PAINT_EXPORT RecordPaintCanvas : public PaintCanvas {
const SkRect& src,
const SkRect& dst,
const PaintFlags* flags,
- SrcRectConstraint constraint) override;
+ SkCanvas::SrcRectConstraint constraint) override;
void drawSkottie(scoped_refptr<SkottieWrapper> skottie,
const SkRect& dst,
float t) override;
diff --git a/chromium/cc/paint/scoped_raster_flags_unittest.cc b/chromium/cc/paint/scoped_raster_flags_unittest.cc
index 2a4ac36c6e1..1bef28bc3ac 100644
--- a/chromium/cc/paint/scoped_raster_flags_unittest.cc
+++ b/chromium/cc/paint/scoped_raster_flags_unittest.cc
@@ -131,13 +131,13 @@ TEST(ScopedRasterFlagsTest, ThinAliasedStroke) {
uint8_t expect_alpha;
} tests[] = {
// No downscaling => no stroke change.
- {SkMatrix::MakeScale(1.0f, 1.0f), 255, true, false, 1.0f, 0xFF},
+ {SkMatrix::Scale(1.0f, 1.0f), 255, true, false, 1.0f, 0xFF},
// Symmetric downscaling => modulated hairline stroke.
- {SkMatrix::MakeScale(0.5f, 0.5f), 255, false, false, 0.0f, 0x80},
+ {SkMatrix::Scale(0.5f, 0.5f), 255, false, false, 0.0f, 0x80},
// Symmetric downscaling w/ alpha => modulated hairline stroke.
- {SkMatrix::MakeScale(0.5f, 0.5f), 127, false, false, 0.0f, 0x40},
+ {SkMatrix::Scale(0.5f, 0.5f), 127, false, false, 0.0f, 0x40},
// Anisotropic scaling => AA stroke.
- {SkMatrix::MakeScale(0.5f, 1.5f), 255, false, true, 1.0f, 0xFF},
+ {SkMatrix::Scale(0.5f, 1.5f), 255, false, true, 1.0f, 0xFF},
};
for (const auto& test : tests) {
diff --git a/chromium/cc/paint/skia_paint_canvas.cc b/chromium/cc/paint/skia_paint_canvas.cc
index 91d8acc1d52..3e40ebe88cd 100644
--- a/chromium/cc/paint/skia_paint_canvas.cc
+++ b/chromium/cc/paint/skia_paint_canvas.cc
@@ -274,7 +274,7 @@ void SkiaPaintCanvas::drawImageRect(const PaintImage& image,
const SkRect& src,
const SkRect& dst,
const PaintFlags* flags,
- SrcRectConstraint constraint) {
+ SkCanvas::SrcRectConstraint constraint) {
base::Optional<ScopedRasterFlags> scoped_flags;
if (flags) {
scoped_flags.emplace(flags, image_provider_, canvas_->getTotalMatrix(),
diff --git a/chromium/cc/paint/skia_paint_canvas.h b/chromium/cc/paint/skia_paint_canvas.h
index 312433f5d77..77870695946 100644
--- a/chromium/cc/paint/skia_paint_canvas.h
+++ b/chromium/cc/paint/skia_paint_canvas.h
@@ -8,7 +8,6 @@
#include <memory>
#include "base/compiler_specific.h"
-#include "base/logging.h"
#include "build/build_config.h"
#include "cc/paint/paint_canvas.h"
#include "cc/paint/paint_flags.h"
@@ -109,7 +108,7 @@ class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas {
const SkRect& src,
const SkRect& dst,
const PaintFlags* flags,
- SrcRectConstraint constraint) override;
+ SkCanvas::SrcRectConstraint constraint) override;
void drawSkottie(scoped_refptr<SkottieWrapper> skottie,
const SkRect& dst,
float t) override;
diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.cc b/chromium/cc/raster/gpu_raster_buffer_provider.cc
index aec0c26256c..4dbb25b4d81 100644
--- a/chromium/cc/raster/gpu_raster_buffer_provider.cc
+++ b/chromium/cc/raster/gpu_raster_buffer_provider.cc
@@ -9,6 +9,7 @@
#include <algorithm>
#include <utility>
+#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
diff --git a/chromium/cc/raster/playback_image_provider.cc b/chromium/cc/raster/playback_image_provider.cc
index cbda15cb848..103382658e9 100644
--- a/chromium/cc/raster/playback_image_provider.cc
+++ b/chromium/cc/raster/playback_image_provider.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "cc/tiles/image_decode_cache.h"
+#include "gpu/command_buffer/common/mailbox.h"
namespace cc {
namespace {
@@ -57,9 +58,15 @@ ImageProvider::ScopedResult PlaybackImageProvider::GetRasterContent(
DrawImage adjusted_image(draw_image, 1.f, frame_index, target_color_space_);
if (!cache_->UseCacheForDrawImage(adjusted_image)) {
- return ScopedResult(DecodedDrawImage(
- paint_image.GetSkImage(), SkSize::Make(0, 0), SkSize::Make(1.f, 1.f),
- draw_image.filter_quality(), true /* is_budgeted */));
+ if (settings_->use_oop_raster) {
+ return ScopedResult(DecodedDrawImage(paint_image.GetMailbox(),
+ draw_image.filter_quality()));
+ } else {
+ return ScopedResult(
+ DecodedDrawImage(paint_image.GetRasterSkImage(), SkSize::Make(0, 0),
+ SkSize::Make(1.f, 1.f), draw_image.filter_quality(),
+ true /* is_budgeted */));
+ }
}
auto decoded_draw_image = cache_->GetDecodedImageForDraw(adjusted_image);
diff --git a/chromium/cc/raster/playback_image_provider.h b/chromium/cc/raster/playback_image_provider.h
index 70991ca2692..1f0f1084fd6 100644
--- a/chromium/cc/raster/playback_image_provider.h
+++ b/chromium/cc/raster/playback_image_provider.h
@@ -33,6 +33,10 @@ class CC_EXPORT PlaybackImageProvider : public ImageProvider {
// The frame index to use for the given image id. If no index is provided,
// the frame index provided in the PaintImage will be used.
base::flat_map<PaintImage::Id, size_t> image_to_current_frame_index;
+
+ // Indicates that the consumer of decoded images is paint serialization
+ // for OOP raster.
+ bool use_oop_raster = false;
};
// If no settings are provided, all images are skipped during rasterization.
diff --git a/chromium/cc/raster/raster_source.cc b/chromium/cc/raster/raster_source.cc
index 99d6d88018a..73ad6fc23ae 100644
--- a/chromium/cc/raster/raster_source.cc
+++ b/chromium/cc/raster/raster_source.cc
@@ -5,6 +5,7 @@
#include "cc/raster/raster_source.h"
#include <stddef.h>
+#include <algorithm>
#include "base/metrics/histogram_macros.h"
#include "base/trace_event/trace_event.h"
@@ -102,8 +103,10 @@ void RasterSource::PlaybackToCanvas(
// Treat all subnormal values as zero for performance.
ScopedSubnormalFloatDisabler disabler;
+ // NOTE: The following code should be kept consistent with
+ // PaintOpBufferSerializer::SerializePreamble().
bool is_partial_raster = canvas_bitmap_rect != canvas_playback_rect;
- if (!requires_clear_) {
+ if (!requires_clear_ && raster_transform.translation().IsZero()) {
// Clear opaque raster sources. Opaque rasters sources guarantee that all
// pixels inside the opaque region are painted. However, due to scaling
// it's possible that the last row and column might include pixels that
@@ -134,12 +137,13 @@ void RasterSource::PlaybackToCanvas(
raster_canvas->clear(SK_ColorTRANSPARENT);
}
- PlaybackToCanvas(raster_canvas, settings.image_provider);
+ PlaybackDisplayListToCanvas(raster_canvas, settings.image_provider);
raster_canvas->restore();
}
-void RasterSource::PlaybackToCanvas(SkCanvas* raster_canvas,
- ImageProvider* image_provider) const {
+void RasterSource::PlaybackDisplayListToCanvas(
+ SkCanvas* raster_canvas,
+ ImageProvider* image_provider) const {
// TODO(enne): Temporary CHECK debugging for http://crbug.com/823835
CHECK(display_list_.get());
int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
diff --git a/chromium/cc/raster/raster_source.h b/chromium/cc/raster/raster_source.h
index 7a70742f127..8f7f8679691 100644
--- a/chromium/cc/raster/raster_source.h
+++ b/chromium/cc/raster/raster_source.h
@@ -56,6 +56,9 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> {
// i.e. contents in the rect will be cropped and translated onto the canvas.
// canvas_playback_rect can be used to replay only part of the recording in,
// the content space, so only a sub-rect of the tile gets rastered.
+ //
+ // Note that this should only be called after the image decode controller has
+ // been set, which happens during commit.
void PlaybackToCanvas(SkCanvas* canvas,
const gfx::Size& content_size,
const gfx::Rect& canvas_bitmap_rect,
@@ -63,17 +66,6 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> {
const gfx::AxisTransform2d& raster_transform,
const PlaybackSettings& settings) const;
- // Raster this RasterSource into the given canvas. Canvas states such as
- // CTM and clip region will be respected. This function will replace pixels
- // in the clip region without blending.
- //
- // Virtual for testing.
- //
- // Note that this should only be called after the image decode controller has
- // been set, which happens during commit.
- virtual void PlaybackToCanvas(SkCanvas* canvas,
- ImageProvider* image_provider) const;
-
// Returns whether the given rect at given scale is of solid color in
// this raster source, as well as the solid color value.
bool PerformSolidColorAnalysis(gfx::Rect content_rect, SkColor* color) const;
@@ -139,6 +131,14 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> {
const gfx::Rect& canvas_bitmap_rect,
const gfx::Rect& canvas_playback_rect) const;
+ // Raster the display list of this raster source into the given canvas.
+ // Canvas states such as CTM and clip region will be respected.
+ // This function will replace pixels in the clip region without blending.
+ //
+ // Virtual for testing.
+ virtual void PlaybackDisplayListToCanvas(SkCanvas* canvas,
+ ImageProvider* image_provider) const;
+
// The serialized size for the largest op in this RasterSource. This is
// accessed only on the raster threads with the context lock acquired.
size_t max_op_size_hint_ =
diff --git a/chromium/cc/raster/scoped_gpu_raster.h b/chromium/cc/raster/scoped_gpu_raster.h
index 0ccd91c0259..3d454490bd7 100644
--- a/chromium/cc/raster/scoped_gpu_raster.h
+++ b/chromium/cc/raster/scoped_gpu_raster.h
@@ -7,7 +7,6 @@
#include <memory>
-#include "base/logging.h"
#include "cc/cc_export.h"
namespace viz {
diff --git a/chromium/cc/raster/staging_buffer_pool.cc b/chromium/cc/raster/staging_buffer_pool.cc
index fcbe0072eee..556a53615c7 100644
--- a/chromium/cc/raster/staging_buffer_pool.cc
+++ b/chromium/cc/raster/staging_buffer_pool.cc
@@ -133,9 +133,9 @@ StagingBufferPool::StagingBufferPool(
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, "cc::StagingBufferPool", base::ThreadTaskRunnerHandle::Get());
- memory_pressure_listener_.reset(new base::MemoryPressureListener(
- base::BindRepeating(&StagingBufferPool::OnMemoryPressure,
- weak_ptr_factory_.GetWeakPtr())));
+ memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
+ FROM_HERE, base::BindRepeating(&StagingBufferPool::OnMemoryPressure,
+ weak_ptr_factory_.GetWeakPtr()));
reduce_memory_usage_callback_ = base::BindRepeating(
&StagingBufferPool::ReduceMemoryUsage, weak_ptr_factory_.GetWeakPtr());
diff --git a/chromium/cc/raster/task_graph_runner.h b/chromium/cc/raster/task_graph_runner.h
index 50c952bf60c..8a43daab03a 100644
--- a/chromium/cc/raster/task_graph_runner.h
+++ b/chromium/cc/raster/task_graph_runner.h
@@ -13,7 +13,6 @@
#include <memory>
#include <vector>
-#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "cc/cc_export.h"
#include "cc/raster/task.h"
diff --git a/chromium/cc/resources/resource_pool.cc b/chromium/cc/resources/resource_pool.cc
index 6dbfea69d6b..e854ca403b6 100644
--- a/chromium/cc/resources/resource_pool.cc
+++ b/chromium/cc/resources/resource_pool.cc
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <algorithm>
+#include <memory>
#include <utility>
#include "base/atomic_sequence_num.h"
@@ -101,9 +102,9 @@ ResourcePool::ResourcePool(
clock_(base::DefaultTickClock::GetInstance()) {
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, "cc::ResourcePool", task_runner_.get());
- memory_pressure_listener_.reset(
- new base::MemoryPressureListener(base::BindRepeating(
- &ResourcePool::OnMemoryPressure, weak_ptr_factory_.GetWeakPtr())));
+ memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
+ FROM_HERE, base::BindRepeating(&ResourcePool::OnMemoryPressure,
+ weak_ptr_factory_.GetWeakPtr()));
}
ResourcePool::~ResourcePool() {
diff --git a/chromium/cc/resources/ui_resource_request.h b/chromium/cc/resources/ui_resource_request.h
index 6178fde9094..f70233c85d8 100644
--- a/chromium/cc/resources/ui_resource_request.h
+++ b/chromium/cc/resources/ui_resource_request.h
@@ -7,7 +7,7 @@
#include <memory>
-#include "base/logging.h"
+#include "base/check.h"
#include "cc/cc_export.h"
#include "cc/resources/ui_resource_bitmap.h"
#include "cc/resources/ui_resource_client.h"
diff --git a/chromium/cc/scheduler/commit_earlyout_reason.h b/chromium/cc/scheduler/commit_earlyout_reason.h
index 61c1c93d4c9..4dc8b98462b 100644
--- a/chromium/cc/scheduler/commit_earlyout_reason.h
+++ b/chromium/cc/scheduler/commit_earlyout_reason.h
@@ -5,7 +5,7 @@
#ifndef CC_SCHEDULER_COMMIT_EARLYOUT_REASON_H_
#define CC_SCHEDULER_COMMIT_EARLYOUT_REASON_H_
-#include "base/logging.h"
+#include "base/notreached.h"
#include "cc/cc_export.h"
namespace cc {
diff --git a/chromium/cc/scheduler/scheduler.cc b/chromium/cc/scheduler/scheduler.cc
index 445e0696639..8292ee70801 100644
--- a/chromium/cc/scheduler/scheduler.cc
+++ b/chromium/cc/scheduler/scheduler.cc
@@ -516,20 +516,19 @@ void Scheduler::BeginImplFrameWithDeadline(const viz::BeginFrameArgs& args) {
->BeginMainFrameQueueDurationNotCriticalEstimate();
}
- // We defer the invalidation if we expect the main thread to respond within
- // this frame, and our prediction in the last frame was correct. That
- // is, if we predicted the main thread to be fast and it fails to respond
- // before the deadline in the previous frame, we don't defer the invalidation
- // in the next frame.
- const bool main_thread_response_expected_before_deadline =
- bmf_sent_to_ready_to_commit_estimate - time_since_main_frame_sent <
- bmf_to_activate_threshold;
- const bool previous_invalidation_maybe_skipped_for_main_frame =
- state_machine_.should_defer_invalidation_for_fast_main_frame() &&
- state_machine_.main_thread_failed_to_respond_last_deadline();
+ bool main_thread_response_expected_before_deadline;
+ if (time_since_main_frame_sent > bmf_to_activate_threshold) {
+ // If the response to a main frame is pending past the desired duration
+ // then proactively assume that the main thread is slow instead of late
+ // correction through the frame history.
+ main_thread_response_expected_before_deadline = false;
+ } else {
+ main_thread_response_expected_before_deadline =
+ bmf_sent_to_ready_to_commit_estimate - time_since_main_frame_sent <
+ bmf_to_activate_threshold;
+ }
state_machine_.set_should_defer_invalidation_for_fast_main_frame(
- main_thread_response_expected_before_deadline &&
- !previous_invalidation_maybe_skipped_for_main_frame);
+ main_thread_response_expected_before_deadline);
base::TimeDelta bmf_to_activate_estimate = bmf_to_activate_estimate_critical;
if (!begin_main_frame_args_.on_critical_path) {
@@ -594,7 +593,7 @@ void Scheduler::FinishImplFrame() {
DCHECK(!inside_scheduled_action_);
{
base::AutoReset<bool> mark_inside(&inside_scheduled_action_, true);
- client_->DidFinishImplFrame();
+ client_->DidFinishImplFrame(last_activate_origin_frame_args());
}
if (begin_frame_source_)
@@ -745,9 +744,6 @@ void Scheduler::DrawIfPossible() {
state_machine_.DidDraw(result);
compositor_timing_history_->DidDraw(
drawing_with_new_active_tree,
- client_->CompositedAnimationsCount(),
- client_->MainThreadAnimationsCount(), client_->CurrentFrameHadRAF(),
- client_->NextFrameHasPendingRAF(),
client_->HasCustomPropertyAnimations());
}
@@ -763,9 +759,6 @@ void Scheduler::DrawForced() {
state_machine_.DidDraw(result);
compositor_timing_history_->DidDraw(
drawing_with_new_active_tree,
- client_->CompositedAnimationsCount(),
- client_->MainThreadAnimationsCount(), client_->CurrentFrameHadRAF(),
- client_->NextFrameHasPendingRAF(),
client_->HasCustomPropertyAnimations());
}
diff --git a/chromium/cc/scheduler/scheduler.h b/chromium/cc/scheduler/scheduler.h
index 750e44b4614..0265dc0c3ac 100644
--- a/chromium/cc/scheduler/scheduler.h
+++ b/chromium/cc/scheduler/scheduler.h
@@ -69,7 +69,13 @@ class SchedulerClient {
virtual void ScheduledActionInvalidateLayerTreeFrameSink(
bool needs_redraw) = 0;
virtual void ScheduledActionPerformImplSideInvalidation() = 0;
- virtual void DidFinishImplFrame() = 0;
+ // Called when the scheduler is done processing a frame. Note that the
+ // BeginFrameArgs instance passed may not necessarily be the same instance
+ // that was passed to WillBeginImplFrame(). Rather, |last_activated_args|
+ // represents the latest BeginFrameArgs instance that caused an activation to
+ // happen.
+ virtual void DidFinishImplFrame(
+ const viz::BeginFrameArgs& last_activated_args) = 0;
virtual void DidNotProduceFrame(const viz::BeginFrameAck& ack,
FrameSkippedReason reason) = 0;
virtual void WillNotReceiveBeginFrame() = 0;
@@ -79,11 +85,7 @@ class SchedulerClient {
virtual void FrameIntervalUpdated(base::TimeDelta interval) = 0;
// Functions used for reporting animation targeting UMA, crbug.com/758439.
- virtual size_t CompositedAnimationsCount() const = 0;
- virtual size_t MainThreadAnimationsCount() const = 0;
virtual bool HasCustomPropertyAnimations() const = 0;
- virtual bool CurrentFrameHadRAF() const = 0;
- virtual bool NextFrameHasPendingRAF() const = 0;
protected:
virtual ~SchedulerClient() {}
@@ -281,7 +283,8 @@ class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
// What the latest deadline was, and when it was scheduled.
base::TimeTicks deadline_;
base::TimeTicks deadline_scheduled_at_;
- SchedulerStateMachine::BeginImplFrameDeadlineMode deadline_mode_;
+ SchedulerStateMachine::BeginImplFrameDeadlineMode deadline_mode_ =
+ SchedulerStateMachine::BeginImplFrameDeadlineMode::NONE;
BeginFrameTracker begin_impl_frame_tracker_;
viz::BeginFrameAck last_begin_frame_ack_;
diff --git a/chromium/cc/scheduler/scheduler_state_machine.cc b/chromium/cc/scheduler/scheduler_state_machine.cc
index e69c54ce465..2dcc5e4bba7 100644
--- a/chromium/cc/scheduler/scheduler_state_machine.cc
+++ b/chromium/cc/scheduler/scheduler_state_machine.cc
@@ -1191,8 +1191,6 @@ void SchedulerStateMachine::OnBeginImplFrameIdle() {
// then the main thread is in a high latency mode.
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;
// 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.
diff --git a/chromium/cc/scheduler/scheduler_state_machine.h b/chromium/cc/scheduler/scheduler_state_machine.h
index f6a8d0a37f1..97b2571f99e 100644
--- a/chromium/cc/scheduler/scheduler_state_machine.h
+++ b/chromium/cc/scheduler/scheduler_state_machine.h
@@ -344,10 +344,6 @@ class CC_EXPORT SchedulerStateMachine {
return should_defer_invalidation_for_fast_main_frame_;
}
- bool main_thread_failed_to_respond_last_deadline() const {
- return main_thread_failed_to_respond_last_deadline_;
- }
-
protected:
bool BeginFrameRequiredForAction() const;
bool BeginFrameNeededForVideo() const;
@@ -471,10 +467,6 @@ class CC_EXPORT SchedulerStateMachine {
// 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.
- bool main_thread_failed_to_respond_last_deadline_ = false;
-
bool previous_pending_tree_was_impl_side_ = false;
bool current_pending_tree_is_impl_side_ = false;
diff --git a/chromium/cc/scheduler/scheduler_unittest.cc b/chromium/cc/scheduler/scheduler_unittest.cc
index 4ce0028b0f3..e4be2b3e68b 100644
--- a/chromium/cc/scheduler/scheduler_unittest.cc
+++ b/chromium/cc/scheduler/scheduler_unittest.cc
@@ -121,7 +121,8 @@ class FakeSchedulerClient : public SchedulerClient,
scheduler_->SetNeedsRedraw();
return will_begin_impl_frame_might_have_damage_;
}
- void DidFinishImplFrame() override {
+ void DidFinishImplFrame(
+ const viz::BeginFrameArgs& last_activated_args) override {
EXPECT_TRUE(inside_begin_impl_frame_);
EXPECT_FALSE(inside_action_);
base::AutoReset<bool> mark_inside(&inside_action_, true);
@@ -262,11 +263,7 @@ class FakeSchedulerClient : public SchedulerClient,
PushAction("RemoveObserver(this)");
}
- size_t CompositedAnimationsCount() const override { return 0; }
- size_t MainThreadAnimationsCount() const override { return 0; }
bool HasCustomPropertyAnimations() const override { return false; }
- bool CurrentFrameHadRAF() const override { return false; }
- bool NextFrameHasPendingRAF() const override { return false; }
protected:
bool InsideBeginImplFrameCallback(bool state) {
@@ -4361,5 +4358,46 @@ TEST_F(SchedulerTest, SendEarlyDidNotProduceFrameIfIdle) {
begin_main_frame_args.frame_id.sequence_number);
}
+TEST_F(SchedulerTest,
+ HighImplLatencyModePrioritizesMainFramesOverImplInvalidation) {
+ scheduler_settings_.enable_main_latency_recovery = false;
+ scheduler_settings_.enable_impl_latency_recovery = false;
+ SetUpScheduler(EXTERNAL_BFS);
+ fake_compositor_timing_history_->SetAllEstimatesTo(kFastDuration);
+
+ // Place the impl thread in high latency mode.
+ scheduler_->SetNeedsImplSideInvalidation(true);
+ client_->Reset();
+ EXPECT_SCOPED(AdvanceFrame());
+ EXPECT_ACTIONS("WillBeginImplFrame",
+ "ScheduledActionPerformImplSideInvalidation");
+
+ // Request a main frame and start the next impl frame. Since we have an impl
+ // side pending tree, we will activate and draw it. This finishes the impl
+ // frame before the main thread can respond causing the scheduler to
+ // incorrectly assume the main thread is slow.
+ client_->Reset();
+ EXPECT_SCOPED(AdvanceFrame());
+ EXPECT_ACTIONS("WillBeginImplFrame");
+ client_->Reset();
+ scheduler_->SetNeedsBeginMainFrame();
+ EXPECT_ACTIONS("ScheduledActionSendBeginMainFrame");
+ fake_compositor_timing_history_->SetBeginMainFrameSentTime(
+ task_runner_->NowTicks() + base::TimeDelta::FromMilliseconds(8));
+ client_->Reset();
+ scheduler_->NotifyReadyToActivate();
+ task_runner_->RunTasksWhile(client_->InsideBeginImplFrame(true));
+ EXPECT_ACTIONS("ScheduledActionActivateSyncTree",
+ "ScheduledActionDrawIfPossible");
+
+ // Start a new frame. We should not assume the main thread is slow.
+ client_->Reset();
+ EXPECT_SCOPED(AdvanceFrame());
+ scheduler_->SetNeedsImplSideInvalidation(true);
+ // No invalidation should be performed since we are waiting for the main
+ // thread to respond and merge with the commit.
+ EXPECT_ACTIONS("WillBeginImplFrame");
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/tiles/checker_image_tracker.cc b/chromium/cc/tiles/checker_image_tracker.cc
index 8495b03dda1..4c932e98d61 100644
--- a/chromium/cc/tiles/checker_image_tracker.cc
+++ b/chromium/cc/tiles/checker_image_tracker.cc
@@ -4,6 +4,8 @@
#include "cc/tiles/checker_image_tracker.h"
+#include <sstream>
+
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/stl_util.h"
@@ -407,8 +409,7 @@ void CheckerImageTracker::ScheduleNextImageDecode() {
draw_image = DrawImage(
candidate, SkIRect::MakeWH(candidate.width(), candidate.height()),
it->second.filter_quality,
- SkMatrix::MakeScale(it->second.scale.width(),
- it->second.scale.height()),
+ SkMatrix::Scale(it->second.scale.width(), it->second.scale.height()),
it->second.frame_index, it->second.color_space);
outstanding_image_decode_.emplace(candidate);
break;
diff --git a/chromium/cc/tiles/checker_image_tracker_unittest.cc b/chromium/cc/tiles/checker_image_tracker_unittest.cc
index 57984cc8d00..100e27a77d2 100644
--- a/chromium/cc/tiles/checker_image_tracker_unittest.cc
+++ b/chromium/cc/tiles/checker_image_tracker_unittest.cc
@@ -470,7 +470,7 @@ TEST_F(CheckerImageTrackerTest, ChoosesMaxScaleAndQuality) {
gfx::ColorSpace());
DrawImage scaled_image2 =
DrawImage(image.paint_image(), image.src_rect(), kHigh_SkFilterQuality,
- SkMatrix::MakeScale(1.8f), PaintImage::kDefaultFrameIndex,
+ SkMatrix::Scale(1.8f, 1.8f), PaintImage::kDefaultFrameIndex,
gfx::ColorSpace());
std::vector<DrawImage> draw_images = {scaled_image1, scaled_image2};
diff --git a/chromium/cc/tiles/gpu_image_decode_cache.cc b/chromium/cc/tiles/gpu_image_decode_cache.cc
index 4a2d6586f27..1ed44f98cb2 100644
--- a/chromium/cc/tiles/gpu_image_decode_cache.cc
+++ b/chromium/cc/tiles/gpu_image_decode_cache.cc
@@ -16,6 +16,7 @@
#include "base/debug/alias.h"
#include "base/feature_list.h"
#include "base/hash/hash.h"
+#include "base/logging.h"
#include "base/memory/discardable_memory_allocator.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/safe_math.h"
@@ -1043,9 +1044,9 @@ GpuImageDecodeCache::GpuImageDecodeCache(
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, "cc::GpuImageDecodeCache", base::ThreadTaskRunnerHandle::Get());
}
- memory_pressure_listener_.reset(
- new base::MemoryPressureListener(base::BindRepeating(
- &GpuImageDecodeCache::OnMemoryPressure, base::Unretained(this))));
+ memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
+ FROM_HERE, base::BindRepeating(&GpuImageDecodeCache::OnMemoryPressure,
+ base::Unretained(this)));
}
GpuImageDecodeCache::~GpuImageDecodeCache() {
@@ -1164,7 +1165,12 @@ ImageDecodeCache::TaskResult GpuImageDecodeCache::GetTaskForImageAndRefInternal(
task = GetImageDecodeTaskAndRef(draw_image, tracing_info, task_type);
}
- return TaskResult(task,
+ if (task) {
+ return TaskResult(task,
+ image_data->decode.can_do_hardware_accelerated_decode());
+ }
+
+ return TaskResult(true /* needs_unref */, false /* is_at_raster_decode */,
image_data->decode.can_do_hardware_accelerated_decode());
}
@@ -1922,7 +1928,8 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image,
DLOG(ERROR) << "YUV + Bitmap is unknown and unimplemented!";
NOTREACHED();
} else {
- image_data->decode.SetBitmapImage(draw_image.paint_image().GetSkImage());
+ image_data->decode.SetBitmapImage(
+ draw_image.paint_image().GetRasterSkImage());
}
return;
}
@@ -2086,7 +2093,7 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image,
// Get the encoded data in a contiguous form.
sk_sp<SkData> encoded_data =
- draw_image.paint_image().GetSkImage()->refEncodedData();
+ draw_image.paint_image().GetRasterSkImage()->refEncodedData();
DCHECK(encoded_data);
const uint32_t transfer_cache_id =
ClientImageTransferCacheEntry::GetNextId();
@@ -2526,7 +2533,7 @@ void GpuImageDecodeCache::FlushYUVImages(
CheckContextLockAcquiredIfNecessary();
lock_.AssertAcquired();
for (auto& image : *yuv_images) {
- image->flush(context_->GrContext());
+ image->flushAndSubmit(context_->GrContext());
}
yuv_images->clear();
}
diff --git a/chromium/cc/tiles/gpu_image_decode_cache.h b/chromium/cc/tiles/gpu_image_decode_cache.h
index 4ee3ae8054e..7c345dbbbd5 100644
--- a/chromium/cc/tiles/gpu_image_decode_cache.h
+++ b/chromium/cc/tiles/gpu_image_decode_cache.h
@@ -11,6 +11,7 @@
#include <vector>
#include "base/containers/mru_cache.h"
+#include "base/logging.h"
#include "base/memory/discardable_memory.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/optional.h"
diff --git a/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc b/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc
index 7a9039099bb..416cd8233c8 100644
--- a/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc
+++ b/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc
@@ -157,7 +157,7 @@ TEST_P(GpuImageDecodeCachePerfTestNoSw, DecodeWithMips) {
surface->getCanvas()->drawImageRect(decoded_image.image().get(),
SkRect::MakeWH(1024, 2048),
SkRect::MakeWH(614, 1229), &paint);
- surface->flush();
+ surface->flushAndSubmit();
}
cache_->DrawWithImageFinished(image, decoded_image);
diff --git a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc
index 3ed5b0b5b94..3c9db3a438e 100644
--- a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc
+++ b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc
@@ -429,7 +429,6 @@ class GpuImageDecodeCacheTest
}
PaintImage CreateBitmapImageInternal(const gfx::Size& size) {
- DCHECK(!do_yuv_decode_);
return CreateBitmapImage(size, color_type_);
}
@@ -2928,6 +2927,20 @@ TEST_P(GpuImageDecodeCacheTest, GetBorderlineLargeDecodedImageForDraw) {
cache->UnrefImage(draw_image);
}
+TEST_P(GpuImageDecodeCacheTest, OutOfRasterDecodeForBitmaps) {
+ auto cache = CreateCache();
+
+ PaintImage image = CreateBitmapImageInternal(GetNormalImageSize());
+ DrawImage draw_image = CreateDrawImageInternal(image);
+ ImageDecodeCache::TaskResult result =
+ cache->GetOutOfRasterDecodeTaskForImageAndRef(draw_image);
+ EXPECT_TRUE(result.need_unref);
+ EXPECT_FALSE(result.task);
+ EXPECT_FALSE(result.is_at_raster_decode);
+ EXPECT_FALSE(result.can_do_hardware_accelerated_decode);
+ cache->UnrefImage(draw_image);
+}
+
SkColorType test_color_types[] = {kN32_SkColorType, kARGB_4444_SkColorType,
kRGBA_F16_SkColorType};
bool false_array[] = {false};
diff --git a/chromium/cc/tiles/image_controller_unittest.cc b/chromium/cc/tiles/image_controller_unittest.cc
index fbe924af6fd..06da97ecd4b 100644
--- a/chromium/cc/tiles/image_controller_unittest.cc
+++ b/chromium/cc/tiles/image_controller_unittest.cc
@@ -202,7 +202,7 @@ class BlockingTask : public TileTask {
EXPECT_FALSE(HasCompleted());
EXPECT_FALSE(thread_checker_.CalledOnValidThread());
base::AutoLock hold(lock_);
- if (!can_run_)
+ while (!can_run_)
run_cv_.Wait();
has_run_ = true;
}
diff --git a/chromium/cc/tiles/image_decode_cache.h b/chromium/cc/tiles/image_decode_cache.h
index 6366a6276f6..bd77daad149 100644
--- a/chromium/cc/tiles/image_decode_cache.h
+++ b/chromium/cc/tiles/image_decode_cache.h
@@ -77,11 +77,24 @@ class CC_EXPORT ImageDecodeCache {
ToScopedImageType(ImageType image_type) {
using ScopedImageType =
devtools_instrumentation::ScopedImageDecodeTask::ImageType;
- if (image_type == ImageType::kWEBP)
- return ScopedImageType::kWebP;
- if (image_type == ImageType::kJPEG)
- return ScopedImageType::kJpeg;
- return ScopedImageType::kOther;
+ switch (image_type) {
+ case ImageType::kAVIF:
+ return ScopedImageType::kAvif;
+ case ImageType::kBMP:
+ return ScopedImageType::kBmp;
+ case ImageType::kGIF:
+ return ScopedImageType::kGif;
+ case ImageType::kICO:
+ return ScopedImageType::kIco;
+ case ImageType::kJPEG:
+ return ScopedImageType::kJpeg;
+ case ImageType::kPNG:
+ return ScopedImageType::kPng;
+ case ImageType::kWEBP:
+ return ScopedImageType::kWebP;
+ case ImageType::kInvalid:
+ return ScopedImageType::kOther;
+ }
}
virtual ~ImageDecodeCache() {}
diff --git a/chromium/cc/tiles/software_image_decode_cache.cc b/chromium/cc/tiles/software_image_decode_cache.cc
index c674a447206..412c1b02c3c 100644
--- a/chromium/cc/tiles/software_image_decode_cache.cc
+++ b/chromium/cc/tiles/software_image_decode_cache.cc
@@ -82,7 +82,7 @@ class SoftwareImageDecodeTaskImpl : public TileTask {
const ImageType image_type =
image_metadata ? image_metadata->image_type : ImageType::kInvalid;
devtools_instrumentation::ScopedImageDecodeTask image_decode_task(
- paint_image_.GetSkImage().get(),
+ paint_image_.GetRasterSkImage().get(),
devtools_instrumentation::ScopedImageDecodeTask::kSoftware,
ImageDecodeCache::ToScopedTaskType(tracing_info_.task_type),
ImageDecodeCache::ToScopedImageType(image_type));
@@ -134,24 +134,6 @@ SkFilterQuality GetDecodedFilterQuality(
: kLow_SkFilterQuality;
}
-void RecordLockExistingCachedImageHistogram(TilePriority::PriorityBin bin,
- bool success) {
- switch (bin) {
- case TilePriority::NOW:
- UMA_HISTOGRAM_BOOLEAN("Renderer4.LockExistingCachedImage.Software.NOW",
- success);
- break;
- case TilePriority::SOON:
- UMA_HISTOGRAM_BOOLEAN("Renderer4.LockExistingCachedImage.Software.SOON",
- success);
- break;
- case TilePriority::EVENTUALLY:
- UMA_HISTOGRAM_BOOLEAN(
- "Renderer4.LockExistingCachedImage.Software.EVENTUALLY", success);
- break;
- }
-}
-
} // namespace
SoftwareImageDecodeCache::SoftwareImageDecodeCache(
@@ -357,9 +339,6 @@ SoftwareImageDecodeCache::DecodeImageIfNecessary(const CacheKey& key,
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 TaskProcessingResult::kLockOnly;
}
diff --git a/chromium/cc/tiles/software_image_decode_cache_utils.cc b/chromium/cc/tiles/software_image_decode_cache_utils.cc
index cfd2e8786cf..5cbb20aba61 100644
--- a/chromium/cc/tiles/software_image_decode_cache_utils.cc
+++ b/chromium/cc/tiles/software_image_decode_cache_utils.cc
@@ -5,6 +5,7 @@
#include "cc/tiles/software_image_decode_cache_utils.h"
#include <algorithm>
+#include <sstream>
#include <utility>
#include "base/atomic_sequence_num.h"
diff --git a/chromium/cc/tiles/tile_priority.cc b/chromium/cc/tiles/tile_priority.cc
index 606a8c64e0b..1bfe552c057 100644
--- a/chromium/cc/tiles/tile_priority.cc
+++ b/chromium/cc/tiles/tile_priority.cc
@@ -11,18 +11,6 @@
namespace cc {
-std::string WhichTreeToString(WhichTree tree) {
- switch (tree) {
- case ACTIVE_TREE:
- return "ACTIVE_TREE";
- case PENDING_TREE:
- return "PENDING_TREE";
- default:
- DCHECK(false) << "Unrecognized WhichTree value " << tree;
- return "<unknown WhichTree value>";
- }
-}
-
std::string TileResolutionToString(TileResolution resolution) {
switch (resolution) {
case LOW_RESOLUTION:
diff --git a/chromium/cc/tiles/tile_priority.h b/chromium/cc/tiles/tile_priority.h
index 34cd3bc0c5d..bffd8d8ee40 100644
--- a/chromium/cc/tiles/tile_priority.h
+++ b/chromium/cc/tiles/tile_priority.h
@@ -16,10 +16,6 @@
#include "cc/cc_export.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.h"
-namespace base {
-class Value;
-}
-
namespace cc {
enum WhichTree {
@@ -28,9 +24,7 @@ enum WhichTree {
ACTIVE_TREE = 0,
PENDING_TREE = 1,
LAST_TREE = 1
- // Be sure to update WhichTreeAsValue when adding new fields.
};
-std::unique_ptr<base::Value> WhichTreeAsValue(WhichTree tree);
enum TileResolution {
LOW_RESOLUTION = 0 ,
diff --git a/chromium/cc/trees/damage_tracker_unittest.cc b/chromium/cc/trees/damage_tracker_unittest.cc
index cc784ab1213..db9fcc1fcf6 100644
--- a/chromium/cc/trees/damage_tracker_unittest.cc
+++ b/chromium/cc/trees/damage_tracker_unittest.cc
@@ -236,6 +236,23 @@ class DamageTrackerTest : public LayerTreeImplTestBase, public testing::Test {
return root;
}
+ LayerImpl* CreateAndSetUpTestTreeWithTwoSurfacesDrawingFullyVisible() {
+ LayerImpl* root = CreateTestTreeWithTwoSurfaces();
+ // Make sure render surface takes content outside visible rect into
+ // consideration.
+ root->layer_tree_impl()
+ ->property_trees()
+ ->effect_tree.Node(child1_->effect_tree_index())
+ ->backdrop_filters.Append(
+ FilterOperation::CreateZoomFilter(2.f /* zoom */, 0 /* inset */));
+
+ // Setup includes going past the first frame which always damages
+ // everything, so that we can actually perform specific tests.
+ EmulateDrawingOneFrame(root);
+
+ return root;
+ }
+
LayerImpl* CreateAndSetUpTestTreeWithFourSurfaces() {
LayerImpl* root = CreateTestTreeWithFourSurfaces();
@@ -1833,7 +1850,7 @@ TEST_F(DamageTrackerTest, DamageRectTooBigWithFilter) {
}
TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) {
- LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces();
+ LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfacesDrawingFullyVisible();
// Really far left.
grand_child1_->SetOffsetToTransformParent(
diff --git a/chromium/cc/trees/draw_properties_unittest.cc b/chromium/cc/trees/draw_properties_unittest.cc
index 6abe4863df5..b19a402d142 100644
--- a/chromium/cc/trees/draw_properties_unittest.cc
+++ b/chromium/cc/trees/draw_properties_unittest.cc
@@ -12,6 +12,7 @@
#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
+#include "base/test/scoped_feature_list.h"
#include "cc/animation/animation.h"
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_id_provider.h"
@@ -1415,10 +1416,13 @@ TEST_F(DrawPropertiesTest, DrawableContentRectForLayers) {
UpdateActiveTreeDrawProperties();
- EXPECT_EQ(gfx::Rect(5, 5, 10, 10), grand_child1->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(15, 15, 5, 5), grand_child3->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(15, 15, 5, 5), grand_child3->drawable_content_rect());
- EXPECT_TRUE(grand_child4->drawable_content_rect().IsEmpty());
+ EXPECT_EQ(gfx::Rect(5, 5, 10, 10),
+ grand_child1->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(15, 15, 5, 5),
+ grand_child3->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(15, 15, 5, 5),
+ grand_child3->visible_drawable_content_rect());
+ EXPECT_TRUE(grand_child4->visible_drawable_content_rect().IsEmpty());
}
TEST_F(DrawPropertiesTest, ClipRectIsPropagatedCorrectlyToSurfaces) {
@@ -1775,7 +1779,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsForIdentityTransform) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
// Case 2: Layer is outside the surface rect.
layer_content_rect = gfx::Rect(120, 120, 30, 30);
@@ -1785,7 +1789,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsForIdentityTransform) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
// Case 3: Layer is partially overlapping the surface rect.
layer_content_rect = gfx::Rect(80, 80, 30, 30);
@@ -1795,7 +1799,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsForIdentityTransform) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
}
// Test visible layer rect and drawable content rect are calculated correctly
@@ -1815,7 +1819,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor2DRotations) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
// Case 2: Layer is outside the surface rect.
layer_to_surface_transform.MakeIdentity();
@@ -1827,7 +1831,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor2DRotations) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
// Case 3: The layer is rotated about its top-left corner. In surface space,
// the layer is oriented diagonally, with the left half outside of the render
@@ -1842,7 +1846,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor2DRotations) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
// Case 4: The layer is rotated about its top-left corner, and translated
// upwards. In surface space, the layer is oriented diagonally, with only the
@@ -1859,7 +1863,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor2DRotations) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
}
// Test visible layer rect and drawable content rect are calculated correctly
@@ -1880,7 +1884,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor3dOrthographicTransform) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
// Case 2: Orthographic projection of a layer rotated about y-axis by 45
// degrees, but shifted to the side so only the right-half the layer would be
@@ -1899,7 +1903,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor3dOrthographicTransform) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
}
// Test visible layer rect and drawable content rect are calculated correctly
@@ -1931,7 +1935,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor3dPerspectiveTransform) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
// Case 2: same projection as before, except that the layer is also translated
// to the side, so that only the right half of the layer should be visible.
@@ -1951,7 +1955,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor3dPerspectiveTransform) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
}
// There is currently no explicit concept of an orthographic projection plane
@@ -1979,7 +1983,7 @@ TEST_F(DrawPropertiesDrawRectsTest,
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
}
// Test visible layer rect and drawable content rect are calculated correctly
@@ -2018,7 +2022,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor3dPerspectiveWhenClippedByW) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
}
static bool ProjectionClips(const gfx::Transform& map_transform,
@@ -2070,7 +2074,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsForPerspectiveUnprojection) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
}
TEST_F(DrawPropertiesTest, DrawableAndVisibleContentRectsForSimpleLayers) {
@@ -2106,10 +2110,14 @@ TEST_F(DrawPropertiesTest, DrawableAndVisibleContentRectsForSimpleLayers) {
EXPECT_EQ(gfx::Rect(0, 0, 25, 25), child2_layer->visible_layer_rect());
EXPECT_TRUE(child3_layer->visible_layer_rect().IsEmpty());
- // layer drawable_content_rects are not clipped.
- EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1_layer->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(75, 75, 50, 50), child2_layer->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(125, 125, 50, 50), child3_layer->drawable_content_rect());
+ // layer visible_drawable_content_rects are in target space, but still only
+ // the visible part.
+ EXPECT_EQ(gfx::Rect(0, 0, 50, 50),
+ child1_layer->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(75, 75, 25, 25),
+ child2_layer->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(125, 125, 0, 0),
+ child3_layer->visible_drawable_content_rect());
}
TEST_F(DrawPropertiesTest,
@@ -2153,9 +2161,11 @@ TEST_F(DrawPropertiesTest,
EXPECT_TRUE(grand_child3->visible_layer_rect().IsEmpty());
// All grandchild DrawableContentRects should also be clipped by child.
- EXPECT_EQ(gfx::Rect(5, 5, 50, 50), grand_child1->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(75, 75, 25, 25), grand_child2->drawable_content_rect());
- EXPECT_TRUE(grand_child3->drawable_content_rect().IsEmpty());
+ EXPECT_EQ(gfx::Rect(5, 5, 50, 50),
+ grand_child1->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(75, 75, 25, 25),
+ grand_child2->visible_drawable_content_rect());
+ EXPECT_TRUE(grand_child3->visible_drawable_content_rect().IsEmpty());
}
TEST_F(DrawPropertiesTest, VisibleContentRectWithClippingAndScaling) {
@@ -2286,7 +2296,7 @@ TEST_F(DrawPropertiesTest,
// An unclipped surface grows its DrawableContentRect to include all drawable
// regions of the subtree.
- EXPECT_EQ(gfx::RectF(5.f, 5.f, 170.f, 170.f),
+ EXPECT_EQ(gfx::RectF(5.f, 5.f, 95.f, 95.f),
GetRenderSurface(render_surface)->DrawableContentRect());
// All layers that draw content into the unclipped surface are also unclipped.
@@ -2295,9 +2305,9 @@ TEST_F(DrawPropertiesTest,
EXPECT_EQ(gfx::Rect(0, 0, 25, 25), child2->visible_layer_rect());
EXPECT_EQ(gfx::Rect(0, 0, 0, 0), child3->visible_layer_rect());
- EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(75, 75, 50, 50), child2->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(125, 125, 50, 50), child3->drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(75, 75, 25, 25), child2->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(125, 125, 0, 0), child3->visible_drawable_content_rect());
}
TEST_F(DrawPropertiesTest, VisibleContentRectsForClippedSurfaceWithEmptyClip) {
@@ -2361,7 +2371,7 @@ TEST_F(DrawPropertiesTest,
UpdateActiveTreeDrawProperties();
EXPECT_TRUE(child->visible_layer_rect().IsEmpty());
- EXPECT_TRUE(child->drawable_content_rect().IsEmpty());
+ EXPECT_TRUE(child->visible_drawable_content_rect().IsEmpty());
// Case 2: a matrix with flattened z, uninvertible and not visible according
// to the CSS spec.
@@ -2373,7 +2383,7 @@ TEST_F(DrawPropertiesTest,
UpdateActiveTreeDrawProperties();
EXPECT_TRUE(child->visible_layer_rect().IsEmpty());
- EXPECT_TRUE(child->drawable_content_rect().IsEmpty());
+ EXPECT_TRUE(child->visible_drawable_content_rect().IsEmpty());
// Case 3: a matrix with flattened z, also uninvertible and not visible.
uninvertible_matrix.MakeIdentity();
@@ -2385,7 +2395,7 @@ TEST_F(DrawPropertiesTest,
UpdateActiveTreeDrawProperties();
EXPECT_TRUE(child->visible_layer_rect().IsEmpty());
- EXPECT_TRUE(child->drawable_content_rect().IsEmpty());
+ EXPECT_TRUE(child->visible_drawable_content_rect().IsEmpty());
}
TEST_F(DrawPropertiesTest,
@@ -2592,10 +2602,11 @@ TEST_F(DrawPropertiesTest,
EXPECT_EQ(gfx::Rect(0, 0, 25, 25), child2->visible_layer_rect());
EXPECT_TRUE(child3->visible_layer_rect().IsEmpty());
- // But the DrawableContentRects are unclipped.
- EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(75, 75, 50, 50), child2->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(125, 125, 50, 50), child3->drawable_content_rect());
+ // The visible_drawable_content_rect would be the visible rect in target
+ // space.
+ EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(75, 75, 25, 25), child2->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(125, 125, 0, 0), child3->visible_drawable_content_rect());
}
// Check that clipping does not propagate down surfaces.
@@ -2651,8 +2662,9 @@ TEST_F(DrawPropertiesTest, DrawableAndVisibleContentRectsForSurfaceHierarchy) {
// render_surface1 lives in the "unclipped universe" of render_surface1, and
// is only implicitly clipped by render_surface1's content rect. So,
- // render_surface2 grows to enclose all drawable content of its subtree.
- EXPECT_EQ(gfx::RectF(5.f, 5.f, 170.f, 170.f),
+ // render_surface2 grows to enclose all visible drawable content of its
+ // subtree.
+ EXPECT_EQ(gfx::RectF(5.f, 5.f, 95.f, 95.f),
GetRenderSurface(render_surface2)->DrawableContentRect());
// All layers that draw content into render_surface2 think they are unclipped
@@ -2662,9 +2674,9 @@ TEST_F(DrawPropertiesTest, DrawableAndVisibleContentRectsForSurfaceHierarchy) {
EXPECT_EQ(gfx::Rect(0, 0, 0, 0), child3->visible_layer_rect());
// DrawableContentRects are also unclipped.
- EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(75, 75, 50, 50), child2->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(125, 125, 50, 50), child3->drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(75, 75, 25, 25), child2->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(125, 125, 0, 0), child3->visible_drawable_content_rect());
}
TEST_F(DrawPropertiesTest,
@@ -2943,7 +2955,8 @@ TEST_F(DrawPropertiesTest,
// All layers that draw content into the unclipped surface are also unclipped.
EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_layer_rect());
- EXPECT_EQ(expected_surface_drawable_content, child1->drawable_content_rect());
+ EXPECT_EQ(expected_surface_drawable_content,
+ child1->visible_drawable_content_rect());
}
// Layers that have non-axis aligned bounds (due to transforms) have an
@@ -2967,6 +2980,14 @@ TEST_F(DrawPropertiesTest,
CreateEffectNode(render_surface).render_surface_reason =
RenderSurfaceReason::kTest;
CopyProperties(render_surface, child1);
+ // Make sure render surface takes content outside visible rect into
+ // consideration.
+ root->layer_tree_impl()
+ ->property_trees()
+ ->effect_tree.Node(child1->effect_tree_index())
+ ->backdrop_filters.Append(
+ FilterOperation::CreateZoomFilter(1.f /* zoom */, 0 /* inset */));
+
auto& child1_transform_node = CreateTransformNode(child1);
child1_transform_node.origin = gfx::Point3F(25.f, 25.f, 0.f);
child1_transform_node.post_translation = gfx::Vector2dF(25.f, 25.f);
@@ -2993,7 +3014,7 @@ TEST_F(DrawPropertiesTest,
EXPECT_EQ(gfx::Rect(0, 0, 25, 50), child1->visible_layer_rect());
// The child's DrawableContentRect is unclipped.
- EXPECT_EQ(unclipped_surface_content, child1->drawable_content_rect());
+ EXPECT_EQ(unclipped_surface_content, child1->visible_drawable_content_rect());
}
TEST_F(DrawPropertiesTest, DrawableAndVisibleContentRectsInHighDPI) {
@@ -3048,13 +3069,16 @@ TEST_F(DrawPropertiesTest, DrawableAndVisibleContentRectsInHighDPI) {
GetRenderSurface(render_surface1)->DrawableContentRect());
// render_surface2 lives in the "unclipped universe" of render_surface1, and
- // is only implicitly clipped by render_surface1.
- EXPECT_EQ(gfx::RectF(10.f, 10.f, 350.f, 350.f),
+ // is only implicitly clipped by render_surface1, though it would only contain
+ // the visible content.
+ EXPECT_EQ(gfx::RectF(10.f, 10.f, 180.f, 180.f),
GetRenderSurface(render_surface2)->DrawableContentRect());
- EXPECT_EQ(gfx::Rect(10, 10, 100, 100), child1->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(150, 150, 100, 100), child2->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(250, 250, 100, 100), child3->drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(10, 10, 100, 100),
+ child1->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(150, 150, 30, 30),
+ child2->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(250, 250, 0, 0), child3->visible_drawable_content_rect());
// The root layer does not actually draw content of its own.
EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect());
@@ -3400,8 +3424,8 @@ TEST_F(DrawPropertiesTest, RenderSurfaceTransformsInHighDPI) {
EXPECT_TRANSFORMATION_MATRIX_EQ(
child->ScreenSpaceTransform(),
duplicate_child_non_owner->ScreenSpaceTransform());
- EXPECT_EQ(child->drawable_content_rect(),
- duplicate_child_non_owner->drawable_content_rect());
+ EXPECT_EQ(child->visible_drawable_content_rect(),
+ duplicate_child_non_owner->visible_drawable_content_rect());
EXPECT_EQ(child->bounds(), duplicate_child_non_owner->bounds());
gfx::Transform expected_render_surface_draw_transform;
@@ -3552,6 +3576,74 @@ TEST_F(DrawPropertiesTest, OpacityAnimatingOnPendingTree) {
active_child->effect_tree_index()));
}
+class TransformInteropTest : public DrawPropertiesTestBase,
+ public testing::Test {
+ public:
+ TransformInteropTest() : DrawPropertiesTestBase(TransformInteropSettings()) {}
+
+ protected:
+ LayerTreeSettings TransformInteropSettings() {
+ LayerListSettings settings;
+
+ settings.enable_transform_interop = true;
+ return settings;
+ }
+};
+
+TEST_F(TransformInteropTest, BackfaceInvisibleTransform) {
+ LayerImpl* root = root_layer();
+ root->SetDrawsContent(true);
+ LayerImpl* back_facing = AddLayer<LayerImpl>();
+ LayerImpl* back_facing_double_sided = AddLayer<LayerImpl>();
+ LayerImpl* front_facing = AddLayer<LayerImpl>();
+ back_facing->SetDrawsContent(true);
+ back_facing_double_sided->SetDrawsContent(true);
+ front_facing->SetDrawsContent(true);
+
+ root->SetBounds(gfx::Size(50, 50));
+ back_facing->SetBounds(gfx::Size(50, 50));
+ back_facing_double_sided->SetBounds(gfx::Size(50, 50));
+ front_facing->SetBounds(gfx::Size(50, 50));
+ CopyProperties(root, back_facing);
+ CopyProperties(root, front_facing);
+
+ back_facing->SetShouldCheckBackfaceVisibility(true);
+ back_facing_double_sided->SetShouldCheckBackfaceVisibility(false);
+ front_facing->SetShouldCheckBackfaceVisibility(true);
+
+ auto& back_facing_transform_node = CreateTransformNode(back_facing);
+ back_facing_transform_node.flattens_inherited_transform = false;
+ back_facing_transform_node.sorting_context_id = 1;
+ gfx::Transform rotate_about_y;
+ rotate_about_y.RotateAboutYAxis(180.0);
+ back_facing_transform_node.local = rotate_about_y;
+
+ CopyProperties(back_facing, back_facing_double_sided);
+
+ UpdateActiveTreeDrawProperties();
+
+ EXPECT_TRUE(draw_property_utils::IsLayerBackFaceVisible(
+ back_facing, back_facing->transform_tree_index(),
+ host_impl()->active_tree()->property_trees()));
+ EXPECT_TRUE(draw_property_utils::IsLayerBackFaceVisible(
+ back_facing, back_facing_double_sided->transform_tree_index(),
+ host_impl()->active_tree()->property_trees()));
+ EXPECT_FALSE(draw_property_utils::IsLayerBackFaceVisible(
+ front_facing, front_facing->transform_tree_index(),
+ host_impl()->active_tree()->property_trees()));
+
+ EXPECT_TRUE(
+ draw_property_utils::LayerShouldBeSkippedForDrawPropertiesComputation(
+ back_facing, host_impl()->active_tree()->property_trees()));
+ EXPECT_FALSE(
+ draw_property_utils::LayerShouldBeSkippedForDrawPropertiesComputation(
+ back_facing_double_sided,
+ host_impl()->active_tree()->property_trees()));
+ EXPECT_FALSE(
+ draw_property_utils::LayerShouldBeSkippedForDrawPropertiesComputation(
+ front_facing, host_impl()->active_tree()->property_trees()));
+}
+
using LCDTextTestParam = std::tuple<bool, bool>;
class LCDTextTest : public DrawPropertiesTestBase,
public testing::TestWithParam<LCDTextTestParam> {
@@ -3699,6 +3791,16 @@ TEST_P(LCDTextTest, CanUseLCDText) {
CheckCanUseLCDText(LCDTextDisallowedReason::kNone, grand_child_);
}
+TEST_P(LCDTextTest, CanUseLCDTextWithContentsOpaqueForText) {
+ child_->SetContentsOpaque(false);
+ child_->SetBackgroundColor(SK_ColorGREEN);
+ child_->SetContentsOpaqueForText(true);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, child_);
+
+ child_->SetContentsOpaqueForText(false);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kContentsNotOpaque, child_);
+}
+
TEST_P(LCDTextTest, CanUseLCDTextWithAnimation) {
// Sanity check: Make sure can_use_lcd_text_ is set on each node.
UpdateActiveTreeDrawProperties();
@@ -4232,19 +4334,19 @@ TEST_F(DrawPropertiesTest, ClipParentWithInterveningRenderSurface) {
EXPECT_EQ(gfx::Rect(0, 0, 5, 5), render_surface2->clip_rect());
EXPECT_TRUE(render_surface2->is_clipped());
- // The content rects of render_surface2 should have expanded to contain the
- // clip child.
- EXPECT_EQ(gfx::Rect(0, 0, 40, 40),
- GetRenderSurface(render_surface1)->content_rect());
- EXPECT_EQ(gfx::Rect(-10, -10, 60, 60),
- GetRenderSurface(render_surface2)->content_rect());
-
// The clip child should have inherited the clip parent's clip (projected to
// the right space, of course), but as render_surface1 already applies that
// clip, clip_child need not apply it again.
EXPECT_EQ(gfx::Rect(), clip_child->clip_rect());
EXPECT_EQ(gfx::Rect(9, 9, 40, 40), clip_child->visible_layer_rect());
EXPECT_FALSE(clip_child->is_clipped());
+
+ // The content rects of render_surface2 should have expanded to contain the
+ // clip child, but only the visible part of the clip child.
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40),
+ GetRenderSurface(render_surface1)->content_rect());
+ EXPECT_EQ(clip_child->visible_drawable_content_rect(),
+ GetRenderSurface(render_surface2)->content_rect());
}
TEST_F(DrawPropertiesTest, ClipParentScrolledInterveningLayer) {
@@ -4329,19 +4431,19 @@ TEST_F(DrawPropertiesTest, ClipParentScrolledInterveningLayer) {
EXPECT_EQ(gfx::Rect(0, 0, 5, 5), render_surface2->clip_rect());
EXPECT_TRUE(render_surface2->is_clipped());
- // The content rects of render_surface2 should have expanded to contain the
- // clip child.
- EXPECT_EQ(gfx::Rect(0, 0, 40, 40),
- GetRenderSurface(render_surface1)->content_rect());
- EXPECT_EQ(gfx::Rect(-10, -10, 60, 60),
- GetRenderSurface(render_surface2)->content_rect());
-
// The clip child should have inherited the clip parent's clip (projected to
// the right space, of course), but as render_surface1 already applies that
// clip, clip_child need not apply it again.
EXPECT_EQ(gfx::Rect(), clip_child->clip_rect());
EXPECT_EQ(gfx::Rect(12, 12, 40, 40), clip_child->visible_layer_rect());
EXPECT_FALSE(clip_child->is_clipped());
+
+ // The content rects of render_surface2 should have expanded to contain the
+ // clip child, but only the visible part of the clip child.
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40),
+ GetRenderSurface(render_surface1)->content_rect());
+ EXPECT_EQ(clip_child->visible_drawable_content_rect(),
+ GetRenderSurface(render_surface2)->content_rect());
}
TEST_F(DrawPropertiesTest, DescendantsOfClipChildren) {
@@ -6904,7 +7006,8 @@ TEST_F(DrawPropertiesTest, RenderSurfaceWithUnclippedDescendantsClipsSubtree) {
EXPECT_TRUE(test_layer->is_clipped());
EXPECT_FALSE(test_layer->render_target()->is_clipped());
EXPECT_EQ(gfx::Rect(-2, -2, 30, 30), test_layer->clip_rect());
- EXPECT_EQ(gfx::Rect(28, 28), test_layer->drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(26, 26), test_layer->visible_layer_rect());
+ EXPECT_EQ(gfx::Rect(26, 26), test_layer->visible_drawable_content_rect());
}
TEST_F(DrawPropertiesTest,
@@ -7059,7 +7162,7 @@ TEST_F(DrawPropertiesTest, RenderSurfaceContentRectWithMultipleSurfaces) {
GetRenderSurface(unclipped_surface)->content_rect());
EXPECT_EQ(gfx::Rect(50, 50),
GetRenderSurface(unclipped_desc_surface)->content_rect());
- EXPECT_EQ(gfx::Rect(60, 60),
+ EXPECT_EQ(gfx::Rect(50, 50),
GetRenderSurface(unclipped_desc_surface2)->content_rect());
EXPECT_EQ(gfx::Rect(50, 50),
GetRenderSurface(clipped_surface)->content_rect());
@@ -7724,7 +7827,8 @@ TEST_F(DrawPropertiesTestWithLayerTree, CopyRequestScalingTest) {
EXPECT_EQ(gfx::Rect(10, 10), ImplOf(test_layer)->visible_layer_rect());
EXPECT_EQ(transform, ImplOf(test_layer)->DrawTransform());
EXPECT_EQ(gfx::Rect(50, 50), ImplOf(test_layer)->clip_rect());
- EXPECT_EQ(gfx::Rect(50, 50), ImplOf(test_layer)->drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(50, 50),
+ ImplOf(test_layer)->visible_drawable_content_rect());
// Clear the copy request and call UpdateSurfaceContentsScale.
GetPropertyTrees(root.get())->effect_tree.ClearCopyRequests();
diff --git a/chromium/cc/trees/draw_property_utils.cc b/chromium/cc/trees/draw_property_utils.cc
index f9fdebf8218..7c65e513700 100644
--- a/chromium/cc/trees/draw_property_utils.cc
+++ b/chromium/cc/trees/draw_property_utils.cc
@@ -11,6 +11,7 @@
#include "base/containers/adapters.h"
#include "base/containers/stack.h"
+#include "base/logging.h"
#include "cc/base/math_util.h"
#include "cc/layers/draw_properties.h"
#include "cc/layers/layer.h"
@@ -381,6 +382,12 @@ bool IsTargetSpaceTransformBackFaceVisible(
LayerImpl* layer,
int transform_tree_index,
const PropertyTrees* property_trees) {
+ const TransformTree& transform_tree = property_trees->transform_tree;
+ const TransformNode& transform_node =
+ *transform_tree.Node(transform_tree_index);
+ if (transform_node.delegates_to_parent_for_backface)
+ transform_tree_index = transform_node.parent_id;
+
gfx::Transform to_target;
property_trees->GetToTarget(transform_tree_index,
layer->render_target_effect_tree_index(),
@@ -388,12 +395,42 @@ bool IsTargetSpaceTransformBackFaceVisible(
return to_target.IsBackFaceVisible();
}
-template <typename LayerType>
-bool IsLayerBackFaceVisible(LayerType* layer,
- int transform_tree_index,
- const PropertyTrees* property_trees) {
- return IsTargetSpaceTransformBackFaceVisible(layer, transform_tree_index,
- property_trees);
+bool IsTransformToRootOf3DRenderingContextBackFaceVisible(
+ Layer* layer,
+ int transform_tree_index,
+ const PropertyTrees* property_trees) {
+ // We do not skip back face invisible layers on main thread as target space
+ // transform will not be available here.
+ return false;
+}
+
+bool IsTransformToRootOf3DRenderingContextBackFaceVisible(
+ LayerImpl* layer,
+ int transform_tree_index,
+ const PropertyTrees* property_trees) {
+ const TransformTree& transform_tree = property_trees->transform_tree;
+
+ const TransformNode& transform_node =
+ *transform_tree.Node(transform_tree_index);
+ if (transform_node.delegates_to_parent_for_backface)
+ transform_tree_index = transform_node.parent_id;
+
+ int root_id = transform_tree_index;
+ int sorting_context_id = transform_node.sorting_context_id;
+
+ while (root_id > 0 && transform_tree.Node(root_id - 1)->sorting_context_id ==
+ sorting_context_id)
+ root_id--;
+
+ // TODO(chrishtr): cache this on the transform trees if needed, similar to
+ // |to_target| and |to_screen|.
+ gfx::Transform to_3d_root;
+ if (transform_tree_index != root_id)
+ property_trees->transform_tree.CombineTransformsBetween(
+ transform_tree_index, root_id, &to_3d_root);
+ to_3d_root.PreconcatTransform(
+ property_trees->transform_tree.Node(root_id)->to_parent);
+ return to_3d_root.IsBackFaceVisible();
}
inline bool TransformToScreenIsKnown(Layer* layer,
@@ -445,7 +482,8 @@ bool LayerNeedsUpdate(LayerType* layer,
// backface is not visible.
if (TransformToScreenIsKnown(layer, backface_transform_id, tree) &&
!HasSingularTransform(backface_transform_id, tree) &&
- IsLayerBackFaceVisible(layer, backface_transform_id, property_trees)) {
+ draw_property_utils::IsLayerBackFaceVisible(
+ layer, backface_transform_id, property_trees)) {
UMA_HISTOGRAM_BOOLEAN(
"Compositing.Renderer.LayerUpdateSkippedDueToBackface", true);
return false;
@@ -474,35 +512,6 @@ inline bool LayerShouldBeSkippedForDrawPropertiesComputation(
!effect_node->is_drawn;
}
-inline bool LayerShouldBeSkippedForDrawPropertiesComputation(
- LayerImpl* layer,
- const TransformTree& transform_tree,
- const EffectTree& effect_tree) {
- const EffectNode* effect_node = effect_tree.Node(layer->effect_tree_index());
-
- if (effect_node->HasRenderSurface() && effect_node->subtree_has_copy_request)
- return false;
-
- // Skip if the node's subtree is hidden and no need to cache.
- if (effect_node->subtree_hidden && !effect_node->cache_render_surface)
- return true;
-
- // If the layer transform is not invertible, it should be skipped. In case the
- // transform is animating and singular, we should not skip it.
- const TransformNode* transform_node =
- transform_tree.Node(layer->transform_tree_index());
-
- if (!transform_node->node_and_ancestors_are_animated_or_invertible ||
- !effect_node->is_drawn)
- return true;
-
- UMA_HISTOGRAM_BOOLEAN(
- "Compositing.Renderer.LayerSkippedForDrawPropertiesDueToBackface",
- effect_node->hidden_by_backface_visibility);
-
- return effect_node->hidden_by_backface_visibility;
-}
-
gfx::Rect LayerDrawableContentRect(
const LayerImpl* layer,
const gfx::Rect& layer_bounds_in_target_space,
@@ -922,8 +931,8 @@ void ComputeInitialRenderSurfaceList(LayerTreeImpl* layer_tree_impl,
bool is_root = layer_tree_impl->IsRootLayer(layer);
bool skip_draw_properties_computation =
- LayerShouldBeSkippedForDrawPropertiesComputation(
- layer, property_trees->transform_tree, property_trees->effect_tree);
+ draw_property_utils::LayerShouldBeSkippedForDrawPropertiesComputation(
+ layer, property_trees);
bool skip_for_invertibility = SkipForInvertibility(layer, property_trees);
@@ -1138,11 +1147,19 @@ void ComputeDrawPropertiesOfVisibleLayers(const LayerImplList* layer_list,
// Compute drawable content rects
for (LayerImpl* layer : *layer_list) {
- gfx::Rect bounds_in_target_space = MathUtil::MapEnclosingClippedRect(
- layer->draw_properties().target_space_transform,
- gfx::Rect(layer->bounds()));
- layer->draw_properties().drawable_content_rect = LayerDrawableContentRect(
- layer, bounds_in_target_space, layer->draw_properties().clip_rect);
+ bool only_draws_visible_content =
+ property_trees->effect_tree.Node(layer->effect_tree_index())
+ ->only_draws_visible_content;
+ gfx::Rect drawable_bounds = gfx::Rect(layer->visible_layer_rect());
+ if (!only_draws_visible_content) {
+ drawable_bounds = gfx::Rect(layer->bounds());
+ }
+ gfx::Rect visible_bounds_in_target_space =
+ MathUtil::MapEnclosingClippedRect(
+ layer->draw_properties().target_space_transform, drawable_bounds);
+ layer->draw_properties().visible_drawable_content_rect =
+ LayerDrawableContentRect(layer, visible_bounds_in_target_space,
+ layer->draw_properties().clip_rect);
}
}
@@ -1164,6 +1181,61 @@ bool NodeMayContainBackdropBlurFilter(const EffectNode& node) {
} // namespace
+bool CC_EXPORT LayerShouldBeSkippedForDrawPropertiesComputation(
+ LayerImpl* layer,
+ const PropertyTrees* property_trees) {
+ const TransformTree& transform_tree = property_trees->transform_tree;
+ const EffectTree& effect_tree = property_trees->effect_tree;
+ const EffectNode* effect_node = effect_tree.Node(layer->effect_tree_index());
+
+ if (effect_node->HasRenderSurface() && effect_node->subtree_has_copy_request)
+ return false;
+
+ // Skip if the node's subtree is hidden and no need to cache.
+ if (effect_node->subtree_hidden && !effect_node->cache_render_surface)
+ return true;
+
+ // If the layer transform is not invertible, it should be skipped. In case the
+ // transform is animating and singular, we should not skip it.
+ const TransformNode* transform_node =
+ transform_tree.Node(layer->transform_tree_index());
+
+ if (!transform_node->node_and_ancestors_are_animated_or_invertible ||
+ !effect_node->is_drawn)
+ return true;
+ if (layer->layer_tree_impl()->settings().enable_transform_interop) {
+ return layer->should_check_backface_visibility() &&
+ IsLayerBackFaceVisible(layer, layer->transform_tree_index(),
+ property_trees);
+ } else {
+ return effect_node->hidden_by_backface_visibility;
+ }
+}
+
+bool CC_EXPORT IsLayerBackFaceVisible(LayerImpl* layer,
+ int transform_tree_index,
+ const PropertyTrees* property_trees) {
+ if (layer->layer_tree_impl()->settings().enable_transform_interop) {
+ return IsTransformToRootOf3DRenderingContextBackFaceVisible(
+ layer, transform_tree_index, property_trees);
+ } else {
+ return IsTargetSpaceTransformBackFaceVisible(layer, transform_tree_index,
+ property_trees);
+ }
+}
+
+bool CC_EXPORT IsLayerBackFaceVisible(Layer* layer,
+ int transform_tree_index,
+ const PropertyTrees* property_trees) {
+ if (layer->layer_tree_host()->GetSettings().enable_transform_interop) {
+ return IsTransformToRootOf3DRenderingContextBackFaceVisible(
+ layer, transform_tree_index, property_trees);
+ } else {
+ return IsTargetSpaceTransformBackFaceVisible(layer, transform_tree_index,
+ property_trees);
+ }
+}
+
void ConcatInverseSurfaceContentsScale(const EffectNode* effect_node,
gfx::Transform* transform) {
DCHECK(effect_node->HasRenderSurface());
@@ -1195,7 +1267,6 @@ void FindLayersThatNeedUpdates(LayerTreeHost* layer_tree_host,
void FindLayersThatNeedUpdates(LayerTreeImpl* layer_tree_impl,
std::vector<LayerImpl*>* visible_layer_list) {
const PropertyTrees* property_trees = layer_tree_impl->property_trees();
- const TransformTree& transform_tree = property_trees->transform_tree;
const EffectTree& effect_tree = property_trees->effect_tree;
for (auto* layer_impl : *layer_tree_impl) {
@@ -1204,8 +1275,8 @@ void FindLayersThatNeedUpdates(LayerTreeImpl* layer_tree_impl,
layer_impl->EnsureValidPropertyTreeIndices();
if (!IsRootLayer(layer_impl) &&
- LayerShouldBeSkippedForDrawPropertiesComputation(
- layer_impl, transform_tree, effect_tree))
+ LayerShouldBeSkippedForDrawPropertiesComputation(layer_impl,
+ property_trees))
continue;
bool layer_is_drawn =
diff --git a/chromium/cc/trees/draw_property_utils.h b/chromium/cc/trees/draw_property_utils.h
index c42684eefce..b80b38e3ab9 100644
--- a/chromium/cc/trees/draw_property_utils.h
+++ b/chromium/cc/trees/draw_property_utils.h
@@ -68,6 +68,18 @@ void CC_EXPORT CalculateDrawProperties(
RenderSurfaceList* output_render_surface_list,
LayerImplList* output_update_layer_list_for_testing = nullptr);
+bool CC_EXPORT LayerShouldBeSkippedForDrawPropertiesComputation(
+ LayerImpl* layer,
+ const PropertyTrees* property_trees);
+
+bool CC_EXPORT IsLayerBackFaceVisible(LayerImpl* layer,
+ int transform_tree_index,
+ const PropertyTrees* property_trees);
+
+bool CC_EXPORT IsLayerBackFaceVisible(Layer* layer,
+ int transform_tree_index,
+ const PropertyTrees* property_trees);
+
#if DCHECK_IS_ON()
// Checks and logs if double background blur exists in any layers. Returns
// true if no double background blur is detected, false otherwise.
diff --git a/chromium/cc/trees/effect_node.cc b/chromium/cc/trees/effect_node.cc
index a412c237f1c..9bae1e90b74 100644
--- a/chromium/cc/trees/effect_node.cc
+++ b/chromium/cc/trees/effect_node.cc
@@ -23,6 +23,7 @@ EffectNode::EffectNode()
double_sided(true),
trilinear_filtering(false),
is_drawn(true),
+ only_draws_visible_content(true),
subtree_hidden(false),
has_potential_filter_animation(false),
has_potential_backdrop_filter_animation(false),
@@ -56,7 +57,6 @@ bool EffectNode::operator==(const EffectNode& other) const {
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 &&
// The specific reason is just for tracing/testing/debugging, so just
@@ -67,7 +67,9 @@ bool EffectNode::operator==(const EffectNode& other) const {
hidden_by_backface_visibility == other.hidden_by_backface_visibility &&
double_sided == other.double_sided &&
trilinear_filtering == other.trilinear_filtering &&
- is_drawn == other.is_drawn && subtree_hidden == other.subtree_hidden &&
+ is_drawn == other.is_drawn &&
+ only_draws_visible_content == other.only_draws_visible_content &&
+ subtree_hidden == other.subtree_hidden &&
has_potential_filter_animation ==
other.has_potential_filter_animation &&
has_potential_backdrop_filter_animation ==
@@ -163,6 +165,7 @@ void EffectNode::AsValueInto(base::trace_event::TracedValue* value) const {
hidden_by_backface_visibility);
value->SetBoolean("trilinear_filtering", trilinear_filtering);
value->SetBoolean("is_drawn", is_drawn);
+ value->SetBoolean("only_draws_visible_content", only_draws_visible_content);
value->SetBoolean("has_potential_filter_animation",
has_potential_filter_animation);
value->SetBoolean("has_potential_backdrop_filter_animation",
diff --git a/chromium/cc/trees/effect_node.h b/chromium/cc/trees/effect_node.h
index 01f662563e1..6663457250c 100644
--- a/chromium/cc/trees/effect_node.h
+++ b/chromium/cc/trees/effect_node.h
@@ -96,6 +96,13 @@ struct CC_EXPORT EffectNode {
bool double_sided : 1;
bool trilinear_filtering : 1;
bool is_drawn : 1;
+ // In most cases we only need to draw the visible part of any content
+ // contributing to the effect. For copy request case, we would need to copy
+ // the entire content, and could not only draw the visible part. In the rare
+ // case of a backdrop zoom filter we need to take into consideration the
+ // content offscreen to make sure the backdrop zoom filter is applied with the
+ // correct center.
+ bool only_draws_visible_content : 1;
// TODO(jaydasika) : Delete this after implementation of
// SetHideLayerAndSubtree is cleaned up. (crbug.com/595843)
bool subtree_hidden : 1;
diff --git a/chromium/cc/trees/frame_rate_counter.cc b/chromium/cc/trees/frame_rate_counter.cc
deleted file mode 100644
index 816877c4ab3..00000000000
--- a/chromium/cc/trees/frame_rate_counter.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cc/trees/frame_rate_counter.h"
-
-#include <stddef.h>
-
-#include <algorithm>
-#include <limits>
-
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-
-namespace cc {
-
-// The following constants are measured in seconds.
-
-// Two thresholds (measured in seconds) that describe what is considered to be a
-// "no-op frame" that should not be counted.
-// - if the frame is too fast, then given our compositor implementation, the
-// frame probably was a no-op and did not draw.
-// - if the frame is too slow, then there is probably not animating content, so
-// we should not pollute the average.
-static const double kFrameTooFast = 1.0 / 70.0;
-static const double kFrameTooSlow = 1.5;
-
-// If a frame takes longer than this threshold (measured in seconds) then we
-// (naively) assume that it missed a screen refresh; that is, we dropped a
-// frame.
-// TODO(brianderson): Determine this threshold based on monitor refresh rate,
-// crbug.com/138642.
-static const double kDroppedFrameTime = 1.0 / 50.0;
-
-// static
-std::unique_ptr<FrameRateCounter> FrameRateCounter::Create(
- bool has_impl_thread) {
- return base::WrapUnique(new FrameRateCounter(has_impl_thread));
-}
-
-base::TimeDelta FrameRateCounter::RecentFrameInterval(size_t n) const {
- DCHECK_GT(n, 0u);
- DCHECK_LT(n, ring_buffer_.BufferSize());
- return ring_buffer_.ReadBuffer(n) - ring_buffer_.ReadBuffer(n - 1);
-}
-
-FrameRateCounter::FrameRateCounter(bool has_impl_thread)
- : has_impl_thread_(has_impl_thread), dropped_frame_count_(0) {}
-
-void FrameRateCounter::SaveTimeStamp(base::TimeTicks timestamp) {
- ring_buffer_.SaveToBuffer(timestamp);
-
- // Check if frame interval can be computed.
- if (ring_buffer_.CurrentIndex() < 2)
- return;
-
- base::TimeDelta frame_interval_seconds =
- RecentFrameInterval(ring_buffer_.BufferSize() - 1);
-
- if (!IsBadFrameInterval(frame_interval_seconds) &&
- frame_interval_seconds.InSecondsF() > kDroppedFrameTime)
- dropped_frame_count_ +=
- frame_interval_seconds.InSecondsF() / kDroppedFrameTime;
-}
-
-bool FrameRateCounter::IsBadFrameInterval(
- base::TimeDelta interval_between_consecutive_frames) const {
- double delta = interval_between_consecutive_frames.InSecondsF();
- bool scheduler_allows_double_frames = !has_impl_thread_;
- bool interval_too_fast =
- scheduler_allows_double_frames ? delta < kFrameTooFast : delta <= 0.0;
- bool interval_too_slow = delta > kFrameTooSlow;
- return interval_too_fast || interval_too_slow;
-}
-
-void FrameRateCounter::GetMinAndMaxFPS(double* min_fps, double* max_fps) const {
- *min_fps = std::numeric_limits<double>::max();
- *max_fps = 0.0;
-
- for (RingBufferType::Iterator it = --ring_buffer_.End(); it; --it) {
- base::TimeDelta delta = RecentFrameInterval(it.index() + 1);
-
- if (IsBadFrameInterval(delta))
- continue;
-
- DCHECK_GT(delta.InSecondsF(), 0.f);
- double fps = 1.0 / delta.InSecondsF();
-
- *min_fps = std::min(fps, *min_fps);
- *max_fps = std::max(fps, *max_fps);
- }
-
- if (*min_fps > *max_fps)
- *min_fps = *max_fps;
-}
-
-double FrameRateCounter::GetAverageFPS() const {
- int frame_count = 0;
- double frame_times_total = 0.0;
- double average_fps = 0.0;
-
- // Walk backwards through the samples looking for a run of good frame
- // timings from which to compute the mean.
- //
- // Slow frames occur just because the user is inactive, and should be
- // ignored. Fast frames are ignored if the scheduler is in single-thread
- // mode in order to represent the true frame rate in spite of the fact that
- // the first few swapbuffers happen instantly which skews the statistics
- // too much for short lived animations.
- //
- // IsBadFrameInterval encapsulates the frame too slow/frame too fast logic.
-
- for (RingBufferType::Iterator it = --ring_buffer_.End();
- it && frame_times_total < 1.0; --it) {
- base::TimeDelta delta = RecentFrameInterval(it.index() + 1);
-
- if (!IsBadFrameInterval(delta)) {
- frame_count++;
- frame_times_total += delta.InSecondsF();
- } else if (frame_count) {
- break;
- }
- }
-
- if (frame_count) {
- DCHECK_GT(frame_times_total, 0.0);
- average_fps = frame_count / frame_times_total;
- }
-
- return average_fps;
-}
-
-} // namespace cc
diff --git a/chromium/cc/trees/frame_rate_counter.h b/chromium/cc/trees/frame_rate_counter.h
deleted file mode 100644
index f1445fb16da..00000000000
--- a/chromium/cc/trees/frame_rate_counter.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_TREES_FRAME_RATE_COUNTER_H_
-#define CC_TREES_FRAME_RATE_COUNTER_H_
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/containers/ring_buffer.h"
-#include "base/time/time.h"
-
-namespace cc {
-
-// This class maintains a history of timestamps, and provides functionality to
-// intelligently compute average frames per second.
-class FrameRateCounter {
- public:
- static std::unique_ptr<FrameRateCounter> Create(bool has_impl_thread);
-
- FrameRateCounter(const FrameRateCounter&) = delete;
- FrameRateCounter& operator=(const FrameRateCounter&) = delete;
-
- size_t current_frame_number() const { return ring_buffer_.CurrentIndex(); }
- int dropped_frame_count() const { return dropped_frame_count_; }
- size_t time_stamp_history_size() const { return ring_buffer_.BufferSize(); }
-
- void SaveTimeStamp(base::TimeTicks timestamp);
-
- // n = 0 returns the oldest frame interval retained in the history, while n =
- // time_stamp_history_size() - 1 returns the most recent frame interval.
- base::TimeDelta RecentFrameInterval(size_t n) const;
-
- // This is a heuristic that can be used to ignore frames in a reasonable way.
- // Returns true if the given frame interval is too fast or too slow, based on
- // constant thresholds.
- bool IsBadFrameInterval(
- base::TimeDelta interval_between_consecutive_frames) const;
-
- void GetMinAndMaxFPS(double* min_fps, double* max_fps) const;
- double GetAverageFPS() const;
-
- typedef base::RingBuffer<base::TimeTicks, 136> RingBufferType;
- RingBufferType::Iterator begin() const { return ring_buffer_.Begin(); }
- RingBufferType::Iterator end() const { return ring_buffer_.End(); }
-
- private:
- explicit FrameRateCounter(bool has_impl_thread);
-
- RingBufferType ring_buffer_;
-
- bool has_impl_thread_;
- int dropped_frame_count_;
-};
-
-} // namespace cc
-
-#endif // CC_TREES_FRAME_RATE_COUNTER_H_
diff --git a/chromium/cc/trees/image_animation_controller.cc b/chromium/cc/trees/image_animation_controller.cc
index a7e1ebf6de6..2cb4f9d1bd1 100644
--- a/chromium/cc/trees/image_animation_controller.cc
+++ b/chromium/cc/trees/image_animation_controller.cc
@@ -4,6 +4,8 @@
#include "cc/trees/image_animation_controller.h"
+#include <sstream>
+
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/trace_event/trace_event.h"
@@ -360,6 +362,25 @@ bool ImageAnimationController::AnimationState::AdvanceFrame(
// skipped trying to catch up.
DCHECK_GT(num_of_frames_advanced, 0u);
last_num_frames_skipped_ = num_of_frames_advanced - 1u;
+ switch (repetitions_completed_) {
+ case 0:
+ UMA_HISTOGRAM_COUNTS_100000(
+ "AnimatedImage.NumOfFramesSkipped.FirstAnimationLoop",
+ last_num_frames_skipped_);
+ break;
+ case 1:
+ UMA_HISTOGRAM_COUNTS_100000(
+ "AnimatedImage.NumOfFramesSkipped.SecondAnimationLoop",
+ last_num_frames_skipped_);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ UMA_HISTOGRAM_COUNTS_100000(
+ "AnimatedImage.NumOfFramesSkipped.ThirdToFifthAnimationLoop",
+ last_num_frames_skipped_);
+ break;
+ }
UMA_HISTOGRAM_COUNTS_100000("AnimatedImage.NumOfFramesSkipped.Compositor",
last_num_frames_skipped_);
return needs_invalidation();
diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc
index b1a4798682b..d5e57eaa4a5 100644
--- a/chromium/cc/trees/layer_tree_host.cc
+++ b/chromium/cc/trees/layer_tree_host.cc
@@ -18,6 +18,7 @@
#include "base/command_line.h"
#include "base/containers/adapters.h"
#include "base/location.h"
+#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
@@ -94,6 +95,9 @@ LayerTreeHost::InitParams::InitParams(InitParams&&) = default;
LayerTreeHost::InitParams& LayerTreeHost::InitParams::operator=(InitParams&&) =
default;
+LayerTreeHost::ScrollAnimationState::ScrollAnimationState() = default;
+LayerTreeHost::ScrollAnimationState::~ScrollAnimationState() = default;
+
std::unique_ptr<LayerTreeHost> LayerTreeHost::CreateThreaded(
scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner,
InitParams params) {
@@ -263,14 +267,6 @@ const LayerTreeSettings& LayerTreeHost::GetSettings() const {
void LayerTreeHost::QueueSwapPromise(
std::unique_ptr<SwapPromise> swap_promise) {
swap_promise_manager_.QueueSwapPromise(std::move(swap_promise));
-
- // Request a main frame if one is not already in progress. This might either
- // A) request a commit ahead of time or B) request a commit which is not
- // needed because there are not pending updates. If B) then the frame will
- // be aborted early and the swap promises will be broken (see
- // EarlyOut_NoUpdates).
- if (!inside_main_frame_)
- SetNeedsAnimate();
}
void LayerTreeHost::WillBeginMainFrame() {
@@ -609,6 +605,11 @@ void LayerTreeHost::SetNeedsAnimate() {
events_metrics_manager_.SaveActiveEventMetrics();
}
+void LayerTreeHost::SetNeedsAnimateIfNotInsideMainFrame() {
+ if (!inside_main_frame_)
+ SetNeedsAnimate();
+}
+
DISABLE_CFI_PERF
void LayerTreeHost::SetNeedsUpdateLayers() {
proxy_->SetNeedsUpdateLayers();
@@ -785,6 +786,13 @@ bool LayerTreeHost::CaptureContent(std::vector<NodeId>* content) {
return true;
}
+void LayerTreeHost::DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) {
+ client_->DidObserveFirstScrollDelay(first_scroll_delay,
+ first_scroll_timestamp);
+}
+
bool LayerTreeHost::DoUpdateLayers() {
TRACE_EVENT1("cc,benchmark", "LayerTreeHost::DoUpdateLayers",
"source_frame_number", SourceFrameNumber());
@@ -849,6 +857,24 @@ void LayerTreeHost::ApplyViewportChanges(const ScrollAndScaleSet& info) {
if (info.inner_viewport_scroll.element_id)
inner_viewport_scroll_delta = info.inner_viewport_scroll.scroll_delta;
+ // When a new scroll-animation starts, it is necessary to check
+ // |info.manipulation_info| to make sure the scroll-animation was started by
+ // an input event.
+ // If there is already an ongoing scroll-animation, then it is necessary to
+ // only look at |info.ongoing_scroll_animation| (since it is possible for the
+ // scroll-animation to continue even if no event was handled).
+ bool new_ongoing_scroll =
+ scroll_animation_.in_progress
+ ? info.ongoing_scroll_animation
+ : (info.ongoing_scroll_animation && info.manipulation_info);
+ if (scroll_animation_.in_progress && !new_ongoing_scroll) {
+ scroll_animation_.in_progress = false;
+ if (!scroll_animation_.end_notification.is_null())
+ std::move(scroll_animation_.end_notification).Run();
+ } else {
+ scroll_animation_.in_progress = new_ongoing_scroll;
+ }
+
if (inner_viewport_scroll_delta.IsZero() && info.page_scale_delta == 1.f &&
info.elastic_overscroll_delta.IsZero() && !info.top_controls_delta &&
!info.bottom_controls_delta &&
@@ -985,6 +1011,14 @@ void LayerTreeHost::NotifyThroughputTrackerResults(
client_->NotifyThroughputTrackerResults(std::move(results));
}
+void LayerTreeHost::SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) {
+ client_->SubmitThroughputData(source_id, aggregated_percent, impl_percent,
+ main_percent);
+}
+
const base::WeakPtr<InputHandler>& LayerTreeHost::GetInputHandler() const {
return input_handler_weak_ptr_;
}
@@ -1068,6 +1102,15 @@ void LayerTreeHost::RequestPresentationTimeForNextFrame(
pending_presentation_time_callbacks_.push_back(std::move(callback));
}
+void LayerTreeHost::RequestScrollAnimationEndNotification(
+ base::OnceClosure callback) {
+ DCHECK(scroll_animation_.end_notification.is_null());
+ if (scroll_animation_.in_progress)
+ scroll_animation_.end_notification = std::move(callback);
+ else
+ std::move(callback).Run();
+}
+
void LayerTreeHost::SetRootLayer(scoped_refptr<Layer> root_layer) {
if (root_layer_.get() == root_layer.get())
return;
@@ -1118,9 +1161,13 @@ Layer* LayerTreeHost::InnerViewportScrollLayerForTesting() const {
}
Layer* LayerTreeHost::OuterViewportScrollLayerForTesting() const {
+ return LayerByElementId(OuterViewportScrollElementId());
+}
+
+ElementId LayerTreeHost::OuterViewportScrollElementId() const {
auto* scroll_node =
property_trees()->scroll_tree.Node(viewport_property_ids_.outer_scroll);
- return scroll_node ? LayerByElementId(scroll_node->element_id) : nullptr;
+ return scroll_node ? scroll_node->element_id : ElementId();
}
void LayerTreeHost::RegisterSelection(const LayerSelection& selection) {
@@ -1439,10 +1486,6 @@ void LayerTreeHost::AddLayerShouldPushProperties(Layer* layer) {
layers_that_should_push_properties_.insert(layer);
}
-void LayerTreeHost::RemoveLayerShouldPushProperties(Layer* layer) {
- layers_that_should_push_properties_.erase(layer);
-}
-
void LayerTreeHost::ClearLayersThatShouldPushProperties() {
layers_that_should_push_properties_.clear();
}
@@ -1566,6 +1609,9 @@ void LayerTreeHost::PushLayerTreePropertiesTo(LayerTreeImpl* tree_impl) {
}
tree_impl->set_display_transform_hint(display_transform_hint_);
+
+ if (delegated_ink_metadata_)
+ tree_impl->set_delegated_ink_metadata(std::move(delegated_ink_metadata_));
}
void LayerTreeHost::PushSurfaceRangesTo(LayerTreeImpl* tree_impl) {
diff --git a/chromium/cc/trees/layer_tree_host.h b/chromium/cc/trees/layer_tree_host.h
index 8aae435b230..f365bd9a909 100644
--- a/chromium/cc/trees/layer_tree_host.h
+++ b/chromium/cc/trees/layer_tree_host.h
@@ -13,6 +13,7 @@
#include <set>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
#include "base/callback_forward.h"
@@ -49,6 +50,7 @@
#include "cc/trees/swap_promise_manager.h"
#include "cc/trees/target_property.h"
#include "cc/trees/viewport_layers.h"
+#include "components/viz/common/delegated_ink_metadata.h"
#include "components/viz/common/resources/resource_format.h"
#include "components/viz/common/surfaces/local_surface_id_allocation.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
@@ -215,6 +217,9 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
// full commit synchronization or layer updates.
void SetNeedsAnimate();
+ // Calls SetNeedsAnimate() if there is no main frame already in progress.
+ void SetNeedsAnimateIfNotInsideMainFrame();
+
// Requests a main frame update and also ensure that the host pulls layer
// updates from the client, even if no content might have changed, without
// forcing a full commit synchronization.
@@ -316,6 +321,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
base::OnceCallback<void(const gfx::PresentationFeedback&)>;
void RequestPresentationTimeForNextFrame(PresentationTimeCallback callback);
+ // Registers a callback that is run when any ongoing scroll-animation ends. If
+ // there are no ongoing animations, then the callback is run immediately.
+ void RequestScrollAnimationEndNotification(base::OnceClosure callback);
+
// Layer tree accessors and modifiers ------------------------
// Sets or gets the root of the Layer tree. Children of the root Layer are
@@ -352,6 +361,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
Layer* InnerViewportScrollLayerForTesting() const;
Layer* OuterViewportScrollLayerForTesting() const;
+ ElementId OuterViewportScrollElementId() const;
+
// Sets or gets the position of touch handles for a text selection. These are
// submitted to the display compositor along with the Layer tree's contents
// allowing it to present the selection handles. This is done because the
@@ -388,8 +399,6 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
void SetViewportVisibleRect(const gfx::Rect& visible_rect);
- gfx::Rect viewport_visible_rect() const { return viewport_visible_rect_; }
-
gfx::Rect device_viewport_rect() const { return device_viewport_rect_; }
void SetBrowserControlsParams(const BrowserControlsParams& params);
@@ -457,6 +466,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
return raster_color_space_;
}
+ bool HasCompositorDrivenScrollAnimationForTesting() const {
+ return scroll_animation_.in_progress;
+ }
+
// This layer tree may be embedded in a hierarchy that has page scale
// factor controlled at the top level. We represent that scale here as
// 'external_page_scale_factor', a value that affects raster scale in the
@@ -503,7 +516,6 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
// These are internal methods, called from the Layer itself when changing a
// property or completing a PushPropertiesTo.
void AddLayerShouldPushProperties(Layer* layer);
- void RemoveLayerShouldPushProperties(Layer* layer);
void ClearLayersThatShouldPushProperties();
// The current set of all Layers attached to the LayerTreeHost's tree that
// have been marked as needing PushPropertiesTo in the next commit.
@@ -593,6 +605,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time,
ActiveFrameSequenceTrackers trackers);
void NotifyThroughputTrackerResults(CustomTrackerResults results);
+ void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent);
LayerTreeHostClient* client() { return client_; }
LayerTreeHostSchedulingClient* scheduling_client() {
@@ -693,6 +709,17 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
impl_commit_start_time_ = commit_start_time;
}
+ void SetDelegatedInkMetadata(
+ std::unique_ptr<viz::DelegatedInkMetadata> metadata) {
+ delegated_ink_metadata_ = std::move(metadata);
+ }
+ viz::DelegatedInkMetadata* DelegatedInkMetadataForTesting() {
+ return delegated_ink_metadata_.get();
+ }
+
+ void DidObserveFirstScrollDelay(base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp);
+
protected:
LayerTreeHost(InitParams params, CompositorMode mode);
@@ -886,6 +913,17 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
// added here.
std::vector<PresentationTimeCallback> pending_presentation_time_callbacks_;
+ struct ScrollAnimationState {
+ ScrollAnimationState();
+ ~ScrollAnimationState();
+
+ // Tracks whether there is an ongoing compositor-driven scroll animation.
+ bool in_progress = false;
+
+ // Callback to run when the scroll-animation ends.
+ base::OnceClosure end_notification;
+ } scroll_animation_;
+
// Latency information for work done in ProxyMain::BeginMainFrame. The
// unique_ptr is allocated in RequestMainFrameUpdate, and passed to Blink's
// LocalFrameView that fills in the fields. This object adds the timing for
@@ -899,6 +937,12 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
EventsMetricsManager events_metrics_manager_;
+ // Metadata required for drawing a delegated ink trail onto the end of a
+ // stroke. std::unique_ptr was specifically chosen so that it would be cleared
+ // as it is forwarded along the pipeline to avoid old information incorrectly
+ // sticking around and potentially being reused.
+ std::unique_ptr<viz::DelegatedInkMetadata> delegated_ink_metadata_;
+
// Used to vend weak pointers to LayerTreeHost to ScopedDeferMainFrameUpdate
// objects.
base::WeakPtrFactory<LayerTreeHost> defer_main_frame_update_weak_ptr_factory_{
diff --git a/chromium/cc/trees/layer_tree_host_client.h b/chromium/cc/trees/layer_tree_host_client.h
index ae48bd94a18..b4b8c607969 100644
--- a/chromium/cc/trees/layer_tree_host_client.h
+++ b/chromium/cc/trees/layer_tree_host_client.h
@@ -11,6 +11,7 @@
#include "base/time/time.h"
#include "cc/input/browser_controls_state.h"
#include "cc/metrics/frame_sequence_tracker_collection.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
#include "ui/gfx/geometry/scroll_offset.h"
#include "ui/gfx/geometry/vector2d_f.h"
@@ -101,6 +102,9 @@ class LayerTreeHostClient {
virtual void WillUpdateLayers() = 0;
virtual void DidUpdateLayers() = 0;
+ virtual void DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) = 0;
// Notification that the proxy started or stopped deferring main frame updates
virtual void OnDeferMainFrameUpdatesChanged(bool) = 0;
@@ -169,6 +173,12 @@ class LayerTreeHostClient {
// RecordEndOfFrameMetrics.
virtual std::unique_ptr<BeginMainFrameMetrics> GetBeginMainFrameMetrics() = 0;
virtual void NotifyThroughputTrackerResults(CustomTrackerResults results) = 0;
+ // LayerTreeHost calls this when there is new throughput data. The client send
+ // the data to the browser process who will aggregate them and report to UKM.
+ virtual void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) = 0;
protected:
virtual ~LayerTreeHostClient() = default;
diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc
index 4fcc3210311..0fbcdf35620 100644
--- a/chromium/cc/trees/layer_tree_host_impl.cc
+++ b/chromium/cc/trees/layer_tree_host_impl.cc
@@ -20,6 +20,7 @@
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/location.h"
+#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/metrics/histogram.h"
@@ -30,6 +31,7 @@
#include "base/trace_event/traced_value.h"
#include "build/build_config.h"
#include "cc/base/devtools_instrumentation.h"
+#include "cc/base/features.h"
#include "cc/base/histograms.h"
#include "cc/base/math_util.h"
#include "cc/base/switches.h"
@@ -40,6 +42,7 @@
#include "cc/input/page_scale_animation.h"
#include "cc/input/scroll_elasticity_helper.h"
#include "cc/input/scroll_state.h"
+#include "cc/input/scroll_utils.h"
#include "cc/input/scrollbar.h"
#include "cc/input/scrollbar_animation_controller.h"
#include "cc/input/scroller_size_metrics.h"
@@ -79,7 +82,6 @@
#include "cc/trees/debug_rect_history.h"
#include "cc/trees/draw_property_utils.h"
#include "cc/trees/effect_node.h"
-#include "cc/trees/frame_rate_counter.h"
#include "cc/trees/image_animation_controller.h"
#include "cc/trees/latency_info_swap_promise_monitor.h"
#include "cc/trees/layer_tree_frame_sink.h"
@@ -125,6 +127,7 @@
#include "ui/gfx/geometry/scroll_offset.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
+#include "ui/gfx/geometry/vector2d_f.h"
#include "ui/gfx/skia_util.h"
namespace cc {
@@ -248,6 +251,41 @@ void PopulateMetadataContentColorUsage(
}
}
+// Registers callbacks, as needed, to track First Scroll Latency.
+void ApplyFirstScrollTracking(const ui::LatencyInfo* latency,
+ uint32_t frame_token,
+ LayerTreeHostImpl* impl) {
+ base::TimeTicks creation_timestamp;
+ // If |latency| isn't tracking a scroll, we don't need to do extra
+ // first-scroll tracking.
+ auto scroll_update_started =
+ ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT;
+ auto scroll_update_continued =
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT;
+ if (!latency->FindLatency(scroll_update_started, &creation_timestamp) &&
+ !latency->FindLatency(scroll_update_continued, &creation_timestamp)) {
+ return;
+ }
+
+ // Construct a callback that, given presentation feedback, will report the
+ // time span between the scroll input-event creation and the
+ // presentation timestamp.
+ LayerTreeHost::PresentationTimeCallback presentation_callback =
+ base::BindOnce(
+ [](base::TimeTicks event_creation,
+ LayerTreeHostImpl* layer_tree_host_impl,
+ const gfx::PresentationFeedback& feedback) {
+ layer_tree_host_impl->DidObserveScrollDelay(
+ feedback.timestamp - event_creation, event_creation);
+ },
+ creation_timestamp, impl);
+
+ // Tell the |LayerTreeHostImpl| to run our callback with the presentation
+ // feedback corresponding to the given |frame_token|.
+ impl->RegisterCompositorPresentationTimeCallback(
+ frame_token, std::move(presentation_callback));
+}
+
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class SourceIdConsistency : int {
@@ -333,8 +371,6 @@ LayerTreeHostImpl::LayerTreeHostImpl(
? std::numeric_limits<size_t>::max()
: settings.scheduled_raster_task_limit,
settings.ToTileManagerSettings()),
- fps_counter_(
- FrameRateCounter::Create(task_runner_provider_->HasImplThread())),
memory_history_(MemoryHistory::Create()),
debug_rect_history_(DebugRectHistory::Create()),
mutator_host_(std::move(mutator_host)),
@@ -379,12 +415,14 @@ LayerTreeHostImpl::LayerTreeHostImpl(
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableLayerTreeHostMemoryPressure)) {
- memory_pressure_listener_.reset(
- new base::MemoryPressureListener(base::BindRepeating(
- &LayerTreeHostImpl::OnMemoryPressure, base::Unretained(this))));
+ memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
+ FROM_HERE, base::BindRepeating(&LayerTreeHostImpl::OnMemoryPressure,
+ base::Unretained(this)));
}
SetDebugState(settings.initial_debug_state);
+ compositor_frame_reporting_controller_->SetDroppedFrameCounter(
+ &dropped_frame_counter_);
}
LayerTreeHostImpl::~LayerTreeHostImpl() {
@@ -428,7 +466,6 @@ LayerTreeHostImpl::~LayerTreeHostImpl() {
// Clear the UKM Manager so that we do not try to report when the
// UKM System has shut down.
compositor_frame_reporting_controller_->SetUkmManager(nullptr);
- frame_trackers_.SetUkmManager(nullptr);
}
void LayerTreeHostImpl::WillSendBeginMainFrame() {
@@ -1034,6 +1071,7 @@ bool LayerTreeHostImpl::ScrollLayerTo(ElementId element_id,
}
bool LayerTreeHostImpl::ScrollingShouldSwitchtoMainThread() {
+ DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification));
ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
ScrollNode* scroll_node = scroll_tree.CurrentlyScrollingNode();
@@ -1550,8 +1588,9 @@ DrawResult LayerTreeHostImpl::PrepareToDraw(FrameData* frame) {
base::saturated_cast<int>(active_tree_->picture_layers().size()), 1,
400, 20);
- // TODO(yigu): Maybe we should use the same check above. Need to figure out
- // why exactly we skip 0.
+ // TODO(pdr): Instead of skipping empty picture layers, maybe we should
+ // accumulate layer->GetRasterSource()->GetMemoryUsage() above and skip
+ // recording when the accumulated memory usage is 0.
if (!active_tree()->picture_layers().empty()) {
UMA_HISTOGRAM_CUSTOM_COUNTS(
base::StringPrintf("Compositing.%s.GPUMemoryForTilingsInKb",
@@ -1719,7 +1758,7 @@ void LayerTreeHostImpl::ResetTreesForTesting() {
}
size_t LayerTreeHostImpl::SourceAnimationFrameNumberForTesting() const {
- return fps_counter_->current_frame_number();
+ return *next_frame_token_;
}
void LayerTreeHostImpl::UpdateTileManagerMemoryPolicy(
@@ -2192,6 +2231,15 @@ viz::CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() {
metadata.display_transform_hint = active_tree_->display_transform_hint();
+ if (std::unique_ptr<viz::DelegatedInkMetadata> delegated_ink_metadata =
+ active_tree_->take_delegated_ink_metadata()) {
+ TRACE_EVENT_INSTANT1(
+ "cc", "Delegated Ink Metadata set on compositor frame metadata",
+ TRACE_EVENT_SCOPE_THREAD, "point",
+ delegated_ink_metadata->point().ToString());
+ metadata.delegated_ink_metadata = std::move(delegated_ink_metadata);
+ }
+
return metadata;
}
@@ -2324,6 +2372,19 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) {
std::move(compositor_frame),
/*hit_test_data_changed=*/false, debug_state_.show_hit_test_borders);
+ // This is expected to be true roughly every 5 seconds.
+ if (frame_trackers_.HasThroughputData()) {
+ ukm::SourceId source_id = ukm_manager_->source_id();
+ // source_id can be invalid in tests.
+ if (source_id != ukm::kInvalidSourceId) {
+ int aggregated_percent = frame_trackers_.TakeLastAggregatedPercent();
+ int impl_percent = frame_trackers_.TakeLastImplPercent();
+ base::Optional<int> main_percent = frame_trackers_.TakeLastMainPercent();
+ client_->SubmitThroughputData(source_id, aggregated_percent, impl_percent,
+ main_percent);
+ }
+ }
+
#if DCHECK_IS_ON()
if (!doing_sync_draw_) {
// The throughput computation (in |FrameSequenceTracker|) depends on the
@@ -2387,8 +2448,6 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame(
TRACE_ID_GLOBAL(CurrentBeginFrameArgs().trace_id),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"step", "GenerateCompositorFrame");
- base::TimeTicks frame_time = CurrentBeginFrameArgs().frame_time;
- fps_counter_->SaveTimeStamp(frame_time);
rendering_stats_instrumentation_->IncrementFrameCount(1);
memory_history_->SaveEntry(tile_manager_.memory_stats_from_last_assign());
@@ -2457,6 +2516,8 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame(
if (render_frame_metadata_observer_) {
last_draw_render_frame_metadata_ = MakeRenderFrameMetadata(frame);
+ last_draw_render_frame_metadata_->has_delegated_ink_metadata =
+ metadata.delegated_ink_metadata.get();
// We cache the value of any new vertical scroll direction so that we can
// accurately determine when the next change in vertical scroll direction
@@ -2477,6 +2538,8 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame(
base::TimeTicks draw_time = base::TimeTicks::Now();
ukm::SourceId exemplar = metadata.latency_info.front().ukm_source_id();
+ ApplyFirstScrollTracking(&metadata.latency_info.front(),
+ metadata.frame_token, this);
bool all_valid = true;
bool all_unique = true;
for (auto& latency : metadata.latency_info) {
@@ -3653,6 +3716,16 @@ void LayerTreeHostImpl::DidChangeBrowserControlsPosition() {
SetFullViewportDamage();
}
+void LayerTreeHostImpl::DidObserveScrollDelay(
+ base::TimeDelta scroll_delay,
+ base::TimeTicks scroll_timestamp) {
+ // Record First Scroll Delay.
+ if (!has_observed_first_scroll_delay_) {
+ client_->DidObserveFirstScrollDelay(scroll_delay, scroll_timestamp);
+ has_observed_first_scroll_delay_ = true;
+ }
+}
+
float LayerTreeHostImpl::TopControlsHeight() const {
return active_tree_->top_controls_height();
}
@@ -3690,20 +3763,43 @@ void LayerTreeHostImpl::BindToClient(InputHandlerClient* client) {
input_handler_client_ = client;
}
-gfx::Vector2dF LayerTreeHostImpl::ResolveScrollPercentageToPixels(
+gfx::Vector2dF LayerTreeHostImpl::ResolveScrollGranularityToPixels(
const ScrollNode& scroll_node,
- const gfx::Vector2dF& scroll_delta) {
- gfx::Vector2dF scroll_delta_in_pixels;
- scroll_delta_in_pixels.set_x(scroll_delta.x() *
- scroll_node.container_bounds.width());
- scroll_delta_in_pixels.set_y(scroll_delta.y() *
- scroll_node.container_bounds.height());
- return scroll_delta_in_pixels;
+ const gfx::Vector2dF& scroll_delta,
+ ui::ScrollGranularity granularity) {
+ gfx::Vector2dF pixel_delta = scroll_delta;
+
+ if (granularity == ui::ScrollGranularity::kScrollByPage) {
+ // Page should use a percentage of the scroller so change the parameters
+ // and let the percentage case below resolve it.
+ granularity = ui::ScrollGranularity::kScrollByPercentage;
+ pixel_delta.Scale(kMinFractionToStepWhenPaging);
+ }
+
+ if (granularity == ui::ScrollGranularity::kScrollByPercentage) {
+ gfx::SizeF scroller_size = gfx::SizeF(scroll_node.container_bounds);
+
+ gfx::SizeF viewport_size =
+ InnerViewportScrollNode()
+ ? gfx::SizeF(InnerViewportScrollNode()->container_bounds)
+ : gfx::SizeF(active_tree()->GetDeviceViewport().size());
+
+ // Convert from rootframe coordinates to screen coordinates (physical
+ // pixels).
+ scroller_size.Scale(active_tree()->page_scale_factor_for_scroll());
+
+ pixel_delta = ScrollUtils::ResolveScrollPercentageToPixels(
+ scroll_delta, scroller_size, viewport_size);
+ }
+
+ return pixel_delta;
}
InputHandler::ScrollStatus LayerTreeHostImpl::TryScroll(
const ScrollTree& scroll_tree,
ScrollNode* scroll_node) const {
+ DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification));
+
InputHandler::ScrollStatus scroll_status;
scroll_status.main_thread_scrolling_reasons =
MainThreadScrollingReason::kNotScrollingOnMain;
@@ -3736,9 +3832,9 @@ InputHandler::ScrollStatus LayerTreeHostImpl::TryScroll(
}
// If an associated scrolling layer is not found, the scroll node must not
- // support impl-scrolling. The root, secondary root, and inner viewports are
- // all exceptions to this and may not have a layer because it is not required
- // for hit testing.
+ // support impl-scrolling. The root, secondary root, and inner viewports
+ // are all exceptions to this and may not have a layer because it is not
+ // required for hit testing.
if (scroll_node->id != ScrollTree::kRootNodeId &&
scroll_node->id != ScrollTree::kSecondaryRootNodeId &&
!scroll_node->scrolls_inner_viewport &&
@@ -3804,6 +3900,7 @@ ScrollNode* LayerTreeHostImpl::FindScrollNodeForCompositedScrolling(
LayerImpl* layer_impl,
bool* scroll_on_main_thread,
uint32_t* main_thread_scrolling_reasons) const {
+ DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification));
DCHECK(scroll_on_main_thread);
DCHECK(main_thread_scrolling_reasons);
*main_thread_scrolling_reasons =
@@ -3894,7 +3991,13 @@ InputHandler::ScrollStatus LayerTreeHostImpl::RootScrollBegin(
scroll_state->data()->set_current_native_scrolling_element(
OuterViewportScrollNode()->element_id);
- return ScrollBegin(scroll_state, type);
+ InputHandler::ScrollStatus scroll_status = ScrollBegin(scroll_state, type);
+
+ // Since we provided an ElementId, there should never be a need to perform a
+ // hit test.
+ DCHECK(!scroll_status.needs_main_thread_hit_test);
+
+ return scroll_status;
}
InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
@@ -3937,59 +4040,145 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
// this should only happen in ScrollEnd. We should DCHECK here that the state
// is cleared instead. https://crbug.com/1016229
ClearCurrentlyScrollingNode();
+ auto& scroll_tree = active_tree_->property_trees()->scroll_tree;
- if (auto specified_element_id =
- scroll_state->data()->current_native_scrolling_element()) {
+ ElementId target_element_id = scroll_state->target_element_id();
+
+ if (target_element_id && !scroll_state->is_main_thread_hit_tested()) {
+ TRACE_EVENT_INSTANT0("cc", "Latched scroll node provided",
+ TRACE_EVENT_SCOPE_THREAD);
// If the caller passed in an element_id we can skip all the hit-testing
// bits and provide a node straight-away.
- auto& scroll_tree = active_tree_->property_trees()->scroll_tree;
- scrolling_node = scroll_tree.FindNodeFromElementId(specified_element_id);
-
- // We still need to confirm the targeted node exists and can scroll on the
- // compositor.
- if (scrolling_node) {
- scroll_status = TryScroll(active_tree_->property_trees()->scroll_tree,
- scrolling_node);
- if (IsMainThreadScrolling(scroll_status, scrolling_node))
- scroll_on_main_thread = true;
+ scrolling_node = scroll_tree.FindNodeFromElementId(target_element_id);
+
+ // In unified scrolling, if we found a node we get to scroll it.
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ // We still need to confirm the targeted node exists and can scroll on
+ // the compositor.
+ if (scrolling_node) {
+ scroll_status = TryScroll(active_tree_->property_trees()->scroll_tree,
+ scrolling_node);
+ if (IsMainThreadScrolling(scroll_status, scrolling_node))
+ scroll_on_main_thread = true;
+ }
}
} else {
- gfx::Point viewport_point(scroll_state->position_x(),
- scroll_state->position_y());
+ ScrollNode* starting_node = nullptr;
+ if (target_element_id) {
+ TRACE_EVENT_INSTANT0("cc", "Unlatched scroll node provided",
+ TRACE_EVENT_SCOPE_THREAD);
+ // We had an element id but we should still perform the walk up the
+ // scroll tree from the targeted node to latch to a scroller that can
+ // scroll in the given direction. This mode is only used when scroll
+ // unification is enabled and the targeted scroller comes back from a
+ // main thread hit test.
+ DCHECK(scroll_state->data()->is_main_thread_hit_tested);
+ DCHECK(base::FeatureList::IsEnabled(features::kScrollUnification));
+ starting_node = scroll_tree.FindNodeFromElementId(target_element_id);
+
+ if (!starting_node) {
+ // The main thread sent us an element_id that the compositor doesn't
+ // have a scroll node for. This can happen in some racy conditions, a
+ // freshly created scroller hasn't yet been committed or a
+ // scroller-destroying commit beats the hit test back to the compositor
+ // thread. However, these cases shouldn't be user perceptible.
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNoScrollingLayer;
+ scroll_status.thread = SCROLL_IGNORED;
+ return scroll_status;
+ }
+ } else {
+ TRACE_EVENT_INSTANT0("cc", "Hit Testing for ScrollNode",
+ TRACE_EVENT_SCOPE_THREAD);
+ gfx::Point viewport_point(scroll_state->position_x(),
+ scroll_state->position_y());
+ gfx::PointF device_viewport_point = gfx::ScalePoint(
+ gfx::PointF(viewport_point), active_tree_->device_scale_factor());
+
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ if (scroll_state->data()->is_main_thread_hit_tested) {
+ // The client should have discarded the scroll when the hit test came
+ // back with an invalid element id. If we somehow get here, we should
+ // drop the scroll as continuing could cause us to infinitely bounce
+ // back and forth between here and hit testing on the main thread.
+ NOTREACHED();
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNoScrollingLayer;
+ scroll_status.thread = SCROLL_IGNORED;
+ return scroll_status;
+ }
- gfx::PointF device_viewport_point = gfx::ScalePoint(
- gfx::PointF(viewport_point), active_tree_->device_scale_factor());
- LayerImpl* layer_impl =
- active_tree_->FindLayerThatIsHitByPoint(device_viewport_point);
+ // Touch dragging the scrollbar requires falling back to main-thread
+ // scrolling.
+ // TODO(bokan): This could be trivially handled in the compositor by
+ // the new ScrollbarController and should be removed.
+ {
+ LayerImpl* first_scrolling_layer_or_scrollbar =
+ active_tree_->FindFirstScrollingLayerOrScrollbarThatIsHitByPoint(
+ device_viewport_point);
+ if (IsTouchDraggingScrollbar(first_scrolling_layer_or_scrollbar,
+ type)) {
+ TRACE_EVENT_INSTANT0("cc", "Scrollbar Scrolling",
+ TRACE_EVENT_SCOPE_THREAD);
+ scroll_status.thread = SCROLL_ON_MAIN_THREAD;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kScrollbarScrolling;
+ return scroll_status;
+ }
+ }
- if (layer_impl) {
- LayerImpl* first_scrolling_layer_or_scrollbar =
- active_tree_->FindFirstScrollingLayerOrScrollbarThatIsHitByPoint(
- device_viewport_point);
+ ScrollHitTestResult scroll_hit_test =
+ HitTestScrollNode(device_viewport_point);
+
+ if (!scroll_hit_test.hit_test_successful) {
+ // This result tells the client that the compositor doesn't have
+ // enough information to target this scroll. The client should
+ // perform a hit test in Blink and call this method again, with the
+ // ElementId of the hit-tested scroll node.
+ TRACE_EVENT_INSTANT0("cc", "Request Main Thread Hit Test",
+ TRACE_EVENT_SCOPE_THREAD);
+ scroll_status.thread = SCROLL_ON_IMPL_THREAD;
+ scroll_status.needs_main_thread_hit_test = true;
+ return scroll_status;
+ }
- // Touch dragging the scrollbar requires falling back to main-thread
- // scrolling.
- if (IsTouchDraggingScrollbar(first_scrolling_layer_or_scrollbar, type)) {
- TRACE_EVENT_INSTANT0("cc", "Scrollbar Scrolling",
- TRACE_EVENT_SCOPE_THREAD);
- scroll_status.thread = SCROLL_ON_MAIN_THREAD;
- scroll_status.main_thread_scrolling_reasons =
- MainThreadScrollingReason::kScrollbarScrolling;
- return scroll_status;
- } else if (!IsInitialScrollHitTestReliable(
- layer_impl, first_scrolling_layer_or_scrollbar)) {
- TRACE_EVENT_INSTANT0("cc", "Failed Hit Test", TRACE_EVENT_SCOPE_THREAD);
- scroll_status.thread = SCROLL_UNKNOWN;
- scroll_status.main_thread_scrolling_reasons =
- MainThreadScrollingReason::kFailedHitTest;
- return scroll_status;
+ starting_node = scroll_hit_test.scroll_node;
+ } else {
+ LayerImpl* layer_impl =
+ active_tree_->FindLayerThatIsHitByPoint(device_viewport_point);
+
+ if (layer_impl) {
+ LayerImpl* first_scrolling_layer_or_scrollbar =
+ active_tree_->FindFirstScrollingLayerOrScrollbarThatIsHitByPoint(
+ device_viewport_point);
+
+ // Touch dragging the scrollbar requires falling back to main-thread
+ // scrolling.
+ if (IsTouchDraggingScrollbar(first_scrolling_layer_or_scrollbar,
+ type)) {
+ TRACE_EVENT_INSTANT0("cc", "Scrollbar Scrolling",
+ TRACE_EVENT_SCOPE_THREAD);
+ scroll_status.thread = SCROLL_ON_MAIN_THREAD;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kScrollbarScrolling;
+ return scroll_status;
+ } else if (!IsInitialScrollHitTestReliable(
+ layer_impl, first_scrolling_layer_or_scrollbar)) {
+ TRACE_EVENT_INSTANT0("cc", "Failed Hit Test",
+ TRACE_EVENT_SCOPE_THREAD);
+ scroll_status.thread = SCROLL_UNKNOWN;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kFailedHitTest;
+ return scroll_status;
+ }
+ }
+
+ starting_node = FindScrollNodeForCompositedScrolling(
+ device_viewport_point, layer_impl, &scroll_on_main_thread,
+ &scroll_status.main_thread_scrolling_reasons);
}
}
- ScrollNode* starting_node = FindScrollNodeForCompositedScrolling(
- device_viewport_point, layer_impl, &scroll_on_main_thread,
- &scroll_status.main_thread_scrolling_reasons);
-
// The above finds the ScrollNode that's hit by the given point but we
// still need to walk up the scroll tree looking for the first node that
// can consume delta from the scroll state.
@@ -3997,6 +4186,10 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
}
if (scroll_on_main_thread) {
+ // Under scroll unification we can request a main thread hit test, but we
+ // should never send scrolls to the main thread.
+ DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification));
+
RecordCompositorSlowScrollMetric(type, MAIN_THREAD);
scroll_status.thread = SCROLL_ON_MAIN_THREAD;
return scroll_status;
@@ -4004,11 +4197,20 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
scroll_status.main_thread_scrolling_reasons =
MainThreadScrollingReason::kNoScrollingLayer;
if (settings_.is_layer_tree_for_subframe) {
+ // OOPIFs never have a viewport scroll node so if we can't scroll
+ // we need to be bubble up to the parent frame. This happens by
+ // returning SCROLL_UNKNOWN.
TRACE_EVENT_INSTANT0("cc", "Ignored - No ScrollNode (OOPIF)",
TRACE_EVENT_SCOPE_THREAD);
scroll_status.thread = SCROLL_UNKNOWN;
} else {
- TRACE_EVENT_INSTANT0("cc", "Ignroed - No ScrollNode",
+ // If we didn't hit a layer above we'd usually fallback to the
+ // viewport scroll node. However, there may not be one if a scroll
+ // is received before the root layer has been attached. Chrome now
+ // drops input until the first commit is received so this probably
+ // can't happen in a typical browser session but there may still be
+ // configurations where input is allowed prior to a commit.
+ TRACE_EVENT_INSTANT0("cc", "Ignored - No ScrollNode",
TRACE_EVENT_SCOPE_THREAD);
scroll_status.thread = SCROLL_IGNORED;
}
@@ -4034,13 +4236,22 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
return scroll_status;
}
-ScrollNode* LayerTreeHostImpl::HitTestScrollNode(
+LayerTreeHostImpl::ScrollHitTestResult LayerTreeHostImpl::HitTestScrollNode(
const gfx::PointF& device_viewport_point) const {
+ ScrollHitTestResult result;
+ result.scroll_node = nullptr;
+ result.hit_test_successful = false;
+
LayerImpl* layer_impl =
active_tree_->FindLayerThatIsHitByPoint(device_viewport_point);
- if (!layer_impl)
- return nullptr;
+ if (!layer_impl) {
+ result.hit_test_successful = true;
+ if (InnerViewportScrollNode())
+ result.scroll_node = GetNodeToScroll(InnerViewportScrollNode());
+
+ return result;
+ }
// There are some cases where the hit layer may not be correct (e.g. layer
// squashing). If we detect this case, we can't target a scroll node here.
@@ -4052,10 +4263,19 @@ ScrollNode* LayerTreeHostImpl::HitTestScrollNode(
if (!IsInitialScrollHitTestReliable(layer_impl,
first_scrolling_layer_or_scrollbar)) {
TRACE_EVENT_INSTANT0("cc", "Failed Hit Test", TRACE_EVENT_SCOPE_THREAD);
- return nullptr;
+ return result;
}
}
+ // If we hit a non-fast scrollable region, that means there's some reason we
+ // can't scroll in this region. Primarily, because there's another scroller
+ // there that isn't composited and we don't know about so we'll return
+ // nullptr in that case.
+ if (active_tree_->PointHitsNonFastScrollableRegion(device_viewport_point,
+ *layer_impl)) {
+ return result;
+ }
+
// If we hit a scrollbar layer, get the ScrollNode from its associated
// scrolling layer, rather than directly from the scrollbar layer. The latter
// would return the parent scroller's ScrollNode.
@@ -4068,7 +4288,9 @@ ScrollNode* LayerTreeHostImpl::HitTestScrollNode(
ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
ScrollNode* scroll_node = scroll_tree.Node(layer_impl->scroll_tree_index());
- return GetNodeToScroll(scroll_node);
+ result.scroll_node = GetNodeToScroll(scroll_node);
+ result.hit_test_successful = true;
+ return result;
}
// Requires falling back to main thread scrolling when it hit tests in scrollbar
@@ -4602,10 +4824,9 @@ bool LayerTreeHostImpl::CanConsumeDelta(const ScrollState& scroll_state,
return false;
}
delta_to_scroll = local_scroll_delta;
- } else if (scroll_state.delta_granularity() ==
- ui::ScrollGranularity::kScrollByPercentage) {
- delta_to_scroll =
- ResolveScrollPercentageToPixels(scroll_node, delta_to_scroll);
+ } else {
+ delta_to_scroll = ResolveScrollGranularityToPixels(
+ scroll_node, delta_to_scroll, scroll_state.delta_granularity());
}
if (ComputeScrollDelta(scroll_node, delta_to_scroll) != gfx::Vector2dF())
@@ -4656,21 +4877,27 @@ InputHandlerScrollResult LayerTreeHostImpl::ScrollUpdate(
// The current_native_scrolling_element should only be set for ScrollBegin.
DCHECK(!scroll_state->data()->current_native_scrolling_element());
+ TRACE_EVENT2("cc", "LayerTreeHostImpl::ScrollUpdate", "dx",
+ scroll_state->delta_x(), "dy", scroll_state->delta_y());
if (!CurrentlyScrollingNode())
return InputHandlerScrollResult();
last_scroll_update_state_ = *scroll_state;
- bool is_delta_percent_units = scroll_state->delta_granularity() ==
- ui::ScrollGranularity::kScrollByPercentage;
- if (is_delta_percent_units) {
- gfx::Vector2dF resolvedScrollDelta = ResolveScrollPercentageToPixels(
- *CurrentlyScrollingNode(),
- gfx::Vector2dF(scroll_state->delta_x(), scroll_state->delta_y()));
-
- scroll_state->data()->delta_x = resolvedScrollDelta.x();
- scroll_state->data()->delta_y = resolvedScrollDelta.y();
+ gfx::Vector2dF resolvedScrollDelta = ResolveScrollGranularityToPixels(
+ *CurrentlyScrollingNode(),
+ gfx::Vector2dF(scroll_state->delta_x(), scroll_state->delta_y()),
+ scroll_state->delta_granularity());
+
+ scroll_state->data()->delta_x = resolvedScrollDelta.x();
+ scroll_state->data()->delta_y = resolvedScrollDelta.y();
+ // The decision of whether or not we'll animate a scroll comes down to
+ // whether the granularity is specified in precise pixels or not. Thus we
+ // need to preserve a precise granularity if that's what was specified; all
+ // others are animated and so can be resolved to regular pixels.
+ if (scroll_state->delta_granularity() !=
+ ui::ScrollGranularity::kScrollByPrecisePixel) {
scroll_state->data()->delta_granularity =
ui::ScrollGranularity::kScrollByPixel;
}
@@ -4994,9 +5221,6 @@ void LayerTreeHostImpl::RecordScrollBegin(
ScrollBeginThreadState scroll_start_state) {
auto tracker_type = GetTrackerTypeForScroll(input_type);
DCHECK_NE(tracker_type, FrameSequenceTrackerType::kMaxType);
- auto* metrics = frame_trackers_.StartSequence(tracker_type);
- if (!metrics)
- return;
// The main-thread is the 'scrolling thread' if:
// (1) the scroll is driven by the main thread, or
@@ -5015,7 +5239,7 @@ void LayerTreeHostImpl::RecordScrollBegin(
scrolling_thread = FrameSequenceMetrics::ThreadType::kMain;
break;
}
- metrics->SetScrollingThread(scrolling_thread);
+ frame_trackers_.StartScrollSequence(tracker_type, scrolling_thread);
}
void LayerTreeHostImpl::RecordScrollEnd(ui::ScrollInputType input_type) {
@@ -5078,11 +5302,14 @@ InputHandlerPointerResult LayerTreeHostImpl::MouseMoveAt(
gfx::PointF device_viewport_point = gfx::ScalePoint(
gfx::PointF(viewport_point), active_tree_->device_scale_factor());
- ScrollNode* scroll_node = HitTestScrollNode(device_viewport_point);
+
+ ScrollHitTestResult hit_test = HitTestScrollNode(device_viewport_point);
+
+ ScrollNode* scroll_node = hit_test.scroll_node;
// The hit test can fail in some cases, e.g. we don't know if a region of a
// squashed layer has content or is empty.
- if (!scroll_node)
+ if (!hit_test.hit_test_successful || !scroll_node)
return result;
// Scrollbars for the viewport are registered with the outer viewport layer.
@@ -5242,6 +5469,9 @@ std::unique_ptr<ScrollAndScaleSet> LayerTreeHostImpl::ProcessScrollDeltas() {
scroll_info->overscroll_delta = overscroll_delta_for_main_thread_;
overscroll_delta_for_main_thread_ = gfx::Vector2dF();
+ scroll_info->ongoing_scroll_animation =
+ !!mutator_host_->ImplOnlyScrollAnimatingElement();
+
// Use the |last_latched_scroller_| rather than the |CurrentlyScrollingNode|
// since the latter may be cleared by a GSE before we've committed these
// values to the main thread.
@@ -5398,9 +5628,10 @@ void LayerTreeHostImpl::RegisterScrollbarAnimationController(
}
void LayerTreeHostImpl::DidUnregisterScrollbarLayer(
- ElementId scroll_element_id) {
+ ElementId scroll_element_id,
+ ScrollbarOrientation orientation) {
scrollbar_animation_controllers_.erase(scroll_element_id);
- scrollbar_controller_->DidUnregisterScrollbar(scroll_element_id);
+ scrollbar_controller_->DidUnregisterScrollbar(scroll_element_id, orientation);
}
ScrollbarAnimationController*
@@ -6230,7 +6461,7 @@ void LayerTreeHostImpl::InitializeUkm(
void LayerTreeHostImpl::SetActiveURL(const GURL& url, ukm::SourceId source_id) {
tile_manager_.set_active_url(url);
-
+ has_observed_first_scroll_delay_ = false;
// The active tree might still be from content for the previous page when the
// recorder is updated here, since new content will be pushed with the next
// main frame. But we should only get a few impl frames wrong here in that
diff --git a/chromium/cc/trees/layer_tree_host_impl.h b/chromium/cc/trees/layer_tree_host_impl.h
index f7ce4e673e6..47dcba6decb 100644
--- a/chromium/cc/trees/layer_tree_host_impl.h
+++ b/chromium/cc/trees/layer_tree_host_impl.h
@@ -29,6 +29,7 @@
#include "cc/input/scrollbar_animation_controller.h"
#include "cc/input/scrollbar_controller.h"
#include "cc/layers/layer_collections.h"
+#include "cc/metrics/dropped_frame_counter.h"
#include "cc/metrics/event_metrics.h"
#include "cc/metrics/events_metrics_manager.h"
#include "cc/metrics/frame_sequence_tracker_collection.h"
@@ -83,7 +84,6 @@ class BrowserControlsOffsetManager;
class CompositorFrameReportingController;
class DebugRectHistory;
class EvictionTilePriorityQueue;
-class FrameRateCounter;
class ImageAnimationController;
class LCDTextMetricsReporter;
class LayerImpl;
@@ -176,6 +176,17 @@ class LayerTreeHostImplClient {
virtual void NotifyThroughputTrackerResults(CustomTrackerResults results) = 0;
+ // Send the throughput data to the main thread's LayerTreeHostClient, which
+ // then send the data to the browser process and eventually report to UKM.
+ virtual void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) = 0;
+
+ virtual void DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) = 0;
+
protected:
virtual ~LayerTreeHostImplClient() = default;
};
@@ -333,6 +344,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
float CurrentTopControlsShownRatio() const override;
float CurrentBottomControlsShownRatio() const override;
void DidChangeBrowserControlsPosition() override;
+ void DidObserveScrollDelay(base::TimeDelta scroll_delay,
+ base::TimeTicks scroll_timestamp);
bool HaveRootScrollNode() const override;
void SetNeedsCommit() override;
@@ -469,7 +482,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
void RegisterScrollbarAnimationController(ElementId scroll_element_id,
float initial_opacity);
- void DidUnregisterScrollbarLayer(ElementId scroll_element_id);
+ void DidUnregisterScrollbarLayer(ElementId scroll_element_id,
+ ScrollbarOrientation orientation);
ScrollbarAnimationController* ScrollbarAnimationControllerForElementId(
ElementId scroll_element_id) const;
void FlashAllScrollbars(bool did_scroll);
@@ -610,7 +624,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
const ScrollNode* CurrentlyScrollingNode() const;
bool scroll_affects_scroll_handler() const {
- return scroll_affects_scroll_handler_;
+ return settings_.enable_synchronized_scrolling &&
+ scroll_affects_scroll_handler_;
}
void QueueSwapPromiseForMainThreadScrollUpdate(
std::unique_ptr<SwapPromise> swap_promise);
@@ -641,9 +656,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
const gfx::Transform& DrawTransform() const;
std::unique_ptr<ScrollAndScaleSet> ProcessScrollDeltas();
- FrameRateCounter* fps_counter() { return fps_counter_.get(); }
- base::Optional<int> current_universal_throughput() {
- return frame_trackers_.current_universal_throughput();
+ DroppedFrameCounter* dropped_frame_counter() {
+ return &dropped_frame_counter_;
}
MemoryHistory* memory_history() { return memory_history_.get(); }
DebugRectHistory* debug_rect_history() { return debug_rect_history_.get(); }
@@ -735,6 +749,13 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
gfx::Vector2dF ComputeScrollDelta(const ScrollNode& scroll_node,
const gfx::Vector2dF& delta);
+ // Resolves a delta in the given granularity for the |scroll_node| into
+ // physical pixels to scroll.
+ gfx::Vector2dF ResolveScrollGranularityToPixels(
+ const ScrollNode& scroll_node,
+ const gfx::Vector2dF& scroll_delta,
+ ui::ScrollGranularity granularity);
+
void ScheduleMicroBenchmark(std::unique_ptr<MicroBenchmarkImpl> benchmark);
viz::CompositorFrameMetadata MakeCompositorFrameMetadata();
@@ -785,15 +806,6 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
: task_runner_provider_->MainThreadTaskRunner();
}
- // Determines whether the given scroll node can scroll on the compositor
- // thread or if there are any reasons it must be scrolled on the main thread
- // or not at all. Note: in general, this is not sufficient to determine if a
- // scroll can occur on the compositor thread. If hit testing to a scroll
- // node, the caller must also check whether the hit point intersects a
- // non-fast-scrolling-region of any ancestor scrolling layers.
- InputHandler::ScrollStatus TryScroll(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(
@@ -894,14 +906,20 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
void CollectScrollDeltas(ScrollAndScaleSet* scroll_info);
void CollectScrollbarUpdates(ScrollAndScaleSet* scroll_info) const;
- gfx::Vector2dF ResolveScrollPercentageToPixels(
- const ScrollNode& scroll_node,
- const gfx::Vector2dF& resolved_pixels);
-
// Returns the ScrollNode we should use to scroll, accounting for viewport
// scroll chaining rules.
ScrollNode* GetNodeToScroll(ScrollNode* node) const;
+ // Determines whether the given scroll node can scroll on the compositor
+ // thread or if there are any reasons it must be scrolled on the main thread
+ // or not at all. Note: in general, this is not sufficient to determine if a
+ // scroll can occur on the compositor thread. If hit testing to a scroll
+ // node, the caller must also check whether the hit point intersects a
+ // non-fast-scrolling-region of any ancestor scrolling layers. Can be removed
+ // after scroll unification https://crbug.com/476553.
+ InputHandler::ScrollStatus TryScroll(const ScrollTree& scroll_tree,
+ ScrollNode* scroll_node) const;
+
// Transforms viewport start point and scroll delta to local start point and
// local delta, respectively. If the transformation of either the start or end
// point of a scroll is clipped, the function returns false.
@@ -957,13 +975,14 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
LayerImpl* first_scrolling_layer_or_drawn_scrollbar,
ui::ScrollInputType type);
- // Initial scroll hit testing can be unreliable in the presence of squashed
- // layers. In this case, we fall back to main thread scrolling. This function
- // compares |layer_impl| returned from a regular hit test to the layer
- // returned from a hit test performed only on scrollers and scrollbars. If the
- // closest scrolling ancestor of |layer_impl| is not the other layer, then the
- // layer_impl must be a squasing layer overtop of some other scroller and we
- // must rely on the main thread.
+ // |layer| is returned from a regular hit test, and
+ // |first_scrolling_layer_or_drawn_scrollbar| is returned from a hit test
+ // performed only on scrollers and scrollbars. Initial scroll hit testing can
+ // be unreliable if the latter is not the direct scroll ancestor of the
+ // former. In this case, we will fall back to main thread scrolling because
+ // the compositor thread doesn't know which layer to scroll. This happens when
+ // a layer covers a scroller that doesn't scroll the former, or a scroller is
+ // masked by a mask layer for mask image, clip-path, rounded border, etc.
//
// Note, position: fixed layers use the inner viewport as their ScrollNode
// (since they don't scroll with the outer viewport), however, scrolls from
@@ -1018,21 +1037,35 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
void ClearCurrentlyScrollingNode();
// Performs a hit test to determine the ScrollNode to use when scrolling at
- // |viewport_point|. Can return nullptr if the hit test fails; see the
- // comment in IsInitialScrollHitTestReliable
- ScrollNode* HitTestScrollNode(const gfx::PointF& device_viewport_point) const;
+ // |viewport_point|. If no layer is hit, this falls back to the inner
+ // viewport scroll node. Returns:
+ // - If |hit_test_sucessful| is false, hit testing has failed and the
+ // compositor cannot determine the correct scroll node (e.g. see comments in
+ // IsInitialScrollHitTestReliable). |scroll_node| is always nullptr in this
+ // case.
+ // - If |hit_test_successful| is true, returns the ScrollNode to use in
+ // |scroll_node|. This can be nullptr if no layer was hit and there are no
+ // viewport nodes (e.g. OOPIF, UI compositor).
+ struct ScrollHitTestResult {
+ ScrollNode* scroll_node;
+ bool hit_test_successful;
+ };
+ ScrollHitTestResult HitTestScrollNode(
+ const gfx::PointF& device_viewport_point) const;
// Similar to above but includes complicated logic to determine whether the
// ScrollNode is able to be scrolled on the compositor or requires main
// thread scrolling. If main thread scrolling is required
// |scroll_on_main_thread| is set to true and the reason is given in
// |main_thread_scrolling_reason| to on of the enum values in
- // main_thread_scrolling_reason.h.
+ // main_thread_scrolling_reason.h. Can be removed after scroll unification
+ // https://crbug.com/476553.
ScrollNode* FindScrollNodeForCompositedScrolling(
const gfx::PointF& device_viewport_point,
LayerImpl* layer_hit_by_point,
bool* scroll_on_main_thread,
uint32_t* main_thread_scrolling_reason) const;
+
void StartScrollbarFadeRecursive(LayerImpl* layer);
void SetManagedMemoryPolicy(const ManagedMemoryPolicy& policy);
@@ -1226,7 +1259,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
std::unique_ptr<PageScaleAnimation> page_scale_animation_;
- std::unique_ptr<FrameRateCounter> fps_counter_;
+ DroppedFrameCounter dropped_frame_counter_;
std::unique_ptr<MemoryHistory> memory_history_;
std::unique_ptr<DebugRectHistory> debug_rect_history_;
@@ -1400,6 +1433,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
std::unique_ptr<LCDTextMetricsReporter> lcd_text_metrics_reporter_;
FrameRateEstimator frame_rate_estimator_;
+ bool has_observed_first_scroll_delay_ = false;
// Must be the last member to ensure this is destroyed first in the
// destruction order and invalidates all weak pointers.
diff --git a/chromium/cc/trees/layer_tree_host_impl_unittest.cc b/chromium/cc/trees/layer_tree_host_impl_unittest.cc
index 3d1cfc76a5d..fd82323bf7e 100644
--- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc
@@ -27,10 +27,12 @@
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_id_provider.h"
#include "cc/animation/transform_operations.h"
+#include "cc/base/features.h"
#include "cc/base/histograms.h"
#include "cc/input/browser_controls_offset_manager.h"
#include "cc/input/main_thread_scrolling_reason.h"
#include "cc/input/page_scale_animation.h"
+#include "cc/input/scroll_utils.h"
#include "cc/layers/append_quads_data.h"
#include "cc/layers/heads_up_display_layer_impl.h"
#include "cc/layers/layer_impl.h"
@@ -262,7 +264,16 @@ class LayerTreeHostImplTest : public testing::Test,
void NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) override {}
void NotifyThroughputTrackerResults(CustomTrackerResults results) override {}
+ void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) override {}
+ void DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) override {
+ first_scroll_observed++;
+ }
void set_reduce_memory_result(bool reduce_memory_result) {
reduce_memory_result_ = reduce_memory_result;
}
@@ -496,15 +507,21 @@ class LayerTreeHostImplTest : public testing::Test,
layer_tree_impl->DidBecomeActive();
// The point hits squash1 layer and also scroll layer, because scroll layer
- // is not an ancestor of squash1 layer, we cannot scroll on impl thread.
+ // is not an ancestor of squash1 layer, we cannot scroll on impl thread
+ // (without at least a hit test on the main thread).
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point(230, 150), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- ASSERT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
- ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ ASSERT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+ ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest,
+ status.main_thread_scrolling_reasons);
+ }
// The point hits squash1 layer and also scrollbar layer.
status = host_impl_->ScrollBegin(
@@ -512,9 +529,14 @@ class LayerTreeHostImplTest : public testing::Test,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- ASSERT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
- ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ ASSERT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+ ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest,
+ status.main_thread_scrolling_reasons);
+ }
// The point hits squash2 layer and also scroll layer, because scroll layer
// is an ancestor of squash2 layer, we should scroll on impl.
@@ -670,8 +692,8 @@ class LayerTreeHostImplTest : public testing::Test,
host_impl_ = nullptr;
}
- void WhiteListedTouchActionTestHelper(float device_scale_factor,
- float page_scale_factor) {
+ void AllowedTouchActionTestHelper(float device_scale_factor,
+ float page_scale_factor) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
DrawFrame();
@@ -825,6 +847,26 @@ class LayerTreeHostImplTest : public testing::Test,
scoped_refptr<AnimationTimeline> timeline_;
std::unique_ptr<base::Thread> image_worker_;
int next_layer_id_ = 2;
+ int first_scroll_observed = 0;
+};
+
+// Allows parameterizing the above class on the scroll unification flag. We
+// can't parameterize the base class itself because it serves as a base to
+// other parameterized descendants. Can be removed when unification ships.
+class ScrollUnifiedLayerTreeHostImplTest
+ : public LayerTreeHostImplTest,
+ public testing::WithParamInterface<bool> {
+ public:
+ ScrollUnifiedLayerTreeHostImplTest() {
+ if (GetParam()) {
+ scoped_feature_list.InitAndEnableFeature(features::kScrollUnification);
+ } else {
+ scoped_feature_list.InitAndDisableFeature(features::kScrollUnification);
+ }
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list;
};
class CommitToPendingTreeLayerTreeHostImplTest : public LayerTreeHostImplTest {
@@ -901,7 +943,11 @@ class TestInputHandlerClient : public InputHandlerClient {
float max_page_scale_factor_;
};
-TEST_F(LayerTreeHostImplTest, LocalAndExternalPinchState) {
+INSTANTIATE_TEST_SUITE_P(All,
+ ScrollUnifiedLayerTreeHostImplTest,
+ testing::Bool());
+
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, LocalAndExternalPinchState) {
// PinchGestureBegin/End update pinch_gesture_active() properly.
EXPECT_FALSE(host_impl_->pinch_gesture_active());
host_impl_->PinchGestureBegin();
@@ -925,7 +971,7 @@ TEST_F(LayerTreeHostImplTest, LocalAndExternalPinchState) {
EXPECT_FALSE(host_impl_->pinch_gesture_active());
}
-TEST_F(LayerTreeHostImplTest, NotifyIfCanDrawChanged) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, NotifyIfCanDrawChanged) {
// Note: It is not possible to disable the renderer once it has been set,
// so we do not need to test that disabling the renderer notifies us
// that can_draw changed.
@@ -961,7 +1007,7 @@ TEST_F(LayerTreeHostImplTest, NotifyIfCanDrawChanged) {
on_can_draw_state_changed_called_ = false;
}
-TEST_F(LayerTreeHostImplTest, ResourcelessDrawWithEmptyViewport) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ResourcelessDrawWithEmptyViewport) {
CreateHostImpl(DefaultSettings(), FakeLayerTreeFrameSink::CreateSoftware());
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
@@ -979,7 +1025,7 @@ TEST_F(LayerTreeHostImplTest, ResourcelessDrawWithEmptyViewport) {
ASSERT_EQ(fake_layer_tree_frame_sink->num_sent_frames(), 1u);
}
-TEST_F(LayerTreeHostImplTest, ScrollDeltaNoLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDeltaNoLayers) {
host_impl_->active_tree()->SetRootLayerForTesting(nullptr);
std::unique_ptr<ScrollAndScaleSet> scroll_info =
@@ -987,7 +1033,7 @@ TEST_F(LayerTreeHostImplTest, ScrollDeltaNoLayers) {
ASSERT_EQ(scroll_info->scrolls.size(), 0u);
}
-TEST_F(LayerTreeHostImplTest, ScrollDeltaTreeButNoChanges) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDeltaTreeButNoChanges) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
{
LayerImpl* child1 = AddLayer();
@@ -1015,7 +1061,7 @@ TEST_F(LayerTreeHostImplTest, ScrollDeltaTreeButNoChanges) {
ExpectClearedScrollDeltasRecursive(root);
}
-TEST_F(LayerTreeHostImplTest, ScrollDeltaRepeatedScrolls) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDeltaRepeatedScrolls) {
gfx::ScrollOffset scroll_offset(20, 30);
gfx::ScrollOffset scroll_delta(11, -15);
@@ -1075,7 +1121,9 @@ TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
"Compositing.Renderer.GPUMemoryForTilingsInKb", 1);
}
-TEST_F(LayerTreeHostImplTest, ScrollBeforeRootLayerAttached) {
+// This test verifies that we drop a scroll (and don't crash) if a scroll is
+// received before the root layer has been attached. https://crbug.com/895817.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBeforeRootLayerAttached) {
InputHandler::ScrollStatus status =
host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 1),
ui::ScrollInputType::kWheel)
@@ -1100,7 +1148,7 @@ TEST_F(LayerTreeHostImplTest, ScrollBeforeRootLayerAttached) {
// to pre-commit input deferral which causes some input events to be dropped
// before the first commit in a renderer has occurred. See the flag
// kAllowPreCommitInput and how it's used.
-TEST_F(LayerTreeHostImplTest, ScrollUpdateAndEndNoOpWithoutBegin) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollUpdateAndEndNoOpWithoutBegin) {
SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(1000, 1000));
// Simulate receiving a gesture mid-stream so that the Begin wasn't ever
@@ -1141,7 +1189,7 @@ TEST_F(LayerTreeHostImplTest, ScrollUpdateAndEndNoOpWithoutBegin) {
// Test that specifying a scroller to ScrollBegin (i.e. avoid hit testing)
// returns the correct status if the scroller cannot be scrolled on the
// compositor thread.
-TEST_F(LayerTreeHostImplTest, TargetMainThreadScroller) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, TargetMainThreadScroller) {
SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(1000, 1000));
ScrollStateData scroll_state_data;
@@ -1160,22 +1208,32 @@ TEST_F(LayerTreeHostImplTest, TargetMainThreadScroller) {
}
// Now add a MainThreadScrollingReason. Trying to target the node this time
- // should result in a failed ScrollBegin with the appropriate return value.
+ // should result in a failed ScrollBegin with the appropriate return value,
+ // unless scroll unification is enabled - since all nodes can be scrolled
+ // from the compositor in that mode.
host_impl_->OuterViewportScrollNode()->main_thread_scrolling_reasons =
MainThreadScrollingReason::kThreadedScrollingDisabled;
{
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
scroll_state.get(), ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(
- host_impl_->OuterViewportScrollNode()->main_thread_scrolling_reasons,
- status.main_thread_scrolling_reasons);
- EXPECT_FALSE(host_impl_->CurrentlyScrollingNode());
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ EXPECT_EQ(
+ MainThreadScrollingReason::kThreadedScrollingDisabled,
+ host_impl_->CurrentlyScrollingNode()->main_thread_scrolling_reasons);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(
+ host_impl_->OuterViewportScrollNode()->main_thread_scrolling_reasons,
+ status.main_thread_scrolling_reasons);
+ EXPECT_FALSE(host_impl_->CurrentlyScrollingNode());
+ }
}
}
-TEST_F(LayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -1206,7 +1264,8 @@ TEST_F(LayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) {
// makes some sense since touchscreen/high-precision touchpad scrolling has a
// physical metaphor (movement sticks to finger) so smoothness should be
// prioritized.
-TEST_F(LayerTreeHostImplTest, ActivelyTouchScrollingOnlyAfterScrollMovement) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ActivelyTouchScrollingOnlyAfterScrollMovement) {
SetupViewportLayersOuterScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -1287,7 +1346,7 @@ TEST_F(LayerTreeHostImplTest, ActivelyTouchScrollingOnlyAfterScrollMovement) {
}
}
-TEST_F(LayerTreeHostImplTest, ScrollWithoutRootLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutRootLayer) {
// We should not crash when trying to scroll an empty layer tree.
InputHandler::ScrollStatus status =
host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
@@ -1299,7 +1358,7 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutRootLayer) {
status.main_thread_scrolling_reasons);
}
-TEST_F(LayerTreeHostImplTest, ScrollWithoutRenderer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutRenderer) {
auto gl_owned = std::make_unique<viz::TestGLES2Interface>();
gl_owned->LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
GL_INNOCENT_CONTEXT_RESET_ARB);
@@ -1323,7 +1382,7 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutRenderer) {
status.main_thread_scrolling_reasons);
}
-TEST_F(LayerTreeHostImplTest, ReplaceTreeWhileScrolling) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ReplaceTreeWhileScrolling) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -1356,7 +1415,8 @@ TEST_F(LayerTreeHostImplTest, ReplaceTreeWhileScrolling) {
scroll_delta));
}
-TEST_F(LayerTreeHostImplTest, ActivateTreeScrollingNodeDisappeared) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ActivateTreeScrollingNodeDisappeared) {
SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(1000, 1000));
auto status = host_impl_->ScrollBegin(
@@ -1385,7 +1445,7 @@ TEST_F(LayerTreeHostImplTest, ActivateTreeScrollingNodeDisappeared) {
EXPECT_FALSE(host_impl_->active_tree()->CurrentlyScrollingNode());
}
-TEST_F(LayerTreeHostImplTest, ScrollBlocksOnWheelEventHandlers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBlocksOnWheelEventHandlers) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll = InnerViewportScrollLayer();
scroll->SetWheelEventHandlerRegion(Region(gfx::Rect(20, 20)));
@@ -1415,7 +1475,7 @@ TEST_F(LayerTreeHostImplTest, ScrollBlocksOnWheelEventHandlers) {
host_impl_->ScrollEnd();
}
-TEST_F(LayerTreeHostImplTest, ScrollBlocksOnTouchEventHandlers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBlocksOnTouchEventHandlers) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -1467,9 +1527,9 @@ TEST_F(LayerTreeHostImplTest, ScrollBlocksOnTouchEventHandlers) {
EXPECT_EQ(TouchAction::kPanX, touch_action);
}
-TEST_F(LayerTreeHostImplTest, ShouldScrollOnMainThread) {
- SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
- host_impl_->InnerViewportScrollNode()->main_thread_scrolling_reasons =
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ShouldScrollOnMainThread) {
+ SetupViewportLayersOuterScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
+ host_impl_->OuterViewportScrollNode()->main_thread_scrolling_reasons =
MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects;
DrawFrame();
@@ -1478,30 +1538,48 @@ TEST_F(LayerTreeHostImplTest, ShouldScrollOnMainThread) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ EXPECT_EQ(
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
+ host_impl_->CurrentlyScrollingNode()->main_thread_scrolling_reasons);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
+ status.main_thread_scrolling_reasons);
+ }
status =
host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ EXPECT_EQ(
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
+ host_impl_->CurrentlyScrollingNode()->main_thread_scrolling_reasons);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
+ status.main_thread_scrolling_reasons);
+ }
}
-TEST_F(LayerTreeHostImplTest, ScrollWithOverlappingNonScrollableLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollWithOverlappingNonScrollableLayer) {
CreateAndTestNonScrollableLayers(false);
}
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
ScrollWithOverlappingTransparentNonScrollableLayer) {
CreateAndTestNonScrollableLayers(true);
}
-TEST_F(LayerTreeHostImplTest, ScrolledOverlappingDrawnScrollbarLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrolledOverlappingDrawnScrollbarLayer) {
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
gfx::Size content_size = gfx::Size(360, 600);
gfx::Size scroll_content_size = gfx::Size(345, 3800);
@@ -1534,9 +1612,16 @@ TEST_F(LayerTreeHostImplTest, ScrolledOverlappingDrawnScrollbarLayer) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
+ status.main_thread_scrolling_reasons);
+ }
// The point hits the drawn scrollbar layer completely and should scroll on
// the impl thread.
@@ -1623,7 +1708,7 @@ TEST_F(LayerTreeHostImplTestInvokeMainThreadCallbacks,
mock_details.presentation_feedback);
}
-TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, NonFastScrollableRegionBasic) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
LayerImpl* outer_scroll = OuterViewportScrollLayer();
@@ -1631,24 +1716,40 @@ TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) {
DrawFrame();
- // All scroll types inside the non-fast scrollable region should fail.
+ // All scroll types inside the non-fast scrollable region should fail. When
+ // scroll unification is enabled, these scrolls succeed but request a main
+ // thread hit test.
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point(25, 25), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
status = host_impl_->ScrollBegin(
BeginState(gfx::Point(25, 25), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
// All scroll types outside this region should succeed.
status = host_impl_->ScrollBegin(
@@ -1679,7 +1780,7 @@ TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) {
host_impl_->ScrollEnd();
}
-TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
LayerImpl* outer_scroll = OuterViewportScrollLayer();
@@ -1699,6 +1800,7 @@ TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 1),
ui::ScrollInputType::kWheel)
@@ -1711,12 +1813,19 @@ TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
}
-TEST_F(LayerTreeHostImplTest, ScrollHandlerNotPresent) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHandlerNotPresent) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
EXPECT_FALSE(host_impl_->active_tree()->have_scroll_event_handlers());
DrawFrame();
@@ -1731,7 +1840,7 @@ TEST_F(LayerTreeHostImplTest, ScrollHandlerNotPresent) {
EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
}
-TEST_F(LayerTreeHostImplTest, ScrollHandlerPresent) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHandlerPresent) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
host_impl_->active_tree()->set_have_scroll_event_handlers(true);
DrawFrame();
@@ -1746,7 +1855,7 @@ TEST_F(LayerTreeHostImplTest, ScrollHandlerPresent) {
EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
}
-TEST_F(LayerTreeHostImplTest, ScrollUpdateReturnsCorrectValue) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollUpdateReturnsCorrectValue) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
DrawFrame();
@@ -1848,7 +1957,7 @@ TEST_F(LayerTreeHostImplTest, ScrollUpdateReturnsCorrectValue) {
// TODO(sunyunjia): Move scroll snap tests to a separate file.
// https://crbug.com/851690
-TEST_F(LayerTreeHostImplTest, ScrollSnapOnX) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnX) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
@@ -1893,7 +2002,7 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapOnX) {
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
-TEST_F(LayerTreeHostImplTest, ScrollSnapOnY) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnY) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
@@ -1938,7 +2047,7 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapOnY) {
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
-TEST_F(LayerTreeHostImplTest, ScrollSnapOnBoth) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnBoth) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
@@ -1984,7 +2093,7 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapOnBoth) {
// Simulate a ScrollBegin and ScrollEnd without any intervening ScrollUpdate.
// This test passes if it doesn't crash.
-TEST_F(LayerTreeHostImplTest, SnapAfterEmptyScroll) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAfterEmptyScroll) {
CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
@@ -1999,7 +2108,7 @@ TEST_F(LayerTreeHostImplTest, SnapAfterEmptyScroll) {
host_impl_->ScrollEnd(true);
}
-TEST_F(LayerTreeHostImplTest, ScrollSnapAfterAnimatedScroll) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapAfterAnimatedScroll) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
@@ -2057,7 +2166,7 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapAfterAnimatedScroll) {
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
-TEST_F(LayerTreeHostImplTest, SnapAnimationTargetUpdated) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAnimationTargetUpdated) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
@@ -2120,7 +2229,7 @@ TEST_F(LayerTreeHostImplTest, SnapAnimationTargetUpdated) {
EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 50), CurrentScrollOffset(overflow));
}
-TEST_F(LayerTreeHostImplTest, SnapAnimationCancelledByScroll) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAnimationCancelledByScroll) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
@@ -2183,7 +2292,7 @@ TEST_F(LayerTreeHostImplTest, SnapAnimationCancelledByScroll) {
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
SnapAnimationShouldNotStartWhenScrollEndsAtSnapTarget) {
LayerImpl* overflow = CreateLayerForSnapping();
@@ -2230,7 +2339,7 @@ TEST_F(LayerTreeHostImplTest,
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
GetSnapFlingInfoAndSetAnimatingSnapTargetWhenZoomed) {
LayerImpl* overflow = CreateLayerForSnapping();
// Scales the page to its 1/5.
@@ -2270,7 +2379,8 @@ TEST_F(LayerTreeHostImplTest,
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
-TEST_F(LayerTreeHostImplTest, SnapFlingAnimationEndWithoutFinishing) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SnapFlingAnimationEndWithoutFinishing) {
LayerImpl* overflow = CreateLayerForSnapping();
// Scales the page to its 1/5.
host_impl_->active_tree()->PushPageScaleFromMainThread(0.2f, 0.1f, 5.f);
@@ -2310,7 +2420,8 @@ TEST_F(LayerTreeHostImplTest, SnapFlingAnimationEndWithoutFinishing) {
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
-TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ OverscrollBehaviorPreventsPropagation) {
const gfx::Size kViewportSize(100, 100);
const gfx::Size kContentSize(200, 200);
SetupViewportLayersOuterScrolls(kViewportSize, kContentSize);
@@ -2508,7 +2619,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) {
EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), CurrentScrollOffset(overflow));
}
-TEST_F(LayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) {
const gfx::Size kViewportSize(100, 100);
const gfx::Size kContentSize(200, 200);
SetupViewportLayersOuterScrolls(kViewportSize, kContentSize);
@@ -2582,7 +2693,10 @@ TEST_F(LayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) {
EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), CurrentScrollOffset(overflow));
}
-TEST_F(LayerTreeHostImplTest, ForceMainThreadScrollWithoutScrollLayer) {
+// Test that if a scroll node doesn't have an associated Layer, scrolling
+// is forced to the main thread. With scroll unification, this kind of scroll
+// is now handled on the impl thread but will force a repaint on each scroll.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNodeWithoutScrollLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
ScrollNode* scroll_node = host_impl_->OuterViewportScrollNode();
// Change the scroll node so that it no longer has an associated layer.
@@ -2595,9 +2709,20 @@ TEST_F(LayerTreeHostImplTest, ForceMainThreadScrollWithoutScrollLayer) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
- status.main_thread_scrolling_reasons);
+
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ // We don't have a layer for the scroller but we didn't hit a non-fast
+ // scrolling region or fail hit testing the layer - we don't need a main
+ // thread hit test in this case.
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
@@ -2712,7 +2837,8 @@ TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
EXPECT_FALSE(did_request_commit_);
}
-TEST_F(LayerTreeHostImplTest, AnimationSchedulingCommitToActiveTree) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ AnimationSchedulingCommitToActiveTree) {
EXPECT_TRUE(host_impl_->CommitToActiveTree());
auto* root = SetupDefaultRootLayer(gfx::Size(50, 50));
@@ -2749,7 +2875,8 @@ TEST_F(LayerTreeHostImplTest, AnimationSchedulingCommitToActiveTree) {
host_impl_ = nullptr;
}
-TEST_F(LayerTreeHostImplTest, AnimationSchedulingOnLayerDestruction) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ AnimationSchedulingOnLayerDestruction) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(50, 50));
LayerImpl* child = AddLayer();
@@ -2828,7 +2955,7 @@ class MissingTilesLayer : public LayerImpl {
bool has_missing_tiles_;
};
-TEST_F(LayerTreeHostImplTest, ImplPinchZoom) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ImplPinchZoom) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -2909,7 +3036,7 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoom) {
}
}
-TEST_F(LayerTreeHostImplTest, ViewportScrollbarGeometry) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ViewportScrollbarGeometry) {
// Tests for correct behavior of solid color scrollbars on unscrollable pages
// under tricky fractional scale/size issues.
@@ -2966,7 +3093,7 @@ TEST_F(LayerTreeHostImplTest, ViewportScrollbarGeometry) {
EXPECT_TRUE(h_scrollbar->CanScrollOrientation());
}
-TEST_F(LayerTreeHostImplTest, ViewportScrollOrder) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ViewportScrollOrder) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.25f, 4);
@@ -3036,7 +3163,8 @@ TEST_F(LayerTreeHostImplTest, ViewportScrollOrder) {
// Make sure scrolls smaller than a unit applied to the viewport don't get
// dropped. crbug.com/539334.
-TEST_F(LayerTreeHostImplTest, ScrollViewportWithFractionalAmounts) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollViewportWithFractionalAmounts) {
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
const gfx::Size content_size(1000, 1000);
@@ -3093,7 +3221,7 @@ TEST_F(LayerTreeHostImplTest, ScrollViewportWithFractionalAmounts) {
// Tests that scrolls during a pinch gesture (i.e. "two-finger" scrolls) work
// as expected. That is, scrolling during a pinch should bubble from the inner
// to the outer viewport.
-TEST_F(LayerTreeHostImplTest, ScrollDuringPinchGesture) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDuringPinchGesture) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
@@ -3153,7 +3281,7 @@ TEST_F(LayerTreeHostImplTest, ScrollDuringPinchGesture) {
// Tests the "snapping" of pinch-zoom gestures to the screen edge. That is, when
// a pinch zoom is anchored within a certain margin of the screen edge, we
// should assume the user means to scroll into the edge of the screen.
-TEST_F(LayerTreeHostImplTest, PinchZoomSnapsToScreenEdge) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchZoomSnapsToScreenEdge) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
@@ -3242,7 +3370,8 @@ TEST_F(LayerTreeHostImplTest, PinchZoomSnapsToScreenEdge) {
CurrentScrollOffset(InnerViewportScrollLayer()));
}
-TEST_F(LayerTreeHostImplTest, ImplPinchZoomWheelBubbleBetweenViewports) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ImplPinchZoomWheelBubbleBetweenViewports) {
const gfx::Size content_size(200, 200);
const gfx::Size viewport_size(100, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -3307,7 +3436,7 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoomWheelBubbleBetweenViewports) {
CurrentScrollOffset(inner_scroll_layer));
}
-TEST_F(LayerTreeHostImplTest, ScrollWithSwapPromises) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithSwapPromises) {
ui::LatencyInfo latency_info;
latency_info.set_trace_id(5);
latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT);
@@ -3337,7 +3466,7 @@ TEST_F(LayerTreeHostImplTest, ScrollWithSwapPromises) {
// Test that scrolls targeting a layer with a non-null scroll_parent() don't
// bubble up.
-TEST_F(LayerTreeHostImplTest, ScrollDoesntBubble) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDoesntBubble) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
LayerImpl* viewport_scroll = InnerViewportScrollLayer();
@@ -3420,7 +3549,7 @@ TEST_F(LayerTreeHostImplTest, ScrollDoesntBubble) {
}
}
-TEST_F(LayerTreeHostImplTest, PinchGesture) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -3610,7 +3739,7 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) {
}
}
-TEST_F(LayerTreeHostImplTest, SyncSubpixelScrollDelta) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SyncSubpixelScrollDelta) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -3660,7 +3789,8 @@ TEST_F(LayerTreeHostImplTest, SyncSubpixelScrollDelta) {
scroll_layer->ScreenSpaceTransform().To2dTranslation());
}
-TEST_F(LayerTreeHostImplTest, SyncSubpixelScrollFromFractionalActiveBase) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SyncSubpixelScrollFromFractionalActiveBase) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -3699,7 +3829,8 @@ TEST_F(LayerTreeHostImplTest, SyncSubpixelScrollFromFractionalActiveBase) {
gfx::Vector2dF(0, -1));
}
-TEST_F(LayerTreeHostImplTest, PinchZoomTriggersPageScaleAnimation) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ PinchZoomTriggersPageScaleAnimation) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -3821,7 +3952,7 @@ TEST_F(LayerTreeHostImplTest, PinchZoomTriggersPageScaleAnimation) {
}
}
-TEST_F(LayerTreeHostImplTest, PageScaleAnimation) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PageScaleAnimation) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -3950,7 +4081,7 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimation) {
}
}
-TEST_F(LayerTreeHostImplTest, PageScaleAnimationNoOp) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PageScaleAnimationNoOp) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -4008,7 +4139,8 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimationNoOp) {
}
}
-TEST_F(LayerTreeHostImplTest, PageScaleAnimationTransferedOnSyncTreeActivate) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ PageScaleAnimationTransferedOnSyncTreeActivate) {
CreatePendingTree();
host_impl_->pending_tree()->PushPageScaleFromMainThread(1, 1, 1);
SetupViewportLayers(host_impl_->pending_tree(), gfx::Size(50, 50),
@@ -4132,7 +4264,8 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimationTransferedOnSyncTreeActivate) {
gfx::ScrollOffset(-50, -50)));
}
-TEST_F(LayerTreeHostImplTest, PageScaleAnimationCompletedNotification) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ PageScaleAnimationCompletedNotification) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -4181,7 +4314,8 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimationCompletedNotification) {
host_impl_->DidFinishImplFrame(begin_frame_args);
}
-TEST_F(LayerTreeHostImplTest, MaxScrollOffsetAffectedByViewportBoundsDelta) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ MaxScrollOffsetAffectedByViewportBoundsDelta) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
DrawFrame();
@@ -4207,7 +4341,8 @@ TEST_F(LayerTreeHostImplTest, MaxScrollOffsetAffectedByViewportBoundsDelta) {
// Ensures scroll gestures coming from scrollbars cause animations in the
// appropriate scenarios.
-TEST_F(LayerTreeHostImplTest, AnimatedGranularityCausesSmoothScroll) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ AnimatedGranularityCausesSmoothScroll) {
gfx::Size viewport_size(300, 200);
gfx::Size content_size(1000, 1000);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -4275,7 +4410,8 @@ TEST_F(LayerTreeHostImplTest, AnimatedGranularityCausesSmoothScroll) {
// Ensures scroll gestures coming from scrollbars don't cause animations if
// smooth scrolling is disabled.
-TEST_F(LayerTreeHostImplTest, NonAnimatedGranularityCausesInstantScroll) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ NonAnimatedGranularityCausesInstantScroll) {
// Disable animated scrolling
LayerTreeSettings settings = DefaultSettings();
settings.enable_smooth_scroll = false;
@@ -4574,7 +4710,8 @@ TEST_F(LayerTreeHostImplTestScrollbarAnimation, NoAnimator) {
RunTest(LayerTreeSettings::NO_ANIMATOR);
}
-class LayerTreeHostImplTestScrollbarOpacity : public LayerTreeHostImplTest {
+class LayerTreeHostImplTestScrollbarOpacity
+ : public ScrollUnifiedLayerTreeHostImplTest {
protected:
void RunTest(LayerTreeSettings::ScrollbarAnimator animator) {
LayerTreeSettings settings = DefaultSettings();
@@ -4663,19 +4800,24 @@ class LayerTreeHostImplTestScrollbarOpacity : public LayerTreeHostImplTest {
}
};
-TEST_F(LayerTreeHostImplTestScrollbarOpacity, Android) {
+INSTANTIATE_TEST_SUITE_P(All,
+ LayerTreeHostImplTestScrollbarOpacity,
+ testing::Bool());
+
+TEST_P(LayerTreeHostImplTestScrollbarOpacity, Android) {
RunTest(LayerTreeSettings::ANDROID_OVERLAY);
}
-TEST_F(LayerTreeHostImplTestScrollbarOpacity, AuraOverlay) {
+TEST_P(LayerTreeHostImplTestScrollbarOpacity, AuraOverlay) {
RunTest(LayerTreeSettings::AURA_OVERLAY);
}
-TEST_F(LayerTreeHostImplTestScrollbarOpacity, NoAnimator) {
+TEST_P(LayerTreeHostImplTestScrollbarOpacity, NoAnimator) {
RunTest(LayerTreeSettings::NO_ANIMATOR);
}
-class LayerTreeHostImplTestMultiScrollable : public LayerTreeHostImplTest {
+class LayerTreeHostImplTestMultiScrollable
+ : public ScrollUnifiedLayerTreeHostImplTest {
public:
void SetUpLayers(LayerTreeSettings settings) {
is_aura_scrollbar_ =
@@ -4732,7 +4874,11 @@ class LayerTreeHostImplTestMultiScrollable : public LayerTreeHostImplTest {
SolidColorScrollbarLayerImpl* scrollbar_2_;
};
-TEST_F(LayerTreeHostImplTestMultiScrollable,
+INSTANTIATE_TEST_SUITE_P(All,
+ LayerTreeHostImplTestMultiScrollable,
+ testing::Bool());
+
+TEST_P(LayerTreeHostImplTestMultiScrollable,
ScrollbarFlashAfterAnyScrollUpdate) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_fade_delay = base::TimeDelta::FromMilliseconds(500);
@@ -4777,7 +4923,7 @@ TEST_F(LayerTreeHostImplTestMultiScrollable,
EXPECT_FALSE(animation_task_.is_null());
}
-TEST_F(LayerTreeHostImplTestMultiScrollable, ScrollbarFlashWhenMouseEnter) {
+TEST_P(LayerTreeHostImplTestMultiScrollable, ScrollbarFlashWhenMouseEnter) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_fade_delay = base::TimeDelta::FromMilliseconds(500);
settings.scrollbar_fade_duration = base::TimeDelta::FromMilliseconds(300);
@@ -4803,7 +4949,7 @@ TEST_F(LayerTreeHostImplTestMultiScrollable, ScrollbarFlashWhenMouseEnter) {
EXPECT_FALSE(animation_task_.is_null());
}
-TEST_F(LayerTreeHostImplTest, ScrollHitTestOnScrollbar) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestOnScrollbar) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_fade_delay = base::TimeDelta::FromMilliseconds(500);
settings.scrollbar_fade_duration = base::TimeDelta::FromMilliseconds(300);
@@ -4892,7 +5038,8 @@ TEST_F(LayerTreeHostImplTest, ScrollHitTestOnScrollbar) {
}
}
-TEST_F(LayerTreeHostImplTest, ScrollbarVisibilityChangeCausesRedrawAndCommit) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollbarVisibilityChangeCausesRedrawAndCommit) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY;
settings.scrollbar_fade_delay = base::TimeDelta::FromMilliseconds(20);
@@ -4961,7 +5108,7 @@ TEST_F(LayerTreeHostImplTest, ScrollbarVisibilityChangeCausesRedrawAndCommit) {
}
}
-TEST_F(LayerTreeHostImplTest, ScrollbarInnerLargerThanOuter) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollbarInnerLargerThanOuter) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -4983,7 +5130,7 @@ TEST_F(LayerTreeHostImplTest, ScrollbarInnerLargerThanOuter) {
EXPECT_EQ(300, horiz_scrollbar->clip_layer_length());
}
-TEST_F(LayerTreeHostImplTest, ScrollbarRegistration) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollbarRegistration) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_animator = LayerTreeSettings::ANDROID_OVERLAY;
settings.scrollbar_fade_delay = base::TimeDelta::FromMilliseconds(20);
@@ -5077,7 +5224,7 @@ TEST_F(LayerTreeHostImplTest, ScrollbarRegistration) {
root_scroll_element_id));
}
-TEST_F(LayerTreeHostImplTest, ScrollBeforeMouseMove) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBeforeMouseMove) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY;
settings.scrollbar_fade_delay = base::TimeDelta::FromMilliseconds(20);
@@ -5218,18 +5365,18 @@ void LayerTreeHostImplTest::SetupMouseMoveAtWithDeviceScale(
scrollbar_animation_controller->MouseIsOverScrollbarThumb(VERTICAL));
}
-TEST_F(LayerTreeHostImplTest, MouseMoveAtWithDeviceScaleOf1) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, MouseMoveAtWithDeviceScaleOf1) {
SetupMouseMoveAtWithDeviceScale(1);
}
-TEST_F(LayerTreeHostImplTest, MouseMoveAtWithDeviceScaleOf2) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, MouseMoveAtWithDeviceScaleOf2) {
SetupMouseMoveAtWithDeviceScale(2);
}
// This test verifies that only SurfaceLayers in the viewport and have fallbacks
// that are different are included in viz::CompositorFrameMetadata's
// |activation_dependencies|.
-TEST_F(LayerTreeHostImplTest, ActivationDependenciesInMetadata) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ActivationDependenciesInMetadata) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
LayerImpl* root = root_layer();
@@ -5308,7 +5455,8 @@ TEST_F(LayerTreeHostImplTest, ActivationDependenciesInMetadata) {
// Verify that updating the set of referenced surfaces for the active tree
// causes a new CompositorFrame to be submitted, even if there is no other
// damage.
-TEST_F(LayerTreeHostImplTest, SurfaceReferencesChangeCausesDamage) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SurfaceReferencesChangeCausesDamage) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* fake_layer_tree_frame_sink =
static_cast<FakeLayerTreeFrameSink*>(host_impl_->layer_tree_frame_sink());
@@ -5340,7 +5488,7 @@ TEST_F(LayerTreeHostImplTest, SurfaceReferencesChangeCausesDamage) {
}
}
-TEST_F(LayerTreeHostImplTest, CompositorFrameMetadata) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, CompositorFrameMetadata) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
DrawFrame();
@@ -5467,7 +5615,8 @@ class DidDrawCheckLayer : public LayerImpl {
bool did_draw_called_;
};
-TEST_F(LayerTreeHostImplTest, DamageShouldNotCareAboutContributingLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ DamageShouldNotCareAboutContributingLayers) {
auto* root = SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(),
gfx::Size(10, 10));
@@ -5551,7 +5700,7 @@ TEST_F(LayerTreeHostImplTest, DamageShouldNotCareAboutContributingLayers) {
}
}
-TEST_F(LayerTreeHostImplTest, WillDrawReturningFalseDoesNotCall) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, WillDrawReturningFalseDoesNotCall) {
// The root layer is always drawn, so run this test on a child layer that
// will be masked out by the root layer's bounds.
auto* root = SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(),
@@ -5574,7 +5723,7 @@ TEST_F(LayerTreeHostImplTest, WillDrawReturningFalseDoesNotCall) {
EXPECT_FALSE(layer->did_draw_called());
}
-TEST_F(LayerTreeHostImplTest, DidDrawNotCalledOnHiddenLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, DidDrawNotCalledOnHiddenLayer) {
// The root layer is always drawn, so run this test on a child layer that
// will be masked out by the root layer's bounds.
auto* root = SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(),
@@ -5613,7 +5762,7 @@ TEST_F(LayerTreeHostImplTest, DidDrawNotCalledOnHiddenLayer) {
EXPECT_FALSE(layer->visible_layer_rect().IsEmpty());
}
-TEST_F(LayerTreeHostImplTest, WillDrawNotCalledOnOccludedLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, WillDrawNotCalledOnOccludedLayer) {
gfx::Size big_size(1000, 1000);
auto* root =
SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(), big_size);
@@ -5641,7 +5790,7 @@ TEST_F(LayerTreeHostImplTest, WillDrawNotCalledOnOccludedLayer) {
EXPECT_TRUE(top_layer->did_draw_called());
}
-TEST_F(LayerTreeHostImplTest, DidDrawCalledOnAllLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, DidDrawCalledOnAllLayers) {
auto* root = SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(),
gfx::Size(10, 10));
auto* layer1 = AddLayer<DidDrawCheckLayer>(host_impl_->active_tree());
@@ -5906,11 +6055,12 @@ TEST_F(LayerTreeHostImplPrepareToDrawTest,
}
}
-TEST_F(LayerTreeHostImplTest, ScrollRootIgnored) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollRootIgnored) {
SetupDefaultRootLayer(gfx::Size(10, 10));
DrawFrame();
- // Scroll event is ignored because layer is not scrollable.
+ // Scroll event is ignored because layer is not scrollable and there is no
+ // viewport.
InputHandler::ScrollStatus status =
host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -5923,7 +6073,7 @@ TEST_F(LayerTreeHostImplTest, ScrollRootIgnored) {
EXPECT_FALSE(did_request_commit_);
}
-TEST_F(LayerTreeHostImplTest, ClampingAfterActivation) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ClampingAfterActivation) {
CreatePendingTree();
host_impl_->pending_tree()->PushPageScaleFromMainThread(1, 1, 1);
SetupViewportLayers(host_impl_->pending_tree(), gfx::Size(50, 50),
@@ -5945,7 +6095,8 @@ TEST_F(LayerTreeHostImplTest, ClampingAfterActivation) {
EXPECT_EQ(CurrentScrollOffset(active_outer_layer), gfx::ScrollOffset(0, 0));
}
-class LayerTreeHostImplBrowserControlsTest : public LayerTreeHostImplTest {
+class LayerTreeHostImplBrowserControlsTest
+ : public ScrollUnifiedLayerTreeHostImplTest {
public:
LayerTreeHostImplBrowserControlsTest()
// Make the clip size the same as the layer (content) size so the layer is
@@ -6002,6 +6153,10 @@ class LayerTreeHostImplBrowserControlsTest : public LayerTreeHostImplTest {
LayerTreeSettings settings_;
}; // class LayerTreeHostImplBrowserControlsTest
+INSTANTIATE_TEST_SUITE_P(All,
+ LayerTreeHostImplBrowserControlsTest,
+ testing::Bool());
+
#define EXPECT_VIEWPORT_GEOMETRIES(expected_browser_controls_shown_ratio) \
do { \
auto* tree = host_impl_->active_tree(); \
@@ -6031,7 +6186,7 @@ class LayerTreeHostImplBrowserControlsTest : public LayerTreeHostImplTest {
// size). Since the viewport got larger, the effective scrollable "content" also
// did. This ensures, for one thing, that the overscroll glow is shown in the
// right place.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
HidingBrowserControlsExpandsScrollableSize) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(50, 50), gfx::Size(50, 50), gfx::Size(50, 50));
@@ -6074,11 +6229,108 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
host_impl_->ScrollEnd();
}
+TEST_P(LayerTreeHostImplBrowserControlsTest,
+ HidingBrowserControlsExpandsClipAncestorsOfReplacedOuterScroller) {
+ SetupBrowserControlsAndScrollLayerWithVirtualViewport(
+ gfx::Size(180, 180), gfx::Size(180, 180), gfx::Size(180, 180));
+
+ LayerTreeImpl* active_tree = host_impl_->active_tree();
+ PropertyTrees* property_trees = active_tree->property_trees();
+ LayerImpl* original_outer_scroll = OuterViewportScrollLayer();
+
+ LayerImpl* parent_clip_layer = AddLayer();
+ CopyProperties(original_outer_scroll, parent_clip_layer);
+ parent_clip_layer->SetBounds(gfx::Size(160, 160));
+ CreateClipNode(parent_clip_layer);
+ LayerImpl* clip_layer = AddLayer();
+ clip_layer->SetBounds(gfx::Size(150, 150));
+ CopyProperties(parent_clip_layer, clip_layer);
+ CreateClipNode(clip_layer);
+ LayerImpl* scroll_layer =
+ AddScrollableLayer(clip_layer, gfx::Size(150, 150), gfx::Size(300, 300));
+ GetScrollNode(scroll_layer)->scrolls_outer_viewport = true;
+ ClipNode* original_outer_clip = GetClipNode(original_outer_scroll);
+ ClipNode* parent_clip = GetClipNode(parent_clip_layer);
+ ClipNode* scroll_clip = GetClipNode(clip_layer);
+
+ auto viewport_property_ids = active_tree->ViewportPropertyIdsForTesting();
+ viewport_property_ids.outer_clip = clip_layer->clip_tree_index();
+ viewport_property_ids.outer_scroll = scroll_layer->scroll_tree_index();
+ active_tree->SetViewportPropertyIds(viewport_property_ids);
+ UpdateDrawProperties(active_tree);
+
+ EXPECT_EQ(scroll_layer, OuterViewportScrollLayer());
+ EXPECT_EQ(GetScrollNode(scroll_layer),
+ active_tree->OuterViewportScrollNode());
+
+ EXPECT_EQ(1, active_tree->CurrentTopControlsShownRatio());
+ EXPECT_EQ(50, host_impl_->browser_controls_manager()->ContentTopOffset());
+ EXPECT_EQ(gfx::Vector2dF(),
+ property_trees->inner_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::Vector2dF(),
+ property_trees->outer_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::SizeF(300, 300), active_tree->ScrollableSize());
+ EXPECT_EQ(gfx::RectF(0, 0, 180, 180), original_outer_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 160, 160), parent_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 150, 150), scroll_clip->clip);
+
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ host_impl_
+ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 25),
+ ui::ScrollInputType::kTouchscreen)
+ .get(),
+ ui::ScrollInputType::kTouchscreen)
+ .thread);
+
+ // Hide the browser controls by a bit, the scrollable size should increase but
+ // the actual content bounds shouldn't.
+ host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
+ EXPECT_EQ(0.5f, active_tree->CurrentTopControlsShownRatio());
+ EXPECT_EQ(25, host_impl_->browser_controls_manager()->ContentTopOffset());
+ EXPECT_EQ(gfx::Vector2dF(0, 25),
+ property_trees->inner_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::Vector2dF(0, 25),
+ property_trees->outer_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::SizeF(300, 300), active_tree->ScrollableSize());
+ EXPECT_EQ(gfx::RectF(0, 0, 150, 175), scroll_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 160, 175), parent_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 180, 180), original_outer_clip->clip);
+
+ // Fully hide the browser controls.
+ host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
+ EXPECT_EQ(0, active_tree->CurrentTopControlsShownRatio());
+ EXPECT_EQ(0, host_impl_->browser_controls_manager()->ContentTopOffset());
+ EXPECT_EQ(gfx::Vector2dF(0, 50),
+ property_trees->inner_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::Vector2dF(0, 50),
+ property_trees->outer_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::SizeF(300, 300), active_tree->ScrollableSize());
+ EXPECT_EQ(gfx::RectF(0, 0, 150, 200), scroll_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 160, 200), parent_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 180, 200), original_outer_clip->clip);
+
+ // Scrolling additionally shouldn't have any effect.
+ host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
+ EXPECT_EQ(0, active_tree->CurrentTopControlsShownRatio());
+ EXPECT_EQ(0, host_impl_->browser_controls_manager()->ContentTopOffset());
+ EXPECT_EQ(gfx::Vector2dF(0, 50),
+ property_trees->inner_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::Vector2dF(0, 50),
+ property_trees->outer_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::SizeF(300, 300), active_tree->ScrollableSize());
+ EXPECT_EQ(gfx::RectF(0, 0, 150, 200), scroll_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 160, 200), parent_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 180, 200), original_outer_clip->clip);
+
+ host_impl_->browser_controls_manager()->ScrollEnd();
+ host_impl_->ScrollEnd();
+}
+
// Ensure that moving the browser controls (i.e. omnibox/url-bar on mobile) on
// pages with a non-1 minimum page scale factor (e.g. legacy desktop page)
// correctly scales the clipping adjustment performed to show the newly exposed
// region of the page.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
MovingBrowserControlsOuterClipDeltaScaled) {
gfx::Size inner_size = gfx::Size(100, 100);
gfx::Size outer_size = gfx::Size(100, 100);
@@ -6122,7 +6374,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
}
// Tests that browser controls affect the position of horizontal scrollbars.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
HidingBrowserControlsAdjustsScrollbarPosition) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(50, 50), gfx::Size(50, 50), gfx::Size(50, 50));
@@ -6198,7 +6450,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
host_impl_->ScrollEnd();
}
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
ScrollBrowserControlsByFractionalAmount) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(10, 10), gfx::Size(10, 10), gfx::Size(10, 10));
@@ -6231,7 +6483,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// scroll initiated on the inner viewport, causing the browser controls to show
// and thus making the outer viewport scrollable, still scrolls the outer
// viewport.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsOuterViewportBecomesScrollable) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(10, 50), gfx::Size(10, 50), gfx::Size(10, 100));
@@ -6322,7 +6574,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// Test that the fixed position container delta is appropriately adjusted
// by the browser controls showing/hiding and page scale doesn't affect it.
-TEST_F(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) {
+TEST_P(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(100, 100), gfx::Size(100, 100), gfx::Size(100, 100));
DrawFrame();
@@ -6396,7 +6648,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) {
// Push a browser controls ratio from the main thread that we didn't send as a
// delta and make sure that the ratio is clamped to the [0, 1] range.
-TEST_F(LayerTreeHostImplBrowserControlsTest, BrowserControlsPushUnsentRatio) {
+TEST_P(LayerTreeHostImplBrowserControlsTest, BrowserControlsPushUnsentRatio) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(10, 50), gfx::Size(10, 50), gfx::Size(10, 100));
DrawFrame();
@@ -6420,7 +6672,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, BrowserControlsPushUnsentRatio) {
// Test that if a scrollable sublayer doesn't consume the scroll,
// browser controls should hide when scrolling down.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsScrollableSublayer) {
gfx::Size sub_content_size(100, 400);
gfx::Size sub_content_layer_size(100, 300);
@@ -6470,7 +6722,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// Ensure setting the browser controls position explicitly using the setters on
// the TreeImpl correctly affects the browser controls manager and viewport
// bounds for the active tree.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
PositionBrowserControlsToActiveTreeExplicitly) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
layer_size_, layer_size_, layer_size_);
@@ -6501,7 +6753,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// Ensure setting the browser controls position explicitly using the setters on
// the TreeImpl correctly affects the browser controls manager and viewport
// bounds for the pending tree.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
PositionBrowserControlsToPendingTreeExplicitly) {
CreatePendingTree();
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
@@ -6546,7 +6798,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// Test that the top_controls delta and sent delta are appropriately
// applied on sync tree activation. The total browser controls offset shouldn't
// change after the activation.
-TEST_F(LayerTreeHostImplBrowserControlsTest, ApplyDeltaOnTreeActivation) {
+TEST_P(LayerTreeHostImplBrowserControlsTest, ApplyDeltaOnTreeActivation) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
layer_size_, layer_size_, layer_size_);
DrawFrame();
@@ -6586,7 +6838,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, ApplyDeltaOnTreeActivation) {
// the inner viewport container bounds. That is, the browser controls layout
// height is the amount that the inner viewport container was shrunk outside
// the compositor to accommodate the browser controls.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsLayoutHeightChanged) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
layer_size_, layer_size_, layer_size_);
@@ -6629,7 +6881,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// Test that showing/hiding the browser controls when the viewport is fully
// scrolled doesn't incorrectly change the viewport offset due to clamping from
// changing viewport bounds.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsViewportOffsetClamping) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(100, 100), gfx::Size(200, 200), gfx::Size(200, 400));
@@ -6705,7 +6957,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// Test that the browser controls coming in and out maintains the same aspect
// ratio between the inner and outer viewports.
-TEST_F(LayerTreeHostImplBrowserControlsTest, BrowserControlsAspectRatio) {
+TEST_P(LayerTreeHostImplBrowserControlsTest, BrowserControlsAspectRatio) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(100, 100), gfx::Size(200, 200), gfx::Size(200, 400));
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 2);
@@ -6745,7 +6997,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, BrowserControlsAspectRatio) {
}
// Test that scrolling the outer viewport affects the browser controls.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsScrollOuterViewport) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(100, 100), gfx::Size(200, 200), gfx::Size(200, 400));
@@ -6821,7 +7073,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
host_impl_->ScrollEnd();
}
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
ScrollNonScrollableRootWithBrowserControls) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
layer_size_, layer_size_, layer_size_);
@@ -6892,7 +7144,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// was occurring because the UpdateViewportContainerSizes was being called
// before the property trees were updated with the bounds_delta.
// crbug.com/597266.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
ViewportBoundsDeltaOnTreeActivation) {
const gfx::Size inner_viewport_size(1000, 1000);
const gfx::Size outer_viewport_size(1000, 1000);
@@ -7016,7 +7268,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
}
}
-TEST_F(LayerTreeHostImplTest, ScrollNonCompositedRoot) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNonCompositedRoot) {
// Test the configuration where a non-composited root layer is embedded in a
// scrollable outer layer.
gfx::Size surface_size(10, 10);
@@ -7054,7 +7306,7 @@ TEST_F(LayerTreeHostImplTest, ScrollNonCompositedRoot) {
EXPECT_TRUE(did_request_commit_);
}
-TEST_F(LayerTreeHostImplTest, ScrollChildCallsCommitAndRedraw) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollChildCallsCommitAndRedraw) {
gfx::Size surface_size(10, 10);
gfx::Size contents_size(20, 20);
@@ -7083,33 +7335,35 @@ TEST_F(LayerTreeHostImplTest, ScrollChildCallsCommitAndRedraw) {
EXPECT_TRUE(did_request_commit_);
}
-TEST_F(LayerTreeHostImplTest, ScrollMissesChild) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollMissesChild) {
gfx::Size viewport_size(5, 5);
gfx::Size surface_size(10, 10);
- LayerImpl* root = SetupDefaultRootLayer(surface_size);
- AddScrollableLayer(root, viewport_size, surface_size);
+ SetupViewportLayersOuterScrolls(viewport_size, surface_size);
+ AddScrollableLayer(OuterViewportScrollLayer(), viewport_size, surface_size);
DrawFrame();
- // Scroll event is ignored because the input coordinate is outside the layer
- // boundaries.
+ // A scroll that doesn't hit any layers should fall back to viewport
+ // scrolling.
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point(15, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
- status.main_thread_scrolling_reasons);
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(host_impl_->CurrentlyScrollingNode(),
+ host_impl_->OuterViewportScrollNode());
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
}
-TEST_F(LayerTreeHostImplTest, ScrollMissesBackfacingChild) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollMissesBackfacingChild) {
gfx::Size viewport_size(5, 5);
gfx::Size surface_size(10, 10);
- LayerImpl* root = SetupDefaultRootLayer(viewport_size);
- LayerImpl* child = AddScrollableLayer(root, viewport_size, surface_size);
+
+ SetupViewportLayersOuterScrolls(viewport_size, surface_size);
+ LayerImpl* child = AddScrollableLayer(OuterViewportScrollLayer(),
+ viewport_size, surface_size);
gfx::Transform matrix;
matrix.RotateAboutXAxis(180.0);
@@ -7118,22 +7372,22 @@ TEST_F(LayerTreeHostImplTest, ScrollMissesBackfacingChild) {
CreateEffectNode(child).double_sided = false;
DrawFrame();
- // Scroll event is ignored because the scrollable layer is not facing the
- // viewer and there is nothing scrollable behind it.
+ // The scroll shouldn't hit the child layer because the it isn't facing the
+ // viewer. The hit test should go through it and hit the outer viewport.
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
- status.main_thread_scrolling_reasons);
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(host_impl_->CurrentlyScrollingNode(),
+ host_impl_->OuterViewportScrollNode());
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
}
-TEST_F(LayerTreeHostImplTest, ScrollBlockedByContentLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollLayerWithMainThreadReason) {
gfx::Size scroll_container_size(5, 5);
gfx::Size surface_size(10, 10);
LayerImpl* root = SetupDefaultRootLayer(surface_size);
@@ -7148,19 +7402,28 @@ TEST_F(LayerTreeHostImplTest, ScrollBlockedByContentLayer) {
MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects;
DrawFrame();
- // Scrolling fails because the content layer is asking to be scrolled on the
- // main thread.
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ EXPECT_EQ(
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
+ host_impl_->CurrentlyScrollingNode()->main_thread_scrolling_reasons);
+ } else {
+ // Scrolling fails because the content layer is asking to be scrolled on the
+ // main thread.
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
+ status.main_thread_scrolling_reasons);
+ }
}
-TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnMainThread) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollRootAndChangePageScaleOnMainThread) {
gfx::Size inner_viewport_size(20, 20);
gfx::Size outer_viewport_size(40, 40);
gfx::Size content_size(80, 80);
@@ -7203,7 +7466,8 @@ TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnMainThread) {
EXPECT_EQ(1, host_impl_->active_tree()->page_scale_delta());
}
-TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnImplThread) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollRootAndChangePageScaleOnImplThread) {
gfx::Size inner_viewport_size(20, 20);
gfx::Size outer_viewport_size(40, 40);
gfx::Size content_size(80, 80);
@@ -7255,7 +7519,8 @@ TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnImplThread) {
EXPECT_EQ(page_scale, host_impl_->active_tree()->current_page_scale_factor());
}
-TEST_F(LayerTreeHostImplTest, PageScaleDeltaAppliedToRootScrollLayerOnly) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ PageScaleDeltaAppliedToRootScrollLayerOnly) {
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
gfx::Size viewport_size(5, 5);
gfx::Size surface_size(10, 10);
@@ -7305,7 +7570,8 @@ TEST_F(LayerTreeHostImplTest, PageScaleDeltaAppliedToRootScrollLayerOnly) {
outer_scroll->DrawTransform().matrix().getDouble(1, 1));
}
-TEST_F(LayerTreeHostImplTest, ScrollChildAndChangePageScaleOnMainThread) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollChildAndChangePageScaleOnMainThread) {
SetupViewportLayers(host_impl_->active_tree(), gfx::Size(15, 15),
gfx::Size(30, 30), gfx::Size(50, 50));
LayerImpl* outer_scroll = OuterViewportScrollLayer();
@@ -7345,7 +7611,7 @@ TEST_F(LayerTreeHostImplTest, ScrollChildAndChangePageScaleOnMainThread) {
EXPECT_EQ(1, host_impl_->active_tree()->page_scale_delta());
}
-TEST_F(LayerTreeHostImplTest, ScrollChildBeyondLimit) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollChildBeyondLimit) {
// Scroll a child layer beyond its maximum scroll range and make sure the
// parent layer isn't scrolled.
gfx::Size surface_size(10, 10);
@@ -7490,7 +7756,7 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedLatchToChild) {
host_impl_ = nullptr;
}
-TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutBubbling) {
// Scroll a child layer beyond its maximum scroll range and make sure the
// the scroll doesn't bubble up to the parent layer.
gfx::Size surface_size(20, 20);
@@ -7629,7 +7895,8 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) {
// Ensure that layers who's scroll parent is the InnerViewportScrollNode are
// still able to scroll on the compositor.
-TEST_F(LayerTreeHostImplTest, ChildrenOfInnerScrollNodeCanScrollOnThread) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ChildrenOfInnerScrollNodeCanScrollOnThread) {
gfx::Size viewport_size(10, 10);
gfx::Size content_size(20, 20);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -7673,7 +7940,7 @@ TEST_F(LayerTreeHostImplTest, ChildrenOfInnerScrollNodeCanScrollOnThread) {
}
}
-TEST_F(LayerTreeHostImplTest, ScrollEventBubbling) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollEventBubbling) {
// When we try to scroll a non-scrollable child layer, the scroll delta
// should be applied to one of its ancestors if possible.
gfx::Size viewport_size(10, 10);
@@ -7716,7 +7983,7 @@ TEST_F(LayerTreeHostImplTest, ScrollEventBubbling) {
}
}
-TEST_F(LayerTreeHostImplTest, ScrollBeforeRedraw) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBeforeRedraw) {
gfx::Size surface_size(10, 10);
SetupViewportLayersNoScrolls(surface_size);
UpdateDrawProperties(host_impl_->active_tree());
@@ -7740,7 +8007,7 @@ TEST_F(LayerTreeHostImplTest, ScrollBeforeRedraw) {
.thread);
}
-TEST_F(LayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
scroll_layer->SetDrawsContent(true);
@@ -7798,7 +8065,7 @@ TEST_F(LayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
wheel_scroll_delta));
}
-TEST_F(LayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
float child_layer_angle = -20;
@@ -7891,7 +8158,7 @@ TEST_F(LayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) {
}
}
-TEST_F(LayerTreeHostImplTest, ScrollPerspectiveTransformedLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollPerspectiveTransformedLayer) {
// When scrolling an element with perspective, the distance scrolled
// depends on the point at which the scroll begins.
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
@@ -7973,7 +8240,7 @@ TEST_F(LayerTreeHostImplTest, ScrollPerspectiveTransformedLayer) {
}
}
-TEST_F(LayerTreeHostImplTest, ScrollScaledLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollScaledLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
@@ -8032,7 +8299,7 @@ TEST_F(LayerTreeHostImplTest, ScrollScaledLayer) {
wheel_scroll_delta));
}
-TEST_F(LayerTreeHostImplTest, ScrollViewportRounding) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollViewportRounding) {
int width = 332;
int height = 20;
int scale = 3;
@@ -8048,7 +8315,7 @@ TEST_F(LayerTreeHostImplTest, ScrollViewportRounding) {
MaxScrollOffset(inner_viewport_scroll_layer));
}
-TEST_F(LayerTreeHostImplTest, RootLayerScrollOffsetDelegation) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RootLayerScrollOffsetDelegation) {
TestInputHandlerClient scroll_watcher;
SetupViewportLayersInnerScrolls(gfx::Size(10, 20), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
@@ -8166,7 +8433,7 @@ void CheckLayerScrollDelta(LayerImpl* layer, gfx::Vector2dF scroll_delta) {
EXPECT_EQ(expected_point.ToString(), translated_point.ToString());
}
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
ExternalRootLayerScrollOffsetDelegationReflectedInNextDraw) {
SetupViewportLayersInnerScrolls(gfx::Size(10, 20), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
@@ -8201,7 +8468,7 @@ TEST_F(LayerTreeHostImplTest,
// Ensure the viewport correctly handles the user_scrollable bits. That is, if
// the outer viewport disables user scrolling, we should still be able to
// scroll the inner viewport.
-TEST_F(LayerTreeHostImplTest, ViewportUserScrollable) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ViewportUserScrollable) {
gfx::Size viewport_size(100, 100);
gfx::Size content_size(200, 200);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -8367,7 +8634,7 @@ TEST_F(LayerTreeHostImplTest, ViewportUserScrollable) {
// Ensure that the SetSynchronousInputHandlerRootScrollOffset method used by
// the WebView API correctly respects the user_scrollable bits on both of the
// inner and outer viewport scroll nodes.
-TEST_F(LayerTreeHostImplTest, SetRootScrollOffsetUserScrollable) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SetRootScrollOffsetUserScrollable) {
gfx::Size viewport_size(100, 100);
gfx::Size content_size(200, 200);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -8480,14 +8747,14 @@ TEST_F(LayerTreeHostImplTest, SetRootScrollOffsetUserScrollable) {
// The SetSynchronousInputHandlerRootScrollOffset API can be called while there
// is no inner viewport set. This test passes if we don't crash.
-TEST_F(LayerTreeHostImplTest, SetRootScrollOffsetNoViewportCrash) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SetRootScrollOffsetNoViewportCrash) {
auto* inner_scroll = InnerViewportScrollLayer();
ASSERT_FALSE(inner_scroll);
gfx::ScrollOffset scroll_offset(25, 30);
host_impl_->SetSynchronousInputHandlerRootScrollOffset(scroll_offset);
}
-TEST_F(LayerTreeHostImplTest, OverscrollRoot) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
InputHandlerScrollResult scroll_result;
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
@@ -8641,7 +8908,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollRoot) {
host_impl_->ScrollEnd();
}
-TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
// Scroll child layers beyond their maximum scroll range and make sure root
// overscroll does not accumulate.
InputHandlerScrollResult scroll_result;
@@ -8746,7 +9013,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
}
}
-TEST_F(LayerTreeHostImplTest, OverscrollChildEventBubbling) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollChildEventBubbling) {
// When we try to scroll a non-scrollable child layer, the scroll delta
// should be applied to one of its ancestors if possible. Overscroll should
// be reflected only when it has bubbled up to the root scrolling layer.
@@ -8784,7 +9051,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildEventBubbling) {
}
}
-TEST_F(LayerTreeHostImplTest, OverscrollAlways) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollAlways) {
InputHandlerScrollResult scroll_result;
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -8813,7 +9080,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollAlways) {
EXPECT_EQ(gfx::Vector2dF(0, 10), host_impl_->accumulated_root_overscroll());
}
-TEST_F(LayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) {
InputHandlerScrollResult scroll_result;
gfx::Size viewport_size(100, 100);
gfx::Size content_size(200, 200);
@@ -8898,7 +9165,7 @@ TEST_F(LayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) {
}
}
-TEST_F(LayerTreeHostImplTest, NoOverscrollOnNonViewportLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, NoOverscrollOnNonViewportLayers) {
const gfx::Size content_size(200, 200);
const gfx::Size viewport_size(100, 100);
@@ -8963,7 +9230,15 @@ TEST_F(LayerTreeHostImplTest, NoOverscrollOnNonViewportLayers) {
host_impl_->ScrollEnd();
}
-TEST_F(LayerTreeHostImplTest, OverscrollOnMainThread) {
+// This ensures that the --disable-threaded-scrolling flag is respected even
+// when a scroll occurs outside of a layer which normally falls back to the
+// inner viewport layer. https://crbug.com/625100.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollOnMainThread) {
+ // TODO(bokan): The meaning of this flag is lost with scroll unification. We
+ // need to decide what we should do with it. https://crbug.com/1086625.
+ if (base::FeatureList::IsEnabled(features::kScrollUnification))
+ return;
+
InputHandlerScrollResult scroll_result;
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -9005,7 +9280,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollOnMainThread) {
// Test that scrolling the inner viewport directly works, as can happen when the
// scroll chains up to it from an sibling of the outer viewport.
-TEST_F(LayerTreeHostImplTest, ScrollFromOuterViewportSibling) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollFromOuterViewportSibling) {
const gfx::Size viewport_size(100, 100);
SetupViewportLayersNoScrolls(viewport_size);
@@ -9081,7 +9356,8 @@ TEST_F(LayerTreeHostImplTest, ScrollFromOuterViewportSibling) {
// Test that scrolls chain correctly when a child scroller on the page (e.g. a
// scrolling div) is set as the outer viewport. This happens in the
// rootScroller proposal.
-TEST_F(LayerTreeHostImplTest, ScrollChainingWithReplacedOuterViewport) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollChainingWithReplacedOuterViewport) {
const gfx::Size content_size(200, 200);
const gfx::Size viewport_size(100, 100);
@@ -9211,7 +9487,7 @@ TEST_F(LayerTreeHostImplTest, ScrollChainingWithReplacedOuterViewport) {
// scrolling div) is set as the outer viewport but scrolls start from a layer
// that's not a descendant of the outer viewport. This happens in the
// rootScroller proposal.
-TEST_F(LayerTreeHostImplTest, RootScrollerScrollNonDescendant) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RootScrollerScrollNonDescendant) {
const gfx::Size content_size(300, 300);
const gfx::Size viewport_size(300, 300);
@@ -9381,7 +9657,7 @@ TEST_F(LayerTreeHostImplTest, RootScrollerScrollNonDescendant) {
}
}
-TEST_F(LayerTreeHostImplTest, OverscrollOnImplThread) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollOnImplThread) {
InputHandlerScrollResult scroll_result;
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -9514,7 +9790,7 @@ class BlendStateCheckLayer : public LayerImpl {
viz::ResourceId resource_id_;
};
-TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
root->SetDrawsContent(false);
@@ -9710,7 +9986,7 @@ static bool MayContainVideoBitSetOnFrameData(LayerTreeHostImpl* host_impl) {
return frame.may_contain_video;
}
-TEST_F(LayerTreeHostImplTest, MayContainVideo) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, MayContainVideo) {
gfx::Size big_size(1000, 1000);
auto* root =
SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(), big_size);
@@ -10020,7 +10296,7 @@ class FakeDrawableLayerImpl : public LayerImpl {
// Make sure damage tracking propagates all the way to the viz::CompositorFrame
// submitted to the LayerTreeFrameSink, where it should request to swap only
// the sub-buffer that is damaged.
-TEST_F(LayerTreeHostImplTest, PartialSwapReceivesDamageRect) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PartialSwapReceivesDamageRect) {
auto gl_owned = std::make_unique<viz::TestGLES2Interface>();
gl_owned->set_have_post_sub_buffer(true);
scoped_refptr<viz::TestContextProvider> context_provider(
@@ -10108,7 +10384,7 @@ TEST_F(LayerTreeHostImplTest, PartialSwapReceivesDamageRect) {
layer_tree_host_impl->ReleaseLayerTreeFrameSink();
}
-TEST_F(LayerTreeHostImplTest, RootLayerDoesntCreateExtraSurface) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RootLayerDoesntCreateExtraSurface) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
LayerImpl* child = AddLayer();
child->SetBounds(gfx::Size(10, 10));
@@ -10153,7 +10429,7 @@ class FakeLayerWithQuads : public LayerImpl {
: LayerImpl(tree_impl, id) {}
};
-TEST_F(LayerTreeHostImplTest, LayersFreeTextures) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, LayersFreeTextures) {
scoped_refptr<viz::TestContextProvider> context_provider =
viz::TestContextProvider::Create();
viz::TestSharedImageInterface* sii = context_provider->SharedImageInterface();
@@ -10187,7 +10463,7 @@ TEST_F(LayerTreeHostImplTest, LayersFreeTextures) {
EXPECT_EQ(0u, sii->shared_image_count());
}
-TEST_F(LayerTreeHostImplTest, HasTransparentBackground) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, HasTransparentBackground) {
SetupDefaultRootLayer(gfx::Size(10, 10));
host_impl_->active_tree()->set_background_color(SK_ColorWHITE);
UpdateDrawProperties(host_impl_->active_tree());
@@ -10332,7 +10608,7 @@ class GLRendererWithSetupQuadForAntialiasing : public viz::GLRenderer {
using viz::GLRenderer::ShouldAntialiasQuad;
};
-TEST_F(LayerTreeHostImplTest, FarAwayQuadsDontNeedAA) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, FarAwayQuadsDontNeedAA) {
// Due to precision issues (especially on Android), sometimes far
// away quads can end up thinking they need AA.
float device_scale_factor = 4 / 3;
@@ -10433,7 +10709,7 @@ class CountingSoftwareDevice : public viz::SoftwareOutputDevice {
int frames_began_, frames_ended_;
};
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
ForcedDrawToSoftwareDeviceSkipsUnsupportedLayers) {
set_reduce_memory_result(false);
EXPECT_TRUE(CreateHostImpl(DefaultSettings(),
@@ -10468,7 +10744,7 @@ TEST_F(LayerTreeHostImplTest,
}
// Checks that we use the memory limits provided.
-TEST_F(LayerTreeHostImplTest, MemoryLimits) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, MemoryLimits) {
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
@@ -10628,7 +10904,7 @@ TEST_F(LayerTreeHostImplTestPrepareTiles, PrepareTilesWhenInvisible) {
EXPECT_TRUE(fake_host_impl_->prepare_tiles_needed());
}
-TEST_F(LayerTreeHostImplTest, UIResourceManagement) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, UIResourceManagement) {
auto test_context_provider = viz::TestContextProvider::Create();
viz::TestSharedImageInterface* sii =
test_context_provider->SharedImageInterface();
@@ -10671,7 +10947,7 @@ TEST_F(LayerTreeHostImplTest, UIResourceManagement) {
EXPECT_EQ(0u, sii->shared_image_count());
}
-TEST_F(LayerTreeHostImplTest, CreateETC1UIResource) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, CreateETC1UIResource) {
auto test_context_provider = viz::TestContextProvider::Create();
viz::TestSharedImageInterface* sii =
test_context_provider->SharedImageInterface();
@@ -10696,7 +10972,7 @@ TEST_F(LayerTreeHostImplTest, CreateETC1UIResource) {
EXPECT_NE(0u, id1);
}
-TEST_F(LayerTreeHostImplTest, ObeyMSAACaps) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ObeyMSAACaps) {
LayerTreeSettings msaaSettings = DefaultSettings();
msaaSettings.gpu_rasterization_msaa_sample_count = 4;
@@ -10806,22 +11082,27 @@ TEST_P(LayerTreeHostImplTestWithRenderer, ShutdownReleasesContext) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
struct Helper {
std::unique_ptr<viz::CopyOutputResult> unprocessed_result;
- void OnResult(std::unique_ptr<viz::CopyOutputResult> result) {
+ void OnResult(base::OnceClosure finished,
+ std::unique_ptr<viz::CopyOutputResult> result) {
unprocessed_result = std::move(result);
+ std::move(finished).Run();
}
} helper;
GetEffectNode(root)->has_copy_request = true;
+ base::RunLoop copy_request_run_loop;
GetPropertyTrees(root)->effect_tree.AddCopyRequest(
root->effect_tree_index(),
std::make_unique<viz::CopyOutputRequest>(
viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
- base::BindOnce(&Helper::OnResult, base::Unretained(&helper))));
+ base::BindOnce(&Helper::OnResult, base::Unretained(&helper),
+ copy_request_run_loop.QuitClosure())));
DrawFrame();
auto* sii = context_provider->SharedImageInterface();
// The CopyOutputResult has a ref on the viz::ContextProvider and a shared
// image allocated.
+ copy_request_run_loop.Run();
EXPECT_TRUE(helper.unprocessed_result);
EXPECT_FALSE(context_provider->HasOneRef());
EXPECT_EQ(1u, sii->shared_image_count());
@@ -10839,12 +11120,15 @@ TEST_P(LayerTreeHostImplTestWithRenderer, ShutdownReleasesContext) {
helper.unprocessed_result.reset();
}
-TEST_F(LayerTreeHostImplTest, ScrollUnknownNotOnAncestorChain) {
+// This tests the case where hit testing only on scrollable layers returns a
+// layer that's outside the scroll chain of the first hit test *any* layer. See
+// LayerTreeHostImpl::IsInitialScrollHitTestReliable for details.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestIsNotReliable) {
// If we ray cast a scroller that is not on the first layer's ancestor chain,
// we should return SCROLL_UNKNOWN.
gfx::Size viewport_size(50, 50);
gfx::Size content_size(100, 100);
- SetupViewportLayersInnerScrolls(viewport_size, content_size);
+ SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* occluder_layer = AddLayer();
occluder_layer->SetDrawsContent(true);
@@ -10863,20 +11147,27 @@ TEST_F(LayerTreeHostImplTest, ScrollUnknownNotOnAncestorChain) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
+ status.main_thread_scrolling_reasons);
+ }
}
-TEST_F(LayerTreeHostImplTest, ScrollUnknownScrollAncestorMismatch) {
+// Similar but different case to above. See
+// LayerTreeHostImpl::IsInitialScrollHitTestReliable for details.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestAncestorMismatch) {
// If we ray cast a scroller this is on the first layer's ancestor chain, but
// is not the first scroller we encounter when walking up from the layer, we
// should also return SCROLL_UNKNOWN.
gfx::Size viewport_size(50, 50);
gfx::Size content_size(100, 100);
- SetupViewportLayersInnerScrolls(viewport_size, content_size);
+ SetupViewportLayersOuterScrolls(viewport_size, content_size);
- LayerImpl* scroll_layer = InnerViewportScrollLayer();
+ LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerImpl* child_scroll_clip = AddLayer();
CopyProperties(scroll_layer, child_scroll_clip);
@@ -10899,12 +11190,17 @@ TEST_F(LayerTreeHostImplTest, ScrollUnknownScrollAncestorMismatch) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
+ status.main_thread_scrolling_reasons);
+ }
}
-TEST_F(LayerTreeHostImplTest, ScrollInvisibleScroller) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollInvisibleScroller) {
gfx::Size viewport_size(50, 50);
gfx::Size content_size(100, 100);
SetupViewportLayersInnerScrolls(viewport_size, content_size);
@@ -10932,7 +11228,8 @@ TEST_F(LayerTreeHostImplTest, ScrollInvisibleScroller) {
// Make sure LatencyInfo carried by LatencyInfoSwapPromise are passed
// in viz::CompositorFrameMetadata.
-TEST_F(LayerTreeHostImplTest, LatencyInfoPassedToCompositorFrameMetadata) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ LatencyInfoPassedToCompositorFrameMetadata) {
LayerTreeSettings settings = DefaultSettings();
settings.commit_to_active_tree = false;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -10962,7 +11259,8 @@ TEST_F(LayerTreeHostImplTest, LatencyInfoPassedToCompositorFrameMetadata) {
}
#if defined(OS_ANDROID)
-TEST_F(LayerTreeHostImplTest, SelectionBoundsPassedToCompositorFrameMetadata) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SelectionBoundsPassedToCompositorFrameMetadata) {
LayerImpl* root = SetupRootLayer<SolidColorLayerImpl>(
host_impl_->active_tree(), gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
@@ -10992,7 +11290,7 @@ TEST_F(LayerTreeHostImplTest, SelectionBoundsPassedToCompositorFrameMetadata) {
EXPECT_TRUE(selection_after.end.visible());
}
-TEST_F(LayerTreeHostImplTest, HiddenSelectionBoundsStayHidden) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, HiddenSelectionBoundsStayHidden) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
@@ -11051,7 +11349,7 @@ class SimpleSwapPromiseMonitor : public SwapPromiseMonitor {
int* set_needs_redraw_count_;
};
-TEST_F(LayerTreeHostImplTest, SimpleSwapPromiseMonitor) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SimpleSwapPromiseMonitor) {
int set_needs_commit_count = 0;
int set_needs_redraw_count = 0;
@@ -11622,7 +11920,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest,
// Tests that when we set a child scroller (e.g. a scrolling div) as the outer
// viewport, scrolling it controls the browser controls.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
ReplacedOuterViewportScrollsBrowserControls) {
const gfx::Size scroll_content_size(400, 400);
const gfx::Size root_layer_size(200, 200);
@@ -11859,7 +12157,8 @@ TEST_F(LayerTreeHostImplWithImplicitLimitsTest, ImplicitMemoryLimits) {
150u * 1024u * 1024u);
}
-TEST_F(LayerTreeHostImplTest, ExternalTransformReflectedInNextDraw) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ExternalTransformReflectedInNextDraw) {
const gfx::Size viewport_size(50, 50);
const gfx::Size layer_size(100, 100);
gfx::Transform external_transform;
@@ -11885,7 +12184,7 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformReflectedInNextDraw) {
external_transform, layer->draw_properties().target_space_transform);
}
-TEST_F(LayerTreeHostImplTest, ExternalTransformSetNeedsRedraw) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ExternalTransformSetNeedsRedraw) {
const gfx::Size viewport_size(100, 100);
SetupDefaultRootLayer(viewport_size);
UpdateDrawProperties(host_impl_->active_tree());
@@ -11914,7 +12213,7 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformSetNeedsRedraw) {
EXPECT_FALSE(last_on_draw_frame_->has_no_damage);
}
-TEST_F(LayerTreeHostImplTest, OnMemoryPressure) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OnMemoryPressure) {
gfx::Size size(200, 200);
viz::ResourceFormat format = viz::RGBA_8888;
gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
@@ -11936,7 +12235,7 @@ TEST_F(LayerTreeHostImplTest, OnMemoryPressure) {
EXPECT_LT(memory_usage_after_memory_pressure, current_memory_usage);
}
-TEST_F(LayerTreeHostImplTest, OnDrawConstraintSetNeedsRedraw) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OnDrawConstraintSetNeedsRedraw) {
const gfx::Size viewport_size(100, 100);
SetupDefaultRootLayer(viewport_size);
UpdateDrawProperties(host_impl_->active_tree());
@@ -11969,7 +12268,7 @@ TEST_F(LayerTreeHostImplTest, OnDrawConstraintSetNeedsRedraw) {
// This test verifies that the viewport damage rect is the full viewport and not
// just part of the viewport in the presence of an external viewport.
-TEST_F(LayerTreeHostImplTest, FullViewportDamageAfterOnDraw) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, FullViewportDamageAfterOnDraw) {
const gfx::Size viewport_size(100, 100);
SetupDefaultRootLayer(viewport_size);
UpdateDrawProperties(host_impl_->active_tree());
@@ -12089,7 +12388,8 @@ TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
EXPECT_TRUE(host_impl_->active_tree()->needs_update_draw_properties());
}
-TEST_F(LayerTreeHostImplTest, ExternalViewportAffectsVisibleRects) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ExternalViewportAffectsVisibleRects) {
const gfx::Size viewport_size(50, 50);
const gfx::Size layer_size(100, 100);
SetupViewportLayersInnerScrolls(viewport_size, layer_size);
@@ -12118,7 +12418,8 @@ TEST_F(LayerTreeHostImplTest, ExternalViewportAffectsVisibleRects) {
EXPECT_EQ(gfx::Rect(90, 90), content_layer->visible_layer_rect());
}
-TEST_F(LayerTreeHostImplTest, ExternalTransformAffectsVisibleRects) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ExternalTransformAffectsVisibleRects) {
const gfx::Size viewport_size(50, 50);
const gfx::Size layer_size(100, 100);
SetupViewportLayersInnerScrolls(viewport_size, layer_size);
@@ -12152,7 +12453,8 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformAffectsVisibleRects) {
EXPECT_EQ(gfx::Rect(50, 50), content_layer->visible_layer_rect());
}
-TEST_F(LayerTreeHostImplTest, ExternalTransformAffectsSublayerScaleFactor) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ExternalTransformAffectsSublayerScaleFactor) {
const gfx::Size viewport_size(50, 50);
const gfx::Size layer_size(100, 100);
SetupViewportLayersInnerScrolls(viewport_size, layer_size);
@@ -12196,7 +12498,7 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformAffectsSublayerScaleFactor) {
#if defined(OS_MACOSX)
// Ensure Mac wheel scrolling causes instant scrolling. This test can be removed
// once https://crbug.com/574283 is fixed.
-TEST_F(LayerTreeHostImplTest, MacWheelIsNonAnimated) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, MacWheelIsNonAnimated) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -12219,7 +12521,116 @@ TEST_F(LayerTreeHostImplTest, MacWheelIsNonAnimated) {
}
#endif
-TEST_F(LayerTreeHostImplTest, ScrollAnimated) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OneScrollForFirstScrollDelay) {
+ LayerTreeSettings settings = DefaultSettings();
+ settings.commit_to_active_tree = false;
+ CreateHostImpl(settings, CreateLayerTreeFrameSink());
+ SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(),
+ gfx::Size(10, 10));
+ UpdateDrawProperties(host_impl_->active_tree());
+ EXPECT_EQ(first_scroll_observed, 0);
+
+ // LatencyInfo for the first scroll.
+ ui::LatencyInfo latency_info;
+ latency_info.set_trace_id(5);
+ latency_info.AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT);
+ std::unique_ptr<SwapPromise> swap_promise(
+ new LatencyInfoSwapPromise(latency_info));
+ host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise));
+
+ host_impl_->SetFullViewportDamage();
+ host_impl_->SetNeedsRedraw();
+ DrawFrame();
+
+ constexpr uint32_t frame_token_1 = 1;
+ viz::FrameTimingDetails mock_details;
+ mock_details.presentation_feedback = ExampleFeedback();
+ // When the LayerTreeHostImpl receives presentation feedback, the callback
+ // will be fired.
+ host_impl_->DidPresentCompositorFrame(frame_token_1, mock_details);
+
+ EXPECT_EQ(first_scroll_observed, 1);
+}
+
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OtherInputsForFirstScrollDelay) {
+ LayerTreeSettings settings = DefaultSettings();
+ settings.commit_to_active_tree = false;
+ CreateHostImpl(settings, CreateLayerTreeFrameSink());
+ SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(),
+ gfx::Size(10, 10));
+ UpdateDrawProperties(host_impl_->active_tree());
+ EXPECT_EQ(first_scroll_observed, 0);
+
+ // LatencyInfo for the first input, which is not scroll.
+ ui::LatencyInfo latency_info;
+ latency_info.set_trace_id(5);
+ latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT);
+ std::unique_ptr<SwapPromise> swap_promise(
+ new LatencyInfoSwapPromise(latency_info));
+ host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise));
+
+ host_impl_->SetFullViewportDamage();
+ host_impl_->SetNeedsRedraw();
+ DrawFrame();
+
+ constexpr uint32_t frame_token_1 = 1;
+ viz::FrameTimingDetails mock_details;
+ mock_details.presentation_feedback = ExampleFeedback();
+ // When the LayerTreeHostImpl receives presentation feedback, the callback
+ // will be fired.
+ host_impl_->DidPresentCompositorFrame(frame_token_1, mock_details);
+
+ EXPECT_EQ(first_scroll_observed, 0);
+}
+
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, MultipleScrollsForFirstScrollDelay) {
+ LayerTreeSettings settings = DefaultSettings();
+ settings.commit_to_active_tree = false;
+ CreateHostImpl(settings, CreateLayerTreeFrameSink());
+ SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(),
+ gfx::Size(10, 10));
+ UpdateDrawProperties(host_impl_->active_tree());
+ EXPECT_EQ(first_scroll_observed, 0);
+
+ // LatencyInfo for the first scroll.
+ ui::LatencyInfo latency_info;
+ latency_info.set_trace_id(5);
+ latency_info.AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT);
+ std::unique_ptr<SwapPromise> swap_promise(
+ new LatencyInfoSwapPromise(latency_info));
+ host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise));
+ DrawFrame();
+ constexpr uint32_t frame_token_1 = 1;
+ viz::FrameTimingDetails mock_details;
+ mock_details.presentation_feedback = ExampleFeedback();
+ // When the LayerTreeHostImpl receives presentation feedback, the callback
+ // will be fired.
+ host_impl_->DidPresentCompositorFrame(frame_token_1, mock_details);
+ EXPECT_EQ(first_scroll_observed, 1);
+
+ // LatencyInfo for the second scroll.
+ ui::LatencyInfo latency_info2;
+ latency_info2.set_trace_id(6);
+ latency_info2.AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT);
+ std::unique_ptr<SwapPromise> swap_promise2(
+ new LatencyInfoSwapPromise(latency_info2));
+ host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise2));
+ host_impl_->SetFullViewportDamage();
+ host_impl_->SetNeedsRedraw();
+ DrawFrame();
+ constexpr uint32_t frame_token_2 = 2;
+ viz::FrameTimingDetails mock_details2;
+ mock_details2.presentation_feedback = ExampleFeedback();
+ // When the LayerTreeHostImpl receives presentation feedback, the callback
+ // will be fired.
+ host_impl_->DidPresentCompositorFrame(frame_token_2, mock_details2);
+ EXPECT_EQ(first_scroll_observed, 1);
+}
+
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimated) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -12327,7 +12738,7 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimated) {
// Tests latching behavior, in particular when a ScrollEnd is received but a
// new ScrollBegin is received before the animation from the previous gesture
// stream is finished.
-TEST_F(LayerTreeHostImplTest, ScrollAnimatedLatching) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimatedLatching) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -12426,7 +12837,7 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimatedLatching) {
// factor. That is, if you zoom into the page, a wheel scroll should scroll the
// content *less* than before so that it appears to move the same distance when
// zoomed in.
-TEST_F(LayerTreeHostImplTest, ScrollAnimatedWhileZoomed) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimatedWhileZoomed) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -12503,7 +12914,7 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimatedWhileZoomed) {
// inner viewport is animating. Specifically this test makes sure the animation
// update doesn't get confused between the currently scrolling node and the
// currently animating node which are different. See https://crbug.com/1070561.
-TEST_F(LayerTreeHostImplTest, ScrollAnimatedUpdateInnerViewport) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimatedUpdateInnerViewport) {
const gfx::Size content_size(210, 1000);
const gfx::Size viewport_size(200, 200);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -12602,7 +13013,8 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimatedUpdateInnerViewport) {
}
// This tests that faded-out Aura scrollbars can't be interacted with.
-TEST_F(LayerTreeHostImplTest, FadedOutPaintedOverlayScrollbarHitTest) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ FadedOutPaintedOverlayScrollbarHitTest) {
LayerTreeSettings settings = DefaultSettings();
settings.compositor_threaded_scrollbar_scrolling = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -12645,7 +13057,7 @@ TEST_F(LayerTreeHostImplTest, FadedOutPaintedOverlayScrollbarHitTest) {
auto& scrollbar_effect_node = CreateEffectNode(scrollbar);
scrollbar_effect_node.opacity = 0.8;
- host_impl_->MouseDown(gfx::PointF(350, 18), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 18), /*jump_key_modifier*/ false);
InputHandlerPointerResult result =
host_impl_->MouseMoveAt(gfx::Point(350, 28));
EXPECT_GT(result.scroll_offset.y(), 0u);
@@ -12654,7 +13066,7 @@ TEST_F(LayerTreeHostImplTest, FadedOutPaintedOverlayScrollbarHitTest) {
// Scrolling shouldn't occur at opacity = 0.
scrollbar_effect_node.opacity = 0;
- host_impl_->MouseDown(gfx::PointF(350, 18), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 18), /*jump_key_modifier*/ false);
result = host_impl_->MouseMoveAt(gfx::Point(350, 28));
EXPECT_EQ(result.scroll_offset.y(), 0u);
host_impl_->MouseUp(gfx::PointF(350, 28));
@@ -12664,11 +13076,73 @@ TEST_F(LayerTreeHostImplTest, FadedOutPaintedOverlayScrollbarHitTest) {
host_impl_ = nullptr;
}
+// Tests that deleting a horizontal scrollbar doesn't affect the autoscroll task
+// for the vertical scrollbar.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, AutoscrollOnDeletedScrollbar) {
+ LayerTreeSettings settings = DefaultSettings();
+ settings.compositor_threaded_scrollbar_scrolling = true;
+ CreateHostImpl(settings, CreateLayerTreeFrameSink());
+
+ // Setup the viewport.
+ const gfx::Size viewport_size = gfx::Size(360, 600);
+ const gfx::Size content_size = gfx::Size(345, 3800);
+ SetupViewportLayersOuterScrolls(viewport_size, content_size);
+ LayerImpl* scroll_layer = OuterViewportScrollLayer();
+
+ // Set up the scrollbar and its dimensions.
+ LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
+ auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
+ layer_tree_impl, ScrollbarOrientation::VERTICAL, false, true);
+ SetupScrollbarLayerCommon(scroll_layer, scrollbar);
+ scrollbar->SetHitTestable(true);
+
+ const gfx::Size scrollbar_size = gfx::Size(15, 600);
+ scrollbar->SetBounds(scrollbar_size);
+
+ // Set up the thumb dimensions.
+ scrollbar->SetThumbThickness(15);
+ scrollbar->SetThumbLength(50);
+ scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
+ scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
+
+ TestInputHandlerClient input_handler_client;
+ host_impl_->BindToClient(&input_handler_client);
+
+ // PointerDown on the scrollbar schedules an autoscroll task.
+ host_impl_->MouseDown(gfx::PointF(350, 580), /*jump_key_modifier*/ false);
+ EXPECT_TRUE(!host_impl_->CurrentlyScrollingNode());
+
+ // Now, unregister the horizontal scrollbar and test that the autoscroll task
+ // that was scheduled for the vertical scrollbar still exists. (Note that
+ // adding a horizontal scrollbar layer is not needed. This test merely checks
+ // the logic inside ScrollbarController::DidUnregisterScrollbar)
+ host_impl_->DidUnregisterScrollbarLayer(scroll_layer->element_id(),
+ ScrollbarOrientation::HORIZONTAL);
+ EXPECT_TRUE(host_impl_->scrollbar_controller_for_testing()
+ ->AutoscrollTaskIsScheduled());
+
+ // If a call comes in to delete the scrollbar layer for which the autoscroll
+ // was scheduled, the autoscroll task should be cancelled.
+ host_impl_->DidUnregisterScrollbarLayer(scroll_layer->element_id(),
+ ScrollbarOrientation::VERTICAL);
+ EXPECT_FALSE(host_impl_->scrollbar_controller_for_testing()
+ ->AutoscrollTaskIsScheduled());
+
+ // End the scroll.
+ host_impl_->MouseUp(gfx::PointF(350, 580));
+ host_impl_->ScrollEnd();
+ EXPECT_TRUE(!host_impl_->CurrentlyScrollingNode());
+
+ // Tear down the LayerTreeHostImpl before the InputHandlerClient.
+ host_impl_->ReleaseLayerTreeFrameSink();
+ host_impl_ = nullptr;
+}
+
// Tests that a pointerdown followed by pointermove(s) produces
// InputHandlerPointerResult with scroll_offset > 0 even though the GSB might
// have been dispatched *after* the first pointermove was handled by the
// ScrollbarController.
-TEST_F(LayerTreeHostImplTest, PointerMoveOutOfSequence) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PointerMoveOutOfSequence) {
LayerTreeSettings settings = DefaultSettings();
settings.compositor_threaded_scrollbar_scrolling = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -12701,7 +13175,7 @@ TEST_F(LayerTreeHostImplTest, PointerMoveOutOfSequence) {
// PointerDown sets up the state needed to initiate a drag. Although, the
// resulting GSB won't be dispatched until the next VSync. Hence, the
// CurrentlyScrollingNode is expected to be null.
- host_impl_->MouseDown(gfx::PointF(350, 18), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 18), /*jump_key_modifier*/ false);
EXPECT_TRUE(!host_impl_->CurrentlyScrollingNode());
// PointerMove arrives before the next VSync. This still needs to be handled
@@ -12740,7 +13214,7 @@ TEST_F(LayerTreeHostImplTest, PointerMoveOutOfSequence) {
}
// This tests that faded-out Mac scrollbars can't be interacted with.
-TEST_F(LayerTreeHostImplTest, FadedOutPaintedScrollbarHitTest) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, FadedOutPaintedScrollbarHitTest) {
LayerTreeSettings settings = DefaultSettings();
settings.compositor_threaded_scrollbar_scrolling = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -12774,14 +13248,14 @@ TEST_F(LayerTreeHostImplTest, FadedOutPaintedScrollbarHitTest) {
// scroll.
scrollbar->set_scrollbar_painted_opacity(0);
InputHandlerPointerResult result =
- host_impl_->MouseDown(gfx::PointF(350, 100), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 100), /*jump_key_modifier*/ false);
EXPECT_EQ(result.scroll_offset.y(), 0u);
// MouseDown on the track of a scrollbar with opacity > 0 should produce a
// scroll.
scrollbar->set_scrollbar_painted_opacity(1);
result =
- host_impl_->MouseDown(gfx::PointF(350, 100), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 100), /*jump_key_modifier*/ false);
EXPECT_GT(result.scroll_offset.y(), 0u);
// Tear down the LayerTreeHostImpl before the InputHandlerClient.
@@ -12789,7 +13263,8 @@ TEST_F(LayerTreeHostImplTest, FadedOutPaintedScrollbarHitTest) {
host_impl_ = nullptr;
}
-TEST_F(LayerTreeHostImplTest, SingleGSUForScrollbarThumbDragPerFrame) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SingleGSUForScrollbarThumbDragPerFrame) {
LayerTreeSettings settings = DefaultSettings();
settings.compositor_threaded_scrollbar_scrolling = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -12839,7 +13314,7 @@ TEST_F(LayerTreeHostImplTest, SingleGSUForScrollbarThumbDragPerFrame) {
// MouseDown on the thumb should not produce a scroll.
InputHandlerPointerResult result =
- host_impl_->MouseDown(gfx::PointF(350, 18), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 18), /*jump_key_modifier*/ false);
EXPECT_EQ(result.scroll_offset.y(), 0u);
// The first request for a GSU should be processed as expected.
@@ -12875,9 +13350,176 @@ TEST_F(LayerTreeHostImplTest, SingleGSUForScrollbarThumbDragPerFrame) {
host_impl_ = nullptr;
}
+// Tests that the scheduled autoscroll task aborts if a 2nd mousedown occurs in
+// the same frame.
+TEST_F(LayerTreeHostImplTest, AutoscrollTaskAbort) {
+ LayerTreeSettings settings = DefaultSettings();
+ settings.compositor_threaded_scrollbar_scrolling = true;
+ CreateHostImpl(settings, CreateLayerTreeFrameSink());
+
+ // Setup the viewport.
+ const gfx::Size viewport_size = gfx::Size(360, 600);
+ const gfx::Size content_size = gfx::Size(345, 3800);
+ SetupViewportLayersOuterScrolls(viewport_size, content_size);
+ LayerImpl* scroll_layer = OuterViewportScrollLayer();
+
+ // Set up the scrollbar and its dimensions.
+ LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
+ auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
+ layer_tree_impl, VERTICAL, /*is_left_side_vertical_scrollbar*/ false,
+ /*is_overlay*/ false);
+
+ SetupScrollbarLayer(scroll_layer, scrollbar);
+ const gfx::Size scrollbar_size = gfx::Size(15, 600);
+ scrollbar->SetBounds(scrollbar_size);
+ host_impl_->set_force_smooth_wheel_scrolling_for_testing(true);
+
+ // Set up the thumb dimensions.
+ scrollbar->SetThumbThickness(15);
+ scrollbar->SetThumbLength(50);
+ scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
+
+ // Set up scrollbar arrows.
+ scrollbar->SetBackButtonRect(
+ gfx::Rect(gfx::Point(345, 0), gfx::Size(15, 15)));
+ scrollbar->SetForwardButtonRect(
+ gfx::Rect(gfx::Point(345, 570), gfx::Size(15, 15)));
+ scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
+
+ TestInputHandlerClient input_handler_client;
+ host_impl_->BindToClient(&input_handler_client);
+
+ {
+ // An autoscroll task gets scheduled on mousedown.
+ InputHandlerPointerResult result = host_impl_->MouseDown(
+ gfx::PointF(350, 575), /*jump_key_modifier*/ false);
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ auto begin_state = BeginState(gfx::Point(350, 575), gfx::Vector2d(0, 40),
+ ui::ScrollInputType::kScrollbar);
+ EXPECT_EQ(
+ InputHandler::SCROLL_ON_IMPL_THREAD,
+ host_impl_
+ ->ScrollBegin(begin_state.get(), ui::ScrollInputType::kScrollbar)
+ .thread);
+ EXPECT_TRUE(host_impl_->scrollbar_controller_for_testing()
+ ->AutoscrollTaskIsScheduled());
+ }
+
+ {
+ // Another mousedown occurs in the same frame. InputHandlerProxy calls
+ // LayerTreeHostImpl::ScrollEnd and the autoscroll task should be cancelled.
+ InputHandlerPointerResult result = host_impl_->MouseDown(
+ gfx::PointF(350, 575), /*jump_key_modifier*/ false);
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ host_impl_->ScrollEnd();
+ EXPECT_FALSE(host_impl_->scrollbar_controller_for_testing()
+ ->AutoscrollTaskIsScheduled());
+ }
+
+ // Tear down the LayerTreeHostImpl before the InputHandlerClient.
+ host_impl_->ReleaseLayerTreeFrameSink();
+ host_impl_ = nullptr;
+}
+
+// Tests that the ScrollbarController handles jump clicks.
+TEST_F(LayerTreeHostImplTest, JumpOnScrollbarClick) {
+ LayerTreeSettings settings = DefaultSettings();
+ settings.compositor_threaded_scrollbar_scrolling = true;
+ CreateHostImpl(settings, CreateLayerTreeFrameSink());
+
+ // Setup the viewport.
+ const gfx::Size viewport_size = gfx::Size(360, 600);
+ const gfx::Size content_size = gfx::Size(345, 3800);
+ SetupViewportLayersOuterScrolls(viewport_size, content_size);
+ LayerImpl* scroll_layer = OuterViewportScrollLayer();
+
+ // Set up the scrollbar and its dimensions.
+ LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
+ auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
+ layer_tree_impl, VERTICAL, /*is_left_side_vertical_scrollbar*/ false,
+ /*is_overlay*/ false);
+
+ SetupScrollbarLayer(scroll_layer, scrollbar);
+ const gfx::Size scrollbar_size = gfx::Size(15, 600);
+ scrollbar->SetBounds(scrollbar_size);
+ host_impl_->set_force_smooth_wheel_scrolling_for_testing(true);
+
+ // Set up the thumb dimensions.
+ scrollbar->SetThumbThickness(15);
+ scrollbar->SetThumbLength(50);
+ scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
+
+ // Set up scrollbar arrows.
+ scrollbar->SetBackButtonRect(
+ gfx::Rect(gfx::Point(345, 0), gfx::Size(15, 15)));
+ scrollbar->SetForwardButtonRect(
+ gfx::Rect(gfx::Point(345, 570), gfx::Size(15, 15)));
+ scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
+
+ TestInputHandlerClient input_handler_client;
+ host_impl_->BindToClient(&input_handler_client);
+
+ // Verify all 4 combinations of JumpOnTrackClick and jump_key_modifier.
+ {
+ // Click on track when JumpOnTrackClick is false and jump_key_modifier is
+ // false. Expected to perform a regular track scroll.
+ scrollbar->SetJumpOnTrackClick(false);
+ InputHandlerPointerResult result = host_impl_->MouseDown(
+ gfx::PointF(350, 400), /*jump_key_modifier*/ false);
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 525);
+ result = host_impl_->MouseUp(gfx::PointF(350, 400));
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 0);
+ }
+
+ {
+ // Click on track when JumpOnTrackClick is false and jump_key_modifier is
+ // true. Expected to perform scroller jump to the clicked location.
+ scrollbar->SetJumpOnTrackClick(false);
+ InputHandlerPointerResult result = host_impl_->MouseDown(
+ gfx::PointF(350, 400), /*jump_key_modifier*/ true);
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 2194);
+ result = host_impl_->MouseUp(gfx::PointF(350, 400));
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 0);
+ }
+
+ {
+ // Click on track when JumpOnTrackClick is true and jump_key_modifier is
+ // false. Expected to perform scroller jump to the clicked location.
+ scrollbar->SetJumpOnTrackClick(true);
+ InputHandlerPointerResult result = host_impl_->MouseDown(
+ gfx::PointF(350, 400), /*jump_key_modifier*/ false);
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 2194);
+ result = host_impl_->MouseUp(gfx::PointF(350, 400));
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 0);
+ }
+
+ {
+ // Click on track when JumpOnTrackClick is true and jump_key_modifier is
+ // true. Expected to perform a regular track scroll.
+ scrollbar->SetJumpOnTrackClick(true);
+ InputHandlerPointerResult result = host_impl_->MouseDown(
+ gfx::PointF(350, 400), /*jump_key_modifier*/ true);
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 525);
+ result = host_impl_->MouseUp(gfx::PointF(350, 400));
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 0);
+ }
+
+ // Tear down the LayerTreeHostImpl before the InputHandlerClient.
+ host_impl_->ReleaseLayerTreeFrameSink();
+ host_impl_ = nullptr;
+}
+
// Tests that an animated scrollbar scroll aborts when a different device (like
// a mousewheel) wants to animate the scroll offset.
-TEST_F(LayerTreeHostImplTest, AnimatedScrollYielding) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, AnimatedScrollYielding) {
LayerTreeSettings settings = DefaultSettings();
settings.compositor_threaded_scrollbar_scrolling = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -12919,7 +13561,7 @@ TEST_F(LayerTreeHostImplTest, AnimatedScrollYielding) {
{
// Set up an animated scrollbar autoscroll.
host_impl_->scrollbar_controller_for_testing()->HandlePointerDown(
- gfx::PointF(350, 560), /*shift_modifier*/ false);
+ gfx::PointF(350, 560), /*jump_key_modifier*/ false);
auto begin_state = BeginState(gfx::Point(350, 560), gfx::Vector2d(0, 40),
ui::ScrollInputType::kScrollbar);
EXPECT_EQ(
@@ -12981,7 +13623,7 @@ TEST_F(LayerTreeHostImplTest, AnimatedScrollYielding) {
// Tests that changing the scroller length in the middle of a thumb drag doesn't
// cause the scroller to jump.
-TEST_F(LayerTreeHostImplTest, ThumbDragScrollerLengthIncrease) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ThumbDragScrollerLengthIncrease) {
LayerTreeSettings settings = DefaultSettings();
settings.compositor_threaded_scrollbar_scrolling = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -13028,7 +13670,7 @@ TEST_F(LayerTreeHostImplTest, ThumbDragScrollerLengthIncrease) {
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
InputHandlerPointerResult result =
- host_impl_->MouseDown(gfx::PointF(350, 18), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 18), /*jump_key_modifier*/ false);
EXPECT_EQ(result.scroll_offset.y(), 0);
EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
host_impl_->DidFinishImplFrame(begin_frame_args);
@@ -13082,7 +13724,7 @@ TEST_F(LayerTreeHostImplTest, ThumbDragScrollerLengthIncrease) {
host_impl_ = nullptr;
}
-TEST_F(LayerTreeHostImplTest, MainThreadFallback) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, MainThreadFallback) {
LayerTreeSettings settings = DefaultSettings();
settings.compositor_threaded_scrollbar_scrolling = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -13118,7 +13760,7 @@ TEST_F(LayerTreeHostImplTest, MainThreadFallback) {
// Clicking on the track should produce a scroll on the compositor thread.
InputHandlerPointerResult compositor_threaded_scrolling_result =
- host_impl_->MouseDown(gfx::PointF(350, 500), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 500), /*jump_key_modifier*/ false);
host_impl_->MouseUp(gfx::PointF(350, 500));
EXPECT_EQ(compositor_threaded_scrolling_result.scroll_offset.y(), 525u);
EXPECT_FALSE(GetScrollNode(scroll_layer)->main_thread_scrolling_reasons);
@@ -13129,7 +13771,7 @@ TEST_F(LayerTreeHostImplTest, MainThreadFallback) {
GetScrollNode(scroll_layer)->main_thread_scrolling_reasons =
MainThreadScrollingReason::kHasNonLayerViewportConstrainedObjects;
compositor_threaded_scrolling_result =
- host_impl_->MouseDown(gfx::PointF(350, 500), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 500), /*jump_key_modifier*/ false);
host_impl_->MouseUp(gfx::PointF(350, 500));
EXPECT_EQ(compositor_threaded_scrolling_result.scroll_offset.y(), 0u);
@@ -13138,7 +13780,8 @@ TEST_F(LayerTreeHostImplTest, MainThreadFallback) {
host_impl_ = nullptr;
}
-TEST_F(LayerTreeHostImplTest, SecondScrollAnimatedBeginNotIgnored) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SecondScrollAnimatedBeginNotIgnored) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -13165,7 +13808,8 @@ TEST_F(LayerTreeHostImplTest, SecondScrollAnimatedBeginNotIgnored) {
// Verfify that a smooth scroll animation doesn't jump when UpdateTarget gets
// called before the animation is started.
-TEST_F(LayerTreeHostImplTest, AnimatedScrollUpdateTargetBeforeStarting) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ AnimatedScrollUpdateTargetBeforeStarting) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -13223,7 +13867,7 @@ TEST_F(LayerTreeHostImplTest, AnimatedScrollUpdateTargetBeforeStarting) {
EXPECT_TRUE(y > 1 && y < 49);
}
-TEST_F(LayerTreeHostImplTest, ScrollAnimatedWithDelay) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimatedWithDelay) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -13752,7 +14396,7 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedChangingBounds) {
EXPECT_EQ(gfx::ScrollOffset(250, 250), CurrentScrollOffset(scrolling_layer));
}
-TEST_F(LayerTreeHostImplTest, InvalidLayerNotAddedToRasterQueue) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, InvalidLayerNotAddedToRasterQueue) {
CreatePendingTree();
Region empty_invalidation;
@@ -13786,7 +14430,7 @@ TEST_F(LayerTreeHostImplTest, InvalidLayerNotAddedToRasterQueue) {
EXPECT_TRUE(empty_raster_priority_queue_all->IsEmpty());
}
-TEST_F(LayerTreeHostImplTest, DidBecomeActive) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, DidBecomeActive) {
CreatePendingTree();
host_impl_->ActivateSyncTree();
CreatePendingTree();
@@ -13819,7 +14463,8 @@ TEST_F(LayerTreeHostImplTest, DidBecomeActive) {
EXPECT_EQ(2u, raw_mask_layer->did_become_active_call_count());
}
-TEST_F(LayerTreeHostImplTest, WheelScrollWithPageScaleFactorOnInnerLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ WheelScrollWithPageScaleFactorOnInnerLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect(50, 50));
@@ -13892,7 +14537,7 @@ size_t CountRenderPassesWithId(const viz::RenderPassList& list,
[id](const std::unique_ptr<viz::RenderPass>& p) { return p->id == id; });
}
-TEST_F(LayerTreeHostImplTest, RemoveUnreferencedRenderPass) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RemoveUnreferencedRenderPass) {
TestFrameData frame;
frame.render_passes.push_back(viz::RenderPass::Create());
viz::RenderPass* pass3 = frame.render_passes.back().get();
@@ -13927,7 +14572,7 @@ TEST_F(LayerTreeHostImplTest, RemoveUnreferencedRenderPass) {
EXPECT_EQ(1u, frame.render_passes[0]->id);
}
-TEST_F(LayerTreeHostImplTest, RemoveEmptyRenderPass) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RemoveEmptyRenderPass) {
TestFrameData frame;
frame.render_passes.push_back(viz::RenderPass::Create());
viz::RenderPass* pass3 = frame.render_passes.back().get();
@@ -13968,7 +14613,7 @@ TEST_F(LayerTreeHostImplTest, RemoveEmptyRenderPass) {
pass1->quad_list.ElementAt(0)->material);
}
-TEST_F(LayerTreeHostImplTest, DoNotRemoveEmptyRootRenderPass) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, DoNotRemoveEmptyRootRenderPass) {
TestFrameData frame;
frame.render_passes.push_back(viz::RenderPass::Create());
viz::RenderPass* pass3 = frame.render_passes.back().get();
@@ -14024,7 +14669,7 @@ class FakeVideoFrameController : public VideoFrameController {
bool did_draw_frame_ = false;
};
-TEST_F(LayerTreeHostImplTest, AddVideoFrameControllerInsideFrame) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, AddVideoFrameControllerInsideFrame) {
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2);
FakeVideoFrameController controller;
@@ -14047,7 +14692,8 @@ TEST_F(LayerTreeHostImplTest, AddVideoFrameControllerInsideFrame) {
EXPECT_FALSE(controller.did_draw_frame());
}
-TEST_F(LayerTreeHostImplTest, AddVideoFrameControllerOutsideFrame) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ AddVideoFrameControllerOutsideFrame) {
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2);
FakeVideoFrameController controller;
@@ -14078,7 +14724,8 @@ TEST_F(LayerTreeHostImplTest, AddVideoFrameControllerOutsideFrame) {
}
// Tests that SetDeviceScaleFactor correctly impacts GPU rasterization.
-TEST_F(LayerTreeHostImplTest, GpuRasterizationStatusDeviceScaleFactor) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ GpuRasterizationStatusDeviceScaleFactor) {
// Create a host impl with MSAA support (4 samples).
LayerTreeSettings msaaSettings = DefaultSettings();
msaaSettings.gpu_rasterization_msaa_sample_count = -1;
@@ -14108,7 +14755,8 @@ TEST_F(LayerTreeHostImplTest, GpuRasterizationStatusDeviceScaleFactor) {
}
// Tests that explicit MSAA sample count correctly impacts GPU rasterization.
-TEST_F(LayerTreeHostImplTest, GpuRasterizationStatusExplicitMSAACount) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ GpuRasterizationStatusExplicitMSAACount) {
// Create a host impl with MSAA support and a forced sample count of 4.
LayerTreeSettings msaaSettings = DefaultSettings();
msaaSettings.gpu_rasterization_msaa_sample_count = 4;
@@ -14245,7 +14893,7 @@ TEST_F(MsaaCompatibilityLayerTreeHostImplTest,
0);
}
-TEST_F(LayerTreeHostImplTest, UpdatePageScaleFactorOnActiveTree) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, UpdatePageScaleFactorOnActiveTree) {
// Check page scale factor update in property trees when an update is made
// on the active tree.
CreatePendingTree();
@@ -14293,7 +14941,8 @@ TEST_F(LayerTreeHostImplTest, UpdatePageScaleFactorOnActiveTree) {
EXPECT_EQ(gfx::Point3F(), active_tree_node->origin);
}
-TEST_F(LayerTreeHostImplTest, SubLayerScaleForNodeInSubtreeOfPageScaleLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SubLayerScaleForNodeInSubtreeOfPageScaleLayer) {
// Checks that the sublayer scale of a transform node in the subtree of the
// page scale layer is updated without a property tree rebuild.
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 3);
@@ -14321,7 +14970,8 @@ TEST_F(LayerTreeHostImplTest, SubLayerScaleForNodeInSubtreeOfPageScaleLayer) {
// Checks that if we lose a GPU raster enabled LayerTreeFrameSink and replace
// it with a software LayerTreeFrameSink, LayerTreeHostImpl correctly
// re-computes GPU rasterization status.
-TEST_F(LayerTreeHostImplTest, RecomputeGpuRasterOnLayerTreeFrameSinkChange) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ RecomputeGpuRasterOnLayerTreeFrameSinkChange) {
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
@@ -14533,7 +15183,7 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates(
animation_task_.Reset();
// Only the MouseMove's location will affect the overlay scrollbar.
- host_impl_->MouseDown(gfx::PointF(60, 50), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(60, 50), /*jump_key_modifier*/ false);
host_impl_->MouseMoveAt(gfx::Point(60, 50));
host_impl_->MouseUp(gfx::PointF(60, 50));
@@ -14544,29 +15194,30 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates(
host_impl_->MouseMoveAt(gfx::Point(40, 150));
animation_task_.Reset();
- host_impl_->MouseDown(gfx::PointF(40, 150), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(40, 150), /*jump_key_modifier*/ false);
host_impl_->MouseUp(gfx::PointF(40, 150));
EXPECT_TRUE(animation_task_.is_null());
// Near scrollbar_1, then mouse down and unregister
// scrollbar_2_animation_controller, then mouse up should not cause crash.
host_impl_->MouseMoveAt(gfx::Point(40, 150));
- host_impl_->MouseDown(gfx::PointF(40, 150), /*shift_modifier*/ false);
- host_impl_->DidUnregisterScrollbarLayer(root_scroll->element_id());
+ host_impl_->MouseDown(gfx::PointF(40, 150), /*jump_key_modifier*/ false);
+ host_impl_->DidUnregisterScrollbarLayer(root_scroll->element_id(),
+ ScrollbarOrientation::VERTICAL);
host_impl_->MouseUp(gfx::PointF(40, 150));
}
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
LayerTreeHostImplTestScrollbarStatesInMainThreadScrolling) {
SetupMouseMoveAtTestScrollbarStates(true);
}
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
LayerTreeHostImplTestScrollbarStatesInNotMainThreadScrolling) {
SetupMouseMoveAtTestScrollbarStates(false);
}
-TEST_F(LayerTreeHostImplTest, CheckerImagingTileInvalidation) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, CheckerImagingTileInvalidation) {
LayerTreeSettings settings = LegacySWSettings();
settings.commit_to_active_tree = false;
settings.enable_checker_imaging = true;
@@ -14651,7 +15302,7 @@ TEST_F(LayerTreeHostImplTest, CheckerImagingTileInvalidation) {
EXPECT_EQ(expected_invalidation, *(root->GetPendingInvalidation()));
}
-TEST_F(LayerTreeHostImplTest, RasterColorSpace) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RasterColorSpace) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
// The default raster color space should be sRGB.
@@ -14666,7 +15317,7 @@ TEST_F(LayerTreeHostImplTest, RasterColorSpace) {
gfx::ColorSpace::CreateDisplayP3D65());
}
-TEST_F(LayerTreeHostImplTest, RasterColorSpaceSoftware) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RasterColorSpaceSoftware) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, FakeLayerTreeFrameSink::CreateSoftware());
// Software composited resources should always use sRGB as their color space.
@@ -14680,7 +15331,7 @@ TEST_F(LayerTreeHostImplTest, RasterColorSpaceSoftware) {
gfx::ColorSpace::CreateSRGB());
}
-TEST_F(LayerTreeHostImplTest, RasterColorPrefersSRGB) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RasterColorPrefersSRGB) {
auto p3 = gfx::ColorSpace::CreateDisplayP3D65();
LayerTreeSettings settings = DefaultSettings();
@@ -14697,7 +15348,7 @@ TEST_F(LayerTreeHostImplTest, RasterColorPrefersSRGB) {
EXPECT_EQ(host_impl_->GetRasterColorSpace(gfx::ContentColorUsage::kSRGB), p3);
}
-TEST_F(LayerTreeHostImplTest, UpdatedTilingsForNonDrawingLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, UpdatedTilingsForNonDrawingLayers) {
gfx::Size layer_bounds(500, 500);
CreatePendingTree();
@@ -14749,7 +15400,8 @@ TEST_F(LayerTreeHostImplTest, UpdatedTilingsForNonDrawingLayers) {
->can_require_tiles_for_activation());
}
-TEST_F(LayerTreeHostImplTest, RasterTilePrioritizationForNonDrawingLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ RasterTilePrioritizationForNonDrawingLayers) {
gfx::Size layer_bounds(500, 500);
CreatePendingTree();
auto* root =
@@ -14811,7 +15463,7 @@ TEST_F(LayerTreeHostImplTest, RasterTilePrioritizationForNonDrawingLayers) {
EXPECT_EQ(queue->Top().tile()->layer_id(), 3);
}
-TEST_F(LayerTreeHostImplTest, DrawAfterDroppingTileResources) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, DrawAfterDroppingTileResources) {
LayerTreeSettings settings = DefaultSettings();
settings.using_synchronous_renderer_compositor = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -14845,20 +15497,20 @@ TEST_F(LayerTreeHostImplTest, DrawAfterDroppingTileResources) {
EXPECT_GT(layer->tilings()->num_tilings(), 0u);
}
-TEST_F(LayerTreeHostImplTest, WhiteListedTouchActionTest1) {
- WhiteListedTouchActionTestHelper(1.0f, 1.0f);
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, AllowedTouchActionTest1) {
+ AllowedTouchActionTestHelper(1.0f, 1.0f);
}
-TEST_F(LayerTreeHostImplTest, WhiteListedTouchActionTest2) {
- WhiteListedTouchActionTestHelper(1.0f, 0.789f);
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, AllowedTouchActionTest2) {
+ AllowedTouchActionTestHelper(1.0f, 0.789f);
}
-TEST_F(LayerTreeHostImplTest, WhiteListedTouchActionTest3) {
- WhiteListedTouchActionTestHelper(2.345f, 1.0f);
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, AllowedTouchActionTest3) {
+ AllowedTouchActionTestHelper(2.345f, 1.0f);
}
-TEST_F(LayerTreeHostImplTest, WhiteListedTouchActionTest4) {
- WhiteListedTouchActionTestHelper(2.654f, 0.678f);
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, AllowedTouchActionTest4) {
+ AllowedTouchActionTestHelper(2.654f, 0.678f);
}
// Test implementation of RenderFrameMetadataObserver which can optionally
@@ -14893,7 +15545,7 @@ class TestRenderFrameMetadataObserver : public RenderFrameMetadataObserver {
base::Optional<RenderFrameMetadata> last_metadata_;
};
-TEST_F(LayerTreeHostImplTest, RenderFrameMetadata) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RenderFrameMetadata) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect(50, 50));
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
@@ -15026,7 +15678,8 @@ TEST_F(LayerTreeHostImplTest, RenderFrameMetadata) {
}
}
-TEST_F(LayerTreeHostImplTest, SelectionBoundsPassedToRenderFrameMetadata) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SelectionBoundsPassedToRenderFrameMetadata) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
@@ -15078,7 +15731,7 @@ TEST_F(LayerTreeHostImplTest, SelectionBoundsPassedToRenderFrameMetadata) {
EXPECT_TRUE(selection_2.end.visible());
}
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
VerticalScrollDirectionChangesPassedToRenderFrameMetadata) {
// Set up the viewport.
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
@@ -15166,7 +15819,8 @@ TEST_F(LayerTreeHostImplTest,
// Tests ScrollUpdate() to see if the method sets the scroll tree's currently
// scrolling node.
-TEST_F(LayerTreeHostImplTest, ScrollUpdateDoesNotSetScrollingNode) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollUpdateDoesNotSetScrollingNode) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
UpdateDrawProperties(host_impl_->active_tree());
@@ -15531,7 +16185,8 @@ TEST_F(HitTestRegionListGeneratingLayerTreeHostImplTest, InvalidFrameSinkId) {
hit_test_region_list->regions[0].rect.ToString());
}
-TEST_F(LayerTreeHostImplTest, ImplThreadPhaseUponImplSideInvalidation) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ImplThreadPhaseUponImplSideInvalidation) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
// In general invalidation should never be ran outside the impl frame.
@@ -15552,7 +16207,7 @@ TEST_F(LayerTreeHostImplTest, ImplThreadPhaseUponImplSideInvalidation) {
// Test passes when there is no crash.
}
-TEST_F(LayerTreeHostImplTest, SkipOnDrawDoesNotUpdateDrawParams) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SkipOnDrawDoesNotUpdateDrawParams) {
EXPECT_TRUE(CreateHostImpl(DefaultSettings(),
FakeLayerTreeFrameSink::CreateSoftware()));
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
@@ -15581,7 +16236,7 @@ TEST_F(LayerTreeHostImplTest, SkipOnDrawDoesNotUpdateDrawParams) {
// Test that a touch scroll over a SolidColorScrollbarLayer, the scrollbar used
// on Android, does not register as a scrollbar scroll and result in main
// threaded scrolling.
-TEST_F(LayerTreeHostImplTest, TouchScrollOnAndroidScrollbar) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, TouchScrollOnAndroidScrollbar) {
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
gfx::Size viewport_size = gfx::Size(360, 600);
gfx::Size scroll_content_size = gfx::Size(360, 3800);
@@ -15786,7 +16441,7 @@ TEST_F(ForceActivateAfterPaintWorkletPaintLayerTreeHostImplTest,
// Verify that the device scale factor is not used to rescale scrollbar deltas
// in percent-based scrolling.
-TEST_F(LayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) {
LayerTreeSettings settings = DefaultSettings();
settings.percent_based_scrolling = true;
settings.use_zoom_for_dsf = true;
@@ -15798,21 +16453,21 @@ TEST_F(LayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) {
LayerImpl* content_layer = AddContentLayer();
LayerImpl* scroll_layer = AddScrollableLayer(
- content_layer, gfx::Size(185, 200), gfx::Size(185, 3800));
+ content_layer, gfx::Size(185, 500), gfx::Size(185, 3800));
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
host_impl_->active_tree(), VERTICAL, false, true);
SetupScrollbarLayer(scroll_layer, scrollbar);
- scrollbar->SetBounds(gfx::Size(15, 200));
+ scrollbar->SetBounds(gfx::Size(15, 500));
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(50);
- scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 185));
+ scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 485));
scrollbar->SetBackButtonRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(15, 15)));
scrollbar->SetForwardButtonRect(
- gfx::Rect(gfx::Point(0, 185), gfx::Size(15, 15)));
+ gfx::Rect(gfx::Point(0, 485), gfx::Size(15, 15)));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(185, 0));
DrawFrame();
@@ -15821,13 +16476,13 @@ TEST_F(LayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) {
host_impl_->BindToClient(&input_handler_client);
// Test scrolling with device scale factor = 3.
- const float expected_delta = kPercentDeltaForDirectionalScroll * 200u;
+ const float expected_delta = floorf(kPercentDeltaForDirectionalScroll * 500);
host_impl_->active_tree()->set_painted_device_scale_factor(3);
InputHandlerPointerResult scroll_result =
- host_impl_->MouseDown(gfx::PointF(190, 190), false);
- host_impl_->MouseUp(gfx::PointF(190, 190));
+ host_impl_->MouseDown(gfx::PointF(190, 490), false);
+ host_impl_->MouseUp(gfx::PointF(190, 490));
EXPECT_EQ(scroll_result.scroll_offset.y(), expected_delta);
EXPECT_FALSE(GetScrollNode(scroll_layer)->main_thread_scrolling_reasons);
@@ -15837,8 +16492,8 @@ TEST_F(LayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) {
host_impl_->active_tree()->set_painted_device_scale_factor(1);
InputHandlerPointerResult scroll_with_dsf_1 =
- host_impl_->MouseDown(gfx::PointF(190, 190), false);
- host_impl_->MouseUp(gfx::PointF(190, 190));
+ host_impl_->MouseDown(gfx::PointF(190, 490), false);
+ host_impl_->MouseUp(gfx::PointF(190, 490));
EXPECT_EQ(scroll_with_dsf_1.scroll_offset.y(), expected_delta);
EXPECT_FALSE(GetScrollNode(scroll_layer)->main_thread_scrolling_reasons);
@@ -15848,5 +16503,497 @@ TEST_F(LayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) {
host_impl_ = nullptr;
}
+class UnifiedScrollingTest : public LayerTreeHostImplTest {
+ public:
+ using Point = gfx::Point;
+ using ScrollInputType = ui::ScrollInputType;
+ using ScrollOffset = gfx::ScrollOffset;
+ using ScrollStatus = InputHandler::ScrollStatus;
+ using Size = gfx::Size;
+ using Rect = gfx::Rect;
+ using Vector2d = gfx::Vector2d;
+
+ void SetUp() override {
+ scoped_feature_list.InitAndEnableFeature(features::kScrollUnification);
+ LayerTreeHostImplTest::SetUp();
+
+ cur_time_ = base::TimeTicks() + base::TimeDelta::FromMilliseconds(100);
+ begin_frame_args_ =
+ viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
+
+ SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
+ }
+
+ void CreateUncompositedScrollerAndNonFastScrollableRegion() {
+ // Create an uncompositd scroll node that corresponds to a non fast
+ // scrollable region on the outer viewport scroll layer.
+ Size scrollable_content_bounds(100, 100);
+ Size container_bounds(50, 50);
+ CreateScrollNodeForUncompositedScroller(
+ GetPropertyTrees(), host_impl_->OuterViewportScrollNode()->id,
+ ScrollerElementId(), scrollable_content_bounds, container_bounds);
+ OuterViewportScrollLayer()->SetNonFastScrollableRegion(Rect(0, 0, 50, 50));
+
+ host_impl_->active_tree()->set_needs_update_draw_properties();
+ UpdateDrawProperties(host_impl_->active_tree());
+ host_impl_->active_tree()->DidBecomeActive();
+ DrawFrame();
+ }
+
+ void CreateScroller(uint32_t main_thread_scrolling_reasons) {
+ // Creates a regular compositeds scroller that comes with a ScrollNode and
+ // Layer.
+ Size scrollable_content_bounds(100, 100);
+ Size container_bounds(50, 50);
+
+ LayerImpl* layer =
+ AddScrollableLayer(OuterViewportScrollLayer(), container_bounds,
+ scrollable_content_bounds);
+ scroller_layer_ = layer;
+ GetScrollNode(layer)->main_thread_scrolling_reasons =
+ main_thread_scrolling_reasons;
+ GetScrollNode(layer)->is_composited = true;
+
+ UpdateDrawProperties(host_impl_->active_tree());
+ host_impl_->active_tree()->DidBecomeActive();
+ DrawFrame();
+ }
+
+ void CreateSuashingLayerCoveringWholeViewport() {
+ // Add a (simulated) "squashing layer". It's scroll parent is the outer
+ // viewport but it covers the entire viewport and any scrollers underneath
+ // it.
+ LayerImpl* squashing_layer = AddLayer();
+ squashing_layer->SetBounds(gfx::Size(100, 100));
+ squashing_layer->SetDrawsContent(true);
+ squashing_layer->SetHitTestable(true);
+ CopyProperties(OuterViewportScrollLayer(), squashing_layer);
+ UpdateDrawProperties(host_impl_->active_tree());
+ }
+
+ ScrollStatus ScrollBegin(const Vector2d& delta) {
+ auto scroll_state =
+ BeginState(Point(25, 25), delta, ScrollInputType::kWheel);
+ ScrollStatus status =
+ host_impl_->ScrollBegin(scroll_state.get(), ScrollInputType::kWheel);
+
+ if (status.needs_main_thread_hit_test)
+ to_be_continued_scroll_begin_ = std::move(scroll_state);
+
+ return status;
+ }
+
+ ScrollStatus ContinuedScrollBegin(ElementId element_id) {
+ DCHECK(to_be_continued_scroll_begin_)
+ << "ContinuedScrollBegin needs to come after a ScrollBegin that "
+ "requested a main frame";
+ std::unique_ptr<ScrollState> scroll_state =
+ std::move(to_be_continued_scroll_begin_);
+
+ scroll_state->data()->set_current_native_scrolling_element(element_id);
+ scroll_state->data()->is_main_thread_hit_tested = true;
+
+ return host_impl_->ScrollBegin(scroll_state.get(), ScrollInputType::kWheel);
+ }
+
+ InputHandlerScrollResult ScrollUpdate(const Vector2d& delta) {
+ auto scroll_state =
+ UpdateState(Point(25, 25), delta, ScrollInputType::kWheel);
+ return host_impl_->ScrollUpdate(scroll_state.get());
+ }
+
+ InputHandlerScrollResult AnimatedScrollUpdate(const Vector2d& delta) {
+ auto scroll_state = AnimatedUpdateState(Point(25, 25), delta);
+ return host_impl_->ScrollUpdate(scroll_state.get());
+ }
+
+ void ScrollEnd() { return host_impl_->ScrollEnd(); }
+
+ // An animation is setup in the first BeginFrame so it won't actually update
+ // in the first one. Use a named method for that so it's clear rather than
+ // mysteriously calling BeginFrame twice.
+ void StartAnimation() { BeginFrame(base::TimeDelta()); }
+
+ void BeginFrame(base::TimeDelta forward) {
+ cur_time_ += forward;
+ begin_frame_args_.frame_time = cur_time_;
+ begin_frame_args_.frame_id.sequence_number++;
+ host_impl_->WillBeginImplFrame(begin_frame_args_);
+ host_impl_->Animate();
+ host_impl_->UpdateAnimationState(true);
+ host_impl_->DidFinishImplFrame(begin_frame_args_);
+ }
+
+ ScrollOffset GetScrollOffset(ScrollNode* node) {
+ return GetPropertyTrees()->scroll_tree.current_scroll_offset(
+ node->element_id);
+ }
+
+ ScrollOffset ScrollerOffset() {
+ return GetPropertyTrees()->scroll_tree.current_scroll_offset(
+ ScrollerElementId());
+ }
+
+ PropertyTrees* GetPropertyTrees() {
+ return host_impl_->active_tree()->property_trees();
+ }
+
+ ScrollNode* CurrentlyScrollingNode() {
+ return host_impl_->CurrentlyScrollingNode();
+ }
+
+ ScrollNode* ScrollerNode() {
+ ScrollNode* node = GetPropertyTrees()->scroll_tree.FindNodeFromElementId(
+ ScrollerElementId());
+ DCHECK(node);
+ return node;
+ }
+ ElementId ScrollerElementId() const {
+ if (scroller_layer_)
+ return scroller_layer_->element_id();
+
+ return ElementId(1234);
+ }
+
+ base::TimeDelta kFrameInterval = base::TimeDelta::FromMilliseconds(16);
+
+ // Parameterized test body. Defined inline with tests.
+ void TestUncompositedScrollingState(bool mutates_transform_tree);
+
+ private:
+ LayerImpl* scroller_layer_ = nullptr;
+
+ base::TimeTicks cur_time_;
+ viz::BeginFrameArgs begin_frame_args_;
+
+ std::unique_ptr<ScrollState> to_be_continued_scroll_begin_;
+ base::test::ScopedFeatureList scoped_feature_list;
+};
+
+// A ScrollBegin that hits a non fast scrollable region must return a request
+// for a main thread hit test.
+TEST_F(UnifiedScrollingTest, UnifiedScrollNonFastScrollableRegion) {
+ CreateUncompositedScrollerAndNonFastScrollableRegion();
+
+ // When ScrollUnification is turned on, scrolling inside a non-fast
+ // scrollable region should request a main thread hit test. It's the client's
+ // responsibility to request a hit test from Blink. It can then call
+ // ScrollBegin again, providing the element_id to scroll.
+ {
+ ScrollStatus status = ScrollBegin(Vector2d(0, 10));
+
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+
+ // The scroll hasn't started yet though.
+ EXPECT_FALSE(CurrentlyScrollingNode());
+ }
+
+ // Simulate the scroll hit test coming back from the main thread. This time
+ // ScrollBegin will be called with an element id provided so that a hit test
+ // is unnecessary.
+ {
+ ScrollStatus status = ContinuedScrollBegin(ScrollerElementId());
+
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+
+ EXPECT_TRUE(CurrentlyScrollingNode());
+ EXPECT_EQ(ScrollerNode(), CurrentlyScrollingNode());
+ }
+
+ // Ensure ScrollUpdates can successfully scroll this node. They shouldn't
+ // mutate the associated transform node.
+ {
+ EXPECT_TRUE(ScrollUpdate(Vector2d(0, 10)).did_scroll);
+ EXPECT_EQ(ScrollOffset(0, 10), ScrollerOffset());
+ }
+
+ // Try to scroll past the end. Ensure the max scrolling bounds are respected.
+ {
+ EXPECT_TRUE(ScrollUpdate(Vector2d(0, 1000)).did_scroll);
+ EXPECT_EQ(ScrollOffset(0, 50), ScrollerOffset());
+ }
+
+ // Overscrolling should cause the scroll update to be dropped.
+ {
+ EXPECT_FALSE(ScrollUpdate(Vector2d(0, 10)).did_scroll);
+ EXPECT_EQ(ScrollOffset(0, 50), ScrollerOffset());
+ }
+
+ host_impl_->ScrollEnd();
+}
+
+// A main thread hit test should still go through latch bubbling. That is, if
+// the hit tested scroller is fully scrolled and cannot consume the scroll, we
+// should chain up to its ancestor.
+TEST_F(UnifiedScrollingTest, MainThreadHitTestLatchBubbling) {
+ CreateUncompositedScrollerAndNonFastScrollableRegion();
+
+ // Start with the scroller fully scrolled.
+ {
+ ScrollBegin(Vector2d(0, 1000));
+ ContinuedScrollBegin(ScrollerElementId());
+ ScrollUpdate(Vector2d(0, 1000));
+ ScrollEnd();
+ ASSERT_EQ(ScrollOffset(0, 50), ScrollerOffset());
+ }
+
+ {
+ ScrollStatus status = ScrollBegin(Vector2d(0, 10));
+ ASSERT_TRUE(status.needs_main_thread_hit_test);
+ status = ContinuedScrollBegin(ScrollerElementId());
+
+ // Since the hit tested scroller in ContinuedScrollBegin was fully
+ // scrolled, we should latch to the viewport instead.
+ EXPECT_TRUE(CurrentlyScrollingNode());
+ EXPECT_EQ(host_impl_->OuterViewportScrollNode(), CurrentlyScrollingNode());
+ }
+}
+
+using UnifiedScrollingDeathTest = UnifiedScrollingTest;
+
+// A main thread hit test that with an empty target id should be dropped.
+TEST_F(UnifiedScrollingDeathTest, EmptyMainThreadHitTest) {
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ CreateUncompositedScrollerAndNonFastScrollableRegion();
+ {
+ ElementId kInvalidId;
+ DCHECK(!kInvalidId);
+
+ ScrollStatus status = ScrollBegin(Vector2d(0, 10));
+
+ // Note, we have a NOTREACHED here to make sure this cannot happen. If
+ // DCHECKs are enabled we can just make sure we get killed when we end up
+ // in this situation.
+#if DCHECK_IS_ON()
+ EXPECT_DEATH_IF_SUPPORTED({ status = ContinuedScrollBegin(kInvalidId); },
+ "");
+#else
+ status = ContinuedScrollBegin(kInvalidId);
+ EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
+ status.main_thread_scrolling_reasons);
+#endif
+ }
+}
+
+// A main thread hit test that returns a scroll node we can't find should be
+// dropped.
+TEST_F(UnifiedScrollingTest, MainThreadHitTestScrollNodeNotFound) {
+ CreateUncompositedScrollerAndNonFastScrollableRegion();
+
+ {
+ ElementId kUnknown(42);
+ DCHECK(!GetPropertyTrees()->scroll_tree.FindNodeFromElementId(kUnknown));
+
+ ScrollStatus status = ScrollBegin(Vector2d(0, 10));
+ status = ContinuedScrollBegin(kUnknown);
+ EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
+ status.main_thread_scrolling_reasons);
+ }
+}
+
+// The presence of a squashing layer is one reason we might "fail to hit test"
+// on the compositor. A heuristic is used that if the first hit scrollable
+// layer isn't a scrolling ancestor of the first hit layer, we probably hit a
+// squashing layer which might have empty regions we don't know how to hit
+// test. This requires falling back to the main thread. However, with scroll
+// unification, we may still be able to scroll the final scroller entirely on
+// the compositor. See LayerTreeHostImpl::IsInitialScrollHitTestReliable.
+TEST_F(UnifiedScrollingTest, SquashingLayerCausesMainThreadHitTest) {
+ // Create a scroller that should scroll on the compositor thread and the add
+ // "squashing" layer over top of it. This simulates the case where a
+ // squashing layer obscuring a scroller makes the hit test unreliable.
+ CreateScroller(MainThreadScrollingReason::kNotScrollingOnMain);
+ CreateSuashingLayerCoveringWholeViewport();
+
+ // When ScrollUnification is turned on, scrolling over a squashing-like layer
+ // that cannot be reliably hit tested on the compositor should request a main
+ // thread hit test.
+ {
+ ScrollStatus status = ScrollBegin(Vector2d(0, 10));
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ }
+
+ // Resolving the hit test should allow the scroller underneath to scroll as
+ // normal on the impl thread.
+ {
+ ScrollStatus status = ContinuedScrollBegin(ScrollerElementId());
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+
+ EXPECT_TRUE(CurrentlyScrollingNode());
+ EXPECT_EQ(ScrollerNode(), CurrentlyScrollingNode());
+ }
+}
+
+// Under unified scroling, a composited scroller with a main thread scrolling
+// reason should be scrolled on the compositor. Ensure ScrollBegin returns
+// success without needing a main thread hit test.
+TEST_F(UnifiedScrollingTest, MainThreadScrollingReasonsScrollOnCompositor) {
+ CreateScroller(
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects);
+
+ {
+ ScrollStatus status = ScrollBegin(Vector2d(0, 10));
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ }
+}
+
+// This tests whether or not various kinds of scrolling mutates the transform
+// tree or not. It is parameterized and used by tests below.
+void UnifiedScrollingTest::TestUncompositedScrollingState(
+ bool mutates_transform_tree) {
+ TransformTree& tree = GetPropertyTrees()->transform_tree;
+ TransformNode* transform_node = tree.Node(ScrollerNode()->transform_id);
+
+ // Ensure we're in a clean state to start.
+ {
+ ASSERT_EQ(transform_node->element_id, ScrollerElementId());
+ ASSERT_TRUE(transform_node->scrolls);
+
+ ASSERT_EQ(ScrollOffset(0, 0), transform_node->scroll_offset);
+ ASSERT_FALSE(transform_node->transform_changed);
+ ASSERT_FALSE(transform_node->needs_local_transform_update);
+ ASSERT_FALSE(tree.needs_update());
+ }
+
+ // Start a scroll, ensure the scroll tree was updated and a commit was
+ // requested. Check that the transform tree mutation was as expected for the
+ // test parameter.
+ {
+ ScrollStatus status = ScrollBegin(Vector2d(0, 10));
+ if (status.needs_main_thread_hit_test)
+ ContinuedScrollBegin(ScrollerElementId());
+
+ ASSERT_EQ(ScrollerNode(), CurrentlyScrollingNode());
+
+ did_request_commit_ = false;
+
+ ScrollUpdate(Vector2d(0, 10));
+ ASSERT_EQ(ScrollOffset(0, 10), ScrollerOffset());
+ EXPECT_TRUE(did_request_commit_);
+
+ // Ensure the transform tree was updated only if expected.
+ if (mutates_transform_tree)
+ EXPECT_EQ(ScrollOffset(0, 10), transform_node->scroll_offset);
+ else
+ EXPECT_EQ(ScrollOffset(0, 0), transform_node->scroll_offset);
+ EXPECT_EQ(mutates_transform_tree, transform_node->transform_changed);
+ EXPECT_EQ(mutates_transform_tree,
+ transform_node->needs_local_transform_update);
+ EXPECT_EQ(mutates_transform_tree, tree.needs_update());
+ }
+
+ // Perform animated scroll update. Ensure the same things.
+ {
+ did_request_commit_ = false;
+
+ AnimatedScrollUpdate(Vector2d(0, 10));
+ ASSERT_TRUE(host_impl_->mutator_host()->ImplOnlyScrollAnimatingElement());
+ ASSERT_EQ(ScrollOffset(0, 10), ScrollerOffset());
+
+ StartAnimation();
+ BeginFrame(kFrameInterval);
+ BeginFrame(base::TimeDelta::FromMilliseconds(500));
+ BeginFrame(kFrameInterval);
+
+ ASSERT_EQ(ScrollOffset(0, 20), ScrollerOffset());
+ EXPECT_TRUE(did_request_commit_);
+
+ if (mutates_transform_tree)
+ EXPECT_EQ(ScrollOffset(0, 20), transform_node->scroll_offset);
+ else
+ EXPECT_EQ(ScrollOffset(0, 0), transform_node->scroll_offset);
+ EXPECT_EQ(mutates_transform_tree, transform_node->transform_changed);
+ EXPECT_EQ(mutates_transform_tree,
+ transform_node->needs_local_transform_update);
+ EXPECT_EQ(mutates_transform_tree, tree.needs_update());
+ }
+}
+
+// When scrolling a main-thread hit tested scroller with main thread reasons,
+// we should update the scroll node but the transform tree shouldn't be
+// mutated. Also ensure NeedsCommit is set. A nice additional benefit of scroll
+// unification should be seamless upgrade to a full compositor scroll if a main
+// thread reason is removed.
+TEST_F(UnifiedScrollingTest, MainThreadReasonsScrollDoesntAffectTransform) {
+ CreateScroller(
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects);
+
+ TestUncompositedScrollingState(/*mutates_transform_tree=*/false);
+
+ ASSERT_EQ(ScrollerNode()->main_thread_scrolling_reasons,
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects);
+ TransformTree& tree = GetPropertyTrees()->transform_tree;
+ TransformNode* transform_node = tree.Node(ScrollerNode()->transform_id);
+
+ // Removing the main thread reason bit should start mutating the transform
+ // tree.
+ {
+ ScrollerNode()->main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNotScrollingOnMain;
+ UpdateDrawProperties(host_impl_->active_tree());
+ host_impl_->active_tree()->DidBecomeActive();
+
+ ScrollUpdate(Vector2d(0, 10));
+ ASSERT_EQ(ScrollOffset(0, 30), ScrollerOffset());
+
+ // The transform node should now be updated by the scroll.
+ EXPECT_EQ(ScrollOffset(0, 30), transform_node->scroll_offset);
+ EXPECT_TRUE(transform_node->transform_changed);
+ EXPECT_TRUE(transform_node->needs_local_transform_update);
+ EXPECT_TRUE(tree.needs_update());
+ }
+
+ ScrollEnd();
+}
+
+// When scrolling an uncomposited scroller, we shouldn't modify the transform
+// tree. If a scroller is promoted mid-scroll it should start mutating the
+// transform tree.
+TEST_F(UnifiedScrollingTest, UncompositedScrollerDoesntAffectTransform) {
+ CreateUncompositedScrollerAndNonFastScrollableRegion();
+
+ TestUncompositedScrollingState(/*mutates_transform_tree=*/false);
+
+ ASSERT_FALSE(ScrollerNode()->is_composited);
+ TransformTree& tree = GetPropertyTrees()->transform_tree;
+ TransformNode* transform_node = tree.Node(ScrollerNode()->transform_id);
+
+ // Marking the node as composited should start updating the transform tree.
+ {
+ ScrollerNode()->is_composited = true;
+ UpdateDrawProperties(host_impl_->active_tree());
+ host_impl_->active_tree()->DidBecomeActive();
+
+ ScrollUpdate(Vector2d(0, 10));
+ ASSERT_EQ(ScrollOffset(0, 30), ScrollerOffset());
+
+ // The transform node should now be updated by the scroll.
+ EXPECT_EQ(ScrollOffset(0, 30), transform_node->scroll_offset);
+ EXPECT_TRUE(transform_node->transform_changed);
+ EXPECT_TRUE(transform_node->needs_local_transform_update);
+ EXPECT_TRUE(tree.needs_update());
+ }
+
+ ScrollEnd();
+}
+
+// When scrolling a composited scroller that just happens to have needed a main
+// thread hit test first, we should modify the transform tree as usual.
+TEST_F(UnifiedScrollingTest, CompositedWithSquashedLayerMutatesTransform) {
+ CreateScroller(MainThreadScrollingReason::kNotScrollingOnMain);
+ CreateSuashingLayerCoveringWholeViewport();
+
+ TestUncompositedScrollingState(/*mutates_transform_tree=*/true);
+
+ ScrollEnd();
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc
index b55a4b3b733..0a6809e778a 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc
@@ -6,12 +6,13 @@
#include "base/stl_util.h"
#include "build/build_config.h"
-#include "cc/layers/picture_image_layer.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/paint/paint_image.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/paint/render_surface_filters.h"
#include "cc/paint/skia_paint_canvas.h"
+#include "cc/test/fake_content_layer_client.h"
+#include "cc/test/fake_picture_layer.h"
#include "cc/test/layer_tree_pixel_resource_test.h"
#include "cc/test/pixel_comparator.h"
#include "cc/test/test_layer_tree_frame_sink.h"
@@ -118,23 +119,19 @@ class LayerTreeHostBlendingPixelTest
scoped_refptr<Layer> CreateColorfulBackdropLayer(int width, int height) {
sk_sp<SkSurface> backing_store = CreateColorfulSurface(width, height);
- scoped_refptr<PictureImageLayer> layer = PictureImageLayer::Create();
+ gfx::Size bounds(width, height);
+ backdrop_client_.set_bounds(bounds);
+ backdrop_client_.add_draw_image(backing_store->makeImageSnapshot(),
+ gfx::Point(), PaintFlags());
+ scoped_refptr<FakePictureLayer> layer =
+ FakePictureLayer::Create(&backdrop_client_);
layer->SetIsDrawable(true);
- layer->SetBounds(gfx::Size(width, height));
- layer->SetImage(PaintImageBuilder::WithDefault()
- .set_id(PaintImage::GetNextId())
- .set_image(backing_store->makeImageSnapshot(),
- PaintImage::GetNextContentId())
- .TakePaintImage(),
- SkMatrix::I(), false);
+ layer->SetBounds(bounds);
return layer;
}
void SetupMaskLayer(scoped_refptr<Layer> layer) {
gfx::Size bounds = layer->bounds();
- scoped_refptr<PictureImageLayer> mask = PictureImageLayer::Create();
- mask->SetIsDrawable(true);
- mask->SetBounds(bounds);
sk_sp<SkSurface> surface =
SkSurface::MakeRasterN32Premul(bounds.width(), bounds.height());
@@ -146,12 +143,15 @@ class LayerTreeHostBlendingPixelTest
// cover the right half of it
canvas->drawRect(
SkRect::MakeXYWH(1, 0, bounds.width() - 1, bounds.height()), paint);
- mask->SetImage(PaintImageBuilder::WithDefault()
- .set_id(PaintImage::GetNextId())
- .set_image(surface->makeImageSnapshot(),
- PaintImage::GetNextContentId())
- .TakePaintImage(),
- SkMatrix::I(), false);
+
+ mask_client_.set_bounds(bounds);
+ mask_client_.add_draw_image(surface->makeImageSnapshot(), gfx::Point(),
+ PaintFlags());
+
+ scoped_refptr<FakePictureLayer> mask =
+ FakePictureLayer::Create(&mask_client_);
+ mask->SetIsDrawable(true);
+ mask->SetBounds(bounds);
layer->SetMaskLayer(mask);
}
@@ -254,6 +254,8 @@ class LayerTreeHostBlendingPixelTest
bool force_antialiasing_;
bool force_blending_with_shaders_;
+ FakeContentLayerClient mask_client_;
+ FakeContentLayerClient backdrop_client_;
SkColor misc_opaque_color_ = 0xffc86464;
};
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc
index c93b84e3271..4042e97367c 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -309,55 +309,6 @@ TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurOutsets) {
base::FilePath(FILE_PATH_LITERAL("backdrop_filter_blur_outsets.png")));
}
-class LayerTreeHostImageFiltersPixelTestLayerList
- : public LayerTreeHostFiltersPixelTest {
- public:
- LayerTreeHostImageFiltersPixelTestLayerList() { SetUseLayerLists(); }
-
- void SetupTree() override {
- SetInitialRootBounds(gfx::Size(200, 200));
- LayerTreePixelTest::SetupTree();
-
- Layer* root = layer_tree_host()->root_layer();
- scoped_refptr<SolidColorLayer> background =
- CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorYELLOW);
- CopyProperties(root, background.get());
- root->AddChild(background);
-
- scoped_refptr<SolidColorLayer> foreground =
- CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorRED);
- CopyProperties(root, foreground.get());
- root->AddChild(foreground);
-
- EffectNode& effect_node = CreateEffectNode(foreground.get());
- float matrix[20] = {0};
- // This filter does a red-blue swap, so the foreground becomes blue.
- matrix[2] = matrix[6] = matrix[10] = matrix[18] = 1.0f;
- // Set up a crop rect to filter the bottom 200x100 pixels of the foreground.
- SkImageFilter::CropRect crop_rect(SkRect::MakeXYWH(0, 100, 200, 100));
- FilterOperations filters;
- filters.Append(FilterOperation::CreateReferenceFilter(
- sk_make_sp<ColorFilterPaintFilter>(SkColorFilters::Matrix(matrix),
- nullptr, &crop_rect)));
-
- effect_node.filters = filters;
- effect_node.render_surface_reason = RenderSurfaceReason::kFilter;
-
- // Move the filters origin up by 100 pixels so the crop rect is applied
- // only to the top 100 pixels, not the bottom.
- effect_node.filters_origin = gfx::PointF(0.0f, -100.0f);
- }
-};
-
-INSTANTIATE_TEST_SUITE_P(All,
- LayerTreeHostImageFiltersPixelTestLayerList,
- ::testing::ValuesIn(kRendererTypes));
-
-TEST_P(LayerTreeHostImageFiltersPixelTestLayerList, NonZeroOrigin) {
- RunPixelTestWithLayerList(
- base::FilePath(FILE_PATH_LITERAL("blue_yellow.png")));
-}
-
class LayerTreeHostBlurFiltersPixelTestGPULayerList
: public LayerTreeHostFiltersPixelTest {
public:
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc
index 7e39b785622..702d4e5dbb7 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc
@@ -7,13 +7,13 @@
#include "base/stl_util.h"
#include "build/build_config.h"
#include "cc/layers/content_layer_client.h"
-#include "cc/layers/picture_image_layer.h"
#include "cc/layers/picture_layer.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/paint/paint_flags.h"
#include "cc/paint/paint_image.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/paint/paint_op_buffer.h"
+#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_picture_layer.h"
#include "cc/test/layer_tree_pixel_resource_test.h"
#include "cc/test/pixel_comparator.h"
@@ -318,22 +318,20 @@ TEST_P(LayerTreeHostMaskPixelTestWithLayerList, MaskWithEffectDifferentSize) {
}
TEST_P(LayerTreeHostMaskPixelTestWithLayerList, ImageMaskWithEffect) {
- MaskContentLayerClient client(mask_bounds_);
- scoped_refptr<PictureImageLayer> mask_layer = PictureImageLayer::Create();
+ MaskContentLayerClient mask_client(mask_bounds_);
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(50, 50);
SkCanvas* canvas = surface->getCanvas();
scoped_refptr<DisplayItemList> mask_display_list =
- client.PaintContentsToDisplayList(
+ mask_client.PaintContentsToDisplayList(
ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
mask_display_list->Raster(canvas);
- mask_layer->SetImage(PaintImageBuilder::WithDefault()
- .set_id(PaintImage::GetNextId())
- .set_image(surface->makeImageSnapshot(),
- PaintImage::GetNextContentId())
- .TakePaintImage(),
- SkMatrix::I(), false);
- mask_layer_ = mask_layer;
+
+ FakeContentLayerClient layer_client;
+ layer_client.set_bounds(mask_bounds_);
+ layer_client.add_draw_image(surface->makeImageSnapshot(), gfx::Point(),
+ PaintFlags());
+ mask_layer_ = FakePictureLayer::Create(&layer_client);
pixel_comparator_ =
std::make_unique<FuzzyPixelOffByOneComparator>(true /* discard_alpha */);
@@ -350,10 +348,6 @@ TEST_P(LayerTreeHostMasksPixelTest, ImageMaskOfLayer) {
gfx::Size mask_bounds(50, 50);
- scoped_refptr<PictureImageLayer> mask = PictureImageLayer::Create();
- mask->SetIsDrawable(true);
- mask->SetBounds(mask_bounds);
-
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(50, 50);
SkCanvas* canvas = surface->getCanvas();
MaskContentLayerClient client(mask_bounds);
@@ -361,12 +355,14 @@ TEST_P(LayerTreeHostMasksPixelTest, ImageMaskOfLayer) {
client.PaintContentsToDisplayList(
ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
mask_display_list->Raster(canvas);
- mask->SetImage(PaintImageBuilder::WithDefault()
- .set_id(PaintImage::GetNextId())
- .set_image(surface->makeImageSnapshot(),
- PaintImage::GetNextContentId())
- .TakePaintImage(),
- SkMatrix::I(), false);
+
+ FakeContentLayerClient mask_client;
+ mask_client.set_bounds(mask_bounds);
+ mask_client.add_draw_image(surface->makeImageSnapshot(), gfx::Point(),
+ PaintFlags());
+ scoped_refptr<FakePictureLayer> mask = FakePictureLayer::Create(&mask_client);
+ mask->SetIsDrawable(true);
+ mask->SetBounds(mask_bounds);
scoped_refptr<SolidColorLayer> green = CreateSolidColorLayerWithBorder(
gfx::Rect(25, 25, 50, 50), kCSSGreen, 1, SK_ColorBLACK);
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc b/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc
index 2739d9880bb..34d9ef117a4 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc
@@ -4,7 +4,6 @@
#include "build/build_config.h"
#include "cc/layers/content_layer_client.h"
-#include "cc/layers/picture_image_layer.h"
#include "cc/layers/picture_layer.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/test/fake_content_layer_client.h"
diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc
index edbee8ea336..2d94507a792 100644
--- a/chromium/cc/trees/layer_tree_host_unittest.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest.cc
@@ -52,7 +52,6 @@
#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"
#include "cc/trees/layer_tree_host_impl.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/scroll_and_scale_set.h"
@@ -6187,9 +6186,9 @@ class LayerTreeHostTestSwapPromiseDuringCommit : public LayerTreeHostTest {
&set_needs_commit_count,
&set_needs_redraw_count));
layer_tree_host()->QueueSwapPromise(std::move(swap_promise));
- // Queueing a swap promise from DidBeginMainFrame should cause a
+ // Queueing a swap promise from DidBeginMainFrame should not cause a
// subsequent main frame to be scheduled.
- EXPECT_EQ(1, set_needs_commit_count);
+ EXPECT_EQ(0, set_needs_commit_count);
}
EndTest();
@@ -8902,28 +8901,11 @@ class LayerTreeHostCustomThrougputTrackerTest : public LayerTreeHostTest {
}
void NotifyThroughputTrackerResults(CustomTrackerResults results) override {
+ // Check that data for kSequenceId is captured. Ideally, we should get
+ // 2 frame_expected and 2 frame_produced. But on slow bots, it is difficult
+ // to infer the correct numbers. Both frame_expected and frame_produced
+ // could drop to 1 (or even below). So no sanity check on data itself.
ASSERT_TRUE(base::Contains(results, kSequenceId));
- const auto& throughput = results[kSequenceId];
- // Frame 3 and 4 are counted. See the sequence in DidCommit comment for
- // normal case that expects 2 for both frames_expected and frames_produced.
- //
- // However, on slow bots, things could be different.
- // - Begin frame could be skipped but still counted as expected frames,
- //
- // e(5,5)b(8)B(0,8)E(8)s(3)S(8)e(8,8)b(11)
- // B(8,11)E(11)ts(4)S(11)e(11,11)P(3)e(14,14)P(4)
- //
- // B(0, 8) and B(8, 11) make frame_expected to be 4, more than 2 expected
- // by test.
- //
- // - Finish before frame 4 is presented in multi-thread mode.
- //
- // e(3,3)b(4)B(0,4)E(4)s(2)e(4,4)b(6)B(4,6)E(6)s(3)S(4)e(6,6)
- // P(2)e(7,7)P(3)
- //
- // Only P(3) is counted thus frames_produced is 1.
- EXPECT_GE(throughput.frames_expected, 2u);
- EXPECT_GE(throughput.frames_produced, 1u);
EndTest();
}
@@ -8931,5 +8913,112 @@ class LayerTreeHostCustomThrougputTrackerTest : public LayerTreeHostTest {
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCustomThrougputTrackerTest);
+// Confirm that DelegatedInkMetadata set on the LTH propagates to the
+// CompositorFrameMetadata and RenderFrameMetadata, and then both are correctly
+// reset when another frame is drawn without DelegatedInkMetadata.
+class LayerTreeHostTestDelegatedInkMetadataOnAndOff
+ : public LayerTreeHostTest,
+ public RenderFrameMetadataObserver {
+ public:
+ // Provides a wrapper which can be passed to LayerTreeHost, but just forwards
+ // to the test class.
+ class ForwardingRenderFrameMetadataObserver
+ : public RenderFrameMetadataObserver {
+ public:
+ explicit ForwardingRenderFrameMetadataObserver(
+ RenderFrameMetadataObserver* target)
+ : target_(target) {}
+
+ // RenderFrameMetadataObserver implementation.
+ void BindToCurrentThread() override { target_->BindToCurrentThread(); }
+ void OnRenderFrameSubmission(
+ const RenderFrameMetadata& render_frame_metadata,
+ viz::CompositorFrameMetadata* compositor_frame_metadata,
+ bool force_send) override {
+ target_->OnRenderFrameSubmission(render_frame_metadata,
+ compositor_frame_metadata, force_send);
+ }
+
+ private:
+ RenderFrameMetadataObserver* target_ = nullptr;
+ };
+
+ void BeginTest() override {
+ // Set up a basic render frame observer for the LTH/LTHI to forward to.
+ layer_tree_host()->SetRenderFrameObserver(
+ std::make_unique<ForwardingRenderFrameMetadataObserver>(this));
+
+ // Setting up a basic frame that can be redrawn.
+ layer_tree_host()->SetViewportRectAndScale(gfx::Rect(10, 10), 1.f,
+ viz::LocalSurfaceIdAllocation());
+ layer_tree_host()->root_layer()->SetBounds(gfx::Size(10, 10));
+ layer_ = FakePictureLayer::Create(&client_);
+ layer_tree_host()->root_layer()->AddChild(layer_);
+ client_.set_bounds(layer_->bounds());
+
+ // Values chosen arbitrarily
+ SkColor color = SK_ColorDKGRAY;
+ double diameter = 1.000002;
+ gfx::PointF point = gfx::PointF(135, 45);
+ gfx::RectF area = gfx::RectF(173, 438);
+ base::TimeTicks timestamp = base::TimeTicks::Now();
+
+ expected_metadata_ =
+ viz::DelegatedInkMetadata(point, diameter, color, timestamp, area);
+ layer_tree_host()->SetDelegatedInkMetadata(
+ std::make_unique<viz::DelegatedInkMetadata>(
+ expected_metadata_.value()));
+ }
+
+ void DidCommitAndDrawFrame() override {
+ // Cause a redraw to occur.
+ layer_->SetNeedsDisplay();
+ }
+
+ void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
+ if (expected_metadata_.has_value()) {
+ // Now try again with no metadata to confirm everything is cleared out.
+ expected_metadata_.reset();
+ }
+ }
+
+ void ExpectMetadata(bool had_delegated_ink_metadata,
+ viz::DelegatedInkMetadata* actual_metadata) {
+ if (expected_metadata_.has_value()) {
+ EXPECT_TRUE(had_delegated_ink_metadata);
+ EXPECT_TRUE(actual_metadata);
+ EXPECT_EQ(expected_metadata_->point(), actual_metadata->point());
+ EXPECT_EQ(expected_metadata_->color(), actual_metadata->color());
+ EXPECT_EQ(expected_metadata_->diameter(), actual_metadata->diameter());
+ EXPECT_EQ(expected_metadata_->presentation_area(),
+ actual_metadata->presentation_area());
+ EXPECT_EQ(expected_metadata_->timestamp(), actual_metadata->timestamp());
+ } else {
+ EXPECT_FALSE(had_delegated_ink_metadata);
+ EXPECT_FALSE(actual_metadata);
+ EndTest();
+ }
+ }
+
+ void AfterTest() override {}
+
+ // RenderFrameMetadataObserver implementation.
+ void BindToCurrentThread() override {}
+ void OnRenderFrameSubmission(
+ const RenderFrameMetadata& render_frame_metadata,
+ viz::CompositorFrameMetadata* compositor_frame_metadata,
+ bool force_send) override {
+ ExpectMetadata(render_frame_metadata.has_delegated_ink_metadata,
+ compositor_frame_metadata->delegated_ink_metadata.get());
+ }
+
+ private:
+ base::Optional<viz::DelegatedInkMetadata> expected_metadata_;
+ FakeContentLayerClient client_;
+ scoped_refptr<Layer> layer_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestDelegatedInkMetadataOnAndOff);
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc b/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc
index bb64e9ef4a4..7a188881491 100644
--- a/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc
@@ -10,6 +10,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "cc/layers/effect_tree_layer_list_iterator.h"
+#include "cc/test/cc_test_suite.h"
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_picture_layer.h"
#include "cc/test/layer_tree_test.h"
@@ -309,13 +310,19 @@ class LayerTreeHostCopyRequestTestLayerDestroyed
base::BindOnce(
&LayerTreeHostCopyRequestTestLayerDestroyed::CopyOutputCallback,
base::Unretained(this))));
+ // We expect that the RequestCopyOfOutput won't yet return results until
+ // the main is destroyed. So we RunUntilIdle to ensure no PostTask is
+ // currently queued to return the result.
+ CCTestSuite::RunUntilIdle();
EXPECT_EQ(0, callback_count_);
// Destroy the main thread layer right away.
main_destroyed_->RemoveFromParent();
main_destroyed_.reset();
- // Should callback with a NULL bitmap.
+ // Should callback with a NULL bitmap, result will be in a PostTask so
+ // RunUntilIdle().
+ CCTestSuite::RunUntilIdle();
EXPECT_EQ(1, callback_count_);
// Prevent drawing so we can't make a copy of the impl_destroyed layer.
@@ -350,7 +357,6 @@ class LayerTreeHostCopyRequestTestLayerDestroyed
}
void CopyOutputCallback(std::unique_ptr<viz::CopyOutputResult> result) {
- EXPECT_TRUE(layer_tree_host()->GetTaskRunnerProvider()->IsMainThread());
EXPECT_TRUE(result->IsEmpty());
++callback_count_;
}
diff --git a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc
index a5f7b6e522a..3a95b182bf0 100644
--- a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -10,8 +10,10 @@
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
#include "cc/animation/animation_host.h"
#include "cc/base/completion_event.h"
+#include "cc/base/features.h"
#include "cc/input/main_thread_scrolling_reason.h"
#include "cc/input/scroll_elasticity_helper.h"
#include "cc/layers/layer.h"
@@ -42,11 +44,14 @@ using ::testing::Mock;
namespace cc {
namespace {
-std::unique_ptr<ScrollState> BeginState(const gfx::Point& point) {
+std::unique_ptr<ScrollState> BeginState(const gfx::Point& point,
+ const gfx::Vector2dF& delta_hint) {
ScrollStateData scroll_state_data;
scroll_state_data.is_beginning = true;
scroll_state_data.position_x = point.x();
scroll_state_data.position_y = point.y();
+ scroll_state_data.delta_x_hint = delta_hint.x();
+ scroll_state_data.delta_y_hint = delta_hint.y();
std::unique_ptr<ScrollState> scroll_state(new ScrollState(scroll_state_data));
return scroll_state;
}
@@ -685,8 +690,9 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest {
gfx::Point scroll_point = gfx::ToCeiledPoint(
gfx::PointF(-0.5f, -0.5f) +
GetTransformNode(expected_scroll_layer_impl)->post_translation);
- InputHandler::ScrollStatus status = impl->ScrollBegin(
- BeginState(scroll_point).get(), ui::ScrollInputType::kTouchscreen);
+ InputHandler::ScrollStatus status =
+ impl->ScrollBegin(BeginState(scroll_point, scroll_amount_).get(),
+ ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
impl->ScrollUpdate(UpdateState(gfx::Point(), scroll_amount_).get());
auto* scrolling_node = impl->CurrentlyScrollingNode();
@@ -708,8 +714,9 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest {
gfx::Point scroll_point = gfx::ToCeiledPoint(
gfx::PointF(0.5f, 0.5f) +
GetTransformNode(expected_scroll_layer_impl)->post_translation);
- InputHandler::ScrollStatus status = impl->ScrollBegin(
- BeginState(scroll_point).get(), ui::ScrollInputType::kWheel);
+ InputHandler::ScrollStatus status =
+ impl->ScrollBegin(BeginState(scroll_point, scroll_amount_).get(),
+ ui::ScrollInputType::kWheel);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
impl->ScrollUpdate(UpdateState(gfx::Point(), scroll_amount_).get());
impl->ScrollEnd();
@@ -1058,6 +1065,116 @@ class LayerTreeHostScrollTestImplOnlyScroll : public LayerTreeHostScrollTest {
// This tests scrolling on the impl side which is only possible with a thread.
MULTI_THREAD_TEST_F(LayerTreeHostScrollTestImplOnlyScroll);
+// TODO(crbug.com/574283): Mac currently doesn't support smooth scrolling wheel
+// events.
+#if !defined(OS_MACOSX)
+// This test simulates scrolling on the impl thread such that it starts a scroll
+// animation. It ensures that RequestScrollAnimationEndNotification() correctly
+// notifies the callback after the animation ends.
+class SmoothScrollAnimationEndNotification : public LayerTreeHostScrollTest {
+ public:
+ SmoothScrollAnimationEndNotification() = default;
+
+ void InitializeSettings(LayerTreeSettings* settings) override {
+ LayerTreeHostScrollTest::InitializeSettings(settings);
+ settings->enable_smooth_scroll = true;
+ }
+
+ void SetupTree() override {
+ LayerTreeHostScrollTest::SetupTree();
+
+ Layer* root_layer = layer_tree_host()->root_layer();
+ Layer* root_scroll_layer =
+ layer_tree_host()->OuterViewportScrollLayerForTesting();
+
+ child_layer_ = Layer::Create();
+ child_layer_->SetElementId(
+ LayerIdToElementIdForTesting(child_layer_->id()));
+ child_layer_->SetBounds(gfx::Size(110, 110));
+
+ child_layer_->SetIsDrawable(true);
+ child_layer_->SetHitTestable(true);
+ child_layer_->SetElementId(
+ LayerIdToElementIdForTesting(child_layer_->id()));
+ child_layer_->SetBounds(root_scroll_layer->bounds());
+ root_layer->AddChild(child_layer_);
+
+ CopyProperties(root_scroll_layer, child_layer_.get());
+ CreateTransformNode(child_layer_.get());
+ CreateScrollNode(child_layer_.get(), root_layer->bounds());
+ }
+
+ void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+
+ void WillCommit() override {
+ // Keep the test committing (otherwise the early out for no update
+ // will stall the test).
+ if (layer_tree_host()->SourceFrameNumber() < 2) {
+ layer_tree_host()->SetNeedsCommit();
+ }
+ }
+
+ void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
+ if (host_impl->active_tree()->source_frame_number() < 0)
+ return;
+
+ if (host_impl->active_tree()->source_frame_number() == 0) {
+ const gfx::Point scroll_point(10, 10);
+ const gfx::Vector2dF scroll_amount(350, -350);
+ auto scroll_state = BeginState(scroll_point, scroll_amount);
+ scroll_state->data()->delta_granularity =
+ ui::ScrollGranularity::kScrollByPixel;
+ InputHandler::ScrollStatus status = host_impl->ScrollBegin(
+ scroll_state.get(), ui::ScrollInputType::kWheel);
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ scroll_state = UpdateState(scroll_point, scroll_amount);
+ scroll_state->data()->delta_granularity =
+ ui::ScrollGranularity::kScrollByPixel;
+ host_impl->ScrollUpdate(scroll_state.get());
+
+ EXPECT_TRUE(
+ !!host_impl->mutator_host()->ImplOnlyScrollAnimatingElement());
+ } else if (!scroll_end_requested_) {
+ host_impl->ScrollEnd(false);
+ scroll_end_requested_ = true;
+ }
+ PostSetNeedsCommitToMainThread();
+ }
+
+ void UpdateLayerTreeHost() override {
+ if (scroll_animation_started_)
+ return;
+
+ if (layer_tree_host()->HasCompositorDrivenScrollAnimationForTesting()) {
+ scroll_animation_started_ = true;
+ layer_tree_host()->RequestScrollAnimationEndNotification(
+ base::BindOnce(&SmoothScrollAnimationEndNotification::OnScrollEnd,
+ base::Unretained(this)));
+ }
+ }
+
+ void AfterTest() override {
+ EXPECT_TRUE(scroll_end_requested_);
+ EXPECT_TRUE(scroll_animation_started_);
+ EXPECT_TRUE(scroll_animation_ended_);
+ }
+
+ private:
+ void OnScrollEnd() {
+ scroll_animation_ended_ = true;
+ EndTest();
+ }
+
+ scoped_refptr<Layer> child_layer_;
+
+ bool scroll_end_requested_ = false;
+ bool scroll_animation_started_ = false;
+ bool scroll_animation_ended_ = false;
+};
+
+MULTI_THREAD_TEST_F(SmoothScrollAnimationEndNotification);
+#endif // !defined(OS_MACOSX)
+
void DoGestureScroll(LayerTreeHostImpl* host_impl,
const scoped_refptr<Layer>& scroller,
gfx::Vector2dF offset) {
@@ -1354,12 +1471,13 @@ class LayerTreeHostScrollTestScrollZeroMaxScrollOffset
void SetupTree() override {
LayerTreeHostScrollTest::SetupTree();
- // Add a sub-scroller to test TryScroll against. The outer viewport scroll
+ // Add a sub-scroller to test ScrollBegin against. The outer viewport scroll
// will be latched to for scrolling even if it doesn't have any scroll
// extent in the given direction to support overscroll actions.
scroller_ = Layer::Create();
scroller_->SetIsDrawable(true);
scroller_->SetHitTestable(true);
+ scroller_->SetBounds(gfx::Size(200, 200));
scroller_->SetElementId(LayerIdToElementIdForTesting(scroller_->id()));
CopyProperties(layer_tree_host()->OuterViewportScrollLayerForTesting(),
scroller_.get());
@@ -1369,61 +1487,91 @@ class LayerTreeHostScrollTestScrollZeroMaxScrollOffset
layer_tree_host()->root_layer()->AddChild(scroller_.get());
}
- void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+ void BeginTest() override { NextStep(); }
+
+ void NextStep() {
+ if (TestEnded())
+ return;
+
+ ++cur_step_;
- void UpdateLayerTreeHost() override {
ScrollTree& scroll_tree = layer_tree_host()->property_trees()->scroll_tree;
ScrollNode* scroll_node = scroll_tree.Node(scroller_->scroll_tree_index());
- switch (layer_tree_host()->SourceFrameNumber()) {
- case 0:
+ switch (cur_step_) {
+ case 1:
// Set max_scroll_offset = (100, 100).
scroll_node->bounds = scroll_node->container_bounds;
scroll_node->bounds.Enlarge(100, 100);
break;
- case 1:
+ case 2:
// Set max_scroll_offset = (0, 0).
scroll_node->bounds = scroll_node->container_bounds;
break;
- case 2:
+ case 3:
// Set max_scroll_offset = (-1, -1).
scroll_node->bounds = gfx::Size();
break;
}
+
+ layer_tree_host()->SetNeedsCommit();
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
+ if (TestEnded())
+ return;
+
ScrollTree& scroll_tree =
impl->active_tree()->property_trees()->scroll_tree;
ScrollNode* scroll_node = scroll_tree.Node(scroller_->scroll_tree_index());
+
+ ScrollStateData scroll_state_data;
+ scroll_state_data.is_beginning = true;
+ // The position has to be at (0, 0) since the viewport in this test has
+ // bounds (1, 1).
+ scroll_state_data.position_x = 0;
+ scroll_state_data.position_y = 0;
+ scroll_state_data.delta_x_hint = 10;
+ scroll_state_data.delta_y_hint = 10;
+ scroll_state_data.is_direct_manipulation = true;
+
+ ScrollState scroll_state(scroll_state_data);
InputHandler::ScrollStatus status =
- impl->TryScroll(scroll_tree, scroll_node);
- switch (impl->active_tree()->source_frame_number()) {
- case 0:
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread)
- << "In Frame 0";
- EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
- status.main_thread_scrolling_reasons)
- << "In Frame 0";
- PostSetNeedsCommitToMainThread();
- break;
+ impl->ScrollBegin(&scroll_state, ui::ScrollInputType::kTouchscreen);
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread)
+ << "In Frame " << impl->active_tree()->source_frame_number();
+
+ switch (cur_step_) {
case 1:
- EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread) << "In Frame 1";
- EXPECT_EQ(MainThreadScrollingReason::kNotScrollable,
- status.main_thread_scrolling_reasons)
- << "In Frame 1";
- PostSetNeedsCommitToMainThread();
+ // Since the scroller has scroll extend and is scrollable, we should
+ // have targeted it.
+ EXPECT_EQ(scroll_node, impl->CurrentlyScrollingNode()) << "In Frame 0";
break;
case 2:
- EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread) << "In Frame 2";
- EXPECT_EQ(MainThreadScrollingReason::kNotScrollable,
- status.main_thread_scrolling_reasons)
+ // Since the max_scroll_offset is (0, 0) - we shouldn't target it and
+ // we should instead bubble up to the viewport.
+ EXPECT_EQ(impl->OuterViewportScrollNode(),
+ impl->CurrentlyScrollingNode())
+ << "In Frame 1";
+ break;
+ case 3:
+ // Since the max_scroll_offset is (-1, -1) - we shouldn't target it and
+ // we should instead bubble up to the viewport.
+ EXPECT_EQ(impl->OuterViewportScrollNode(),
+ impl->CurrentlyScrollingNode())
<< "In Frame 2";
EndTest();
break;
}
+ impl->ScrollEnd();
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &LayerTreeHostScrollTestScrollZeroMaxScrollOffset::NextStep,
+ base::Unretained(this)));
}
private:
+ int cur_step_ = 0;
scoped_refptr<Layer> scroller_;
};
@@ -1453,14 +1601,23 @@ class LayerTreeHostScrollTestScrollNonDrawnLayer
// checking whether the screen space point is inside the non-fast
// scrollable region.
InputHandler::ScrollStatus status = impl->ScrollBegin(
- BeginState(gfx::Point(0, 0)).get(), ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
- status.main_thread_scrolling_reasons);
+ BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 1)).get(),
+ ui::ScrollInputType::kTouchscreen);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ impl->ScrollEnd();
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
- status = impl->ScrollBegin(BeginState(gfx::Point(21, 21)).get(),
- ui::ScrollInputType::kTouchscreen);
+ status = impl->ScrollBegin(
+ BeginState(gfx::Point(21, 21), gfx::Vector2dF(0, 1)).get(),
+ ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
@@ -1481,36 +1638,88 @@ class LayerTreeHostScrollTestImplScrollUnderMainThreadScrollingParent
void SetupTree() override {
LayerTreeHostScrollTest::SetupTree();
- GetScrollNode(layer_tree_host()->InnerViewportScrollLayerForTesting())
+ GetScrollNode(layer_tree_host()->OuterViewportScrollLayerForTesting())
->main_thread_scrolling_reasons =
- MainThreadScrollingReason::kScrollbarScrolling;
+ MainThreadScrollingReason::kThreadedScrollingDisabled;
+
+ scroller_ = Layer::Create();
+ scroller_->SetIsDrawable(true);
+ scroller_->SetHitTestable(true);
+ scroller_->SetBounds(gfx::Size(200, 200));
+ scroller_->SetElementId(LayerIdToElementIdForTesting(scroller_->id()));
+ CopyProperties(layer_tree_host()->OuterViewportScrollLayerForTesting(),
+ scroller_.get());
+ CreateTransformNode(scroller_.get());
+ CreateScrollNode(scroller_.get(),
+ layer_tree_host()->root_layer()->bounds());
+ layer_tree_host()->root_layer()->AddChild(scroller_.get());
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
- LayerImpl* inner_scroll_layer =
- impl->active_tree()->InnerViewportScrollLayerForTesting();
- LayerImpl* outer_scroll_layer =
- impl->active_tree()->OuterViewportScrollLayerForTesting();
-
ScrollTree& scroll_tree =
impl->active_tree()->property_trees()->scroll_tree;
- ScrollNode* inner_scroll_node =
- scroll_tree.Node(inner_scroll_layer->scroll_tree_index());
- ScrollNode* outer_scroll_node =
- scroll_tree.Node(outer_scroll_layer->scroll_tree_index());
+ ScrollNode* scroller_scroll_node =
+ scroll_tree.Node(scroller_->scroll_tree_index());
+
+ ScrollStateData scroll_state_data;
+ scroll_state_data.is_beginning = true;
+ // To hit the scroller, the position has to be at (0, 0) since the viewport
+ // in this test has bounds (1, 1) and would otherwise clip the hit test.
+ scroll_state_data.position_x = 0;
+ scroll_state_data.position_y = 0;
+ scroll_state_data.delta_x_hint = 10;
+ scroll_state_data.delta_y_hint = 10;
+ scroll_state_data.is_direct_manipulation = true;
+ ScrollState scroll_state(scroll_state_data);
+
+ // Scroll hitting the scroller layer.
+ {
+ InputHandler::ScrollStatus status =
+ impl->ScrollBegin(&scroll_state, ui::ScrollInputType::kTouchscreen);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(impl->CurrentlyScrollingNode(), scroller_scroll_node);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ } else {
+ // Despite the fact that we hit the scroller, which has no main thread
+ // scrolling reason, we still must fallback to main thread scrolling due
+ // to the fact that it has a main thread scrolling ancestor.
+ EXPECT_EQ(impl->CurrentlyScrollingNode(), nullptr);
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kThreadedScrollingDisabled,
+ status.main_thread_scrolling_reasons);
+ }
+ impl->ScrollEnd();
+ }
- InputHandler::ScrollStatus status =
- impl->TryScroll(scroll_tree, inner_scroll_node);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kScrollbarScrolling,
- status.main_thread_scrolling_reasons);
+ // Scroll hitting the viewport layer.
+ {
+ // A hit test outside the viewport should fallback to scrolling the
+ // viewport.
+ scroll_state.data()->position_y = 1000;
+
+ InputHandler::ScrollStatus status =
+ impl->ScrollBegin(&scroll_state, ui::ScrollInputType::kTouchscreen);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ EXPECT_EQ(impl->CurrentlyScrollingNode(),
+ impl->OuterViewportScrollNode());
+ } else {
+ // Since the viewport has a main thread scrolling reason, this
+ // too should fallback to the main thread.
+ EXPECT_EQ(impl->CurrentlyScrollingNode(), nullptr);
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kThreadedScrollingDisabled,
+ status.main_thread_scrolling_reasons);
+ }
+ impl->ScrollEnd();
+ }
- status = impl->TryScroll(scroll_tree, outer_scroll_node);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
- status.main_thread_scrolling_reasons);
EndTest();
}
+
+ private:
+ scoped_refptr<Layer> scroller_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
@@ -2422,27 +2631,28 @@ class NonScrollingNonFastScrollableRegion : public LayerTreeHostScrollTest {
void SetupTree() override {
SetInitialRootBounds(gfx::Size(800, 600));
LayerTreeHostScrollTest::SetupTree();
- Layer* root = layer_tree_host()->root_layer();
- fake_content_layer_client_.set_bounds(root->bounds());
+ Layer* outer_scroll =
+ layer_tree_host()->OuterViewportScrollLayerForTesting();
+ fake_content_layer_client_.set_bounds(outer_scroll->bounds());
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);
- CopyProperties(root, bottom_.get());
- root->AddChild(bottom_);
+ CopyProperties(outer_scroll, bottom_.get());
+ outer_scroll->AddChild(bottom_);
middle_scrollable_ = FakePictureLayer::Create(&fake_content_layer_client_);
middle_scrollable_->SetElementId(
LayerIdToElementIdForTesting(middle_scrollable_->id()));
- middle_scrollable_->SetBounds(gfx::Size(100, 100));
+ middle_scrollable_->SetBounds(gfx::Size(100, 200));
middle_scrollable_->SetIsDrawable(true);
middle_scrollable_->SetHitTestable(true);
CopyProperties(bottom_.get(), middle_scrollable_.get());
CreateTransformNode(middle_scrollable_.get());
- CreateScrollNode(middle_scrollable_.get(), gfx::Size(100, 200));
- root->AddChild(middle_scrollable_);
+ CreateScrollNode(middle_scrollable_.get(), gfx::Size(100, 100));
+ outer_scroll->AddChild(middle_scrollable_);
top_ = FakePictureLayer::Create(&fake_content_layer_client_);
top_->SetElementId(LayerIdToElementIdForTesting(top_->id()));
@@ -2450,7 +2660,7 @@ class NonScrollingNonFastScrollableRegion : public LayerTreeHostScrollTest {
top_->SetNonFastScrollableRegion(Region(gfx::Rect(0, 0, 50, 50)));
top_->SetHitTestable(true);
CopyProperties(middle_scrollable_.get(), top_.get());
- root->AddChild(top_);
+ outer_scroll->AddChild(top_);
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
@@ -2459,35 +2669,70 @@ class NonScrollingNonFastScrollableRegion : public LayerTreeHostScrollTest {
if (TestEnded())
return;
- // 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(),
- ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, top_left_status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
- top_left_status.main_thread_scrolling_reasons);
-
- // The top-right hit should hit the top layer but not the non-fast region so
- // the scroll should continue to scroll on the impl.
- InputHandler::ScrollStatus top_right_status =
- impl->ScrollBegin(BeginState(gfx::Point(80, 20)).get(),
- ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, top_right_status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
- top_right_status.main_thread_scrolling_reasons);
- impl->ScrollEnd();
+ ScrollNode* scroll_node =
+ impl->active_tree()->property_trees()->scroll_tree.Node(
+ middle_scrollable_->scroll_tree_index());
- // 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(),
- ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, bottom_right_status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
- bottom_right_status.main_thread_scrolling_reasons);
+ // The top-left hit should immediately hit the top layer's non-fast region.
+ {
+ auto status = impl->ScrollBegin(
+ BeginState(gfx::Point(20, 20), gfx::Vector2dF(0, 1)).get(),
+ ui::ScrollInputType::kTouchscreen);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ // Hitting a non fast region should request a hit test from the main
+ // thread.
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ impl->ScrollEnd();
+ } else {
+ // Prior to scroll unification, this forces scrolling to the main
+ // thread.
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
+ }
+
+ // The top-right hit should hit the top layer but not the non-fast region
+ // so the scroll can be handled without involving the main thread.
+ {
+ InputHandler::ScrollStatus status = impl->ScrollBegin(
+ BeginState(gfx::Point(80, 20), gfx::Vector2dF(0, 1)).get(),
+ ui::ScrollInputType::kTouchscreen);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ }
+ EXPECT_EQ(scroll_node, impl->CurrentlyScrollingNode());
+ impl->ScrollEnd();
+ }
+
+ // The bottom-right should hit the bottom layer's non-fast region.
+ {
+ InputHandler::ScrollStatus status = impl->ScrollBegin(
+ BeginState(gfx::Point(80, 80), gfx::Vector2dF(0, 1)).get(),
+ ui::ScrollInputType::kTouchscreen);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ // Even though the point intersects a non-fast region, the first hit
+ // layer is scrollable from the compositor thread so no need to involve
+ // the main thread.
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ EXPECT_EQ(scroll_node, impl->CurrentlyScrollingNode());
+ impl->ScrollEnd();
+ } else {
+ // 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.
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
+ }
EndTest();
}
@@ -2501,5 +2746,70 @@ class NonScrollingNonFastScrollableRegion : public LayerTreeHostScrollTest {
SINGLE_THREAD_TEST_F(NonScrollingNonFastScrollableRegion);
+// This test verifies that scrolling in non layer list mode (used by UI
+// compositor) is always "compositor scrolled", i.e. property trees are mutated
+// and the updated layers redrawn. This test intentionally doesn't inherit
+// from LayerTreeHostScrollTest since that enables LayerLists.
+class UnifiedScrollingRepaintOnScroll : public LayerTreeTest {
+ public:
+ UnifiedScrollingRepaintOnScroll() {
+ scoped_feature_list.InitAndEnableFeature(features::kScrollUnification);
+ }
+
+ void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+
+ void SetupTree() override {
+ LayerTreeTest::SetupTree();
+
+ layer_ = FakePictureLayer::Create(&client_);
+ layer_->SetScrollable(gfx::Size(10, 10));
+ layer_->SetBounds(gfx::Size(100, 100));
+ layer_->SetIsDrawable(true);
+ layer_->SetHitTestable(true);
+ layer_->SetElementId(LayerIdToElementIdForTesting(layer_->id()));
+ client_.set_bounds(layer_->bounds());
+ layer_tree_host()->root_layer()->AddChild(layer_);
+ }
+
+ void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
+ if (is_done_)
+ return;
+ is_done_ = true;
+ EndTest();
+
+ TransformTree& transform_tree =
+ impl->active_tree()->property_trees()->transform_tree;
+ ASSERT_FALSE(transform_tree.needs_update());
+
+ // Perform a scroll over our FakePictureLayer.
+ {
+ InputHandler::ScrollStatus status = impl->ScrollBegin(
+ BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 10)).get(),
+ ui::ScrollInputType::kTouchscreen);
+
+ ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_EQ(layer_->scroll_tree_index(),
+ impl->CurrentlyScrollingNode()->id);
+
+ impl->ScrollUpdate(
+ UpdateState(gfx::Point(), gfx::Vector2dF(0, 10)).get());
+ impl->ScrollEnd();
+ }
+
+ // All scrolling in non-layer-list mode (i.e. UI compositor) should be
+ // "compositor" scrolling so it should mutate the property tree and redraw,
+ // rather than relying on an update from the main thread.
+ ASSERT_TRUE(transform_tree.needs_update());
+ }
+
+ private:
+ bool is_done_ = false;
+ scoped_refptr<Layer> layer_;
+ FakeContentLayerClient client_;
+ base::test::ScopedFeatureList scoped_feature_list;
+};
+
+MULTI_THREAD_TEST_F(UnifiedScrollingRepaintOnScroll);
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_impl.cc b/chromium/cc/trees/layer_tree_impl.cc
index 600c987976c..7ab7bf797ef 100644
--- a/chromium/cc/trees/layer_tree_impl.cc
+++ b/chromium/cc/trees/layer_tree_impl.cc
@@ -10,7 +10,9 @@
#include <algorithm>
#include <iterator>
#include <limits>
+#include <memory>
#include <set>
+#include <utility>
#include "base/containers/adapters.h"
#include "base/debug/crash_logging.h"
@@ -24,6 +26,7 @@
#include "base/trace_event/trace_event.h"
#include "base/trace_event/traced_value.h"
#include "cc/base/devtools_instrumentation.h"
+#include "cc/base/features.h"
#include "cc/base/histograms.h"
#include "cc/base/math_util.h"
#include "cc/base/synced_property.h"
@@ -209,17 +212,38 @@ void LayerTreeImpl::DidUpdateScrollOffset(ElementId id) {
return;
}
+ // This bit controls whether we'll update the transform node based on a
+ // changed scroll offset. If scroll unification is off, we always do this
+ // because the scroll handling code will only invoke a scroll update on nodes
+ // that can compositor scroll. However, with scroll unification, we can
+ // mutate scroll nodes which have main thread scrolling reasons, or aren't
+ // backed by a layer at all. In those cases, we don't want to produce any
+ // immediate changes in the compositor, we want the scroll to propagate
+ // through Blink in a commit and have Blink update properties, paint,
+ // compositing, etc. Thus, we avoid mutating the transform tree in this case.
+ // TODO(bokan): We SetNeedsCommit in LTHI when a scroll happens but in a
+ // normal compositor scroll there isn't much urgency for a commit to be
+ // scheduled. We should look into what we can do to make sure this is
+ // proritized accordingly. https://crbug.com/1082618.
+ bool can_realize_scroll_on_compositor =
+ !base::FeatureList::IsEnabled(features::kScrollUnification) ||
+ (scroll_node->is_composited &&
+ !scroll_node->main_thread_scrolling_reasons);
+
DCHECK(scroll_node->transform_id != TransformTree::kInvalidNodeId);
TransformTree& transform_tree = property_trees()->transform_tree;
auto* transform_node = transform_tree.Node(scroll_node->transform_id);
- if (transform_node->scroll_offset != scroll_tree.current_scroll_offset(id)) {
- transform_node->scroll_offset = scroll_tree.current_scroll_offset(id);
- transform_node->needs_local_transform_update = true;
- transform_tree.set_needs_update(true);
+ if (can_realize_scroll_on_compositor) {
+ if (transform_node->scroll_offset !=
+ scroll_tree.current_scroll_offset(id)) {
+ transform_node->scroll_offset = scroll_tree.current_scroll_offset(id);
+ transform_node->needs_local_transform_update = true;
+ transform_tree.set_needs_update(true);
+ }
+ transform_node->transform_changed = true;
+ property_trees()->changed = true;
+ set_needs_update_draw_properties();
}
- transform_node->transform_changed = true;
- property_trees()->changed = true;
- set_needs_update_draw_properties();
if (IsActiveTree()) {
// Ensure the other trees are kept in sync.
@@ -419,6 +443,13 @@ void LayerTreeImpl::UpdateViewportContainerSizes() {
OuterViewportScrollNode()->container_bounds.height() +
scaled_bounds_delta.y();
outer_clip_node->clip.set_height(adjusted_container_height);
+
+ // Expand all clips between the outer viewport and the inner viewport.
+ auto* outer_ancestor = property_trees->clip_tree.parent(outer_clip_node);
+ while (outer_ancestor && outer_ancestor->id != ClipTree::kRootNodeId) {
+ outer_ancestor->clip.Union(outer_clip_node->clip);
+ outer_ancestor = property_trees->clip_tree.parent(outer_ancestor);
+ }
}
anchor.ResetViewportToAnchoredPosition();
@@ -477,6 +508,16 @@ OwnedLayerImplList LayerTreeImpl::DetachLayersKeepingRootLayerForTesting() {
}
void LayerTreeImpl::SetPropertyTrees(PropertyTrees* property_trees) {
+ // Updating the scroll tree shouldn't clobber the currently scrolling node so
+ // stash it and restore it at the end of this method. To maintain the
+ // current scrolling node we need to use element ids which are stable across
+ // the property tree update in SetPropertyTrees.
+ ElementId scrolling_element_id;
+ if (IsActiveTree()) {
+ if (ScrollNode* scrolling_node = CurrentlyScrollingNode())
+ scrolling_element_id = scrolling_node->element_id;
+ }
+
std::vector<std::unique_ptr<RenderSurfaceImpl>> old_render_surfaces;
property_trees_.effect_tree.TakeRenderSurfaces(&old_render_surfaces);
property_trees_ = *property_trees;
@@ -493,6 +534,13 @@ void LayerTreeImpl::SetPropertyTrees(PropertyTrees* property_trees) {
// effect tree.
if (IsActiveTree())
property_trees_.effect_tree.set_needs_update(true);
+
+ const ScrollNode* scrolling_node = nullptr;
+ if (scrolling_element_id) {
+ auto& scroll_tree = property_trees_.scroll_tree;
+ scrolling_node = scroll_tree.FindNodeFromElementId(scrolling_element_id);
+ }
+ SetCurrentlyScrollingNode(scrolling_node);
}
void LayerTreeImpl::PushPropertyTreesTo(LayerTreeImpl* target_tree) {
@@ -508,21 +556,8 @@ void LayerTreeImpl::PushPropertyTreesTo(LayerTreeImpl* target_tree) {
target_tree->MoveChangeTrackingToLayers();
}
- // To maintain the current scrolling node we need to use element ids which
- // are stable across the property tree update in SetPropertyTrees.
- ElementId scrolling_element_id;
- if (ScrollNode* scrolling_node = target_tree->CurrentlyScrollingNode())
- scrolling_element_id = scrolling_node->element_id;
-
target_tree->SetPropertyTrees(&property_trees_);
- const ScrollNode* scrolling_node = nullptr;
- if (scrolling_element_id) {
- auto& scroll_tree = target_tree->property_trees()->scroll_tree;
- scrolling_node = scroll_tree.FindNodeFromElementId(scrolling_element_id);
- }
- target_tree->SetCurrentlyScrollingNode(scrolling_node);
-
std::vector<EventMetrics> events_metrics;
events_metrics.swap(events_metrics_from_main_thread_);
target_tree->AppendEventsMetricsFromMainThread(std::move(events_metrics));
@@ -618,6 +653,13 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) {
target_tree->HandleScrollbarShowRequestsFromMain();
target_tree->AddPresentationCallbacks(std::move(presentation_callbacks_));
presentation_callbacks_.clear();
+
+ if (delegated_ink_metadata_) {
+ TRACE_EVENT_INSTANT1("cc", "Delegated ink metadata pushed to tree",
+ TRACE_EVENT_SCOPE_THREAD, "point",
+ delegated_ink_metadata_->point().ToString());
+ target_tree->set_delegated_ink_metadata(std::move(delegated_ink_metadata_));
+ }
}
void LayerTreeImpl::HandleTickmarksVisibilityChange() {
@@ -1581,12 +1623,8 @@ ImageAnimationController* LayerTreeImpl::image_animation_controller() const {
return host_impl_->image_animation_controller();
}
-FrameRateCounter* LayerTreeImpl::frame_rate_counter() const {
- return host_impl_->fps_counter();
-}
-
-base::Optional<int> LayerTreeImpl::current_universal_throughput() {
- return host_impl_->current_universal_throughput();
+DroppedFrameCounter* LayerTreeImpl::dropped_frame_counter() const {
+ return host_impl_->dropped_frame_counter();
}
MemoryHistory* LayerTreeImpl::memory_history() const {
@@ -1937,9 +1975,11 @@ void LayerTreeImpl::UnregisterScrollbar(
if (scrollbar_ids.horizontal == Layer::INVALID_ID &&
scrollbar_ids.vertical == Layer::INVALID_ID) {
element_id_to_scrollbar_layer_ids_.erase(scroll_element_id);
- if (IsActiveTree()) {
- host_impl_->DidUnregisterScrollbarLayer(scroll_element_id);
- }
+ }
+
+ if (IsActiveTree()) {
+ host_impl_->DidUnregisterScrollbarLayer(scroll_element_id,
+ scrollbar_layer->orientation());
}
}
@@ -2256,6 +2296,19 @@ LayerTreeImpl::FindLayersHitByPointInNonFastScrollableRegion(
return layers;
}
+bool LayerTreeImpl::PointHitsNonFastScrollableRegion(
+ const gfx::PointF& screen_space_point,
+ const LayerImpl& layer) const {
+ // We assume the layer has already been hit tested.
+ DCHECK(PointHitsLayer(&layer, screen_space_point, nullptr));
+
+ if (layer.non_fast_scrollable_region().IsEmpty())
+ return false;
+
+ return PointHitsRegion(screen_space_point, layer.ScreenSpaceTransform(),
+ layer.non_fast_scrollable_region(), &layer);
+}
+
struct HitTestFramedVisibleScrollableOrTouchableFunctor {
bool operator()(LayerImpl* layer) const {
return layer->HitTestable() && layer->frame_element_id();
diff --git a/chromium/cc/trees/layer_tree_impl.h b/chromium/cc/trees/layer_tree_impl.h
index 5c2f586a0ea..d4583970c74 100644
--- a/chromium/cc/trees/layer_tree_impl.h
+++ b/chromium/cc/trees/layer_tree_impl.h
@@ -46,7 +46,7 @@ class ContextProvider;
namespace cc {
class DebugRectHistory;
-class FrameRateCounter;
+class DroppedFrameCounter;
class HeadsUpDisplayLayerImpl;
class ImageDecodeCache;
class LayerTreeDebugState;
@@ -127,8 +127,7 @@ class CC_EXPORT LayerTreeImpl {
TileManager* tile_manager() const;
ImageDecodeCache* image_decode_cache() const;
ImageAnimationController* image_animation_controller() const;
- FrameRateCounter* frame_rate_counter() const;
- base::Optional<int> current_universal_throughput();
+ DroppedFrameCounter* dropped_frame_counter() const;
MemoryHistory* memory_history() const;
DebugRectHistory* debug_rect_history() const;
bool IsActiveTree() const;
@@ -591,6 +590,8 @@ class CC_EXPORT LayerTreeImpl {
// Return all layers with a hit non-fast scrollable region.
std::vector<const LayerImpl*> FindLayersHitByPointInNonFastScrollableRegion(
const gfx::PointF& screen_space_point);
+ bool PointHitsNonFastScrollableRegion(const gfx::PointF& scree_space_point,
+ const LayerImpl& layer) const;
// Returns the ElementId representing a frame's document at the given point.
// In cases where cc doesn't have enough information to perform accurate
@@ -710,6 +711,18 @@ class CC_EXPORT LayerTreeImpl {
return host_impl_->DrawTransform();
}
+ // These functions are used for plumbing DelegatedInkMetadata from blink
+ // through the compositor and into viz via a compositor frame. They should
+ // only be called after the JS API |updateInkTrailStartPoint| has been
+ // called, which populates the metadata with provided information.
+ void set_delegated_ink_metadata(
+ std::unique_ptr<viz::DelegatedInkMetadata> metadata) {
+ delegated_ink_metadata_ = std::move(metadata);
+ }
+ std::unique_ptr<viz::DelegatedInkMetadata> take_delegated_ink_metadata() {
+ return std::move(delegated_ink_metadata_);
+ }
+
protected:
float ClampPageScaleFactorToLimits(float page_scale_factor) const;
void PushPageScaleFactorAndLimits(const float* page_scale_factor,
@@ -861,6 +874,8 @@ class CC_EXPORT LayerTreeImpl {
// Event metrics that are reported back from the main thread.
std::vector<EventMetrics> events_metrics_from_main_thread_;
+
+ std::unique_ptr<viz::DelegatedInkMetadata> delegated_ink_metadata_;
};
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_settings.h b/chromium/cc/trees/layer_tree_settings.h
index 95a901678c2..446d46764c5 100644
--- a/chromium/cc/trees/layer_tree_settings.h
+++ b/chromium/cc/trees/layer_tree_settings.h
@@ -187,10 +187,18 @@ class CC_EXPORT LayerTreeSettings {
// Whether experimental de-jelly effect is allowed.
bool allow_de_jelly_effect = false;
+ // Whether the compositor should attempt to sync with the scroll handlers
+ // before submitting a frame.
+ bool enable_synchronized_scrolling = true;
+
#if DCHECK_IS_ON()
// Whether to check if any double blur exists.
bool log_on_ui_double_background_blur = false;
#endif
+
+ // When enabled, enforces new interoperable semantics for 3D transforms.
+ // See crbug.com/1008483.
+ bool enable_transform_interop = false;
};
class CC_EXPORT LayerListSettings : public LayerTreeSettings {
diff --git a/chromium/cc/trees/mutator_host.h b/chromium/cc/trees/mutator_host.h
index 1292e0ec826..e3cf671314b 100644
--- a/chromium/cc/trees/mutator_host.h
+++ b/chromium/cc/trees/mutator_host.h
@@ -160,7 +160,6 @@ class MutatorHost {
// the scroller. Otherwise returns an invalid ElementId.
virtual ElementId ImplOnlyScrollAnimatingElement() const = 0;
- virtual size_t CompositedAnimationsCount() const = 0;
virtual size_t MainThreadAnimationsCount() const = 0;
virtual bool HasCustomPropertyAnimations() const = 0;
virtual bool CurrentFrameHadRAF() const = 0;
diff --git a/chromium/cc/trees/occlusion_tracker.h b/chromium/cc/trees/occlusion_tracker.h
index d2498676dec..c734beff463 100644
--- a/chromium/cc/trees/occlusion_tracker.h
+++ b/chromium/cc/trees/occlusion_tracker.h
@@ -59,7 +59,7 @@ class CC_EXPORT OcclusionTracker {
protected:
struct StackObject {
- StackObject() : target(0) {}
+ StackObject() : target(nullptr) {}
explicit StackObject(const RenderSurfaceImpl* target) : target(target) {}
const RenderSurfaceImpl* target;
SimpleEnclosedRegion occlusion_from_outside_target;
diff --git a/chromium/cc/trees/property_tree.cc b/chromium/cc/trees/property_tree.cc
index cbdb9d43e73..00ce2f2e65d 100644
--- a/chromium/cc/trees/property_tree.cc
+++ b/chromium/cc/trees/property_tree.cc
@@ -493,24 +493,27 @@ void TransformTree::UpdateSnapping(TransformNode* node) {
// rounded, then what we're after is the scroll delta X, where ST * X = ST'.
// I.e., we want a transform that will realize our snap. It follows that
// X = ST^-1 * ST'. We cache ST and ST^-1 to make this more efficient.
- gfx::Transform rounded = ToScreen(node->id);
- rounded.RoundTranslationComponents();
- gfx::Transform delta = FromScreen(node->id);
- delta *= rounded;
-
- DCHECK(delta.IsApproximatelyIdentityOrTranslation(SkDoubleToScalar(1e-4)))
+ DCHECK_LT(node->id, static_cast<int>(cached_data_.size()));
+ gfx::Transform& to_screen = cached_data_[node->id].to_screen;
+ to_screen.RoundTranslationComponents();
+ gfx::Transform& from_screen = cached_data_[node->id].from_screen;
+ gfx::Transform delta = from_screen;
+ delta *= to_screen;
+
+ constexpr float kTolerance = 1e-4f;
+ DCHECK(delta.IsApproximatelyIdentityOrTranslation(kTolerance))
<< delta.ToString();
gfx::Vector2dF translation = delta.To2dTranslation();
+ node->snap_amount = translation;
+ if (translation.IsZero())
+ return;
- // Now that we have our delta, we must apply it to each of our combined,
- // to/from matrices.
- SetToScreen(node->id, rounded);
- node->to_parent.Translate(translation.x(), translation.y());
- gfx::Transform from_screen = FromScreen(node->id);
from_screen.matrix().postTranslate(-translation.x(), -translation.y(), 0);
- SetFromScreen(node->id, from_screen);
- node->snap_amount = translation;
+ node->to_parent.Translate(translation.x(), translation.y());
+ // Avoid accumulation of errors in to_parent.
+ if (node->to_parent.IsApproximatelyIdentityOrIntegerTranslation(kTolerance))
+ node->to_parent.RoundTranslationComponents();
}
void TransformTree::UpdateTransformChanged(TransformNode* node,
@@ -745,6 +748,17 @@ void EffectTree::UpdateHasMaskingChild(EffectNode* node,
}
}
+void EffectTree::UpdateOnlyDrawsVisibleContent(EffectNode* node,
+ EffectNode* parent_node) {
+ node->only_draws_visible_content = !node->has_copy_request;
+ if (parent_node)
+ node->only_draws_visible_content &= parent_node->only_draws_visible_content;
+ if (!node->backdrop_filters.IsEmpty()) {
+ node->only_draws_visible_content &=
+ !node->backdrop_filters.HasFilterOfType(FilterOperation::ZOOM);
+ }
+}
+
void EffectTree::UpdateSurfaceContentsScale(EffectNode* effect_node) {
if (!effect_node->HasRenderSurface()) {
effect_node->surface_contents_scale = gfx::Vector2dF(1.0f, 1.0f);
@@ -829,6 +843,7 @@ void EffectTree::UpdateEffects(int id) {
UpdateEffectChanged(node, parent_node);
UpdateBackfaceVisibility(node, parent_node);
UpdateHasMaskingChild(node, parent_node);
+ UpdateOnlyDrawsVisibleContent(node, parent_node);
UpdateSurfaceContentsScale(node);
}
@@ -1273,6 +1288,8 @@ void ScrollTree::OnScrollOffsetAnimated(ElementId id,
if (!property_trees()->is_active)
return;
+ TRACE_EVENT2("cc", "ScrollTree::OnScrollOffsetAnimated", "x",
+ scroll_offset.x(), "y", scroll_offset.y());
ScrollNode* scroll_node = Node(scroll_tree_index);
if (SetScrollOffset(id,
ClampScrollOffsetToLimits(scroll_offset, *scroll_node)))
@@ -1570,6 +1587,9 @@ void ScrollTree::SetBaseScrollOffset(ElementId id,
bool ScrollTree::SetScrollOffset(ElementId id,
const gfx::ScrollOffset& scroll_offset) {
+ // TODO(crbug.com/1087088): Remove TRACE_EVENT call when the bug is fixed
+ TRACE_EVENT2("cc", "ScrollTree::SetScrollOffset", "x", scroll_offset.x(), "y",
+ scroll_offset.y());
if (property_trees()->is_main_thread) {
if (scroll_offset_map_[id] == scroll_offset)
return false;
diff --git a/chromium/cc/trees/property_tree.h b/chromium/cc/trees/property_tree.h
index 4dcf7d7a0bd..6c41097cc7a 100644
--- a/chromium/cc/trees/property_tree.h
+++ b/chromium/cc/trees/property_tree.h
@@ -356,6 +356,7 @@ class CC_EXPORT EffectTree final : public PropertyTree<EffectNode> {
void UpdateIsDrawn(EffectNode* node, EffectNode* parent_node);
void UpdateBackfaceVisibility(EffectNode* node, EffectNode* parent_node);
void UpdateHasMaskingChild(EffectNode* node, EffectNode* parent_node);
+ void UpdateOnlyDrawsVisibleContent(EffectNode* node, EffectNode* parent_node);
// Stores copy requests, keyed by node id.
std::unordered_multimap<int, std::unique_ptr<viz::CopyOutputRequest>>
diff --git a/chromium/cc/trees/property_tree_builder.cc b/chromium/cc/trees/property_tree_builder.cc
index b036be21cbd..fcf85d88d2d 100644
--- a/chromium/cc/trees/property_tree_builder.cc
+++ b/chromium/cc/trees/property_tree_builder.cc
@@ -612,6 +612,7 @@ void PropertyTreeBuilderContext::AddScrollNodeIfNeeded(
node.user_scrollable_vertical = layer->GetUserScrollableVertical();
node.element_id = layer->element_id();
node.transform_id = data_for_children->transform_tree_parent;
+ node.is_composited = true;
node_id = scroll_tree_.Insert(node, parent_id);
data_for_children->scroll_tree_parent = node_id;
diff --git a/chromium/cc/trees/property_tree_builder_unittest.cc b/chromium/cc/trees/property_tree_builder_unittest.cc
index af760bc828a..183c3342de0 100644
--- a/chromium/cc/trees/property_tree_builder_unittest.cc
+++ b/chromium/cc/trees/property_tree_builder_unittest.cc
@@ -201,7 +201,7 @@ TEST_F(PropertyTreeBuilderTest, RenderSurfaceListForTransparentChild) {
EXPECT_EQ(1U, GetRenderSurfaceList().size());
EXPECT_EQ(static_cast<viz::RenderPassId>(root->id()),
GetRenderSurfaceList().at(0)->id());
- EXPECT_EQ(gfx::Rect(), ImplOf(root)->drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(), ImplOf(root)->visible_drawable_content_rect());
}
TEST_F(PropertyTreeBuilderTest,
diff --git a/chromium/cc/trees/proxy.h b/chromium/cc/trees/proxy.h
index 0fe7a90fe3f..9afdb987bfc 100644
--- a/chromium/cc/trees/proxy.h
+++ b/chromium/cc/trees/proxy.h
@@ -8,7 +8,6 @@
#include <memory>
#include <string>
-#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
diff --git a/chromium/cc/trees/proxy_impl.cc b/chromium/cc/trees/proxy_impl.cc
index 544ad6392e3..24e222ffbe0 100644
--- a/chromium/cc/trees/proxy_impl.cc
+++ b/chromium/cc/trees/proxy_impl.cc
@@ -370,26 +370,10 @@ void ProxyImpl::SetVideoNeedsBeginFrames(bool needs_begin_frames) {
scheduler_->SetVideoNeedsBeginFrames(needs_begin_frames);
}
-size_t ProxyImpl::CompositedAnimationsCount() const {
- return host_impl_->mutator_host()->CompositedAnimationsCount();
-}
-
-size_t ProxyImpl::MainThreadAnimationsCount() const {
- return host_impl_->mutator_host()->MainThreadAnimationsCount();
-}
-
bool ProxyImpl::HasCustomPropertyAnimations() const {
return host_impl_->mutator_host()->HasCustomPropertyAnimations();
}
-bool ProxyImpl::CurrentFrameHadRAF() const {
- return host_impl_->mutator_host()->CurrentFrameHadRAF();
-}
-
-bool ProxyImpl::NextFrameHasPendingRAF() const {
- return host_impl_->mutator_host()->NextFrameHasPendingRAF();
-}
-
bool ProxyImpl::IsInsideDraw() {
return inside_draw_;
}
@@ -542,14 +526,37 @@ void ProxyImpl::NotifyThroughputTrackerResults(CustomTrackerResults results) {
proxy_main_weak_ptr_, std::move(results)));
}
+void ProxyImpl::SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) {
+ DCHECK(IsImplThread());
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&ProxyMain::SubmitThroughputData, proxy_main_weak_ptr_,
+ source_id, aggregated_percent, impl_percent,
+ main_percent));
+}
+
+void ProxyImpl::DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) {
+ DCHECK(IsImplThread());
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE, base::BindOnce(&ProxyMain::DidObserveFirstScrollDelay,
+ proxy_main_weak_ptr_, first_scroll_delay,
+ first_scroll_timestamp));
+}
+
bool ProxyImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) {
DCHECK(IsImplThread());
return host_impl_->WillBeginImplFrame(args);
}
-void ProxyImpl::DidFinishImplFrame() {
+void ProxyImpl::DidFinishImplFrame(
+ const viz::BeginFrameArgs& last_activated_args) {
DCHECK(IsImplThread());
- host_impl_->DidFinishImplFrame(scheduler_->last_activate_origin_frame_args());
+ host_impl_->DidFinishImplFrame(last_activated_args);
}
void ProxyImpl::DidNotProduceFrame(const viz::BeginFrameAck& ack,
@@ -703,9 +710,6 @@ DrawResult ProxyImpl::DrawInternal(bool forced_draw) {
base::AutoReset<bool> mark_inside(&inside_draw_, true);
- if (host_impl_->pending_tree())
- host_impl_->pending_tree()->UpdateDrawProperties();
-
// This method is called on a forced draw, regardless of whether we are able
// to produce a frame, as the calling site on main thread is blocked until its
// request completes, and we signal completion here. If CanDraw() is false, we
@@ -756,6 +760,12 @@ DrawResult ProxyImpl::DrawInternal(bool forced_draw) {
proxy_main_weak_ptr_));
}
+ // The tile visibility/priority of the pending tree needs to be updated so
+ // that it doesn't get activated before the raster is complete. But this needs
+ // to happen after the draw, off of the critical path to draw.
+ if (host_impl_->pending_tree())
+ host_impl_->pending_tree()->UpdateDrawProperties();
+
DCHECK_NE(INVALID_RESULT, result);
return result;
}
diff --git a/chromium/cc/trees/proxy_impl.h b/chromium/cc/trees/proxy_impl.h
index 3050199e43d..dbe9b20d3ba 100644
--- a/chromium/cc/trees/proxy_impl.h
+++ b/chromium/cc/trees/proxy_impl.h
@@ -119,10 +119,18 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient,
void NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) override;
void NotifyThroughputTrackerResults(CustomTrackerResults results) override;
+ void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) override;
+ void DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) override;
// SchedulerClient implementation
bool WillBeginImplFrame(const viz::BeginFrameArgs& args) override;
- void DidFinishImplFrame() override;
+ void DidFinishImplFrame(
+ const viz::BeginFrameArgs& last_activated_args) override;
void DidNotProduceFrame(const viz::BeginFrameAck& ack,
FrameSkippedReason reason) override;
void WillNotReceiveBeginFrame() override;
@@ -140,11 +148,7 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient,
void ScheduledActionBeginMainFrameNotExpectedUntil(
base::TimeTicks time) override;
void FrameIntervalUpdated(base::TimeDelta interval) override {}
- size_t CompositedAnimationsCount() const override;
- size_t MainThreadAnimationsCount() const override;
bool HasCustomPropertyAnimations() const override;
- bool CurrentFrameHadRAF() const override;
- bool NextFrameHasPendingRAF() const override;
DrawResult DrawInternal(bool forced_draw);
diff --git a/chromium/cc/trees/proxy_main.cc b/chromium/cc/trees/proxy_main.cc
index 22976f62b02..1ad9795cd11 100644
--- a/chromium/cc/trees/proxy_main.cc
+++ b/chromium/cc/trees/proxy_main.cc
@@ -378,6 +378,22 @@ void ProxyMain::NotifyThroughputTrackerResults(CustomTrackerResults results) {
layer_tree_host_->NotifyThroughputTrackerResults(std::move(results));
}
+void ProxyMain::SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) {
+ DCHECK(!task_runner_provider_->IsImplThread());
+ layer_tree_host_->SubmitThroughputData(source_id, aggregated_percent,
+ impl_percent, main_percent);
+}
+
+void ProxyMain::DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) {
+ layer_tree_host_->DidObserveFirstScrollDelay(first_scroll_delay,
+ first_scroll_timestamp);
+}
+
bool ProxyMain::IsStarted() const {
DCHECK(IsMainThread());
return started_;
diff --git a/chromium/cc/trees/proxy_main.h b/chromium/cc/trees/proxy_main.h
index 8738cb1f38f..050af82306b 100644
--- a/chromium/cc/trees/proxy_main.h
+++ b/chromium/cc/trees/proxy_main.h
@@ -58,6 +58,12 @@ class CC_EXPORT ProxyMain : public Proxy {
std::vector<LayerTreeHost::PresentationTimeCallback> callbacks,
const gfx::PresentationFeedback& feedback);
void NotifyThroughputTrackerResults(CustomTrackerResults results);
+ void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent);
+ void DidObserveFirstScrollDelay(base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp);
CommitPipelineStage max_requested_pipeline_stage() const {
return max_requested_pipeline_stage_;
diff --git a/chromium/cc/trees/render_frame_metadata.h b/chromium/cc/trees/render_frame_metadata.h
index 43af9536c47..2b884785a9d 100644
--- a/chromium/cc/trees/render_frame_metadata.h
+++ b/chromium/cc/trees/render_frame_metadata.h
@@ -41,8 +41,7 @@ class CC_EXPORT RenderFrameMetadata {
// specified.
SkColor root_background_color = SK_ColorWHITE;
- // Scroll offset of the root layer. This optional parameter is only valid
- // during tests.
+ // Scroll offset of the root layer.
base::Optional<gfx::Vector2dF> root_scroll_offset;
// Selection region relative to the current viewport. If the selection is
@@ -56,6 +55,11 @@ class CC_EXPORT RenderFrameMetadata {
// are the same).
bool is_mobile_optimized = false;
+ // Flag used to notify the browser process to start or stop forwarding points
+ // to viz for use in a delegated ink trail. True the entire time points should
+ // be forwarded, and forwarding stops as soon as it is false again.
+ bool has_delegated_ink_metadata = false;
+
// The device scale factor used to generate a CompositorFrame.
float device_scale_factor = 1.f;
diff --git a/chromium/cc/trees/scroll_and_scale_set.h b/chromium/cc/trees/scroll_and_scale_set.h
index cef0850f33b..b7e9d9ed91a 100644
--- a/chromium/cc/trees/scroll_and_scale_set.h
+++ b/chromium/cc/trees/scroll_and_scale_set.h
@@ -97,6 +97,10 @@ struct CC_EXPORT ScrollAndScaleSet {
// ended.
bool scroll_gesture_did_end;
+ // Tracks whether there is an ongoing compositor-driven animation for a
+ // scroll.
+ bool ongoing_scroll_animation = false;
+
// Tracks different methods of scrolling (e.g. wheel, touch, precision
// touchpad, etc.).
ManipulationInfo manipulation_info;
diff --git a/chromium/cc/trees/single_thread_proxy.cc b/chromium/cc/trees/single_thread_proxy.cc
index 6f469a42c4e..c5449d5e85a 100644
--- a/chromium/cc/trees/single_thread_proxy.cc
+++ b/chromium/cc/trees/single_thread_proxy.cc
@@ -415,26 +415,10 @@ void SingleThreadProxy::SetVideoNeedsBeginFrames(bool needs_begin_frames) {
scheduler_on_impl_thread_->SetVideoNeedsBeginFrames(needs_begin_frames);
}
-size_t SingleThreadProxy::CompositedAnimationsCount() const {
- return 0;
-}
-
-size_t SingleThreadProxy::MainThreadAnimationsCount() const {
- return 0;
-}
-
bool SingleThreadProxy::HasCustomPropertyAnimations() const {
return false;
}
-bool SingleThreadProxy::CurrentFrameHadRAF() const {
- return false;
-}
-
-bool SingleThreadProxy::NextFrameHasPendingRAF() const {
- return false;
-}
-
bool SingleThreadProxy::IsInsideDraw() {
return inside_draw_;
}
@@ -635,7 +619,7 @@ void SingleThreadProxy::CompositeImmediately(base::TimeTicks frame_begin_time,
// another draw will never be scheduled, so break remaining promises.
host_impl_->active_tree()->BreakSwapPromises(SwapPromise::SWAP_FAILS);
- DidFinishImplFrame();
+ DidFinishImplFrame(begin_frame_args);
}
}
@@ -958,9 +942,9 @@ void SingleThreadProxy::ScheduledActionPerformImplSideInvalidation() {
NotifyReadyToActivate();
}
-void SingleThreadProxy::DidFinishImplFrame() {
- host_impl_->DidFinishImplFrame(
- scheduler_on_impl_thread_->last_activate_origin_frame_args());
+void SingleThreadProxy::DidFinishImplFrame(
+ const viz::BeginFrameArgs& last_activated_args) {
+ host_impl_->DidFinishImplFrame(last_activated_args);
#if DCHECK_IS_ON()
DCHECK(inside_impl_frame_)
<< "DidFinishImplFrame called while not inside an impl frame!";
diff --git a/chromium/cc/trees/single_thread_proxy.h b/chromium/cc/trees/single_thread_proxy.h
index 96228af593c..ee1915c5222 100644
--- a/chromium/cc/trees/single_thread_proxy.h
+++ b/chromium/cc/trees/single_thread_proxy.h
@@ -77,7 +77,8 @@ class CC_EXPORT SingleThreadProxy : public Proxy,
// SchedulerClient implementation
bool WillBeginImplFrame(const viz::BeginFrameArgs& args) override;
- void DidFinishImplFrame() override;
+ void DidFinishImplFrame(
+ const viz::BeginFrameArgs& last_activated_args) override;
void DidNotProduceFrame(const viz::BeginFrameAck& ack,
FrameSkippedReason reason) override;
void WillNotReceiveBeginFrame() override;
@@ -95,11 +96,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy,
void ScheduledActionBeginMainFrameNotExpectedUntil(
base::TimeTicks time) override;
void FrameIntervalUpdated(base::TimeDelta interval) override;
- size_t CompositedAnimationsCount() const override;
- size_t MainThreadAnimationsCount() const override;
bool HasCustomPropertyAnimations() const override;
- bool CurrentFrameHadRAF() const override;
- bool NextFrameHasPendingRAF() const override;
// LayerTreeHostImplClient implementation
void DidLoseLayerTreeFrameSinkOnImplThread() override;
@@ -137,9 +134,21 @@ class CC_EXPORT SingleThreadProxy : public Proxy,
void NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) override;
void NotifyThroughputTrackerResults(CustomTrackerResults results) override;
+ void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) override {}
void RequestNewLayerTreeFrameSink();
+ void DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) override {
+ // Single-threaded mode is only for browser compositing and for renderers in
+ // layout tests. This will still get called in the latter case, but we don't
+ // need to record UKM in that case.
+ }
+
// Called by the legacy path where RenderWidget does the scheduling.
// Rasterization of tiles is only performed when |raster| is true.
void CompositeImmediately(base::TimeTicks frame_begin_time, bool raster);
diff --git a/chromium/cc/trees/task_runner_provider.h b/chromium/cc/trees/task_runner_provider.h
index 0bc19d31f38..232bc6da0f6 100644
--- a/chromium/cc/trees/task_runner_provider.h
+++ b/chromium/cc/trees/task_runner_provider.h
@@ -8,7 +8,7 @@
#include <memory>
#include <string>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
diff --git a/chromium/cc/trees/transform_node.cc b/chromium/cc/trees/transform_node.cc
index 780bba5ec67..b19c820baab 100644
--- a/chromium/cc/trees/transform_node.cc
+++ b/chromium/cc/trees/transform_node.cc
@@ -32,6 +32,7 @@ TransformNode::TransformNode()
moved_by_outer_viewport_bounds_delta_y(false),
in_subtree_of_page_scale_layer(false),
transform_changed(false),
+ delegates_to_parent_for_backface(false),
maximum_animation_scale(kNotScaled),
starting_animation_scale(kNotScaled) {}
@@ -62,6 +63,8 @@ bool TransformNode::operator==(const TransformNode& other) const {
other.moved_by_outer_viewport_bounds_delta_y &&
in_subtree_of_page_scale_layer ==
other.in_subtree_of_page_scale_layer &&
+ delegates_to_parent_for_backface ==
+ other.delegates_to_parent_for_backface &&
transform_changed == other.transform_changed &&
scroll_offset == other.scroll_offset &&
snap_amount == other.snap_amount &&
diff --git a/chromium/cc/trees/transform_node.h b/chromium/cc/trees/transform_node.h
index 401af01e617..47bb059ea1c 100644
--- a/chromium/cc/trees/transform_node.h
+++ b/chromium/cc/trees/transform_node.h
@@ -105,6 +105,10 @@ struct CC_EXPORT TransformNode {
// We need to track changes to to_screen transform to compute the damage rect.
bool transform_changed : 1;
+ // Whether the parent transform node should be used for checking backface
+ // visibility, not this transform one.
+ bool delegates_to_parent_for_backface : 1;
+
gfx::ScrollOffset scroll_offset;
// This value stores the snapped amount whenever we snap. If the snap is due
diff --git a/chromium/cc/trees/ukm_manager.cc b/chromium/cc/trees/ukm_manager.cc
index 2141cf1a21c..9d4eab1315f 100644
--- a/chromium/cc/trees/ukm_manager.cc
+++ b/chromium/cc/trees/ukm_manager.cc
@@ -4,7 +4,12 @@
#include "cc/trees/ukm_manager.h"
+#include <algorithm>
+#include <utility>
+
+#include "cc/metrics/compositor_frame_reporter.h"
#include "cc/metrics/throughput_ukm_reporter.h"
+#include "components/viz/common/quads/compositor_frame.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
@@ -187,11 +192,13 @@ void UkmManager::RecordAggregateThroughput(AggregationType aggregation_type,
builder.Record(recorder_.get());
}
-void UkmManager::RecordLatencyUKM(
+void UkmManager::RecordCompositorLatencyUKM(
CompositorFrameReporter::FrameReportType report_type,
const std::vector<CompositorFrameReporter::StageData>& stage_history,
const CompositorFrameReporter::ActiveTrackers& active_trackers,
const viz::FrameTimingDetails& viz_breakdown) const {
+ using StageType = CompositorFrameReporter::StageType;
+
ukm::builders::Graphics_Smoothness_Latency builder(source_id_);
if (report_type == CompositorFrameReporter::FrameReportType::kDroppedFrame) {
@@ -202,7 +209,7 @@ void UkmManager::RecordLatencyUKM(
for (const CompositorFrameReporter::StageData& stage : stage_history) {
switch (stage.stage_type) {
#define CASE_FOR_STAGE(name) \
- case CompositorFrameReporter::StageType::k##name: \
+ case StageType::k##name: \
builder.Set##name((stage.end_time - stage.start_time).InMicroseconds()); \
break;
CASE_FOR_STAGE(BeginImplFrameToSendBeginMainFrame);
@@ -215,8 +222,7 @@ void UkmManager::RecordLatencyUKM(
#undef CASE_FOR_STAGE
// Break out kSubmitCompositorFrameToPresentationCompositorFrame to report
// the viz breakdown.
- case CompositorFrameReporter::StageType::
- kSubmitCompositorFrameToPresentationCompositorFrame:
+ case StageType::kSubmitCompositorFrameToPresentationCompositorFrame:
builder.SetSubmitCompositorFrameToPresentationCompositorFrame(
(stage.end_time - stage.start_time).InMicroseconds());
if (viz_breakdown.received_compositor_frame_timestamp.is_null())
@@ -287,4 +293,80 @@ void UkmManager::RecordLatencyUKM(
builder.Record(recorder_.get());
}
+void UkmManager::RecordEventLatencyUKM(
+ const std::vector<EventMetrics>& events_metrics,
+ const std::vector<CompositorFrameReporter::StageData>& stage_history,
+ const viz::FrameTimingDetails& viz_breakdown) const {
+ using StageType = CompositorFrameReporter::StageType;
+
+ for (const EventMetrics& event_metrics : events_metrics) {
+ ukm::builders::Graphics_Smoothness_EventLatency builder(source_id_);
+
+ builder.SetEventType(static_cast<int64_t>(event_metrics.type()));
+
+ if (event_metrics.scroll_type()) {
+ builder.SetScrollInputType(
+ static_cast<int64_t>(*event_metrics.scroll_type()));
+
+ if (!viz_breakdown.swap_timings.is_null()) {
+ builder.SetTotalLatencyToSwapEnd(
+ (viz_breakdown.swap_timings.swap_end - event_metrics.time_stamp())
+ .InMicroseconds());
+ }
+ }
+
+ // It is possible for an event to arrive in the compositor in the middle of
+ // a frame (e.g. the browser received the event *after* renderer received a
+ // begin-impl, and the event reached the compositor before that frame
+ // ended). To handle such cases, find the first stage that happens after the
+ // event's arrival in the browser.
+ auto stage_it = std::find_if(
+ stage_history.begin(), stage_history.end(),
+ [&event_metrics](const CompositorFrameReporter::StageData& stage) {
+ return stage.start_time > event_metrics.time_stamp();
+ });
+ // TODO(crbug.com/1079116): Ideally, at least the start time of
+ // SubmitCompositorFrameToPresentationCompositorFrame stage should be
+ // greater than the event time stamp, but apparently, this is not always the
+ // case (see crbug.com/1093698). For now, skip to the next event in such
+ // cases. Hopefully, the work to reduce discrepancies between the new
+ // EventLatency and the old Event.Latency metrics would fix this issue. If
+ // not, we need to reconsider investigating this issue.
+ if (stage_it == stage_history.end())
+ continue;
+
+ builder.SetBrowserToRendererCompositor(
+ (stage_it->start_time - event_metrics.time_stamp()).InMicroseconds());
+
+ for (; stage_it != stage_history.end(); ++stage_it) {
+ // Total latency is calculated since the event timestamp.
+ const base::TimeTicks start_time =
+ stage_it->stage_type == StageType::kTotalLatency
+ ? event_metrics.time_stamp()
+ : stage_it->start_time;
+
+ switch (stage_it->stage_type) {
+#define CASE_FOR_STAGE(name) \
+ case StageType::k##name: \
+ builder.Set##name((stage_it->end_time - start_time).InMicroseconds()); \
+ break;
+ CASE_FOR_STAGE(BeginImplFrameToSendBeginMainFrame);
+ CASE_FOR_STAGE(SendBeginMainFrameToCommit);
+ CASE_FOR_STAGE(Commit);
+ CASE_FOR_STAGE(EndCommitToActivation);
+ CASE_FOR_STAGE(Activation);
+ CASE_FOR_STAGE(EndActivateToSubmitCompositorFrame);
+ CASE_FOR_STAGE(SubmitCompositorFrameToPresentationCompositorFrame);
+ CASE_FOR_STAGE(TotalLatency);
+#undef CASE_FOR_STAGE
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ builder.Record(recorder_.get());
+ }
+}
+
} // namespace cc
diff --git a/chromium/cc/trees/ukm_manager.h b/chromium/cc/trees/ukm_manager.h
index 50653814f85..6a2cc1c03db 100644
--- a/chromium/cc/trees/ukm_manager.h
+++ b/chromium/cc/trees/ukm_manager.h
@@ -5,9 +5,14 @@
#ifndef CC_TREES_UKM_MANAGER_H_
#define CC_TREES_UKM_MANAGER_H_
+#include <memory>
+#include <vector>
+
#include "cc/cc_export.h"
#include "cc/metrics/compositor_frame_reporter.h"
+#include "cc/metrics/event_metrics.h"
#include "cc/metrics/frame_sequence_metrics.h"
+#include "components/viz/common/frame_timing_details.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "url/gurl.h"
@@ -33,6 +38,7 @@ class CC_EXPORT UkmManager {
~UkmManager();
void SetSourceId(ukm::SourceId source_id);
+ ukm::SourceId source_id() const { return source_id_; }
// These metrics are recorded while a user interaction is in progress.
void SetUserInteractionInProgress(bool in_progress);
@@ -48,12 +54,17 @@ class CC_EXPORT UkmManager {
int64_t throughput) const;
void RecordAggregateThroughput(AggregationType aggregation_type,
int64_t throughput_percent) const;
- void RecordLatencyUKM(
+ void RecordCompositorLatencyUKM(
CompositorFrameReporter::FrameReportType report_type,
const std::vector<CompositorFrameReporter::StageData>& stage_history,
const CompositorFrameReporter::ActiveTrackers& active_trackers,
const viz::FrameTimingDetails& viz_breakdown) const;
+ void RecordEventLatencyUKM(
+ const std::vector<EventMetrics>& events_metrics,
+ const std::vector<CompositorFrameReporter::StageData>& stage_history,
+ const viz::FrameTimingDetails& viz_breakdown) const;
+
ukm::UkmRecorder* recorder_for_testing() { return recorder_.get(); }
private:
diff --git a/chromium/cc/trees/ukm_manager_unittest.cc b/chromium/cc/trees/ukm_manager_unittest.cc
index 9f4dd926f5b..2b55162d45f 100644
--- a/chromium/cc/trees/ukm_manager_unittest.cc
+++ b/chromium/cc/trees/ukm_manager_unittest.cc
@@ -4,7 +4,14 @@
#include "cc/trees/ukm_manager.h"
+#include <vector>
+
+#include "base/time/time.h"
+#include "cc/metrics/compositor_frame_reporter.h"
+#include "cc/metrics/event_metrics.h"
#include "components/ukm/test_ukm_recorder.h"
+#include "components/viz/common/frame_timing_details.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -23,6 +30,55 @@ const char kCheckerboardAreaRatio[] = "CheckerboardedContentAreaRatio";
const char kMissingTiles[] = "NumMissingTiles";
const char kCheckerboardedImagesCount[] = "CheckerboardedImagesCount";
+// Names of compositor/event latency UKM events.
+const char kCompositorLatency[] = "Graphics.Smoothness.Latency";
+const char kEventLatency[] = "Graphics.Smoothness.EventLatency";
+
+// Names of enum metrics used in compositor/event latency UKM metrics.
+const char kMissedFrame[] = "MissedFrame";
+const char kEventType[] = "EventType";
+const char kScrollInputType[] = "ScrollInputType";
+
+// Names of compositor stages and substages used in compositor/event latency UKM
+// metrics.
+const char kBrowserToRendererCompositor[] = "BrowserToRendererCompositor";
+const char kBeginImplFrameToSendBeginMainFrame[] =
+ "BeginImplFrameToSendBeginMainFrame";
+const char kSendBeginMainFrameToCommit[] = "SendBeginMainFrameToCommit";
+const char kCommit[] = "Commit";
+const char kEndCommitToActivation[] = "EndCommitToActivation";
+const char kActivation[] = "Activation";
+const char kEndActivateToSubmitCompositorFrame[] =
+ "EndActivateToSubmitCompositorFrame";
+const char kSubmitCompositorFrameToPresentationCompositorFrame[] =
+ "SubmitCompositorFrameToPresentationCompositorFrame";
+const char kVizBreakdownSubmitToReceiveCompositorFrame[] =
+ "SubmitCompositorFrameToPresentationCompositorFrame."
+ "SubmitToReceiveCompositorFrame";
+const char kVizBreakdownReceivedCompositorFrameToStartDraw[] =
+ "SubmitCompositorFrameToPresentationCompositorFrame."
+ "ReceivedCompositorFrameToStartDraw";
+const char kVizBreakdownStartDrawToSwapStart[] =
+ "SubmitCompositorFrameToPresentationCompositorFrame.StartDrawToSwapStart";
+const char kVizBreakdownSwapStartToSwapEnd[] =
+ "SubmitCompositorFrameToPresentationCompositorFrame.SwapStartToSwapEnd";
+const char kVizBreakdownSwapEndToPresentationCompositorFrame[] =
+ "SubmitCompositorFrameToPresentationCompositorFrame."
+ "SwapEndToPresentationCompositorFrame";
+const char kTotalLatencyToSwapEnd[] = "TotalLatencyToSwapEnd";
+const char kTotalLatency[] = "TotalLatency";
+
+// Names of frame sequence types use in compositor latency UKM metrics (see
+// FrameSequenceTrackerType enum).
+const char kCompositorAnimation[] = "CompositorAnimation";
+const char kMainThreadAnimation[] = "MainThreadAnimation";
+const char kPinchZoom[] = "PinchZoom";
+const char kRAF[] = "RAF";
+const char kTouchScroll[] = "TouchScroll";
+const char kUniversal[] = "Universal";
+const char kVideo[] = "Video";
+const char kWheelScroll[] = "WheelScroll";
+
class UkmManagerTest : public testing::Test {
public:
UkmManagerTest() {
@@ -36,6 +92,8 @@ class UkmManagerTest : public testing::Test {
manager_->SetSourceId(kTestSourceId1);
}
+ ~UkmManagerTest() override = default;
+
protected:
ukm::TestUkmRecorder* test_ukm_recorder_;
std::unique_ptr<UkmManager> manager_;
@@ -99,5 +157,300 @@ TEST_F(UkmManagerTest, Basic) {
}
}
+class UkmManagerCompositorLatencyTest
+ : public UkmManagerTest,
+ public testing::WithParamInterface<
+ CompositorFrameReporter::FrameReportType> {
+ public:
+ UkmManagerCompositorLatencyTest() : report_type_(GetParam()) {}
+ ~UkmManagerCompositorLatencyTest() override = default;
+
+ protected:
+ CompositorFrameReporter::FrameReportType report_type() const {
+ return report_type_;
+ }
+
+ private:
+ CompositorFrameReporter::FrameReportType report_type_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ All,
+ UkmManagerCompositorLatencyTest,
+ testing::Values(
+ CompositorFrameReporter::FrameReportType::kNonDroppedFrame,
+ CompositorFrameReporter::FrameReportType::kMissedDeadlineFrame,
+ CompositorFrameReporter::FrameReportType::kDroppedFrame,
+ CompositorFrameReporter::FrameReportType::kCompositorOnlyFrame));
+
+TEST_P(UkmManagerCompositorLatencyTest, CompositorLatency) {
+ base::TimeTicks now = base::TimeTicks::Now();
+
+ const base::TimeTicks begin_impl_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks begin_main_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks begin_commit_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks end_commit_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks begin_activate_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks end_activate_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks submit_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+
+ viz::FrameTimingDetails viz_breakdown;
+ viz_breakdown.received_compositor_frame_timestamp =
+ (now += base::TimeDelta::FromMicroseconds(1));
+ viz_breakdown.draw_start_timestamp =
+ (now += base::TimeDelta::FromMicroseconds(2));
+ viz_breakdown.swap_timings.swap_start =
+ (now += base::TimeDelta::FromMicroseconds(3));
+ viz_breakdown.swap_timings.swap_end =
+ (now += base::TimeDelta::FromMicroseconds(4));
+ viz_breakdown.presentation_feedback.timestamp =
+ (now += base::TimeDelta::FromMicroseconds(5));
+
+ std::vector<CompositorFrameReporter::StageData> stage_history = {
+ {
+ CompositorFrameReporter::StageType::
+ kBeginImplFrameToSendBeginMainFrame,
+ begin_impl_time,
+ begin_main_time,
+ },
+ {
+ CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit,
+ begin_main_time,
+ begin_commit_time,
+ },
+ {
+ CompositorFrameReporter::StageType::kCommit,
+ begin_commit_time,
+ end_commit_time,
+ },
+ {
+ CompositorFrameReporter::StageType::kEndCommitToActivation,
+ end_commit_time,
+ begin_activate_time,
+ },
+ {
+ CompositorFrameReporter::StageType::kActivation,
+ begin_activate_time,
+ end_activate_time,
+ },
+ {
+ CompositorFrameReporter::StageType::
+ kEndActivateToSubmitCompositorFrame,
+ end_activate_time,
+ submit_time,
+ },
+ {
+ CompositorFrameReporter::StageType::
+ kSubmitCompositorFrameToPresentationCompositorFrame,
+ submit_time,
+ viz_breakdown.presentation_feedback.timestamp,
+ },
+ {
+ CompositorFrameReporter::StageType::kTotalLatency,
+ begin_impl_time,
+ viz_breakdown.presentation_feedback.timestamp,
+ },
+ };
+
+ CompositorFrameReporter::ActiveTrackers active_trackers;
+ active_trackers.set(
+ static_cast<size_t>(FrameSequenceTrackerType::kTouchScroll));
+ active_trackers.set(
+ static_cast<size_t>(FrameSequenceTrackerType::kCompositorAnimation));
+ active_trackers.set(
+ static_cast<size_t>(FrameSequenceTrackerType::kUniversal));
+
+ manager_->RecordCompositorLatencyUKM(report_type(), stage_history,
+ active_trackers, viz_breakdown);
+
+ const auto& entries =
+ test_ukm_recorder_->GetEntriesByName(kCompositorLatency);
+ EXPECT_EQ(1u, entries.size());
+ const auto* entry = entries[0];
+
+ EXPECT_NE(ukm::kInvalidSourceId, entry->source_id);
+ test_ukm_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestUrl));
+
+ if (report_type() ==
+ CompositorFrameReporter::FrameReportType::kDroppedFrame) {
+ test_ukm_recorder_->ExpectEntryMetric(entry, kMissedFrame, true);
+ } else {
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kMissedFrame));
+ }
+
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kBeginImplFrameToSendBeginMainFrame,
+ (begin_main_time - begin_impl_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kSendBeginMainFrameToCommit,
+ (begin_commit_time - begin_main_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kCommit, (end_commit_time - begin_commit_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kEndCommitToActivation,
+ (begin_activate_time - end_commit_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kActivation,
+ (end_activate_time - begin_activate_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kEndActivateToSubmitCompositorFrame,
+ (submit_time - end_activate_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kSubmitCompositorFrameToPresentationCompositorFrame,
+ (viz_breakdown.presentation_feedback.timestamp - submit_time)
+ .InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kVizBreakdownSubmitToReceiveCompositorFrame,
+ (viz_breakdown.received_compositor_frame_timestamp - submit_time)
+ .InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kVizBreakdownReceivedCompositorFrameToStartDraw,
+ (viz_breakdown.draw_start_timestamp -
+ viz_breakdown.received_compositor_frame_timestamp)
+ .InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(entry,
+ kVizBreakdownStartDrawToSwapStart,
+ (viz_breakdown.swap_timings.swap_start -
+ viz_breakdown.draw_start_timestamp)
+ .InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(entry, kVizBreakdownSwapStartToSwapEnd,
+ (viz_breakdown.swap_timings.swap_end -
+ viz_breakdown.swap_timings.swap_start)
+ .InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kVizBreakdownSwapEndToPresentationCompositorFrame,
+ (viz_breakdown.presentation_feedback.timestamp -
+ viz_breakdown.swap_timings.swap_end)
+ .InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kTotalLatency,
+ (viz_breakdown.presentation_feedback.timestamp - begin_impl_time)
+ .InMicroseconds());
+
+ test_ukm_recorder_->ExpectEntryMetric(entry, kCompositorAnimation, true);
+ test_ukm_recorder_->ExpectEntryMetric(entry, kTouchScroll, true);
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kMainThreadAnimation));
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kPinchZoom));
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kRAF));
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kUniversal));
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kVideo));
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kWheelScroll));
+}
+
+TEST_F(UkmManagerTest, EventLatency) {
+ base::TimeTicks now = base::TimeTicks::Now();
+
+ const base::TimeTicks event_time = now;
+ std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
+ EventMetrics::Create(ui::ET_GESTURE_SCROLL_BEGIN, event_time,
+ ui::ScrollInputType::kWheel),
+ EventMetrics::Create(ui::ET_GESTURE_SCROLL_UPDATE, event_time,
+ ui::ScrollInputType::kWheel),
+ EventMetrics::Create(ui::ET_GESTURE_SCROLL_UPDATE, event_time,
+ ui::ScrollInputType::kWheel),
+ };
+ EXPECT_THAT(event_metrics_ptrs, ::testing::Each(::testing::NotNull()));
+ std::vector<EventMetrics> events_metrics = {
+ *event_metrics_ptrs[0], *event_metrics_ptrs[1], *event_metrics_ptrs[2]};
+
+ const base::TimeTicks begin_impl_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks end_activate_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks submit_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+
+ viz::FrameTimingDetails viz_breakdown;
+ viz_breakdown.received_compositor_frame_timestamp =
+ (now += base::TimeDelta::FromMicroseconds(1));
+ viz_breakdown.draw_start_timestamp =
+ (now += base::TimeDelta::FromMicroseconds(2));
+ viz_breakdown.swap_timings.swap_start =
+ (now += base::TimeDelta::FromMicroseconds(3));
+ viz_breakdown.swap_timings.swap_end =
+ (now += base::TimeDelta::FromMicroseconds(4));
+ viz_breakdown.presentation_feedback.timestamp =
+ (now += base::TimeDelta::FromMicroseconds(5));
+
+ const base::TimeTicks swap_end_time = viz_breakdown.swap_timings.swap_end;
+ const base::TimeTicks present_time =
+ viz_breakdown.presentation_feedback.timestamp;
+
+ std::vector<CompositorFrameReporter::StageData> stage_history = {
+ {
+ CompositorFrameReporter::StageType::
+ kBeginImplFrameToSendBeginMainFrame,
+ begin_impl_time,
+ end_activate_time,
+ },
+ {
+ CompositorFrameReporter::StageType::
+ kEndActivateToSubmitCompositorFrame,
+ end_activate_time,
+ submit_time,
+ },
+ {
+ CompositorFrameReporter::StageType::
+ kSubmitCompositorFrameToPresentationCompositorFrame,
+ submit_time,
+ present_time,
+ },
+ {
+ CompositorFrameReporter::StageType::kTotalLatency,
+ event_time,
+ present_time,
+ },
+ };
+
+ manager_->RecordEventLatencyUKM(events_metrics, stage_history, viz_breakdown);
+
+ const auto& entries = test_ukm_recorder_->GetEntriesByName(kEventLatency);
+ EXPECT_EQ(3u, entries.size());
+ for (size_t i = 0; i < entries.size(); i++) {
+ const auto* entry = entries[i];
+ const auto* event_metrics = event_metrics_ptrs[i].get();
+
+ EXPECT_NE(ukm::kInvalidSourceId, entry->source_id);
+ test_ukm_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestUrl));
+
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kEventType, static_cast<int64_t>(event_metrics->type()));
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kScrollInputType,
+ static_cast<int64_t>(*event_metrics->scroll_type()));
+
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kBrowserToRendererCompositor,
+ (begin_impl_time - event_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kBeginImplFrameToSendBeginMainFrame,
+ (end_activate_time - begin_impl_time).InMicroseconds());
+ EXPECT_FALSE(
+ test_ukm_recorder_->EntryHasMetric(entry, kSendBeginMainFrameToCommit));
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kCommit));
+ EXPECT_FALSE(
+ test_ukm_recorder_->EntryHasMetric(entry, kEndCommitToActivation));
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kActivation));
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kEndActivateToSubmitCompositorFrame,
+ (submit_time - end_activate_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kSubmitCompositorFrameToPresentationCompositorFrame,
+ (present_time - submit_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kTotalLatencyToSwapEnd,
+ (swap_end_time - event_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kTotalLatency, (present_time - event_time).InMicroseconds());
+ }
+}
+
} // namespace
} // namespace cc