summaryrefslogtreecommitdiff
path: root/chromium/cc
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-29 10:46:47 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-11-02 12:02:10 +0000
commit99677208ff3b216fdfec551fbe548da5520cd6fb (patch)
tree476a4865c10320249360e859d8fdd3e01833b03a /chromium/cc
parentc30a6232df03e1efbd9f3b226777b07e087a1122 (diff)
downloadqtwebengine-chromium-99677208ff3b216fdfec551fbe548da5520cd6fb.tar.gz
BASELINE: Update Chromium to 86.0.4240.124
Change-Id: Ide0ff151e94cd665ae6521a446995d34a9d1d644 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/cc')
-rw-r--r--chromium/cc/BUILD.gn27
-rw-r--r--chromium/cc/OWNERS3
-rw-r--r--chromium/cc/PRESUBMIT.py71
-rw-r--r--chromium/cc/animation/keyframe_model.cc40
-rw-r--r--chromium/cc/animation/keyframed_animation_curve.cc7
-rw-r--r--chromium/cc/animation/scroll_offset_animation_curve.cc16
-rw-r--r--chromium/cc/base/BUILD.gn20
-rw-r--r--chromium/cc/base/android/java/src/org/chromium/cc/base/CcSwitches.java.tmpl16
-rw-r--r--chromium/cc/base/switches.cc3
-rw-r--r--chromium/cc/base/switches.h1
-rw-r--r--chromium/cc/base/time_util.h22
-rw-r--r--chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc4
-rw-r--r--chromium/cc/cc.gni5
-rw-r--r--chromium/cc/debug/debug_colors.cc3
-rw-r--r--chromium/cc/input/browser_controls_offset_manager.cc70
-rw-r--r--chromium/cc/input/browser_controls_offset_manager.h4
-rw-r--r--chromium/cc/input/browser_controls_offset_manager_client.h6
-rw-r--r--chromium/cc/input/browser_controls_offset_manager_unittest.cc93
-rw-r--r--chromium/cc/input/compositor_input_interfaces.h78
-rw-r--r--chromium/cc/input/input_handler.h40
-rw-r--r--chromium/cc/input/main_thread_scrolling_reason.cc2
-rw-r--r--chromium/cc/input/main_thread_scrolling_reason.h4
-rw-r--r--chromium/cc/input/main_thread_scrolling_reason_unittest.cc1
-rw-r--r--chromium/cc/input/page_scale_animation.cc3
-rw-r--r--chromium/cc/input/scroll_utils.cc15
-rw-r--r--chromium/cc/input/scroll_utils.h9
-rw-r--r--chromium/cc/input/scrollbar_animation_controller.cc6
-rw-r--r--chromium/cc/input/scrollbar_controller.cc76
-rw-r--r--chromium/cc/input/scrollbar_controller.h15
-rw-r--r--chromium/cc/input/single_scrollbar_animation_controller_thinning.cc11
-rw-r--r--chromium/cc/input/single_scrollbar_animation_controller_thinning.h1
-rw-r--r--chromium/cc/input/snap_fling_curve.cc10
-rw-r--r--chromium/cc/input/threaded_input_handler.cc2097
-rw-r--r--chromium/cc/input/threaded_input_handler.h432
-rw-r--r--chromium/cc/ipc/cc_param_traits_macros.h11
-rw-r--r--chromium/cc/layers/heads_up_display_layer.cc4
-rw-r--r--chromium/cc/layers/heads_up_display_layer_impl.cc21
-rw-r--r--chromium/cc/layers/layer.cc10
-rw-r--r--chromium/cc/layers/layer.h5
-rw-r--r--chromium/cc/layers/layer_impl.cc6
-rw-r--r--chromium/cc/layers/layer_impl.h9
-rw-r--r--chromium/cc/layers/layer_unittest.cc23
-rw-r--r--chromium/cc/layers/mirror_layer_impl.cc2
-rw-r--r--chromium/cc/layers/mirror_layer_impl.h3
-rw-r--r--chromium/cc/layers/nine_patch_layer_impl_unittest.cc13
-rw-r--r--chromium/cc/layers/painted_scrollbar_layer.cc57
-rw-r--r--chromium/cc/layers/painted_scrollbar_layer.h4
-rw-r--r--chromium/cc/layers/picture_layer.cc45
-rw-r--r--chromium/cc/layers/picture_layer.h9
-rw-r--r--chromium/cc/layers/picture_layer_impl.cc301
-rw-r--r--chromium/cc/layers/picture_layer_impl.h29
-rw-r--r--chromium/cc/layers/picture_layer_impl_unittest.cc513
-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/render_surface_unittest.cc2
-rw-r--r--chromium/cc/layers/scrollbar_layer_impl_base.h5
-rw-r--r--chromium/cc/layers/scrollbar_layer_unittest.cc62
-rw-r--r--chromium/cc/layers/solid_color_layer_impl_unittest.cc1
-rw-r--r--chromium/cc/layers/texture_layer_unittest.cc9
-rw-r--r--chromium/cc/layers/viewport.cc69
-rw-r--r--chromium/cc/layers/viewport.h12
-rw-r--r--chromium/cc/metrics/average_lag_tracker.cc230
-rw-r--r--chromium/cc/metrics/average_lag_tracker.h146
-rw-r--r--chromium/cc/metrics/average_lag_tracker_unittest.cc539
-rw-r--r--chromium/cc/metrics/average_lag_tracking_manager.cc103
-rw-r--r--chromium/cc/metrics/average_lag_tracking_manager.h72
-rw-r--r--chromium/cc/metrics/average_lag_tracking_manager_unittest.cc285
-rw-r--r--chromium/cc/metrics/begin_main_frame_metrics.h3
-rw-r--r--chromium/cc/metrics/compositor_frame_reporter.cc84
-rw-r--r--chromium/cc/metrics/compositor_frame_reporter.h13
-rw-r--r--chromium/cc/metrics/compositor_frame_reporter_unittest.cc38
-rw-r--r--chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc43
-rw-r--r--chromium/cc/metrics/compositor_timing_history.cc90
-rw-r--r--chromium/cc/metrics/compositor_timing_history.h4
-rw-r--r--chromium/cc/metrics/dropped_frame_counter.cc1
-rw-r--r--chromium/cc/metrics/dropped_frame_counter.h2
-rw-r--r--chromium/cc/metrics/event_metrics.cc20
-rw-r--r--chromium/cc/metrics/event_metrics.h8
-rw-r--r--chromium/cc/metrics/events_metrics_manager.h4
-rw-r--r--chromium/cc/metrics/events_metrics_manager_unittest.cc16
-rw-r--r--chromium/cc/metrics/frame_sequence_metrics.cc88
-rw-r--r--chromium/cc/metrics/frame_sequence_metrics.h12
-rw-r--r--chromium/cc/metrics/frame_sequence_metrics_unittest.cc25
-rw-r--r--chromium/cc/metrics/frame_sequence_tracker.cc21
-rw-r--r--chromium/cc/metrics/frame_sequence_tracker.h5
-rw-r--r--chromium/cc/metrics/frame_sequence_tracker_collection.cc5
-rw-r--r--chromium/cc/metrics/frame_sequence_tracker_unittest.cc102
-rw-r--r--chromium/cc/metrics/jank_metrics.cc124
-rw-r--r--chromium/cc/metrics/jank_metrics.h57
-rw-r--r--chromium/cc/metrics/jank_metrics_unittest.cc274
-rw-r--r--chromium/cc/metrics/total_frame_counter.cc63
-rw-r--r--chromium/cc/metrics/total_frame_counter.h50
-rw-r--r--chromium/cc/metrics/total_frame_counter_unittest.cc93
-rw-r--r--chromium/cc/metrics/video_playback_roughness_reporter.cc46
-rw-r--r--chromium/cc/metrics/video_playback_roughness_reporter.h22
-rw-r--r--chromium/cc/metrics/video_playback_roughness_reporter_unittest.cc237
-rw-r--r--chromium/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc15
-rw-r--r--chromium/cc/mojom/BUILD.gn64
-rw-r--r--chromium/cc/mojom/browser_controls_params.mojom8
-rw-r--r--chromium/cc/paint/BUILD.gn3
-rw-r--r--chromium/cc/paint/clear_for_opaque_raster.cc79
-rw-r--r--chromium/cc/paint/clear_for_opaque_raster.h36
-rw-r--r--chromium/cc/paint/clear_for_opaque_raster_unittest.cc175
-rw-r--r--chromium/cc/paint/decoded_draw_image.h5
-rw-r--r--chromium/cc/paint/discardable_image_map.cc48
-rw-r--r--chromium/cc/paint/discardable_image_map.h10
-rw-r--r--chromium/cc/paint/discardable_image_map_unittest.cc107
-rw-r--r--chromium/cc/paint/draw_image.cc20
-rw-r--r--chromium/cc/paint/draw_image.h13
-rw-r--r--chromium/cc/paint/image_transfer_cache_entry.cc22
-rw-r--r--chromium/cc/paint/image_transfer_cache_entry.h9
-rw-r--r--chromium/cc/paint/image_transfer_cache_entry_unittest.cc18
-rw-r--r--chromium/cc/paint/oop_pixeltest.cc303
-rw-r--r--chromium/cc/paint/paint_image.cc190
-rw-r--r--chromium/cc/paint/paint_image.h57
-rw-r--r--chromium/cc/paint/paint_image_builder.h8
-rw-r--r--chromium/cc/paint/paint_image_generator.h12
-rw-r--r--chromium/cc/paint/paint_image_unittest.cc109
-rw-r--r--chromium/cc/paint/paint_op_buffer.cc335
-rw-r--r--chromium/cc/paint/paint_op_buffer.h52
-rw-r--r--chromium/cc/paint/paint_op_buffer_fuzzer.cc5
-rw-r--r--chromium/cc/paint/paint_op_buffer_serializer.cc78
-rw-r--r--chromium/cc/paint/paint_op_buffer_unittest.cc13
-rw-r--r--chromium/cc/paint/paint_op_reader.cc53
-rw-r--r--chromium/cc/paint/paint_op_reader.h17
-rw-r--r--chromium/cc/paint/paint_op_writer.cc73
-rw-r--r--chromium/cc/paint/paint_op_writer.h16
-rw-r--r--chromium/cc/paint/paint_shader.cc6
-rw-r--r--chromium/cc/paint/paint_shader.h7
-rw-r--r--chromium/cc/paint/raw_memory_transfer_cache_entry.cc3
-rw-r--r--chromium/cc/paint/raw_memory_transfer_cache_entry.h6
-rw-r--r--chromium/cc/paint/scoped_raster_flags.cc4
-rw-r--r--chromium/cc/paint/shader_transfer_cache_entry.cc4
-rw-r--r--chromium/cc/paint/shader_transfer_cache_entry.h3
-rw-r--r--chromium/cc/paint/skia_paint_image_generator.cc13
-rw-r--r--chromium/cc/paint/skottie_transfer_cache_entry.cc4
-rw-r--r--chromium/cc/paint/skottie_transfer_cache_entry.h3
-rw-r--r--chromium/cc/paint/solid_color_analyzer.cc8
-rw-r--r--chromium/cc/paint/solid_color_analyzer_unittest.cc18
-rw-r--r--chromium/cc/paint/texture_backing.h15
-rw-r--r--chromium/cc/paint/transfer_cache_entry.cc5
-rw-r--r--chromium/cc/paint/transfer_cache_entry.h4
-rw-r--r--chromium/cc/raster/bitmap_raster_buffer_provider.cc2
-rw-r--r--chromium/cc/raster/gpu_raster_buffer_provider.cc21
-rw-r--r--chromium/cc/raster/gpu_raster_buffer_provider.h3
-rw-r--r--chromium/cc/raster/lcd_text_disallowed_reason.cc4
-rw-r--r--chromium/cc/raster/lcd_text_disallowed_reason.h4
-rw-r--r--chromium/cc/raster/one_copy_raster_buffer_provider.cc30
-rw-r--r--chromium/cc/raster/one_copy_raster_buffer_provider.h1
-rw-r--r--chromium/cc/raster/playback_image_provider.cc9
-rw-r--r--chromium/cc/raster/playback_image_provider.h6
-rw-r--r--chromium/cc/raster/raster_buffer.h6
-rw-r--r--chromium/cc/raster/raster_buffer_provider.cc2
-rw-r--r--chromium/cc/raster/raster_buffer_provider.h2
-rw-r--r--chromium/cc/raster/raster_buffer_provider_perftest.cc11
-rw-r--r--chromium/cc/raster/raster_buffer_provider_unittest.cc5
-rw-r--r--chromium/cc/raster/raster_source.cc92
-rw-r--r--chromium/cc/raster/raster_source.h17
-rw-r--r--chromium/cc/raster/raster_source_unittest.cc261
-rw-r--r--chromium/cc/raster/scoped_gpu_raster.cc4
-rw-r--r--chromium/cc/raster/task_category.h13
-rw-r--r--chromium/cc/raster/tile_task.cc14
-rw-r--r--chromium/cc/raster/tile_task.h22
-rw-r--r--chromium/cc/raster/zero_copy_raster_buffer_provider.cc9
-rw-r--r--chromium/cc/scheduler/commit_earlyout_reason.h3
-rw-r--r--chromium/cc/scheduler/scheduler.cc12
-rw-r--r--chromium/cc/scheduler/scheduler_state_machine.cc11
-rw-r--r--chromium/cc/scheduler/scheduler_state_machine_unittest.cc51
-rw-r--r--chromium/cc/scheduler/scheduler_unittest.cc34
-rw-r--r--chromium/cc/tiles/gpu_image_decode_cache.cc270
-rw-r--r--chromium/cc/tiles/gpu_image_decode_cache.h12
-rw-r--r--chromium/cc/tiles/gpu_image_decode_cache_perftest.cc1
-rw-r--r--chromium/cc/tiles/gpu_image_decode_cache_unittest.cc610
-rw-r--r--chromium/cc/tiles/image_controller_unittest.cc8
-rw-r--r--chromium/cc/tiles/picture_layer_tiling.cc31
-rw-r--r--chromium/cc/tiles/picture_layer_tiling.h7
-rw-r--r--chromium/cc/tiles/picture_layer_tiling_set.cc20
-rw-r--r--chromium/cc/tiles/picture_layer_tiling_set.h4
-rw-r--r--chromium/cc/tiles/picture_layer_tiling_set_unittest.cc53
-rw-r--r--chromium/cc/tiles/picture_layer_tiling_unittest.cc3
-rw-r--r--chromium/cc/tiles/software_image_decode_cache.cc59
-rw-r--r--chromium/cc/tiles/software_image_decode_cache.h4
-rw-r--r--chromium/cc/tiles/software_image_decode_cache_unittest.cc57
-rw-r--r--chromium/cc/tiles/tile.cc14
-rw-r--r--chromium/cc/tiles/tile.h46
-rw-r--r--chromium/cc/tiles/tile_manager.cc92
-rw-r--r--chromium/cc/tiles/tile_manager.h17
-rw-r--r--chromium/cc/tiles/tile_manager_unittest.cc143
-rw-r--r--chromium/cc/trees/browser_controls_params.cc4
-rw-r--r--chromium/cc/trees/browser_controls_params.h5
-rw-r--r--chromium/cc/trees/compositor_commit_data.cc (renamed from chromium/cc/trees/scroll_and_scale_set.cc)19
-rw-r--r--chromium/cc/trees/compositor_commit_data.h (renamed from chromium/cc/trees/scroll_and_scale_set.h)19
-rw-r--r--chromium/cc/trees/damage_tracker.cc8
-rw-r--r--chromium/cc/trees/damage_tracker_unittest.cc147
-rw-r--r--chromium/cc/trees/draw_properties_unittest.cc195
-rw-r--r--chromium/cc/trees/draw_property_utils.cc24
-rw-r--r--chromium/cc/trees/effect_node.cc4
-rw-r--r--chromium/cc/trees/effect_node.h2
-rw-r--r--chromium/cc/trees/latency_info_swap_promise_monitor.cc10
-rw-r--r--chromium/cc/trees/latency_info_swap_promise_monitor.h3
-rw-r--r--chromium/cc/trees/layer_tree_host.cc197
-rw-r--r--chromium/cc/trees/layer_tree_host.h34
-rw-r--r--chromium/cc/trees/layer_tree_host_client.h9
-rw-r--r--chromium/cc/trees/layer_tree_host_impl.cc2179
-rw-r--r--chromium/cc/trees/layer_tree_host_impl.h366
-rw-r--r--chromium/cc/trees/layer_tree_host_impl_unittest.cc1807
-rw-r--r--chromium/cc/trees/layer_tree_host_perftest.cc5
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_blending.cc14
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_filters.cc56
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_masks.cc70
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_mirror.cc16
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_readback.cc58
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc18
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc22
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc87
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest.cc584
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc140
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest_record_gpu_histogram.cc2
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest_scroll.cc48
-rw-r--r--chromium/cc/trees/layer_tree_impl.cc209
-rw-r--r--chromium/cc/trees/layer_tree_impl.h41
-rw-r--r--chromium/cc/trees/layer_tree_impl_unittest.cc94
-rw-r--r--chromium/cc/trees/layer_tree_settings.h6
-rw-r--r--chromium/cc/trees/occlusion_tracker_unittest.cc4
-rw-r--r--chromium/cc/trees/property_tree.cc41
-rw-r--r--chromium/cc/trees/property_tree.h8
-rw-r--r--chromium/cc/trees/property_tree_builder_unittest.cc5
-rw-r--r--chromium/cc/trees/property_tree_unittest.cc41
-rw-r--r--chromium/cc/trees/proxy.h3
-rw-r--r--chromium/cc/trees/proxy_common.cc2
-rw-r--r--chromium/cc/trees/proxy_common.h7
-rw-r--r--chromium/cc/trees/proxy_impl.cc15
-rw-r--r--chromium/cc/trees/proxy_impl.h1
-rw-r--r--chromium/cc/trees/proxy_main.cc32
-rw-r--r--chromium/cc/trees/proxy_main.h1
-rw-r--r--chromium/cc/trees/single_thread_proxy.cc25
-rw-r--r--chromium/cc/trees/single_thread_proxy.h3
-rw-r--r--chromium/cc/trees/swap_promise_manager_unittest.cc5
-rw-r--r--chromium/cc/trees/swap_promise_monitor.cc19
-rw-r--r--chromium/cc/trees/swap_promise_monitor.h9
-rw-r--r--chromium/cc/trees/transform_node.cc8
-rw-r--r--chromium/cc/trees/transform_node.h10
-rw-r--r--chromium/cc/trees/tree_synchronizer_unittest.cc36
-rw-r--r--chromium/cc/trees/ukm_manager.cc8
-rw-r--r--chromium/cc/trees/ukm_manager_unittest.cc13
245 files changed, 12657 insertions, 5667 deletions
diff --git a/chromium/cc/BUILD.gn b/chromium/cc/BUILD.gn
index 7e5e837b8e7..6834a251f66 100644
--- a/chromium/cc/BUILD.gn
+++ b/chromium/cc/BUILD.gn
@@ -1,5 +1,5 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
+# 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.
import("//build/config/sanitizers/sanitizers.gni")
import("//gpu/vulkan/features.gni")
@@ -32,6 +32,7 @@ cc_component("cc") {
"input/browser_controls_offset_manager.cc",
"input/browser_controls_offset_manager.h",
"input/browser_controls_offset_manager_client.h",
+ "input/compositor_input_interfaces.h",
"input/input_handler.h",
"input/layer_selection_bound.cc",
"input/layer_selection_bound.h",
@@ -63,6 +64,8 @@ cc_component("cc") {
"input/snap_fling_curve.h",
"input/snap_selection_strategy.cc",
"input/snap_selection_strategy.h",
+ "input/threaded_input_handler.cc",
+ "input/threaded_input_handler.h",
"input/touch_action.h",
"layers/append_quads_data.cc",
"layers/append_quads_data.h",
@@ -149,6 +152,10 @@ cc_component("cc") {
"layers/video_layer_impl.h",
"layers/viewport.cc",
"layers/viewport.h",
+ "metrics/average_lag_tracker.cc",
+ "metrics/average_lag_tracker.h",
+ "metrics/average_lag_tracking_manager.cc",
+ "metrics/average_lag_tracking_manager.h",
"metrics/begin_main_frame_metrics.cc",
"metrics/begin_main_frame_metrics.h",
"metrics/compositor_frame_reporter.cc",
@@ -169,12 +176,16 @@ cc_component("cc") {
"metrics/frame_sequence_tracker.h",
"metrics/frame_sequence_tracker_collection.cc",
"metrics/frame_sequence_tracker_collection.h",
+ "metrics/jank_metrics.cc",
+ "metrics/jank_metrics.h",
"metrics/latency_ukm_reporter.cc",
"metrics/latency_ukm_reporter.h",
"metrics/lcd_text_metrics_reporter.cc",
"metrics/lcd_text_metrics_reporter.h",
"metrics/throughput_ukm_reporter.cc",
"metrics/throughput_ukm_reporter.h",
+ "metrics/total_frame_counter.cc",
+ "metrics/total_frame_counter.h",
"metrics/video_playback_roughness_reporter.cc",
"metrics/video_playback_roughness_reporter.h",
"raster/bitmap_raster_buffer_provider.cc",
@@ -303,6 +314,8 @@ cc_component("cc") {
"trees/clip_expander.h",
"trees/clip_node.cc",
"trees/clip_node.h",
+ "trees/compositor_commit_data.cc",
+ "trees/compositor_commit_data.h",
"trees/compositor_mode.h",
"trees/damage_tracker.cc",
"trees/damage_tracker.h",
@@ -365,8 +378,6 @@ cc_component("cc") {
"trees/render_frame_metadata.h",
"trees/render_frame_metadata_observer.h",
"trees/scoped_abort_remaining_swap_promises.h",
- "trees/scroll_and_scale_set.cc",
- "trees/scroll_and_scale_set.h",
"trees/scroll_node.cc",
"trees/scroll_node.h",
"trees/single_thread_proxy.cc",
@@ -589,6 +600,7 @@ cc_test_static_library("test_support") {
"//services/viz/privileged/mojom",
"//skia",
"//testing/gtest",
+ "//ui/base:features",
"//ui/gfx",
"//ui/gfx:test_support",
"//ui/gfx/geometry",
@@ -660,14 +672,19 @@ cc_test("cc_unittests") {
"layers/video_frame_provider_client_impl_unittest.cc",
"layers/video_layer_impl_unittest.cc",
"layers/viewport_unittest.cc",
+ "metrics/average_lag_tracker_unittest.cc",
+ "metrics/average_lag_tracking_manager_unittest.cc",
"metrics/compositor_frame_reporter_unittest.cc",
"metrics/compositor_frame_reporting_controller_unittest.cc",
"metrics/compositor_timing_history_unittest.cc",
"metrics/events_metrics_manager_unittest.cc",
"metrics/frame_sequence_metrics_unittest.cc",
"metrics/frame_sequence_tracker_unittest.cc",
+ "metrics/jank_metrics_unittest.cc",
+ "metrics/total_frame_counter_unittest.cc",
"metrics/video_playback_roughness_reporter_unittest.cc",
"mojo_embedder/async_layer_tree_frame_sink_unittest.cc",
+ "paint/clear_for_opaque_raster_unittest.cc",
"paint/discardable_image_map_unittest.cc",
"paint/display_item_list_unittest.cc",
"paint/filter_operations_unittest.cc",
@@ -825,7 +842,7 @@ cc_test("cc_unittests") {
}
}
- if (skia_use_dawn) {
+ if (skia_use_dawn && enable_skia_dawn_gtests) {
defines += [ "ENABLE_CC_DAWN_TESTS" ]
}
}
diff --git a/chromium/cc/OWNERS b/chromium/cc/OWNERS
index 682d6e8030e..c9cc8d29a52 100644
--- a/chromium/cc/OWNERS
+++ b/chromium/cc/OWNERS
@@ -18,7 +18,6 @@ sunnyps@chromium.org
# tiles, tile management, and raster work
vmpstr@chromium.org
-ericrk@chromium.org
# texture uploading
sunnyps@chromium.org
@@ -37,13 +36,11 @@ flackr@chromium.org
smcgruer@chromium.org
# images
-ericrk@chromium.org
khushalsagar@chromium.org
vmpstr@chromium.org
# surfaces
kylechar@chromium.org
-samans@chromium.org
jonross@chromium.org
# input, scrolling
diff --git a/chromium/cc/PRESUBMIT.py b/chromium/cc/PRESUBMIT.py
index 0b6761a28f8..c375a37d367 100644
--- a/chromium/cc/PRESUBMIT.py
+++ b/chromium/cc/PRESUBMIT.py
@@ -14,15 +14,18 @@ import string
CC_SOURCE_FILES=(r'^cc[\\/].*\.(cc|h)$',)
def CheckChangeLintsClean(input_api, output_api):
- source_filter = lambda x: input_api.FilterSourceFile(
- x, white_list=CC_SOURCE_FILES, black_list=None)
+ allowlist = CC_SOURCE_FILES
+ denylist = None
+ source_filter = lambda x: input_api.FilterSourceFile(x, allowlist, denylist)
return input_api.canned_checks.CheckChangeLintsClean(
input_api, output_api, source_filter, lint_filters=[], verbose_level=1)
-def CheckAsserts(input_api, output_api, white_list=CC_SOURCE_FILES, black_list=None):
- black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
- source_file_filter = lambda x: input_api.FilterSourceFile(x, white_list, black_list)
+def CheckAsserts(input_api, output_api, allowlist=CC_SOURCE_FILES,
+ denylist=None):
+ denylist = tuple(denylist or input_api.DEFAULT_FILES_TO_SKIP)
+ source_file_filter = lambda x: input_api.FilterSourceFile(x, allowlist,
+ denylist)
assert_files = []
@@ -39,11 +42,11 @@ def CheckAsserts(input_api, output_api, white_list=CC_SOURCE_FILES, black_list=N
return []
def CheckStdAbs(input_api, output_api,
- white_list=CC_SOURCE_FILES, black_list=None):
- black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
+ allowlist=CC_SOURCE_FILES, denylist=None):
+ denylist = tuple(denylist or input_api.DEFAULT_FILES_TO_SKIP)
source_file_filter = lambda x: input_api.FilterSourceFile(x,
- white_list,
- black_list)
+ allowlist,
+ denylist)
using_std_abs_files = []
found_fabs_files = []
@@ -86,12 +89,12 @@ def CheckStdAbs(input_api, output_api,
def CheckPassByValue(input_api,
output_api,
- white_list=CC_SOURCE_FILES,
- black_list=None):
- black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
+ allowlist=CC_SOURCE_FILES,
+ denylist=None):
+ denylist = tuple(denylist or input_api.DEFAULT_FILES_TO_SKIP)
source_file_filter = lambda x: input_api.FilterSourceFile(x,
- white_list,
- black_list)
+ allowlist,
+ denylist)
local_errors = []
@@ -128,13 +131,13 @@ def CheckTodos(input_api, output_api):
items=errors)]
return []
-def CheckDoubleAngles(input_api, output_api, white_list=CC_SOURCE_FILES,
- black_list=None):
+def CheckDoubleAngles(input_api, output_api, allowlist=CC_SOURCE_FILES,
+ denylist=None):
errors = []
source_file_filter = lambda x: input_api.FilterSourceFile(x,
- white_list,
- black_list)
+ allowlist,
+ denylist)
for f in input_api.AffectedSourceFiles(source_file_filter):
contents = input_api.ReadFile(f, 'rb')
if ('> >') in contents:
@@ -161,7 +164,7 @@ def FindUselessIfdefs(input_api, output_api):
items=errors)]
return []
-def FindNamespaceInBlock(pos, namespace, contents, whitelist=[]):
+def FindNamespaceInBlock(pos, namespace, contents, allowlist=[]):
open_brace = -1
close_brace = -1
quote = -1
@@ -192,12 +195,12 @@ def FindNamespaceInBlock(pos, namespace, contents, whitelist=[]):
elif next == quote:
quote_count = 0 if quote_count else 1
elif next == name and not quote_count:
- in_whitelist = False
- for w in whitelist:
+ in_allowlist = False
+ for w in allowlist:
if re.match(w, contents[next:]):
- in_whitelist = True
+ in_allowlist = True
break
- if not in_whitelist:
+ if not in_allowlist:
return True
pos = next + 1
return False
@@ -212,8 +215,8 @@ def CheckNamespace(input_api, output_api):
contents = input_api.ReadFile(f, 'rb')
match = re.search(r'namespace\s*cc\s*{', contents)
if match:
- whitelist = []
- if FindNamespaceInBlock(match.end(), 'cc', contents, whitelist=whitelist):
+ allowlist = []
+ if FindNamespaceInBlock(match.end(), 'cc', contents, allowlist=allowlist):
errors.append(f.LocalPath())
if errors:
@@ -224,13 +227,13 @@ def CheckNamespace(input_api, output_api):
def CheckForUseOfWrongClock(input_api,
output_api,
- white_list=CC_SOURCE_FILES,
- black_list=None):
+ allowlist=CC_SOURCE_FILES,
+ denylist=None):
"""Make sure new lines of code don't use a clock susceptible to skew."""
- black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
+ denylist = tuple(denylist or input_api.DEFAULT_FILES_TO_SKIP)
source_file_filter = lambda x: input_api.FilterSourceFile(x,
- white_list,
- black_list)
+ allowlist,
+ denylist)
# Regular expression that should detect any explicit references to the
# base::Time type (or base::Clock/DefaultClock), whether in using decls,
# typedefs, or to call static methods.
@@ -275,9 +278,11 @@ def CheckForUseOfWrongClock(input_api,
else:
return []
-def CheckForDisallowMacros(input_api, output_api, white_list=CC_SOURCE_FILES, black_list=None):
- black_list = tuple(black_list or input_api.DEFAULT_BLACK_LIST)
- source_file_filter = lambda x: input_api.FilterSourceFile(x, white_list, black_list)
+def CheckForDisallowMacros(input_api, output_api, allowlist=CC_SOURCE_FILES,
+ denylist=None):
+ denylist = tuple(denylist or input_api.DEFAULT_FILES_TO_SKIP)
+ source_file_filter = lambda x: input_api.FilterSourceFile(x, allowlist,
+ denylist)
disallow_macro_files = []
diff --git a/chromium/cc/animation/keyframe_model.cc b/chromium/cc/animation/keyframe_model.cc
index bb779226080..b7c3ee7c1bc 100644
--- a/chromium/cc/animation/keyframe_model.cc
+++ b/chromium/cc/animation/keyframe_model.cc
@@ -4,9 +4,14 @@
#include "cc/animation/keyframe_model.h"
+#include <algorithm>
#include <cmath>
+#include <limits>
+#include <string>
+#include <utility>
#include "base/memory/ptr_util.h"
+#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
@@ -193,21 +198,22 @@ KeyframeModel::Phase KeyframeModel::CalculatePhase(
(local_time == before_active_boundary_time && playback_rate_ < 0)) {
return KeyframeModel::Phase::BEFORE;
}
- // Scaling the duration is against spec but needed to comply with the cc
- // implementation. By spec (in blink) the playback rate is an Animation level
- // concept but in cc it's per KeyframeModel. We grab the active time
- // calculated here and later scale it with the playback rate in order to get a
- // proper progress. Therefore we need to un-scale it here. This can be fixed
- // once we scale the local time by playback rate. See
- // https://crbug.com/912407.
- base::TimeDelta active_duration =
- curve_->Duration() * iterations_ / std::abs(playback_rate_);
// TODO(crbug.com/909794): By spec end time = max(start delay + duration +
// end delay, 0). The logic should be updated once "end delay" is supported.
- base::TimeDelta active_after_boundary_time =
- std::isfinite(iterations_)
- ? std::max(opposite_time_offset + active_duration, base::TimeDelta())
- : base::TimeDelta::Max();
+ base::TimeDelta active_after_boundary_time = base::TimeDelta::Max();
+ if (std::isfinite(iterations_)) {
+ // Scaling the duration is against spec but needed to comply with the cc
+ // implementation. By spec (in blink) the playback rate is an Animation
+ // level concept but in cc it's per KeyframeModel. We grab the active time
+ // calculated here and later scale it with the playback rate in order to get
+ // a proper progress. Therefore we need to un-scale it here. This can be
+ // fixed once we scale the local time by playback rate. See
+ // https://crbug.com/912407.
+ base::TimeDelta active_duration =
+ curve_->Duration() * iterations_ / std::abs(playback_rate_);
+ active_after_boundary_time =
+ std::max(opposite_time_offset + active_duration, base::TimeDelta());
+ }
if (local_time > active_after_boundary_time ||
(local_time == active_after_boundary_time && playback_rate_ > 0)) {
return KeyframeModel::Phase::AFTER;
@@ -296,7 +302,11 @@ base::TimeDelta KeyframeModel::TrimTimeToCurrentIteration(
// Calculate the iteration time
base::TimeDelta iteration_time;
- if (scaled_active_time - start_offset == repeated_duration &&
+ bool has_defined_time_delta =
+ (start_offset != scaled_active_time) ||
+ !(start_offset.is_max() || start_offset.is_min());
+ if (has_defined_time_delta &&
+ scaled_active_time - start_offset == repeated_duration &&
fmod(iterations_ + iteration_start_, 1) == 0)
iteration_time = curve_->Duration();
else
@@ -309,7 +319,7 @@ base::TimeDelta KeyframeModel::TrimTimeToCurrentIteration(
else if (iteration_time == curve_->Duration())
iteration = ceil(iteration_start_ + iterations_ - 1);
else
- iteration = static_cast<int>(scaled_active_time / curve_->Duration());
+ iteration = base::ClampFloor(scaled_active_time / curve_->Duration());
// Check if we are running the keyframe model in reverse direction for the
// current iteration
diff --git a/chromium/cc/animation/keyframed_animation_curve.cc b/chromium/cc/animation/keyframed_animation_curve.cc
index 67147cb86a4..1a47fccc160 100644
--- a/chromium/cc/animation/keyframed_animation_curve.cc
+++ b/chromium/cc/animation/keyframed_animation_curve.cc
@@ -7,9 +7,10 @@
#include <stddef.h>
#include <algorithm>
+#include <memory>
+#include <utility>
#include "base/memory/ptr_util.h"
-#include "cc/base/time_util.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/geometry/box_f.h"
@@ -45,7 +46,7 @@ base::TimeDelta TransformedAnimationTime(
base::TimeDelta duration =
(keyframes.back()->Time() - keyframes.front()->Time()) *
scaled_duration;
- double progress = TimeUtil::Divide(time - start_time, duration);
+ const double progress = (time - start_time) / duration;
time = (duration * timing_function->GetValue(progress)) + start_time;
}
@@ -77,7 +78,7 @@ double TransformedKeyframeProgress(
base::TimeDelta time1 = keyframes[i]->Time() * scaled_duration;
base::TimeDelta time2 = keyframes[i + 1]->Time() * scaled_duration;
- double progress = TimeUtil::Divide(time - time1, time2 - time1);
+ double progress = (time - time1) / (time2 - time1);
if (keyframes[i]->timing_function()) {
progress = keyframes[i]->timing_function()->GetValue(progress);
diff --git a/chromium/cc/animation/scroll_offset_animation_curve.cc b/chromium/cc/animation/scroll_offset_animation_curve.cc
index 2914ce4c193..8a67e0f0a39 100644
--- a/chromium/cc/animation/scroll_offset_animation_curve.cc
+++ b/chromium/cc/animation/scroll_offset_animation_curve.cc
@@ -6,12 +6,12 @@
#include <algorithm>
#include <cmath>
+#include <utility>
#include "base/check_op.h"
#include "base/memory/ptr_util.h"
#include "base/numerics/ranges.h"
#include "cc/animation/timing_function.h"
-#include "cc/base/time_util.h"
#include "ui/gfx/animation/tween.h"
const double kConstantDuration = 9.0;
@@ -294,19 +294,15 @@ void ScrollOffsetAnimationCurve::ApplyAdjustment(
gfx::ScrollOffset ScrollOffsetAnimationCurve::GetValue(
base::TimeDelta t) const {
- base::TimeDelta duration = total_animation_duration_ - last_retarget_;
+ const base::TimeDelta duration = total_animation_duration_ - last_retarget_;
t -= last_retarget_;
- if (duration.is_zero())
+ if (duration.is_zero() || (t >= duration))
return target_value_;
-
if (t <= base::TimeDelta())
return initial_value_;
- if (t >= duration)
- return target_value_;
-
- double progress = timing_function_->GetValue(TimeUtil::Divide(t, duration));
+ const double progress = timing_function_->GetValue(t / duration);
return gfx::ScrollOffset(
gfx::Tween::FloatValueBetween(progress, initial_value_.x(),
target_value_.x()),
@@ -347,8 +343,8 @@ void ScrollOffsetAnimationCurve::SetAnimationDurationForTesting(
double ScrollOffsetAnimationCurve::CalculateVelocity(base::TimeDelta t) {
base::TimeDelta duration = total_animation_duration_ - last_retarget_;
- double slope = timing_function_->Velocity(
- ((t - last_retarget_).InSecondsF()) / duration.InSecondsF());
+ const double slope =
+ timing_function_->Velocity((t - last_retarget_) / duration);
gfx::Vector2dF delta = target_value_.DeltaFrom(initial_value_);
diff --git a/chromium/cc/base/BUILD.gn b/chromium/cc/base/BUILD.gn
index 6e744cc454e..16d75dac5bc 100644
--- a/chromium/cc/base/BUILD.gn
+++ b/chromium/cc/base/BUILD.gn
@@ -4,6 +4,10 @@
import("//cc/cc.gni")
+if (is_android) {
+ import("//build/config/android/rules.gni")
+}
+
cc_component("base") {
output_name = "cc_base"
sources = [
@@ -43,7 +47,6 @@ cc_component("base") {
"synced_property.h",
"tiling_data.cc",
"tiling_data.h",
- "time_util.h",
"unique_notifier.cc",
"unique_notifier.h",
]
@@ -59,3 +62,18 @@ cc_component("base") {
defines = [ "CC_BASE_IMPLEMENTATION=1" ]
}
+
+if (is_android) {
+ java_cpp_strings("java_switches_srcjar") {
+ # External code should depend on ":cc_base_java" instead.
+ visibility = [ ":*" ]
+ sources = [ "switches.cc" ]
+ template = "android/java/src/org/chromium/cc/base/CcSwitches.java.tmpl"
+ }
+
+ android_library("cc_base_java") {
+ # Right now, this only includes the Java switches. But if we need more Java
+ # files, they should be added here as necessary.
+ srcjar_deps = [ ":java_switches_srcjar" ]
+ }
+}
diff --git a/chromium/cc/base/android/java/src/org/chromium/cc/base/CcSwitches.java.tmpl b/chromium/cc/base/android/java/src/org/chromium/cc/base/CcSwitches.java.tmpl
new file mode 100644
index 00000000000..90a6250c72a
--- /dev/null
+++ b/chromium/cc/base/android/java/src/org/chromium/cc/base/CcSwitches.java.tmpl
@@ -0,0 +1,16 @@
+// 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.
+
+package org.chromium.cc.base;
+
+/**
+ * Contains all of the command line switches that are specific to the cc/ layer.
+ */
+public final class CcSwitches {{
+
+{NATIVE_STRINGS}
+
+ // Prevent instantiation.
+ private CcSwitches() {{}}
+}}
diff --git a/chromium/cc/base/switches.cc b/chromium/cc/base/switches.cc
index e7e8704263d..83422f4dd5d 100644
--- a/chromium/cc/base/switches.cc
+++ b/chromium/cc/base/switches.cc
@@ -103,9 +103,6 @@ const char kCCLayerTreeTestNoTimeout[] = "cc-layer-tree-test-no-timeout";
// Increases timeout for memory checkers.
const char kCCLayerTreeTestLongTimeout[] = "cc-layer-tree-test-long-timeout";
-// Makes pixel tests write their output instead of read it.
-const char kCCRebaselinePixeltests[] = "cc-rebaseline-pixeltests";
-
// Controls the duration of the scroll animation curve.
const char kCCScrollAnimationDurationForTesting[] =
"cc-scroll-animation-duration-in-seconds";
diff --git a/chromium/cc/base/switches.h b/chromium/cc/base/switches.h
index 12615d5dcdc..6a7d2cd8c38 100644
--- a/chromium/cc/base/switches.h
+++ b/chromium/cc/base/switches.h
@@ -62,7 +62,6 @@ CC_BASE_EXPORT extern const char kUIEnableLayerLists[];
// Test related.
CC_BASE_EXPORT extern const char kCCLayerTreeTestNoTimeout[];
CC_BASE_EXPORT extern const char kCCLayerTreeTestLongTimeout[];
-CC_BASE_EXPORT extern const char kCCRebaselinePixeltests[];
CC_BASE_EXPORT extern const char kCCScrollAnimationDurationForTesting[];
} // namespace switches
diff --git a/chromium/cc/base/time_util.h b/chromium/cc/base/time_util.h
deleted file mode 100644
index 2ff09383d98..00000000000
--- a/chromium/cc/base/time_util.h
+++ /dev/null
@@ -1,22 +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.
-
-#ifndef CC_BASE_TIME_UTIL_H_
-#define CC_BASE_TIME_UTIL_H_
-
-#include "base/time/time.h"
-
-namespace cc {
-
-class TimeUtil {
- public:
- static double Divide(base::TimeDelta dividend, base::TimeDelta divisor) {
- return static_cast<double>(dividend.InMicroseconds()) /
- static_cast<double>(divisor.InMicroseconds());
- }
-};
-
-} // namespace cc
-
-#endif // CC_BASE_TIME_UTIL_H_
diff --git a/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc b/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
index 84a5e330c6c..bb9ffacc998 100644
--- a/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
+++ b/chromium/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
@@ -120,6 +120,10 @@ class FixedInvalidationPictureLayerTilingClient
return base_client_->GetPaintWorkletRecords();
}
+ bool IsDirectlyCompositedImage() const override {
+ return base_client_->IsDirectlyCompositedImage();
+ }
+
private:
PictureLayerTilingClient* base_client_;
Region invalidation_;
diff --git a/chromium/cc/cc.gni b/chromium/cc/cc.gni
index 36b576f2aa1..4f1ba6ad82e 100644
--- a/chromium/cc/cc.gni
+++ b/chromium/cc/cc.gni
@@ -2,7 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//build/config/jumbo.gni")
import("//testing/test.gni")
cc_remove_configs = []
@@ -18,7 +17,7 @@ if (!is_debug) {
}
template("cc_component") {
- jumbo_component(target_name) {
+ component(target_name) {
forward_variables_from(invoker, "*", [ "configs" ])
if (defined(invoker.configs)) {
configs += invoker.configs
@@ -29,7 +28,7 @@ template("cc_component") {
}
template("cc_test_static_library") {
- jumbo_static_library(target_name) {
+ static_library(target_name) {
forward_variables_from(invoker, "*", [ "configs" ])
if (defined(invoker.configs)) {
configs += invoker.configs
diff --git a/chromium/cc/debug/debug_colors.cc b/chromium/cc/debug/debug_colors.cc
index 1401b47b0d9..ef1e92b5bb5 100644
--- a/chromium/cc/debug/debug_colors.cc
+++ b/chromium/cc/debug/debug_colors.cc
@@ -338,7 +338,10 @@ SkColor DebugColors::NonLCDTextHighlightColor(LCDTextDisallowedReason reason) {
case LCDTextDisallowedReason::kNonIntegralYOffset:
return SkColorSetARGB(96, 255, 0, 128);
case LCDTextDisallowedReason::kWillChangeTransform:
+ case LCDTextDisallowedReason::kTransformAnimation:
return SkColorSetARGB(96, 128, 0, 255);
+ case LCDTextDisallowedReason::kPixelOrColorEffect:
+ return SkColorSetARGB(96, 0, 128, 0);
}
NOTREACHED();
return SK_ColorTRANSPARENT;
diff --git a/chromium/cc/input/browser_controls_offset_manager.cc b/chromium/cc/input/browser_controls_offset_manager.cc
index 5ebc925c1e6..052f621a775 100644
--- a/chromium/cc/input/browser_controls_offset_manager.cc
+++ b/chromium/cc/input/browser_controls_offset_manager.cc
@@ -7,6 +7,7 @@
#include <stdint.h>
#include <algorithm>
+#include <utility>
#include "base/check_op.h"
#include "base/memory/ptr_util.h"
@@ -178,6 +179,20 @@ void BrowserControlsOffsetManager::UpdateBrowserControlsState(
return;
}
+ // Don't do anything if the currently running animations end in our desired
+ // state.
+ float animated_top_shown_ratio = top_controls_animation_.IsInitialized()
+ ? top_controls_animation_.FinalValue()
+ : TopControlsShownRatio();
+ float animated_bottom_shown_ratio =
+ bottom_controls_animation_.IsInitialized()
+ ? bottom_controls_animation_.FinalValue()
+ : BottomControlsShownRatio();
+ if (animated_top_shown_ratio == final_top_shown_ratio &&
+ animated_bottom_shown_ratio == final_bottom_shown_ratio) {
+ return;
+ }
+
ResetAnimations();
if (animate)
@@ -256,11 +271,14 @@ void BrowserControlsOffsetManager::OnBrowserControlsParamsChanged(
// - If the total height changed, we will run an animation from
// old-total-height / new-total-height to 1.
// - If the total height didn't change, we don't need to do anything.
- // 3- The controls shown ratio is between the minimum and the maximum. This
- // would be the case if there is a show/hide animation running or there is
- // a scroll event and the controls are half-shown. In this case, we won't
- // run an animation--but update the shown ratio so the visible height
- // remains the same (See updated_{top,bottom}_ratio above).
+ // 3- The controls shown ratio is between the minimum and the maximum.
+ // - If an animation is running to the old min-height, start a new
+ // animation to min-height / total-height.
+ // - Otherwise don't start an animation. We're either animating the
+ // controls to their expanded state, in which case we can let that
+ // animation continue, or we're scrolling and no animation is needed.
+ // Update the shown ratio so the visible height remains the same (see
+ // new_{top,bottom}_ratio above).
//
// When this method is called as a result of a height change,
// TopControlsHeight(), TopControlsMinHeight(), BottomControlsHeight(), and
@@ -270,6 +288,19 @@ void BrowserControlsOffsetManager::OnBrowserControlsParamsChanged(
bool top_controls_need_animation = animate_changes;
bool bottom_controls_need_animation = animate_changes;
+ // To handle the case where the min-height changes while we're animating to
+ // the previous min-height, base our "are we at the minimum shown ratio"
+ // check on the post-animation ratio if an animation is running, rather than
+ // its current value.
+ float effective_top_shown_ratio = TopControlsShownRatio();
+ if (top_controls_animation_.IsInitialized()) {
+ effective_top_shown_ratio = top_controls_animation_.FinalValue();
+ }
+ float effective_bottom_shown_ratio = BottomControlsShownRatio();
+ if (bottom_controls_animation_.IsInitialized()) {
+ effective_bottom_shown_ratio = bottom_controls_animation_.FinalValue();
+ }
+
float top_target_ratio;
// We can't animate if we don't have top controls.
if (!TopControlsHeight()) {
@@ -283,7 +314,7 @@ void BrowserControlsOffsetManager::OnBrowserControlsParamsChanged(
// If the top controls min-height changed when they were at the minimum
// shown ratio. For example, the min height changed from 0 to a positive
// value while the top controls were completely hidden.
- } else if (TopControlsShownRatio() == OldTopControlsMinShownRatio() &&
+ } else if (effective_top_shown_ratio == OldTopControlsMinShownRatio() &&
TopControlsMinHeight() !=
old_browser_controls_params_.top_controls_min_height) {
top_target_ratio = TopControlsMinShownRatio();
@@ -303,7 +334,7 @@ void BrowserControlsOffsetManager::OnBrowserControlsParamsChanged(
// If the bottom controls min-height changed when they were at the minimum
// shown ratio.
- } else if (BottomControlsShownRatio() == OldBottomControlsMinShownRatio() &&
+ } else if (effective_bottom_shown_ratio == OldBottomControlsMinShownRatio() &&
BottomControlsMinHeight() !=
old_browser_controls_params_.bottom_controls_min_height) {
bottom_target_ratio = BottomControlsMinShownRatio();
@@ -369,7 +400,21 @@ gfx::Vector2dF BrowserControlsOffsetManager::ScrollBy(
pending_delta.y() < 0))
return pending_delta;
- accumulated_scroll_delta_ += pending_delta.y();
+ // Scroll the page up before expanding the browser controls if
+ // OnlyExpandTopControlsAtPageTop() returns true.
+ float viewport_offset_y = client_->ViewportScrollOffset().y();
+ if (client_->OnlyExpandTopControlsAtPageTop() && pending_delta.y() < 0 &&
+ viewport_offset_y > 0) {
+ // Reset the baseline so the controls will immediately begin to scroll
+ // once we're at the top.
+ ResetBaseline();
+ // Only scroll the controls by the amount remaining after the page contents
+ // have been scrolled to the top.
+ accumulated_scroll_delta_ =
+ std::min(0.f, pending_delta.y() + viewport_offset_y);
+ } else {
+ accumulated_scroll_delta_ += pending_delta.y();
+ }
// We want to base our calculations on top or bottom controls. After consuming
// the scroll delta, we will calculate a shown ratio for the controls. The
@@ -520,8 +565,9 @@ void BrowserControlsOffsetManager::SetupAnimation(
if (top_controls_animation_.IsInitialized() &&
top_controls_animation_.Direction() == direction &&
bottom_controls_animation_.IsInitialized() &&
- bottom_controls_animation_.Direction() == direction)
+ bottom_controls_animation_.Direction() == direction) {
return;
+ }
if (!TopControlsHeight() && !BottomControlsHeight()) {
float ratio = direction == AnimationDirection::HIDING_CONTROLS ? 0.f : 1.f;
@@ -652,7 +698,7 @@ base::Optional<float> BrowserControlsOffsetManager::Animation::Tick(
monotonic_time, start_time_, start_value_, stop_time_, stop_value_);
if (IsComplete(value)) {
- value = base::ClampToRange(stop_value_, min_value_, max_value_);
+ value = FinalValue();
Reset();
}
@@ -691,4 +737,8 @@ bool BrowserControlsOffsetManager::Animation::IsComplete(float value) {
(value <= stop_value_ || value <= min_value_));
}
+float BrowserControlsOffsetManager::Animation::FinalValue() {
+ return base::ClampToRange(stop_value_, min_value_, max_value_);
+}
+
} // namespace cc
diff --git a/chromium/cc/input/browser_controls_offset_manager.h b/chromium/cc/input/browser_controls_offset_manager.h
index bb7d29a8e08..bd6f0438aa6 100644
--- a/chromium/cc/input/browser_controls_offset_manager.h
+++ b/chromium/cc/input/browser_controls_offset_manager.h
@@ -180,6 +180,10 @@ class CC_EXPORT BrowserControlsOffsetManager {
// (clamped to min-max).
base::Optional<float> Reset();
+ // Returns the value the animation will end on. This will be the stop_value
+ // passed to the constructor clamped by the currently configured bounds.
+ float FinalValue();
+
// Return the bounds.
float min_value() { return min_value_; }
float max_value() { return max_value_; }
diff --git a/chromium/cc/input/browser_controls_offset_manager_client.h b/chromium/cc/input/browser_controls_offset_manager_client.h
index 8d97b85dd48..fc65900a8f7 100644
--- a/chromium/cc/input/browser_controls_offset_manager_client.h
+++ b/chromium/cc/input/browser_controls_offset_manager_client.h
@@ -5,6 +5,10 @@
#ifndef CC_INPUT_BROWSER_CONTROLS_OFFSET_MANAGER_CLIENT_H_
#define CC_INPUT_BROWSER_CONTROLS_OFFSET_MANAGER_CLIENT_H_
+namespace gfx {
+class ScrollOffset;
+}
+
namespace cc {
class CC_EXPORT BrowserControlsOffsetManagerClient {
@@ -17,7 +21,9 @@ class CC_EXPORT BrowserControlsOffsetManagerClient {
float bottom_ratio) = 0;
virtual float CurrentTopControlsShownRatio() const = 0;
virtual float CurrentBottomControlsShownRatio() const = 0;
+ virtual gfx::ScrollOffset ViewportScrollOffset() const = 0;
virtual void DidChangeBrowserControlsPosition() = 0;
+ virtual bool OnlyExpandTopControlsAtPageTop() const = 0;
virtual bool HaveRootScrollNode() const = 0;
virtual void SetNeedsCommit() = 0;
diff --git a/chromium/cc/input/browser_controls_offset_manager_unittest.cc b/chromium/cc/input/browser_controls_offset_manager_unittest.cc
index 507ca1f3a5a..7c64038668f 100644
--- a/chromium/cc/input/browser_controls_offset_manager_unittest.cc
+++ b/chromium/cc/input/browser_controls_offset_manager_unittest.cc
@@ -6,6 +6,7 @@
#include <algorithm>
#include <cmath>
+#include <limits>
#include <memory>
#include "base/time/time.h"
@@ -30,7 +31,8 @@ class MockBrowserControlsOffsetManagerClient
: host_impl_(&task_runner_provider_, &task_graph_runner_),
redraw_needed_(false),
update_draw_properties_needed_(false),
- browser_controls_params_({top_controls_height, 0, 0, 0, false, false}),
+ browser_controls_params_(
+ {top_controls_height, 0, 0, 0, false, false, false}),
bottom_controls_shown_ratio_(1.f),
top_controls_shown_ratio_(1.f),
browser_controls_show_threshold_(browser_controls_show_threshold),
@@ -66,6 +68,14 @@ class MockBrowserControlsOffsetManagerClient
return browser_controls_params_.top_controls_min_height;
}
+ bool OnlyExpandTopControlsAtPageTop() const override {
+ return browser_controls_params_.only_expand_top_controls_at_page_top;
+ }
+
+ gfx::ScrollOffset ViewportScrollOffset() const override {
+ return viewport_scroll_offset_;
+ }
+
void SetCurrentBrowserControlsShownRatio(float top_ratio,
float bottom_ratio) override {
AssertAndClamp(&top_ratio);
@@ -110,6 +120,15 @@ class MockBrowserControlsOffsetManagerClient
params.animate_browser_controls_height_changes);
}
+ void SetViewportScrollOffset(float x, float y) {
+ viewport_scroll_offset_ = gfx::ScrollOffset(x, y);
+ }
+
+ void ScrollVerticallyBy(float dy) {
+ gfx::Vector2dF viewport_scroll_delta = manager()->ScrollBy({0.f, dy});
+ viewport_scroll_offset_.Add(gfx::ScrollOffset(viewport_scroll_delta));
+ }
+
private:
FakeImplTaskRunnerProvider task_runner_provider_;
TestTaskGraphRunner task_graph_runner_;
@@ -125,6 +144,7 @@ class MockBrowserControlsOffsetManagerClient
float top_controls_shown_ratio_;
float browser_controls_show_threshold_;
float browser_controls_hide_threshold_;
+ gfx::ScrollOffset viewport_scroll_offset_;
};
TEST(BrowserControlsOffsetManagerTest, EnsureScrollThresholdApplied) {
@@ -1143,5 +1163,76 @@ TEST(BrowserControlsOffsetManagerTest,
EXPECT_FLOAT_EQ(1.f, client.CurrentBottomControlsShownRatio());
}
+TEST(BrowserControlsOffsetManagerTest, OnlyExpandTopControlsAtPageTop) {
+ MockBrowserControlsOffsetManagerClient client(0.f, 0.5f, 0.5f);
+ client.SetBrowserControlsParams(
+ {/*top_controls_height=*/100.f, 0, 0, 0, false, false,
+ /*only_expand_top_controls_at_page_top=*/true});
+ BrowserControlsOffsetManager* manager = client.manager();
+
+ // Scroll down to hide the controls entirely.
+ manager->ScrollBegin();
+ client.ScrollVerticallyBy(150.f);
+ EXPECT_FLOAT_EQ(-100.f, manager->ControlsTopOffset());
+ EXPECT_FLOAT_EQ(50.f, client.ViewportScrollOffset().y());
+ manager->ScrollEnd();
+
+ manager->ScrollBegin();
+
+ // Scroll back up a bit and ensure the controls don't move until we're at
+ // the top.
+ client.ScrollVerticallyBy(-20.f);
+ EXPECT_FLOAT_EQ(-100.f, manager->ControlsTopOffset());
+ EXPECT_FLOAT_EQ(30.f, client.ViewportScrollOffset().y());
+
+ client.ScrollVerticallyBy(-10.f);
+ EXPECT_FLOAT_EQ(-100.f, manager->ControlsTopOffset());
+ EXPECT_FLOAT_EQ(20.f, client.ViewportScrollOffset().y());
+
+ // After scrolling past the top, the top controls should start showing.
+ client.ScrollVerticallyBy(-30.f);
+ EXPECT_FLOAT_EQ(-90.f, manager->ControlsTopOffset());
+ EXPECT_FLOAT_EQ(0.f, client.ViewportScrollOffset().y());
+
+ client.ScrollVerticallyBy(-50.f);
+ EXPECT_FLOAT_EQ(-40.f, manager->ControlsTopOffset());
+ // The final offset is greater than gtest's epsilon.
+ EXPECT_GT(0.0001f, client.ViewportScrollOffset().y());
+
+ manager->ScrollEnd();
+}
+
+// Tests that if the min-height changes while we're animating to the previous
+// min-height, the animation gets updated to end at the new value.
+TEST(BrowserControlsOffsetManagerTest, MinHeightChangeUpdatesAnimation) {
+ MockBrowserControlsOffsetManagerClient client(100, 0.5f, 0.5f);
+ client.SetBrowserControlsParams(
+ {/*top_controls_height=*/100, /*top_controls_min_height=*/50, 0, 0,
+ /*animate_browser_controls_height_changes=*/true, false, false});
+ BrowserControlsOffsetManager* manager = client.manager();
+
+ // Hide the controls to start an animation to min-height.
+ EXPECT_FLOAT_EQ(1.f, manager->TopControlsShownRatio());
+ manager->UpdateBrowserControlsState(BrowserControlsState::kHidden,
+ BrowserControlsState::kBoth, true);
+ base::TimeTicks time = base::TimeTicks::Now();
+ manager->Animate(time);
+ EXPECT_TRUE(manager->HasAnimation());
+
+ // Update the min-height to a smaller value.
+ client.SetBrowserControlsParams(
+ {/*top_controls_height=*/100, /*top_controls_min_height=*/10, 0, 0,
+ /*animate_browser_controls_height_changes=*/true, false, false});
+ EXPECT_TRUE(manager->HasAnimation());
+
+ // Make sure the animation finishes at the new min-height.
+ while (manager->HasAnimation()) {
+ time = base::TimeDelta::FromMicroseconds(100) + time;
+ manager->Animate(time);
+ }
+ EXPECT_FALSE(manager->HasAnimation());
+ EXPECT_FLOAT_EQ(0.1f, manager->TopControlsShownRatio());
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/input/compositor_input_interfaces.h b/chromium/cc/input/compositor_input_interfaces.h
new file mode 100644
index 00000000000..7ffc5eb1085
--- /dev/null
+++ b/chromium/cc/input/compositor_input_interfaces.h
@@ -0,0 +1,78 @@
+// 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_COMPOSITOR_INPUT_INTERFACES_H_
+#define CC_INPUT_COMPOSITOR_INPUT_INTERFACES_H_
+
+#include "base/time/time.h"
+#include "cc/input/scrollbar.h"
+#include "cc/paint/element_id.h"
+
+namespace viz {
+struct BeginFrameArgs;
+}
+
+namespace cc {
+
+struct CompositorCommitData;
+
+// This is the interface that LayerTreeHostImpl and the "graphics" side of the
+// compositor uses to talk to the compositor ThreadedInputHandler. This
+// interface is two-way; it's used used both to communicate state changes from
+// the LayerTree to the input handler and also to query and update state in the
+// input handler.
+class InputDelegateForCompositor {
+ public:
+ // Called during a commit to fill in the changes that have occurred since the
+ // last commit.
+ virtual void ProcessCommitDeltas(CompositorCommitData* commit_data) = 0;
+
+ // Called to let the input handler perform animations.
+ virtual void TickAnimations(base::TimeTicks monotonic_time) = 0;
+
+ // Compositor lifecycle state observation.
+ virtual void WillShutdown() = 0;
+ virtual void WillDraw() = 0;
+ virtual void WillBeginImplFrame(const viz::BeginFrameArgs& args) = 0;
+ virtual void DidCommit() = 0;
+ virtual void DidActivatePendingTree() = 0;
+
+ // Called when the state of the "root layer" may have changed from outside
+ // the input system. The state includes: scroll offset, scrollable size,
+ // scroll limits, page scale, page scale limits.
+ virtual void RootLayerStateMayHaveChanged() = 0;
+
+ // Called to let the input handler know that a scrollbar for the given
+ // elementId has been removed.
+ virtual void DidUnregisterScrollbar(ElementId scroll_element_id,
+ ScrollbarOrientation orientation) = 0;
+
+ // Called to let the input handler know that a scroll offset animation has
+ // completed.
+ virtual void ScrollOffsetAnimationFinished() = 0;
+
+ // Returns true if we're currently in a "gesture" (user-initiated) scroll.
+ // That is, between a GestureScrollBegin and a GestureScrollEnd. Note, a
+ // GestureScrollEnd is deferred if the gesture ended but we're still
+ // animating the scroll to its final position (e.g. the user released their
+ // finger from the touchscreen but we're scroll snapping).
+ virtual bool IsCurrentlyScrolling() const = 0;
+
+ // Returns true if there is an active scroll in progress. "Active" here
+ // means that it's been latched (i.e. we have a CurrentlyScrollingNode()) but
+ // also that some ScrollUpdates have been received and their delta consumed
+ // for scrolling. These can differ significantly e.g. the page allows the
+ // touchstart but preventDefaults all the touchmoves. In that case, we latch
+ // and have a CurrentlyScrollingNode() but will never receive a ScrollUpdate.
+ //
+ // "Precision" means it's a non-animated scroll like a touchscreen or
+ // high-precision touchpad. The latter distinction is important for things
+ // like scheduling decisions which might schedule a wheel and a touch
+ // scrolling differently due to user perception.
+ virtual bool IsActivelyPrecisionScrolling() const = 0;
+};
+
+} // namespace cc
+
+#endif // CC_INPUT_COMPOSITOR_INPUT_INTERFACES_H_
diff --git a/chromium/cc/input/input_handler.h b/chromium/cc/input/input_handler.h
index 43309848b23..66d3ff2540d 100644
--- a/chromium/cc/input/input_handler.h
+++ b/chromium/cc/input/input_handler.h
@@ -38,7 +38,7 @@ namespace cc {
class EventMetrics;
class ScrollElasticityHelper;
-enum PointerResultType { kUnhandled = 0, kScrollbarScroll };
+enum class PointerResultType { kUnhandled = 0, kScrollbarScroll };
// These enum values are reported in UMA. So these values should never be
// removed or changed.
@@ -53,7 +53,7 @@ struct CC_EXPORT InputHandlerPointerResult {
InputHandlerPointerResult() = default;
// Tells what type of processing occurred in the input handler as a result of
// the pointer event.
- PointerResultType type = kUnhandled;
+ PointerResultType type = PointerResultType::kUnhandled;
// Tells what scroll_units should be used.
ui::ScrollGranularity scroll_units =
@@ -120,6 +120,36 @@ class CC_EXPORT InputHandlerClient {
InputHandlerClient() = default;
};
+// Data passed from the input handler to the main thread. Used to notify the
+// main thread about changes that have occurred as a result of input since the
+// last commit.
+struct InputHandlerCommitData {
+ // Defined in threaded_input_handler.cc to avoid inlining since flat_set has
+ // non-trivial size destructor.
+ InputHandlerCommitData();
+ ~InputHandlerCommitData();
+
+ // Unconsumed scroll delta since the last commit.
+ gfx::Vector2dF overscroll_delta;
+
+ // Elements that have scroll snapped to a new target since the last commit.
+ base::flat_set<ElementId> updated_snapped_elements;
+
+ // If a scroll was active at any point since the last commit, this will
+ // identify the scroller (even if it has since ended).
+ ElementId last_latched_scroller;
+
+ // True if a scroll gesture has ended since the last commit.
+ bool scroll_gesture_did_end = false;
+
+ // The following bits are set if a gesture of any type was started since
+ // the last commit.
+ bool has_pinch_zoomed = false;
+ bool has_scrolled_by_wheel = false;
+ bool has_scrolled_by_touch = false;
+ bool has_scrolled_by_precisiontouchpad = false;
+};
+
// The InputHandler is a way for the embedders to interact with the impl thread
// side of the compositor implementation. There is one InputHandler per
// LayerTreeHost. To use the input handler, implement the InputHanderClient
@@ -128,7 +158,7 @@ class CC_EXPORT InputHandler {
public:
// Note these are used in a histogram. Do not reorder or delete existing
// entries.
- enum ScrollThread {
+ enum class ScrollThread {
SCROLL_ON_MAIN_THREAD = 0,
SCROLL_ON_IMPL_THREAD,
SCROLL_IGNORED,
@@ -150,7 +180,7 @@ class CC_EXPORT InputHandler {
: 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;
+ ScrollThread thread = ScrollThread::SCROLL_ON_IMPL_THREAD;
uint32_t main_thread_scrolling_reasons =
MainThreadScrollingReason::kNotScrollingOnMain;
bool bubble = false;
@@ -226,7 +256,7 @@ class CC_EXPORT InputHandler {
const gfx::PointF& mouse_position) = 0;
virtual void MouseLeave() = 0;
- // Returns frame_element_id from the layer hit by the given point.
+ // Returns visible_frame_element_id from the layer hit by the given point.
// If the hit test failed, an invalid element ID is returned.
virtual ElementId FindFrameElementIdAtPoint(
const gfx::PointF& mouse_position) = 0;
diff --git a/chromium/cc/input/main_thread_scrolling_reason.cc b/chromium/cc/input/main_thread_scrolling_reason.cc
index a116970e49b..abd24047855 100644
--- a/chromium/cc/input/main_thread_scrolling_reason.cc
+++ b/chromium/cc/input/main_thread_scrolling_reason.cc
@@ -42,8 +42,6 @@ void MainThreadScrollingReason::AddToTracedValue(
traced_value.AppendString("Frame overlay");
if (reasons & kHandlingScrollFromMainThread)
traced_value.AppendString("Handling scroll from main thread");
- if (reasons & kHasTransformAndLCDText)
- traced_value.AppendString("Has transform and LCD text");
if (reasons & kNotOpaqueForTextAndLCDText)
traced_value.AppendString("Not opaque for text and LCD text");
if (reasons & kCantPaintScrollingBackgroundAndLCDText)
diff --git a/chromium/cc/input/main_thread_scrolling_reason.h b/chromium/cc/input/main_thread_scrolling_reason.h
index 8354bbb401d..4ddd536afcc 100644
--- a/chromium/cc/input/main_thread_scrolling_reason.h
+++ b/chromium/cc/input/main_thread_scrolling_reason.h
@@ -43,7 +43,6 @@ struct CC_EXPORT MainThreadScrollingReason {
// only be applied by blending glyphs with the background at a specific
// screen position; transparency and transforms break this.
kNonCompositedReasonsFirst = 17,
- kHasTransformAndLCDText = 1 << 17,
kNotOpaqueForTextAndLCDText = 1 << 18,
kCantPaintScrollingBackgroundAndLCDText = 1 << 19,
kNonCompositedReasonsLast = 22,
@@ -67,8 +66,7 @@ struct CC_EXPORT MainThreadScrollingReason {
};
static const uint32_t kNonCompositedReasons =
- kHasTransformAndLCDText | kNotOpaqueForTextAndLCDText |
- kCantPaintScrollingBackgroundAndLCDText;
+ 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 4f8b75516af..becbfe296db 100644
--- a/chromium/cc/input/main_thread_scrolling_reason_unittest.cc
+++ b/chromium/cc/input/main_thread_scrolling_reason_unittest.cc
@@ -19,7 +19,6 @@ TEST_F(MainThreadScrollingReasonTest, AsText) {
"Scrollbar scrolling, "
"Frame overlay, "
"Handling scroll from main thread, "
- "Has transform and LCD text, "
"Not opaque for text and LCD text, "
"Can't paint scrolling background and LCD text, "
"Non fast scrollable region, "
diff --git a/chromium/cc/input/page_scale_animation.cc b/chromium/cc/input/page_scale_animation.cc
index c307ff967d2..96b209c6008 100644
--- a/chromium/cc/input/page_scale_animation.cc
+++ b/chromium/cc/input/page_scale_animation.cc
@@ -190,8 +190,7 @@ float PageScaleAnimation::InterpAtTime(base::TimeTicks monotonic_time) const {
DCHECK(monotonic_time >= start_time_);
if (IsAnimationCompleteAtTime(monotonic_time))
return 1.f;
- const double normalized_time =
- (monotonic_time - start_time_).InSecondsF() / duration_.InSecondsF();
+ const double normalized_time = (monotonic_time - start_time_) / duration_;
return static_cast<float>(timing_function_.Solve(normalized_time));
}
diff --git a/chromium/cc/input/scroll_utils.cc b/chromium/cc/input/scroll_utils.cc
index e9c036e3896..af78599f472 100644
--- a/chromium/cc/input/scroll_utils.cc
+++ b/chromium/cc/input/scroll_utils.cc
@@ -4,6 +4,8 @@
#include "cc/input/scroll_utils.h"
+#include <algorithm>
+
#include "base/numerics/ranges.h"
#include "ui/gfx/geometry/size_f.h"
#include "ui/gfx/geometry/vector2d_f.h"
@@ -21,22 +23,13 @@ gfx::Vector2dF ScrollUtils::ResolveScrollPercentageToPixels(
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) {
+ 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) {
+ 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));
diff --git a/chromium/cc/input/scroll_utils.h b/chromium/cc/input/scroll_utils.h
index 34fb3be7773..c73d5c42282 100644
--- a/chromium/cc/input/scroll_utils.h
+++ b/chromium/cc/input/scroll_utils.h
@@ -21,17 +21,12 @@ static constexpr float kMinFractionToStepWhenPaging = 0.875f;
// 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.
+ // |scroller_size|. Limits it by a maximum of 12.5% of |viewport_size| to
+ // avoid too large deltas. Inputs and output must be in physical pixels.
static gfx::Vector2dF ResolveScrollPercentageToPixels(
const gfx::Vector2dF& scroll_delta,
const gfx::SizeF& scroller_size,
diff --git a/chromium/cc/input/scrollbar_animation_controller.cc b/chromium/cc/input/scrollbar_animation_controller.cc
index 50242fb337c..39d3ec0b41c 100644
--- a/chromium/cc/input/scrollbar_animation_controller.cc
+++ b/chromium/cc/input/scrollbar_animation_controller.cc
@@ -5,6 +5,7 @@
#include "cc/input/scrollbar_animation_controller.h"
#include <algorithm>
+#include <memory>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
@@ -158,9 +159,8 @@ bool ScrollbarAnimationController::Animate(base::TimeTicks now) {
float ScrollbarAnimationController::AnimationProgressAtTime(
base::TimeTicks now) {
- base::TimeDelta delta = now - last_awaken_time_;
- float progress = delta.InSecondsF() / fade_duration_.InSecondsF();
- return base::ClampToRange(progress, 0.0f, 1.0f);
+ const base::TimeDelta delta = now - last_awaken_time_;
+ return base::ClampToRange(float{delta / fade_duration_}, 0.0f, 1.0f);
}
void ScrollbarAnimationController::RunAnimationFrame(float progress) {
diff --git a/chromium/cc/input/scrollbar_controller.cc b/chromium/cc/input/scrollbar_controller.cc
index 490cd44d1d4..3a3f008082a 100644
--- a/chromium/cc/input/scrollbar_controller.cc
+++ b/chromium/cc/input/scrollbar_controller.cc
@@ -11,6 +11,7 @@
#include "cc/input/scroll_utils.h"
#include "cc/input/scrollbar.h"
#include "cc/input/scrollbar_controller.h"
+#include "cc/layers/viewport.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/scroll_node.h"
@@ -100,7 +101,11 @@ InputHandlerPointerResult ScrollbarController::HandlePointerDown(
Granularity(scrollbar_part, perform_jump_click_on_track);
if (scrollbar_part == ScrollbarPart::THUMB) {
drag_state_ = DragState();
- drag_state_->drag_origin = position_in_widget;
+ bool clipped = false;
+ drag_state_->drag_origin =
+ GetScrollbarRelativePosition(position_in_widget, &clipped);
+ // If the point were clipped we shouldn't have hit tested to a valid part.
+ DCHECK(!clipped);
// Record the current scroller offset. This will be needed to snap the
// thumb back to its original position if the pointer moves too far away
@@ -236,20 +241,32 @@ float ScrollbarController::GetScrollDeltaForAbsoluteJump() const {
const float delta =
round(std::abs(desired_thumb_origin - current_thumb_origin));
- return delta * GetScrollerToScrollbarRatio();
+ return delta * GetScrollerToScrollbarRatio() * GetPageScaleFactorForScroll();
}
int ScrollbarController::GetScrollDeltaForDragPosition(
const gfx::PointF pointer_position_in_widget) const {
const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
- const float pointer_delta =
+ // Convert the move position to scrollbar layer relative for comparison with
+ // |drag_state_| drag_origin. Ignore clipping as if we're within the region
+ // that doesn't cause snapping back, we do want the delta in the appropriate
+ // dimension to cause a scroll.
+ bool clipped = false;
+ const gfx::PointF scrollbar_relative_position(
+ GetScrollbarRelativePosition(pointer_position_in_widget, &clipped));
+ 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();
+ ? scrollbar_relative_position.y() - drag_state_->drag_origin.y()
+ : scrollbar_relative_position.x() - drag_state_->drag_origin.x();
const float new_offset = pointer_delta * GetScrollerToScrollbarRatio();
- const float scroll_delta = drag_state_->scroll_position_at_start_ +
- new_offset - scrollbar->current_pos();
+ float scroll_delta = drag_state_->scroll_position_at_start_ + new_offset -
+ scrollbar->current_pos();
+
+ // The scroll delta computed is layer relative. In order to scroll the
+ // correct amount, we have to convert the delta to be unscaled (i.e. multiply
+ // by the page scale factor), as GSU deltas are always unscaled.
+ scroll_delta *= GetPageScaleFactorForScroll();
// Scroll delta floored to match main thread per pixel behavior
return floorf(scroll_delta);
@@ -326,9 +343,8 @@ InputHandlerPointerResult ScrollbarController::HandlePointerMove(
ScrollbarOrientation::VERTICAL
? gfx::ScrollOffset(0, delta)
: gfx::ScrollOffset(delta, 0));
- const gfx::Vector2dF clamped_scroll_offset(
- layer_tree_host_impl_->ComputeScrollDelta(
- *target_node, ScrollOffsetToVector2dF(scroll_offset)));
+ const gfx::Vector2dF clamped_scroll_offset =
+ ComputeClampedDelta(*target_node, ScrollOffsetToVector2dF(scroll_offset));
if (clamped_scroll_offset.IsZero())
return scroll_result;
@@ -342,6 +358,24 @@ InputHandlerPointerResult ScrollbarController::HandlePointerMove(
return scroll_result;
}
+gfx::Vector2dF ScrollbarController::ComputeClampedDelta(
+ const ScrollNode& target_node,
+ const gfx::Vector2dF& scroll_delta) const {
+ DCHECK(!target_node.scrolls_inner_viewport);
+ if (target_node.scrolls_outer_viewport)
+ return layer_tree_host_impl_->viewport().ComputeClampedDelta(scroll_delta);
+
+ // ComputeScrollDelta returns a delta accounting for the current page zoom
+ // level. Since we're producing a delta for an injected GSU, we need to get
+ // back to and unscaled delta (i.e. multiply by the page scale factor).
+ gfx::Vector2dF clamped_delta =
+ layer_tree_host_impl_->GetInputHandler().ComputeScrollDelta(target_node,
+ scroll_delta);
+ const float scale_factor = GetPageScaleFactorForScroll();
+ clamped_delta.Scale(scale_factor);
+ return clamped_delta;
+}
+
float ScrollbarController::GetScrollerToScrollbarRatio() const {
// Calculating the delta by which the scroller layer should move when
// dragging the thumb depends on the following factors:
@@ -589,9 +623,19 @@ int ScrollbarController::GetViewportLength() const {
->property_trees()
->scroll_tree.FindNodeFromElementId(scrollbar->scroll_element_id());
DCHECK(scroll_node);
- return scrollbar->orientation() == ScrollbarOrientation::VERTICAL
- ? scroll_node->container_bounds.height()
- : scroll_node->container_bounds.width();
+ if (!scroll_node->scrolls_outer_viewport) {
+ float length = scrollbar->orientation() == ScrollbarOrientation::VERTICAL
+ ? scroll_node->container_bounds.height()
+ : scroll_node->container_bounds.width();
+ return length;
+ }
+
+ gfx::SizeF viewport_size = layer_tree_host_impl_->viewport()
+ .GetInnerViewportSizeExcludingScrollbars();
+ float length = scrollbar->orientation() == ScrollbarOrientation::VERTICAL
+ ? viewport_size.height()
+ : viewport_size.width();
+ return length / GetPageScaleFactorForScroll();
}
int ScrollbarController::GetScrollDeltaForPercentBasedScroll() const {
@@ -609,7 +653,7 @@ int ScrollbarController::GetScrollDeltaForPercentBasedScroll() const {
: gfx::Vector2dF(kPercentDeltaForDirectionalScroll, 0);
const gfx::Vector2dF pixel_delta =
- layer_tree_host_impl_->ResolveScrollGranularityToPixels(
+ layer_tree_host_impl_->GetInputHandler().ResolveScrollGranularityToPixels(
*scroll_node, scroll_delta,
ui::ScrollGranularity::kScrollByPercentage);
@@ -618,6 +662,10 @@ int ScrollbarController::GetScrollDeltaForPercentBasedScroll() const {
: pixel_delta.x();
}
+float ScrollbarController::GetPageScaleFactorForScroll() const {
+ return layer_tree_host_impl_->active_tree()->page_scale_factor_for_scroll();
+}
+
int ScrollbarController::GetScrollDeltaForScrollbarPart(
const ScrollbarPart scrollbar_part,
const bool jump_key_modifier) const {
diff --git a/chromium/cc/input/scrollbar_controller.h b/chromium/cc/input/scrollbar_controller.h
index ebe4dde9f09..5d5c70e8675 100644
--- a/chromium/cc/input/scrollbar_controller.h
+++ b/chromium/cc/input/scrollbar_controller.h
@@ -5,6 +5,8 @@
#ifndef CC_INPUT_SCROLLBAR_CONTROLLER_H_
#define CC_INPUT_SCROLLBAR_CONTROLLER_H_
+#include <memory>
+
#include "cc/cc_export.h"
#include "cc/input/input_handler.h"
#include "cc/input/scrollbar.h"
@@ -171,7 +173,8 @@ class CC_EXPORT ScrollbarController {
};
struct CC_EXPORT DragState {
- // This marks the point at which the drag initiated (relative to the widget)
+ // This marks the point at which the drag initiated (relative to the
+ // scrollbar layer).
gfx::PointF drag_origin;
// This is needed for thumb snapping when the pointer moves too far away
@@ -216,6 +219,12 @@ class CC_EXPORT ScrollbarController {
const ScrollbarPart scrollbar_part,
const bool jump_key_modifier) const;
+ // Clamps |scroll_delta| based on the available scrollable amount of
+ // |target_node|. The returned delta includes the page scale factor and is
+ // appropriate for use directly as a delta for GSU.
+ gfx::Vector2dF ComputeClampedDelta(const ScrollNode& target_node,
+ const gfx::Vector2dF& scroll_delta) const;
+
// Returns the rect for the ScrollbarPart.
gfx::Rect GetRectForScrollbarPart(const ScrollbarPart scrollbar_part) const;
@@ -257,6 +266,10 @@ class CC_EXPORT ScrollbarController {
// Returns the pixel delta for a percent-based scroll of the scrollbar
int GetScrollDeltaForPercentBasedScroll() const;
+ // Returns the page scale factor (i.e. pinch zoom factor). This is relevant
+ // for root viewport scrollbar scrolling.
+ float GetPageScaleFactorForScroll() const;
+
LayerTreeHostImpl* layer_tree_host_impl_;
// Used to safeguard against firing GSE without firing GSB and GSU. For
diff --git a/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc b/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc
index 8988bf72e65..9cc85ab2ab9 100644
--- a/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc
+++ b/chromium/cc/input/single_scrollbar_animation_controller_thinning.cc
@@ -94,13 +94,12 @@ bool SingleScrollbarAnimationControllerThinning::Animate(base::TimeTicks now) {
float SingleScrollbarAnimationControllerThinning::AnimationProgressAtTime(
base::TimeTicks now) {
- base::TimeDelta delta = now - last_awaken_time_;
- float progress = delta.InSecondsF() / Duration().InSecondsF();
- return base::ClampToRange(progress, 0.0f, 1.0f);
-}
+ // In tests, there may be no duration; snap to the end in such a case.
+ if (thinning_duration_.is_zero())
+ return 1.0f;
-const base::TimeDelta& SingleScrollbarAnimationControllerThinning::Duration() {
- return thinning_duration_;
+ const base::TimeDelta delta = now - last_awaken_time_;
+ return base::ClampToRange(float{delta / thinning_duration_}, 0.0f, 1.0f);
}
void SingleScrollbarAnimationControllerThinning::RunAnimationFrame(
diff --git a/chromium/cc/input/single_scrollbar_animation_controller_thinning.h b/chromium/cc/input/single_scrollbar_animation_controller_thinning.h
index 80a267db105..96fe980ae38 100644
--- a/chromium/cc/input/single_scrollbar_animation_controller_thinning.h
+++ b/chromium/cc/input/single_scrollbar_animation_controller_thinning.h
@@ -70,7 +70,6 @@ class CC_EXPORT SingleScrollbarAnimationControllerThinning {
ScrollbarLayerImplBase* GetScrollbar() const;
float AnimationProgressAtTime(base::TimeTicks now);
void RunAnimationFrame(float progress);
- const base::TimeDelta& Duration();
// Describes whether the current animation should INCREASE (thicken)
// a bar or DECREASE it (thin).
diff --git a/chromium/cc/input/snap_fling_curve.cc b/chromium/cc/input/snap_fling_curve.cc
index b61be7b9925..e63dba154b7 100644
--- a/chromium/cc/input/snap_fling_curve.cc
+++ b/chromium/cc/input/snap_fling_curve.cc
@@ -20,7 +20,7 @@ constexpr double kDistanceEstimatorScalar = 25;
// The delta to be scrolled in next frame is 0.92 of the delta in last frame.
constexpr double kRatio = 0.92;
#endif
-constexpr double kMsPerFrame = 16;
+constexpr auto kFrameTime = base::TimeDelta::FromMilliseconds(16);
constexpr base::TimeDelta kMaximumSnapDuration =
base::TimeDelta::FromSecondsD(5);
@@ -67,7 +67,7 @@ SnapFlingCurve::SnapFlingCurve(const gfx::Vector2dF& start_offset,
start_time_(first_gsu_time),
total_frames_(EstimateFramesFromDistance(total_distance_)),
first_delta_(CalculateFirstDelta(total_distance_, total_frames_)),
- duration_(base::TimeDelta::FromMilliseconds(total_frames_ * kMsPerFrame)),
+ duration_(total_frames_ * kFrameTime),
is_finished_(total_distance_ == 0) {
if (is_finished_)
return;
@@ -78,10 +78,10 @@ SnapFlingCurve::SnapFlingCurve(const gfx::Vector2dF& start_offset,
SnapFlingCurve::~SnapFlingCurve() = default;
double SnapFlingCurve::GetCurrentCurveDistance(base::TimeDelta current_time) {
- double current_frame = current_time.InMillisecondsF() / kMsPerFrame + 1;
- double sum =
+ const double current_frame = current_time / kFrameTime + 1;
+ const double sum =
first_delta_ * (1 - std::pow(kRatio, current_frame)) / (1 - kRatio);
- return sum <= total_distance_ ? sum : total_distance_;
+ return std::min(sum, total_distance_);
}
gfx::Vector2dF SnapFlingCurve::GetScrollDelta(base::TimeTicks time_stamp) {
diff --git a/chromium/cc/input/threaded_input_handler.cc b/chromium/cc/input/threaded_input_handler.cc
new file mode 100644
index 00000000000..051125a6f11
--- /dev/null
+++ b/chromium/cc/input/threaded_input_handler.cc
@@ -0,0 +1,2097 @@
+// 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/threaded_input_handler.h"
+
+#include <utility>
+#include <vector>
+
+#include "build/build_config.h"
+#include "cc/base/features.h"
+#include "cc/input/scroll_elasticity_helper.h"
+#include "cc/input/scroll_utils.h"
+#include "cc/input/snap_selection_strategy.h"
+#include "cc/layers/viewport.h"
+#include "cc/trees/compositor_commit_data.h"
+#include "cc/trees/layer_tree_host_impl.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "cc/trees/layer_tree_settings.h"
+#include "cc/trees/property_tree.h"
+#include "cc/trees/scroll_node.h"
+
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace cc {
+
+namespace {
+
+enum SlowScrollMetricThread { MAIN_THREAD, CC_THREAD };
+
+void RecordCompositorSlowScrollMetric(ui::ScrollInputType type,
+ SlowScrollMetricThread scroll_thread) {
+ bool scroll_on_main_thread = (scroll_thread == MAIN_THREAD);
+ if (type == ui::ScrollInputType::kWheel) {
+ UMA_HISTOGRAM_BOOLEAN("Renderer4.CompositorWheelScrollUpdateThread",
+ scroll_on_main_thread);
+ } else if (type == ui::ScrollInputType::kTouchscreen) {
+ UMA_HISTOGRAM_BOOLEAN("Renderer4.CompositorTouchScrollUpdateThread",
+ scroll_on_main_thread);
+ }
+}
+
+} // namespace
+
+InputHandlerCommitData::InputHandlerCommitData() = default;
+InputHandlerCommitData::~InputHandlerCommitData() = default;
+
+ThreadedInputHandler::ThreadedInputHandler(LayerTreeHostImpl* host_impl)
+ : host_impl_(*host_impl),
+ scrollbar_controller_(std::make_unique<ScrollbarController>(host_impl)) {}
+
+ThreadedInputHandler::~ThreadedInputHandler() = default;
+
+//
+// =========== InputHandler Interface
+//
+void ThreadedInputHandler::BindToClient(InputHandlerClient* client) {
+ DCHECK(input_handler_client_ == nullptr);
+ input_handler_client_ = client;
+}
+
+InputHandler::ScrollStatus ThreadedInputHandler::ScrollBegin(
+ ScrollState* scroll_state,
+ ui::ScrollInputType type) {
+ DCHECK(scroll_state);
+ DCHECK(scroll_state->delta_x() == 0 && scroll_state->delta_y() == 0);
+
+ InputHandler::ScrollStatus scroll_status;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNotScrollingOnMain;
+ TRACE_EVENT0("cc", "ThreadedInputHandler::ScrollBegin");
+
+ // If this ScrollBegin is non-animated then ensure we cancel any ongoing
+ // animated scrolls.
+ // TODO(bokan): This preserves existing behavior when we had diverging
+ // paths for animated and non-animated scrolls but we should probably
+ // decide when it best makes sense to cancel a scroll animation (maybe
+ // ScrollBy is a better place to do it).
+ if (scroll_state->delta_granularity() ==
+ ui::ScrollGranularity::kScrollByPrecisePixel) {
+ host_impl_.mutator_host()->ScrollAnimationAbort();
+ scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
+ }
+
+ if (CurrentlyScrollingNode() && type == latched_scroll_type_) {
+ // It's possible we haven't yet cleared the CurrentlyScrollingNode if we
+ // received a GSE but we're still animating the last scroll. If that's the
+ // case, we'll simply un-defer the GSE and continue latching to the same
+ // node.
+ DCHECK(deferred_scroll_end_);
+ deferred_scroll_end_ = false;
+ return scroll_status;
+ }
+
+ ScrollNode* scrolling_node = nullptr;
+ bool scroll_on_main_thread = false;
+
+ // TODO(bokan): ClearCurrentlyScrollingNode shouldn't happen in ScrollBegin,
+ // this should only happen in ScrollEnd. We should DCHECK here that the state
+ // is cleared instead. https://crbug.com/1016229
+ ClearCurrentlyScrollingNode();
+
+ 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.
+ scrolling_node = GetScrollTree().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(GetScrollTree(), scrolling_node);
+ if (IsMainThreadScrolling(scroll_status, scrolling_node))
+ scroll_on_main_thread = true;
+ }
+ }
+ } else {
+ 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 = GetScrollTree().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 = InputHandler::ScrollThread::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), host_impl_.DeviceScaleFactor());
+
+ 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 = InputHandler::ScrollThread::SCROLL_IGNORED;
+ return scroll_status;
+ }
+
+ // 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 =
+ ActiveTree().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 =
+ InputHandler::ScrollThread::SCROLL_ON_MAIN_THREAD;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kScrollbarScrolling;
+ return scroll_status;
+ }
+ }
+
+ 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 =
+ InputHandler::ScrollThread::SCROLL_ON_IMPL_THREAD;
+ scroll_status.needs_main_thread_hit_test = true;
+ return scroll_status;
+ }
+
+ starting_node = scroll_hit_test.scroll_node;
+ } else {
+ LayerImpl* layer_impl =
+ ActiveTree().FindLayerThatIsHitByPoint(device_viewport_point);
+
+ if (layer_impl) {
+ LayerImpl* first_scrolling_layer_or_scrollbar =
+ ActiveTree().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 =
+ InputHandler::ScrollThread::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 = InputHandler::ScrollThread::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);
+ }
+ }
+
+ // 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.
+ scrolling_node = FindNodeToLatch(scroll_state, starting_node, type);
+ }
+
+ 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 = InputHandler::ScrollThread::SCROLL_ON_MAIN_THREAD;
+ return scroll_status;
+ } else if (!scrolling_node) {
+ 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 = InputHandler::ScrollThread::SCROLL_UNKNOWN;
+ } else {
+ // 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 = InputHandler::ScrollThread::SCROLL_IGNORED;
+ }
+ return scroll_status;
+ }
+
+ DCHECK_EQ(scroll_status.main_thread_scrolling_reasons,
+ MainThreadScrollingReason::kNotScrollingOnMain);
+ DCHECK_EQ(scroll_status.thread,
+ InputHandler::ScrollThread::SCROLL_ON_IMPL_THREAD);
+
+ ActiveTree().SetCurrentlyScrollingNode(scrolling_node);
+
+ DidLatchToScroller(*scroll_state, type);
+
+ // If the viewport is scrolling and it cannot consume any delta hints, the
+ // scroll event will need to get bubbled if the viewport is for a guest or
+ // oopif.
+ if (GetViewport().ShouldScroll(*CurrentlyScrollingNode()) &&
+ !GetViewport().CanScroll(*CurrentlyScrollingNode(), *scroll_state)) {
+ scroll_status.bubble = true;
+ }
+
+ return scroll_status;
+}
+
+InputHandler::ScrollStatus ThreadedInputHandler::RootScrollBegin(
+ ScrollState* scroll_state,
+ ui::ScrollInputType type) {
+ TRACE_EVENT0("cc", "ThreadedInputHandler::RootScrollBegin");
+ if (!OuterViewportScrollNode()) {
+ InputHandler::ScrollStatus scroll_status;
+ scroll_status.thread = InputHandler::ScrollThread::SCROLL_IGNORED;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNoScrollingLayer;
+ return scroll_status;
+ }
+
+ scroll_state->data()->set_current_native_scrolling_element(
+ OuterViewportScrollNode()->element_id);
+ 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;
+}
+
+InputHandlerScrollResult ThreadedInputHandler::ScrollUpdate(
+ ScrollState* scroll_state,
+ base::TimeDelta delayed_by) {
+ DCHECK(scroll_state);
+
+ // The current_native_scrolling_element should only be set for ScrollBegin.
+ DCHECK(!scroll_state->data()->current_native_scrolling_element());
+ TRACE_EVENT2("cc", "ThreadedInputHandler::ScrollUpdate", "dx",
+ scroll_state->delta_x(), "dy", scroll_state->delta_y());
+
+ if (!CurrentlyScrollingNode())
+ return InputHandlerScrollResult();
+
+ last_scroll_update_state_ = *scroll_state;
+
+ 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;
+ }
+
+ host_impl_.AccumulateScrollDeltaForTracing(
+ gfx::Vector2dF(scroll_state->delta_x(), scroll_state->delta_y()));
+
+ // Flash the overlay scrollbar even if the scroll delta is 0.
+ if (Settings().scrollbar_flash_after_any_scroll_update) {
+ host_impl_.FlashAllScrollbars(false);
+ } else {
+ ScrollbarAnimationController* animation_controller =
+ host_impl_.ScrollbarAnimationControllerForElementId(
+ CurrentlyScrollingNode()->element_id);
+ if (animation_controller)
+ animation_controller->WillUpdateScroll();
+ }
+
+ float initial_top_controls_offset =
+ host_impl_.browser_controls_manager()->ControlsTopOffset();
+
+ ScrollLatchedScroller(scroll_state, delayed_by);
+
+ bool did_scroll_x = scroll_state->caused_scroll_x();
+ bool did_scroll_y = scroll_state->caused_scroll_y();
+ did_scroll_x_for_scroll_gesture_ |= did_scroll_x;
+ did_scroll_y_for_scroll_gesture_ |= did_scroll_y;
+ bool did_scroll_content = did_scroll_x || did_scroll_y;
+ if (did_scroll_content) {
+ ShowScrollbarsForImplScroll(CurrentlyScrollingNode()->element_id);
+ bool is_animated_scroll = ShouldAnimateScroll(*scroll_state);
+ host_impl_.DidScrollContent(is_animated_scroll);
+ } else {
+ overscroll_delta_for_main_thread_ +=
+ gfx::Vector2dF(scroll_state->delta_x(), scroll_state->delta_y());
+ }
+
+ SetNeedsCommit();
+
+ // Scrolling along an axis resets accumulated root overscroll for that axis.
+ if (did_scroll_x)
+ accumulated_root_overscroll_.set_x(0);
+ if (did_scroll_y)
+ accumulated_root_overscroll_.set_y(0);
+
+ gfx::Vector2dF unused_root_delta;
+ if (GetViewport().ShouldScroll(*CurrentlyScrollingNode())) {
+ unused_root_delta =
+ gfx::Vector2dF(scroll_state->delta_x(), scroll_state->delta_y());
+ }
+
+ // When inner viewport is unscrollable, disable overscrolls.
+ if (auto* inner_viewport_scroll_node = InnerViewportScrollNode()) {
+ unused_root_delta =
+ UserScrollableDelta(*inner_viewport_scroll_node, unused_root_delta);
+ }
+
+ accumulated_root_overscroll_ += unused_root_delta;
+
+ bool did_scroll_top_controls =
+ initial_top_controls_offset !=
+ host_impl_.browser_controls_manager()->ControlsTopOffset();
+
+ InputHandlerScrollResult scroll_result;
+ scroll_result.did_scroll = did_scroll_content || did_scroll_top_controls;
+ scroll_result.did_overscroll_root = !unused_root_delta.IsZero();
+ scroll_result.accumulated_root_overscroll = accumulated_root_overscroll_;
+ scroll_result.unused_scroll_delta = unused_root_delta;
+ scroll_result.overscroll_behavior =
+ scroll_state->is_scroll_chain_cut()
+ ? OverscrollBehavior(OverscrollBehavior::OverscrollBehaviorType::
+ kOverscrollBehaviorTypeNone)
+ : ActiveTree().overscroll_behavior();
+
+ if (scroll_result.did_scroll) {
+ // Scrolling can change the root scroll offset, so inform the synchronous
+ // input handler.
+ UpdateRootLayerStateForSynchronousInputHandler();
+ }
+
+ scroll_result.current_visual_offset =
+ ScrollOffsetToVector2dF(GetVisualScrollOffset(*CurrentlyScrollingNode()));
+ float scale_factor = ActiveTree().page_scale_factor_for_scroll();
+ scroll_result.current_visual_offset.Scale(scale_factor);
+
+ // Run animations which need to respond to updated scroll offset.
+ host_impl_.mutator_host()->TickScrollAnimations(
+ host_impl_.CurrentBeginFrameArgs().frame_time, GetScrollTree());
+
+ return scroll_result;
+}
+
+void ThreadedInputHandler::ScrollEnd(bool should_snap) {
+ scrollbar_controller_->ResetState();
+ if (!CurrentlyScrollingNode())
+ return;
+
+ // Note that if we deferred the scroll end then we should not snap. We will
+ // snap once we deliver the deferred scroll end.
+ if (host_impl_.mutator_host()->ImplOnlyScrollAnimatingElement()) {
+ DCHECK(!deferred_scroll_end_);
+ deferred_scroll_end_ = true;
+ return;
+ }
+
+ if (should_snap && SnapAtScrollEnd()) {
+ deferred_scroll_end_ = true;
+ return;
+ }
+
+ DCHECK(latched_scroll_type_.has_value());
+
+ host_impl_.browser_controls_manager()->ScrollEnd();
+
+ ClearCurrentlyScrollingNode();
+ deferred_scroll_end_ = false;
+ scroll_gesture_did_end_ = true;
+ SetNeedsCommit();
+}
+
+void ThreadedInputHandler::RecordScrollBegin(
+ ui::ScrollInputType input_type,
+ ScrollBeginThreadState scroll_start_state) {
+ auto tracker_type = GetTrackerTypeForScroll(input_type);
+ DCHECK_NE(tracker_type, FrameSequenceTrackerType::kMaxType);
+
+ // The main-thread is the 'scrolling thread' if:
+ // (1) the scroll is driven by the main thread, or
+ // (2) the scroll is driven by the compositor, but blocked on the main
+ // thread.
+ // Otherwise, the compositor-thread is the 'scrolling thread'.
+ // TODO(crbug.com/1060712): We should also count 'main thread' as the
+ // 'scrolling thread' if the layer being scrolled has scroll-event handlers.
+ FrameSequenceMetrics::ThreadType scrolling_thread;
+ switch (scroll_start_state) {
+ case ScrollBeginThreadState::kScrollingOnCompositor:
+ scrolling_thread = FrameSequenceMetrics::ThreadType::kCompositor;
+ break;
+ case ScrollBeginThreadState::kScrollingOnMain:
+ case ScrollBeginThreadState::kScrollingOnCompositorBlockedOnMain:
+ scrolling_thread = FrameSequenceMetrics::ThreadType::kMain;
+ break;
+ }
+ host_impl_.frame_trackers().StartScrollSequence(tracker_type,
+ scrolling_thread);
+}
+
+void ThreadedInputHandler::RecordScrollEnd(ui::ScrollInputType input_type) {
+ host_impl_.frame_trackers().StopSequence(GetTrackerTypeForScroll(input_type));
+}
+
+InputHandlerPointerResult ThreadedInputHandler::MouseMoveAt(
+ const gfx::Point& viewport_point) {
+ InputHandlerPointerResult result;
+ if (Settings().compositor_threaded_scrollbar_scrolling) {
+ result =
+ scrollbar_controller_->HandlePointerMove(gfx::PointF(viewport_point));
+ }
+
+ // Early out if there are no animation controllers and avoid the hit test.
+ // This happens on platforms without animated scrollbars.
+ if (!host_impl_.HasAnimatedScrollbars())
+ return result;
+
+ gfx::PointF device_viewport_point = gfx::ScalePoint(
+ gfx::PointF(viewport_point), host_impl_.DeviceScaleFactor());
+
+ 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 (!hit_test.hit_test_successful || !scroll_node)
+ return result;
+
+ // Scrollbars for the viewport are registered with the outer viewport layer.
+ if (scroll_node->scrolls_inner_viewport)
+ scroll_node = OuterViewportScrollNode();
+
+ ElementId scroll_element_id = scroll_node->element_id;
+ ScrollbarAnimationController* new_animation_controller =
+ host_impl_.ScrollbarAnimationControllerForElementId(scroll_element_id);
+ if (scroll_element_id != scroll_element_id_mouse_currently_over_) {
+ ScrollbarAnimationController* old_animation_controller =
+ host_impl_.ScrollbarAnimationControllerForElementId(
+ scroll_element_id_mouse_currently_over_);
+ if (old_animation_controller)
+ old_animation_controller->DidMouseLeave();
+
+ scroll_element_id_mouse_currently_over_ = scroll_element_id;
+
+ // Experiment: Enables will flash scrollbar when user move mouse enter a
+ // scrollable area.
+ if (Settings().scrollbar_flash_when_mouse_enter && new_animation_controller)
+ new_animation_controller->DidScrollUpdate();
+ }
+
+ if (!new_animation_controller)
+ return result;
+
+ new_animation_controller->DidMouseMove(device_viewport_point);
+
+ return result;
+}
+
+InputHandlerPointerResult ThreadedInputHandler::MouseDown(
+ const gfx::PointF& viewport_point,
+ bool shift_modifier) {
+ ScrollbarAnimationController* animation_controller =
+ host_impl_.ScrollbarAnimationControllerForElementId(
+ scroll_element_id_mouse_currently_over_);
+ if (animation_controller) {
+ animation_controller->DidMouseDown();
+ scroll_element_id_mouse_currently_captured_ =
+ scroll_element_id_mouse_currently_over_;
+ }
+
+ InputHandlerPointerResult result;
+ if (Settings().compositor_threaded_scrollbar_scrolling) {
+ result = scrollbar_controller_->HandlePointerDown(viewport_point,
+ shift_modifier);
+ }
+
+ return result;
+}
+
+InputHandlerPointerResult ThreadedInputHandler::MouseUp(
+ const gfx::PointF& viewport_point) {
+ if (scroll_element_id_mouse_currently_captured_) {
+ ScrollbarAnimationController* animation_controller =
+ host_impl_.ScrollbarAnimationControllerForElementId(
+ scroll_element_id_mouse_currently_captured_);
+
+ scroll_element_id_mouse_currently_captured_ = ElementId();
+
+ if (animation_controller)
+ animation_controller->DidMouseUp();
+ }
+
+ InputHandlerPointerResult result;
+ if (Settings().compositor_threaded_scrollbar_scrolling)
+ result = scrollbar_controller_->HandlePointerUp(viewport_point);
+
+ return result;
+}
+
+void ThreadedInputHandler::MouseLeave() {
+ for (auto& pair : host_impl_.get_scrollbar_animation_controllers())
+ pair.second->DidMouseLeave();
+ scroll_element_id_mouse_currently_over_ = ElementId();
+}
+
+ElementId ThreadedInputHandler::FindFrameElementIdAtPoint(
+ const gfx::PointF& viewport_point) {
+ gfx::PointF device_viewport_point = gfx::ScalePoint(
+ gfx::PointF(viewport_point), host_impl_.DeviceScaleFactor());
+ return ActiveTree().FindFrameElementIdAtPoint(device_viewport_point);
+}
+
+void ThreadedInputHandler::RequestUpdateForSynchronousInputHandler() {
+ UpdateRootLayerStateForSynchronousInputHandler();
+}
+
+void ThreadedInputHandler::SetSynchronousInputHandlerRootScrollOffset(
+ const gfx::ScrollOffset& root_content_offset) {
+ TRACE_EVENT2(
+ "cc", "ThreadedInputHandler::SetSynchronousInputHandlerRootScrollOffset",
+ "offset_x", root_content_offset.x(), "offset_y", root_content_offset.y());
+
+ gfx::Vector2dF physical_delta =
+ root_content_offset.DeltaFrom(GetViewport().TotalScrollOffset());
+ physical_delta.Scale(ActiveTree().page_scale_factor_for_scroll());
+
+ bool changed = !GetViewport()
+ .ScrollBy(physical_delta,
+ /*viewport_point=*/gfx::Point(),
+ /*is_direct_manipulation=*/false,
+ /*affect_browser_controls=*/false,
+ /*scroll_outer_viewport=*/true)
+ .consumed_delta.IsZero();
+ if (!changed)
+ return;
+
+ ShowScrollbarsForImplScroll(OuterViewportScrollNode()->element_id);
+ SetNeedsCommit();
+
+ // After applying the synchronous input handler's scroll offset, tell it what
+ // we ended up with.
+ UpdateRootLayerStateForSynchronousInputHandler();
+
+ host_impl_.DidSetRootScrollOffsetForSynchronousInputHandler();
+}
+
+void ThreadedInputHandler::PinchGestureBegin() {
+ pinch_gesture_active_ = true;
+ pinch_gesture_end_should_clear_scrolling_node_ = !CurrentlyScrollingNode();
+
+ TRACE_EVENT_INSTANT1("cc", "SetCurrentlyScrollingNode PinchGestureBegin",
+ TRACE_EVENT_SCOPE_THREAD, "isNull",
+ OuterViewportScrollNode() ? false : true);
+ ActiveTree().SetCurrentlyScrollingNode(OuterViewportScrollNode());
+ host_impl_.browser_controls_manager()->PinchBegin();
+ host_impl_.DidStartPinchZoom();
+}
+
+void ThreadedInputHandler::PinchGestureUpdate(float magnify_delta,
+ const gfx::Point& anchor) {
+ TRACE_EVENT0("cc", "ThreadedInputHandler::PinchGestureUpdate");
+ if (!InnerViewportScrollNode())
+ return;
+ has_pinch_zoomed_ = true;
+ GetViewport().PinchUpdate(magnify_delta, anchor);
+ SetNeedsCommit();
+ host_impl_.DidUpdatePinchZoom();
+ // Pinching can change the root scroll offset, so inform the synchronous input
+ // handler.
+ UpdateRootLayerStateForSynchronousInputHandler();
+}
+
+void ThreadedInputHandler::PinchGestureEnd(const gfx::Point& anchor,
+ bool snap_to_min) {
+ pinch_gesture_active_ = false;
+ if (pinch_gesture_end_should_clear_scrolling_node_) {
+ pinch_gesture_end_should_clear_scrolling_node_ = false;
+ ClearCurrentlyScrollingNode();
+ }
+ GetViewport().PinchEnd(anchor, snap_to_min);
+ host_impl_.browser_controls_manager()->PinchEnd();
+ SetNeedsCommit();
+ host_impl_.DidEndPinchZoom();
+}
+
+void ThreadedInputHandler::SetNeedsAnimateInput() {
+ host_impl_.SetNeedsAnimateInput();
+}
+
+bool ThreadedInputHandler::IsCurrentlyScrollingViewport() const {
+ auto* node = CurrentlyScrollingNode();
+ if (!node)
+ return false;
+ return GetViewport().ShouldScroll(*node);
+}
+
+EventListenerProperties ThreadedInputHandler::GetEventListenerProperties(
+ EventListenerClass event_class) const {
+ return ActiveTree().event_listener_properties(event_class);
+}
+
+bool ThreadedInputHandler::HasBlockingWheelEventHandlerAt(
+ const gfx::Point& viewport_point) const {
+ gfx::PointF device_viewport_point = gfx::ScalePoint(
+ gfx::PointF(viewport_point), host_impl_.DeviceScaleFactor());
+
+ LayerImpl* layer_impl_with_wheel_event_handler =
+ ActiveTree().FindLayerThatIsHitByPointInWheelEventHandlerRegion(
+ device_viewport_point);
+
+ return layer_impl_with_wheel_event_handler;
+}
+
+InputHandler::TouchStartOrMoveEventListenerType
+ThreadedInputHandler::EventListenerTypeForTouchStartOrMoveAt(
+ const gfx::Point& viewport_point,
+ TouchAction* out_touch_action) {
+ gfx::PointF device_viewport_point = gfx::ScalePoint(
+ gfx::PointF(viewport_point), host_impl_.DeviceScaleFactor());
+
+ LayerImpl* layer_impl_with_touch_handler =
+ ActiveTree().FindLayerThatIsHitByPointInTouchHandlerRegion(
+ device_viewport_point);
+
+ if (layer_impl_with_touch_handler == nullptr) {
+ if (out_touch_action)
+ *out_touch_action = TouchAction::kAuto;
+ return InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER;
+ }
+
+ if (out_touch_action) {
+ gfx::Transform layer_screen_space_transform =
+ layer_impl_with_touch_handler->ScreenSpaceTransform();
+ gfx::Transform inverse_layer_screen_space(
+ gfx::Transform::kSkipInitialization);
+ bool can_be_inversed =
+ layer_screen_space_transform.GetInverse(&inverse_layer_screen_space);
+ // Getting here indicates that |layer_impl_with_touch_handler| is non-null,
+ // which means that the |hit| in FindClosestMatchingLayer() is true, which
+ // indicates that the inverse is available.
+ DCHECK(can_be_inversed);
+ bool clipped = false;
+ gfx::Point3F planar_point = MathUtil::ProjectPoint3D(
+ inverse_layer_screen_space, device_viewport_point, &clipped);
+ gfx::PointF hit_test_point_in_layer_space =
+ gfx::PointF(planar_point.x(), planar_point.y());
+ const auto& region = layer_impl_with_touch_handler->touch_action_region();
+ gfx::Point point = gfx::ToRoundedPoint(hit_test_point_in_layer_space);
+ *out_touch_action = region.GetAllowedTouchAction(point);
+ }
+
+ if (!CurrentlyScrollingNode())
+ return InputHandler::TouchStartOrMoveEventListenerType::HANDLER;
+
+ // Check if the touch start (or move) hits on the current scrolling layer or
+ // its descendant. layer_impl_with_touch_handler is the layer hit by the
+ // pointer and has an event handler, otherwise it is null. We want to compare
+ // the most inner layer we are hitting on which may not have an event listener
+ // with the actual scrolling layer.
+ LayerImpl* layer_impl =
+ ActiveTree().FindLayerThatIsHitByPoint(device_viewport_point);
+ bool is_ancestor = IsScrolledBy(layer_impl, CurrentlyScrollingNode());
+ return is_ancestor ? InputHandler::TouchStartOrMoveEventListenerType::
+ HANDLER_ON_SCROLLING_LAYER
+ : InputHandler::TouchStartOrMoveEventListenerType::HANDLER;
+}
+
+std::unique_ptr<SwapPromiseMonitor>
+ThreadedInputHandler::CreateLatencyInfoSwapPromiseMonitor(
+ ui::LatencyInfo* latency) {
+ return host_impl_.CreateLatencyInfoSwapPromiseMonitor(latency);
+}
+
+std::unique_ptr<EventsMetricsManager::ScopedMonitor>
+ThreadedInputHandler::GetScopedEventMetricsMonitor(
+ std::unique_ptr<EventMetrics> event_metrics) {
+ return host_impl_.GetScopedEventMetricsMonitor(std::move(event_metrics));
+}
+
+ScrollElasticityHelper* ThreadedInputHandler::CreateScrollElasticityHelper() {
+ DCHECK(!scroll_elasticity_helper_);
+ if (Settings().enable_elastic_overscroll) {
+ scroll_elasticity_helper_.reset(
+ ScrollElasticityHelper::CreateForLayerTreeHostImpl(&host_impl_));
+ }
+ return scroll_elasticity_helper_.get();
+}
+
+bool ThreadedInputHandler::GetScrollOffsetForLayer(ElementId element_id,
+ gfx::ScrollOffset* offset) {
+ ScrollTree& scroll_tree = GetScrollTree();
+ ScrollNode* scroll_node = scroll_tree.FindNodeFromElementId(element_id);
+ if (!scroll_node)
+ return false;
+ *offset = scroll_tree.current_scroll_offset(element_id);
+ return true;
+}
+
+bool ThreadedInputHandler::ScrollLayerTo(ElementId element_id,
+ const gfx::ScrollOffset& offset) {
+ ScrollTree& scroll_tree = GetScrollTree();
+ ScrollNode* scroll_node = scroll_tree.FindNodeFromElementId(element_id);
+ if (!scroll_node)
+ return false;
+
+ scroll_tree.ScrollBy(
+ *scroll_node,
+ ScrollOffsetToVector2dF(offset -
+ scroll_tree.current_scroll_offset(element_id)),
+ &ActiveTree());
+ return true;
+}
+
+bool ThreadedInputHandler::ScrollingShouldSwitchtoMainThread() {
+ DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification));
+ ScrollTree& scroll_tree = GetScrollTree();
+ ScrollNode* scroll_node = scroll_tree.CurrentlyScrollingNode();
+
+ if (!scroll_node)
+ return true;
+
+ for (; scroll_tree.parent(scroll_node);
+ scroll_node = scroll_tree.parent(scroll_node)) {
+ if (!!scroll_node->main_thread_scrolling_reasons)
+ return true;
+ }
+
+ return false;
+}
+
+bool ThreadedInputHandler::GetSnapFlingInfoAndSetAnimatingSnapTarget(
+ const gfx::Vector2dF& natural_displacement_in_viewport,
+ gfx::Vector2dF* out_initial_position,
+ gfx::Vector2dF* out_target_position) {
+ ScrollNode* scroll_node = CurrentlyScrollingNode();
+ if (!scroll_node || !scroll_node->snap_container_data.has_value())
+ return false;
+ const SnapContainerData& data = scroll_node->snap_container_data.value();
+
+ float scale_factor = ActiveTree().page_scale_factor_for_scroll();
+ gfx::Vector2dF natural_displacement_in_content =
+ gfx::ScaleVector2d(natural_displacement_in_viewport, 1.f / scale_factor);
+
+ gfx::ScrollOffset current_offset = GetVisualScrollOffset(*scroll_node);
+ *out_initial_position = ScrollOffsetToVector2dF(current_offset);
+
+ // CC side always uses fractional scroll deltas.
+ bool use_fractional_offsets = true;
+ gfx::ScrollOffset snap_offset;
+ TargetSnapAreaElementIds snap_target_ids;
+ std::unique_ptr<SnapSelectionStrategy> strategy =
+ SnapSelectionStrategy::CreateForEndAndDirection(
+ current_offset, gfx::ScrollOffset(natural_displacement_in_content),
+ use_fractional_offsets);
+ if (!data.FindSnapPosition(*strategy, &snap_offset, &snap_target_ids))
+ return false;
+ scroll_animating_snap_target_ids_ = snap_target_ids;
+
+ *out_target_position = ScrollOffsetToVector2dF(snap_offset);
+ out_target_position->Scale(scale_factor);
+ out_initial_position->Scale(scale_factor);
+ return true;
+}
+
+void ThreadedInputHandler::ScrollEndForSnapFling(bool did_finish) {
+ ScrollNode* scroll_node = CurrentlyScrollingNode();
+ // When a snap fling animation reaches its intended target then we update the
+ // scrolled node's snap targets. This also ensures blink learns about the new
+ // snap targets for this scrolling element.
+ if (did_finish && scroll_node &&
+ scroll_node->snap_container_data.has_value()) {
+ scroll_node->snap_container_data.value().SetTargetSnapAreaElementIds(
+ scroll_animating_snap_target_ids_);
+ updated_snapped_elements_.insert(scroll_node->element_id);
+ SetNeedsCommit();
+ }
+ scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
+ ScrollEnd(false /* should_snap */);
+}
+
+void ThreadedInputHandler::NotifyInputEvent() {
+ host_impl_.NotifyInputEvent();
+}
+
+//
+// =========== InputDelegateForCompositor Interface
+//
+
+void ThreadedInputHandler::ProcessCommitDeltas(
+ CompositorCommitData* commit_data) {
+ DCHECK(commit_data);
+ if (ActiveTree().LayerListIsEmpty())
+ return;
+
+ ElementId inner_viewport_scroll_element_id =
+ InnerViewportScrollNode() ? InnerViewportScrollNode()->element_id
+ : ElementId();
+
+ base::flat_set<ElementId> snapped_elements;
+ updated_snapped_elements_.swap(snapped_elements);
+
+ // Scroll commit data is stored in the scroll tree so it has its own method
+ // for getting it.
+ GetScrollTree().CollectScrollDeltas(
+ commit_data, inner_viewport_scroll_element_id,
+ Settings().commit_fractional_scroll_deltas, snapped_elements);
+
+ // Record and reset scroll source flags.
+ DCHECK(!commit_data->manipulation_info);
+ if (has_scrolled_by_wheel_)
+ commit_data->manipulation_info |= kManipulationInfoWheel;
+ if (has_scrolled_by_touch_)
+ commit_data->manipulation_info |= kManipulationInfoTouch;
+ if (has_scrolled_by_precisiontouchpad_)
+ commit_data->manipulation_info |= kManipulationInfoPrecisionTouchPad;
+ if (has_pinch_zoomed_)
+ commit_data->manipulation_info |= kManipulationInfoPinchZoom;
+
+ has_scrolled_by_wheel_ = false;
+ has_scrolled_by_touch_ = false;
+ has_scrolled_by_precisiontouchpad_ = false;
+ has_pinch_zoomed_ = false;
+
+ commit_data->scroll_gesture_did_end = scroll_gesture_did_end_;
+ scroll_gesture_did_end_ = false;
+
+ commit_data->overscroll_delta = overscroll_delta_for_main_thread_;
+ overscroll_delta_for_main_thread_ = gfx::Vector2dF();
+
+ // 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.
+ // TODO(bokan): This is wrong - if we also started a scroll this frame then
+ // this will clear this value for that scroll. https://crbug.com/1116780.
+ commit_data->scroll_latched_element_id = last_latched_scroller_;
+ if (commit_data->scroll_gesture_did_end)
+ last_latched_scroller_ = ElementId();
+}
+
+void ThreadedInputHandler::TickAnimations(base::TimeTicks monotonic_time) {
+ if (input_handler_client_) {
+ // This does not set did_animate, because if the InputHandlerClient
+ // changes anything it will be through the InputHandler interface which
+ // does SetNeedsRedraw.
+ input_handler_client_->Animate(monotonic_time);
+ }
+}
+
+void ThreadedInputHandler::WillShutdown() {
+ if (input_handler_client_) {
+ input_handler_client_->WillShutdown();
+ input_handler_client_ = nullptr;
+ }
+
+ if (scroll_elasticity_helper_)
+ scroll_elasticity_helper_.reset();
+}
+
+void ThreadedInputHandler::WillDraw() {
+ if (input_handler_client_)
+ input_handler_client_->ReconcileElasticOverscrollAndRootScroll();
+}
+
+void ThreadedInputHandler::WillBeginImplFrame(const viz::BeginFrameArgs& args) {
+ if (input_handler_client_) {
+ scrollbar_controller_->WillBeginImplFrame();
+ input_handler_client_->DeliverInputForBeginFrame(args);
+ }
+}
+
+void ThreadedInputHandler::DidCommit() {
+ // In high latency mode commit cannot finish within the same frame. We need to
+ // flush input here to make sure they got picked up by |PrepareTiles()|.
+ if (input_handler_client_ &&
+ host_impl_.GetCompositorThreadPhase() == ImplThreadPhase::IDLE)
+ input_handler_client_->DeliverInputForHighLatencyMode();
+}
+
+void ThreadedInputHandler::DidActivatePendingTree() {
+ // The previous scrolling node might no longer exist in the new tree.
+ if (!CurrentlyScrollingNode())
+ ClearCurrentlyScrollingNode();
+
+ // Activation can change the root scroll offset, so inform the synchronous
+ // input handler.
+ UpdateRootLayerStateForSynchronousInputHandler();
+}
+
+void ThreadedInputHandler::RootLayerStateMayHaveChanged() {
+ UpdateRootLayerStateForSynchronousInputHandler();
+}
+
+void ThreadedInputHandler::DidUnregisterScrollbar(
+ ElementId scroll_element_id,
+ ScrollbarOrientation orientation) {
+ scrollbar_controller_->DidUnregisterScrollbar(scroll_element_id, orientation);
+}
+
+void ThreadedInputHandler::ScrollOffsetAnimationFinished() {
+ TRACE_EVENT0("cc", "ThreadedInputHandler::ScrollOffsetAnimationFinished");
+ // ScrollOffsetAnimationFinished is called in two cases:
+ // 1- smooth scrolling animation is over (IsAnimatingForSnap == false).
+ // 2- snap scroll animation is over (IsAnimatingForSnap == true).
+ //
+ // Only for case (1) we should check and run snap scroll animation if needed.
+ if (!IsAnimatingForSnap() && SnapAtScrollEnd())
+ return;
+
+ // The end of a scroll offset animation means that the scrolling node is at
+ // the target offset.
+ ScrollNode* scroll_node = CurrentlyScrollingNode();
+ if (scroll_node && scroll_node->snap_container_data.has_value()) {
+ scroll_node->snap_container_data.value().SetTargetSnapAreaElementIds(
+ scroll_animating_snap_target_ids_);
+ updated_snapped_elements_.insert(scroll_node->element_id);
+ SetNeedsCommit();
+ }
+ scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
+
+ // Call scrollEnd with the deferred scroll end state when the scroll animation
+ // completes after GSE arrival.
+ if (deferred_scroll_end_) {
+ ScrollEnd(/*should_snap=*/false);
+ return;
+ }
+}
+
+bool ThreadedInputHandler::IsCurrentlyScrolling() const {
+ return CurrentlyScrollingNode();
+}
+
+bool ThreadedInputHandler::IsActivelyPrecisionScrolling() const {
+ if (!CurrentlyScrollingNode())
+ return false;
+
+ if (!last_scroll_update_state_)
+ return false;
+
+ bool did_scroll_content =
+ did_scroll_x_for_scroll_gesture_ || did_scroll_y_for_scroll_gesture_;
+ return !ShouldAnimateScroll(last_scroll_update_state_.value()) &&
+ did_scroll_content;
+}
+
+ScrollNode* ThreadedInputHandler::CurrentlyScrollingNode() {
+ return GetScrollTree().CurrentlyScrollingNode();
+}
+
+const ScrollNode* ThreadedInputHandler::CurrentlyScrollingNode() const {
+ return GetScrollTree().CurrentlyScrollingNode();
+}
+
+ScrollTree& ThreadedInputHandler::GetScrollTree() {
+ return ActiveTree().property_trees()->scroll_tree;
+}
+
+ScrollTree& ThreadedInputHandler::GetScrollTree() const {
+ return ActiveTree().property_trees()->scroll_tree;
+}
+
+ScrollNode* ThreadedInputHandler::InnerViewportScrollNode() const {
+ return ActiveTree().InnerViewportScrollNode();
+}
+
+ScrollNode* ThreadedInputHandler::OuterViewportScrollNode() const {
+ return ActiveTree().OuterViewportScrollNode();
+}
+
+Viewport& ThreadedInputHandler::GetViewport() const {
+ return host_impl_.viewport();
+}
+
+void ThreadedInputHandler::SetNeedsCommit() {
+ host_impl_.SetNeedsCommitInputChanges();
+}
+
+LayerTreeImpl& ThreadedInputHandler::ActiveTree() {
+ DCHECK(host_impl_.active_tree());
+ return *host_impl_.active_tree();
+}
+
+LayerTreeImpl& ThreadedInputHandler::ActiveTree() const {
+ DCHECK(host_impl_.active_tree());
+ return *host_impl_.active_tree();
+}
+
+const LayerTreeSettings& ThreadedInputHandler::Settings() const {
+ return host_impl_.settings();
+}
+
+FrameSequenceTrackerType ThreadedInputHandler::GetTrackerTypeForScroll(
+ ui::ScrollInputType input_type) const {
+ switch (input_type) {
+ case ui::ScrollInputType::kWheel:
+ return FrameSequenceTrackerType::kWheelScroll;
+ case ui::ScrollInputType::kTouchscreen:
+ return FrameSequenceTrackerType::kTouchScroll;
+ case ui::ScrollInputType::kScrollbar:
+ return FrameSequenceTrackerType::kScrollbarScroll;
+ case ui::ScrollInputType::kAutoscroll:
+ return FrameSequenceTrackerType::kMaxType;
+ }
+}
+
+bool ThreadedInputHandler::IsMainThreadScrolling(
+ const InputHandler::ScrollStatus& status,
+ const ScrollNode* scroll_node) const {
+ if (status.thread == InputHandler::ScrollThread::SCROLL_ON_MAIN_THREAD) {
+ if (!!scroll_node->main_thread_scrolling_reasons) {
+ DCHECK(MainThreadScrollingReason::MainThreadCanSetScrollReasons(
+ status.main_thread_scrolling_reasons));
+ } else {
+ DCHECK(MainThreadScrollingReason::CompositorCanSetScrollReasons(
+ status.main_thread_scrolling_reasons));
+ }
+ return true;
+ }
+ return false;
+}
+
+gfx::Vector2dF ThreadedInputHandler::ResolveScrollGranularityToPixels(
+ const ScrollNode& scroll_node,
+ 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(ActiveTree().GetDeviceViewport().size());
+
+ // Convert from rootframe coordinates to screen coordinates (physical
+ // pixels).
+ scroller_size.Scale(ActiveTree().page_scale_factor_for_scroll());
+
+ pixel_delta = ScrollUtils::ResolveScrollPercentageToPixels(
+ pixel_delta, scroller_size, viewport_size);
+ }
+
+ return pixel_delta;
+}
+
+InputHandler::ScrollStatus ThreadedInputHandler::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;
+ if (scroll_node->main_thread_scrolling_reasons) {
+ TRACE_EVENT1("cc", "LayerImpl::TryScroll: Failed ShouldScrollOnMainThread",
+ "MainThreadScrollingReason",
+ scroll_node->main_thread_scrolling_reasons);
+ scroll_status.thread = InputHandler::ScrollThread::SCROLL_ON_MAIN_THREAD;
+ scroll_status.main_thread_scrolling_reasons =
+ scroll_node->main_thread_scrolling_reasons;
+ return scroll_status;
+ }
+
+ gfx::Transform screen_space_transform =
+ scroll_tree.ScreenSpaceTransform(scroll_node->id);
+ if (!screen_space_transform.IsInvertible()) {
+ TRACE_EVENT0("cc", "LayerImpl::TryScroll: Ignored NonInvertibleTransform");
+ scroll_status.thread = InputHandler::ScrollThread::SCROLL_IGNORED;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNonInvertibleTransform;
+ return scroll_status;
+ }
+
+ if (!scroll_node->scrollable) {
+ TRACE_EVENT0("cc", "LayerImpl::tryScroll: Ignored not scrollable");
+ scroll_status.thread = InputHandler::ScrollThread::SCROLL_IGNORED;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNotScrollable;
+ return scroll_status;
+ }
+
+ // 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.
+ if (scroll_node->id != ScrollTree::kRootNodeId &&
+ scroll_node->id != ScrollTree::kSecondaryRootNodeId &&
+ !scroll_node->scrolls_inner_viewport &&
+ !ActiveTree().LayerByElementId(scroll_node->element_id)) {
+ TRACE_EVENT0("cc",
+ "LayerImpl::tryScroll: Failed due to no scrolling layer");
+ scroll_status.thread = InputHandler::ScrollThread::SCROLL_ON_MAIN_THREAD;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNonFastScrollableRegion;
+ return scroll_status;
+ }
+
+ // The a viewport node should be scrolled even if it has no scroll extent
+ // since it'll scroll using the Viewport class which will generate browser
+ // controls movement and overscroll delta.
+ gfx::ScrollOffset max_scroll_offset =
+ scroll_tree.MaxScrollOffset(scroll_node->id);
+ if (max_scroll_offset.x() <= 0 && max_scroll_offset.y() <= 0 &&
+ !GetViewport().ShouldScroll(*scroll_node)) {
+ TRACE_EVENT0("cc",
+ "LayerImpl::tryScroll: Ignored. Technically scrollable,"
+ " but has no affordance in either direction.");
+ scroll_status.thread = InputHandler::ScrollThread::SCROLL_IGNORED;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNotScrollable;
+ return scroll_status;
+ }
+
+ scroll_status.thread = InputHandler::ScrollThread::SCROLL_ON_IMPL_THREAD;
+ return scroll_status;
+}
+
+base::flat_set<int> ThreadedInputHandler::NonFastScrollableNodes(
+ const gfx::PointF& device_viewport_point) const {
+ base::flat_set<int> non_fast_scrollable_nodes;
+
+ const auto& non_fast_layers =
+ ActiveTree().FindLayersHitByPointInNonFastScrollableRegion(
+ device_viewport_point);
+ for (const auto* layer : non_fast_layers)
+ non_fast_scrollable_nodes.insert(layer->scroll_tree_index());
+
+ return non_fast_scrollable_nodes;
+}
+
+ScrollNode* ThreadedInputHandler::FindScrollNodeForCompositedScrolling(
+ const gfx::PointF& device_viewport_point,
+ LayerImpl* layer_impl,
+ bool* scroll_on_main_thread,
+ uint32_t* main_thread_scrolling_reasons) {
+ DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification));
+ DCHECK(scroll_on_main_thread);
+ DCHECK(main_thread_scrolling_reasons);
+ *main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNotScrollingOnMain;
+
+ const auto& non_fast_scrollable_nodes =
+ NonFastScrollableNodes(device_viewport_point);
+
+ // Walk up the hierarchy and look for a scrollable layer.
+ ScrollTree& scroll_tree = GetScrollTree();
+ ScrollNode* impl_scroll_node = nullptr;
+ if (layer_impl) {
+ // If this is a scrollbar layer, we can't directly use the associated
+ // scroll_node (because the scroll_node associated with this layer will be
+ // the owning scroller's parent). Instead, we first retrieve the scrollable
+ // layer corresponding to the scrollbars owner and then use its
+ // scroll_tree_index instead.
+ int scroll_tree_index = layer_impl->scroll_tree_index();
+ if (layer_impl->IsScrollbarLayer()) {
+ LayerImpl* owner_scroll_layer = ActiveTree().LayerByElementId(
+ ToScrollbarLayer(layer_impl)->scroll_element_id());
+ scroll_tree_index = owner_scroll_layer->scroll_tree_index();
+ }
+
+ ScrollNode* scroll_node = scroll_tree.Node(scroll_tree_index);
+ for (; scroll_tree.parent(scroll_node);
+ scroll_node = scroll_tree.parent(scroll_node)) {
+ // The content layer can also block attempts to scroll outside the main
+ // thread.
+ InputHandler::ScrollStatus status = TryScroll(scroll_tree, scroll_node);
+ if (IsMainThreadScrolling(status, scroll_node)) {
+ *scroll_on_main_thread = true;
+ *main_thread_scrolling_reasons = status.main_thread_scrolling_reasons;
+ return scroll_node;
+ }
+
+ if (non_fast_scrollable_nodes.contains(scroll_node->id)) {
+ *scroll_on_main_thread = true;
+ *main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNonFastScrollableRegion;
+ return scroll_node;
+ }
+
+ if (status.thread == InputHandler::ScrollThread::SCROLL_ON_IMPL_THREAD &&
+ !impl_scroll_node) {
+ impl_scroll_node = scroll_node;
+ }
+ }
+ }
+
+ // TODO(bokan): We shouldn't need this - ordinarily all scrolls should pass
+ // through the outer viewport. If we aren't able to find a scroller we should
+ // return nullptr here and ignore the scroll. However, it looks like on some
+ // pages (reddit.com) we start scrolling from the inner node.
+ if (!impl_scroll_node)
+ impl_scroll_node = InnerViewportScrollNode();
+
+ if (!impl_scroll_node)
+ return nullptr;
+
+ impl_scroll_node = GetNodeToScroll(impl_scroll_node);
+
+ // Ensure that final scroll node scrolls on impl thread (crbug.com/625100)
+ InputHandler::ScrollStatus status = TryScroll(scroll_tree, impl_scroll_node);
+ if (IsMainThreadScrolling(status, impl_scroll_node)) {
+ *scroll_on_main_thread = true;
+ *main_thread_scrolling_reasons = status.main_thread_scrolling_reasons;
+ } else if (non_fast_scrollable_nodes.contains(impl_scroll_node->id)) {
+ *scroll_on_main_thread = true;
+ *main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNonFastScrollableRegion;
+ }
+
+ return impl_scroll_node;
+}
+
+ThreadedInputHandler::ScrollHitTestResult
+ThreadedInputHandler::HitTestScrollNode(
+ const gfx::PointF& device_viewport_point) const {
+ ScrollHitTestResult result;
+ result.scroll_node = nullptr;
+ result.hit_test_successful = false;
+
+ std::vector<const LayerImpl*> layers =
+ ActiveTree().FindAllLayersUpToAndIncludingFirstScrollable(
+ device_viewport_point);
+
+ // It's theoretically possible to hit no layers or only non-scrolling layers.
+ // e.g. an API hit test outside the viewport. In that case, just fallback to
+ // scrolling the viewport.
+ if (layers.empty() || !layers.back()->IsScrollerOrScrollbar()) {
+ result.hit_test_successful = true;
+ if (InnerViewportScrollNode())
+ result.scroll_node = GetNodeToScroll(InnerViewportScrollNode());
+
+ return result;
+ }
+
+ const LayerImpl* scroller_layer = layers.back();
+ layers.pop_back();
+
+ // Go through each layer in front of the scroller. Any of them may block
+ // scrolling if they come from outside the scroller's scroll-subtree or if we
+ // hit a non-fast-scrolling-region.
+ for (const auto* layer_impl : layers) {
+ DCHECK(!layer_impl->IsScrollbarLayer());
+
+ // There are some cases where the hit layer may not be correct (e.g. layer
+ // squashing, pointer-events:none layer) because the compositor doesn't
+ // know what parts of the layer (if any) are actually visible to hit
+ // testing. This is fine if we can determine that the scrolling node will
+ // be the same regardless of whether we hit an opaque or transparent (to
+ // hit testing) point of the layer. If the scrolling node may depend on
+ // this, we have to get a hit test from the main thread.
+ if (!IsInitialScrollHitTestReliable(layer_impl, scroller_layer)) {
+ TRACE_EVENT_INSTANT0("cc", "Failed Hit Test", TRACE_EVENT_SCOPE_THREAD);
+ 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 (ActiveTree().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.
+ if (scroller_layer->IsScrollbarLayer()) {
+ scroller_layer = ActiveTree().LayerByElementId(
+ ToScrollbarLayer(scroller_layer)->scroll_element_id());
+ DCHECK(scroller_layer);
+ } else {
+ // We need to also make sure the scroller itself doesn't have a non-fast
+ // scrolling region in the hit tested area.
+ if (ActiveTree().PointHitsNonFastScrollableRegion(device_viewport_point,
+ *scroller_layer))
+ return result;
+ }
+
+ ScrollNode* scroll_node =
+ GetScrollTree().Node(scroller_layer->scroll_tree_index());
+
+ 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
+// from touch.
+bool ThreadedInputHandler::IsTouchDraggingScrollbar(
+ LayerImpl* first_scrolling_layer_or_scrollbar,
+ ui::ScrollInputType type) {
+ return first_scrolling_layer_or_scrollbar &&
+ first_scrolling_layer_or_scrollbar->IsScrollbarLayer() &&
+ type == ui::ScrollInputType::kTouchscreen;
+}
+
+void ThreadedInputHandler::ShowScrollbarsForImplScroll(ElementId element_id) {
+ if (Settings().scrollbar_flash_after_any_scroll_update) {
+ host_impl_.FlashAllScrollbars(true);
+ return;
+ }
+ if (!element_id)
+ return;
+ if (ScrollbarAnimationController* animation_controller =
+ host_impl_.ScrollbarAnimationControllerForElementId(element_id))
+ animation_controller->DidScrollUpdate();
+}
+
+ScrollNode* ThreadedInputHandler::GetNodeToScroll(ScrollNode* node) const {
+ // Blink has a notion of a "root scroller", which is the scroller in a page
+ // that is considered to host the main content. Typically this will be the
+ // document/LayoutView contents; however, in some situations Blink may choose
+ // a sub-scroller (div, iframe) that should scroll with "viewport" behavior.
+ // The "root scroller" is the node designated as the outer viewport in CC.
+ // See third_party/blink/renderer/core/page/scrolling/README.md for details.
+ //
+ // "Viewport" scrolling ensures generation of overscroll events, top controls
+ // movement, as well as correct multi-viewport panning in pinch-zoom and
+ // other scenarios. We use the viewport's outer scroll node to represent the
+ // viewport in the scroll chain and apply scroll delta using CC's Viewport
+ // class.
+ //
+ // Scrolling from position: fixed layers will chain directly up to the inner
+ // viewport. Whether that should use the outer viewport (and thus the
+ // Viewport class) to scroll or not depends on the root scroller scenario
+ // because we don't want setting a root scroller to change the scroll chain
+ // order. The |prevent_viewport_scrolling_from_inner| bit is used to
+ // communicate that context.
+ DCHECK(!node->prevent_viewport_scrolling_from_inner ||
+ node->scrolls_inner_viewport);
+
+ if (node->scrolls_inner_viewport &&
+ !node->prevent_viewport_scrolling_from_inner) {
+ DCHECK(OuterViewportScrollNode());
+ return OuterViewportScrollNode();
+ }
+
+ return node;
+}
+
+bool ThreadedInputHandler::IsInitialScrollHitTestReliable(
+ const LayerImpl* layer_impl,
+ const LayerImpl* first_scrolling_layer_or_scrollbar) const {
+ if (!first_scrolling_layer_or_scrollbar)
+ return true;
+
+ // Hit tests directly on a composited scrollbar are always reliable.
+ if (layer_impl->IsScrollbarLayer()) {
+ DCHECK(layer_impl == first_scrolling_layer_or_scrollbar);
+ return true;
+ }
+
+ ScrollNode* closest_scroll_node = nullptr;
+ auto& scroll_tree = GetScrollTree();
+ ScrollNode* scroll_node = scroll_tree.Node(layer_impl->scroll_tree_index());
+ for (; scroll_tree.parent(scroll_node);
+ scroll_node = scroll_tree.parent(scroll_node)) {
+ // TODO(bokan): |scrollable| appears to always be true in LayerList mode.
+ // In non-LayerList, scroll hit tests should always be reliable because we
+ // don't have situations where a layer can be hit testable but pass some
+ // points through (e.g. squashing layers). Perhaps we can remove this
+ // condition?
+ if (scroll_node->scrollable) {
+ closest_scroll_node = GetNodeToScroll(scroll_node);
+ break;
+ }
+ }
+ if (!closest_scroll_node)
+ return false;
+
+ // If |first_scrolling_layer_or_scrollbar| is not a scrollbar, it must be
+ // a scrollabe layer with a scroll node. If this scroll node corresponds to
+ // first scrollable ancestor along the scroll tree for |layer_impl|, the hit
+ // test has not escaped to other areas of the scroll tree and is reliable.
+ if (!first_scrolling_layer_or_scrollbar->IsScrollbarLayer()) {
+ return closest_scroll_node->id ==
+ first_scrolling_layer_or_scrollbar->scroll_tree_index();
+ }
+
+ return false;
+}
+
+gfx::Vector2dF ThreadedInputHandler::ComputeScrollDelta(
+ const ScrollNode& scroll_node,
+ const gfx::Vector2dF& delta) {
+ ScrollTree& scroll_tree = GetScrollTree();
+ float scale_factor = ActiveTree().page_scale_factor_for_scroll();
+
+ gfx::Vector2dF adjusted_scroll(delta);
+ adjusted_scroll.Scale(1.f / scale_factor);
+ adjusted_scroll = UserScrollableDelta(scroll_node, adjusted_scroll);
+
+ gfx::ScrollOffset old_offset =
+ scroll_tree.current_scroll_offset(scroll_node.element_id);
+ gfx::ScrollOffset new_offset = scroll_tree.ClampScrollOffsetToLimits(
+ old_offset + gfx::ScrollOffset(adjusted_scroll), scroll_node);
+
+ gfx::ScrollOffset scrolled = new_offset - old_offset;
+ return gfx::Vector2dF(scrolled.x(), scrolled.y());
+}
+
+bool ThreadedInputHandler::CalculateLocalScrollDeltaAndStartPoint(
+ const ScrollNode& scroll_node,
+ const gfx::PointF& viewport_point,
+ const gfx::Vector2dF& viewport_delta,
+ gfx::Vector2dF* out_local_scroll_delta,
+ gfx::PointF* out_local_start_point /*= nullptr*/) {
+ // Layers with non-invertible screen space transforms should not have passed
+ // the scroll hit test in the first place.
+ const gfx::Transform screen_space_transform =
+ GetScrollTree().ScreenSpaceTransform(scroll_node.id);
+ DCHECK(screen_space_transform.IsInvertible());
+ gfx::Transform inverse_screen_space_transform(
+ gfx::Transform::kSkipInitialization);
+ bool did_invert =
+ screen_space_transform.GetInverse(&inverse_screen_space_transform);
+ // TODO(shawnsingh): With the advent of impl-side scrolling for non-root
+ // layers, we may need to explicitly handle uninvertible transforms here.
+ DCHECK(did_invert);
+
+ float scale_from_viewport_to_screen_space = host_impl_.DeviceScaleFactor();
+ gfx::PointF screen_space_point =
+ gfx::ScalePoint(viewport_point, scale_from_viewport_to_screen_space);
+
+ gfx::Vector2dF screen_space_delta = viewport_delta;
+ screen_space_delta.Scale(scale_from_viewport_to_screen_space);
+
+ // Project the scroll start and end points to local layer space to find the
+ // scroll delta in layer coordinates.
+ bool start_clipped, end_clipped;
+ gfx::PointF screen_space_end_point = screen_space_point + screen_space_delta;
+ gfx::PointF local_start_point = MathUtil::ProjectPoint(
+ inverse_screen_space_transform, screen_space_point, &start_clipped);
+ gfx::PointF local_end_point = MathUtil::ProjectPoint(
+ inverse_screen_space_transform, screen_space_end_point, &end_clipped);
+ DCHECK(out_local_scroll_delta);
+ *out_local_scroll_delta = local_end_point - local_start_point;
+
+ if (out_local_start_point)
+ *out_local_start_point = local_start_point;
+
+ if (start_clipped || end_clipped)
+ return false;
+
+ return true;
+}
+
+gfx::Vector2dF ThreadedInputHandler::ScrollNodeWithViewportSpaceDelta(
+ const ScrollNode& scroll_node,
+ const gfx::PointF& viewport_point,
+ const gfx::Vector2dF& viewport_delta) {
+ ScrollTree& scroll_tree = GetScrollTree();
+ gfx::PointF local_start_point;
+ gfx::Vector2dF local_scroll_delta;
+ if (!CalculateLocalScrollDeltaAndStartPoint(
+ scroll_node, viewport_point, viewport_delta, &local_scroll_delta,
+ &local_start_point)) {
+ return gfx::Vector2dF();
+ }
+
+ bool scrolls_outer_viewport = scroll_node.scrolls_outer_viewport;
+ TRACE_EVENT2("cc", "ScrollNodeWithViewportSpaceDelta", "delta_y",
+ local_scroll_delta.y(), "is_outer", scrolls_outer_viewport);
+
+ // Apply the scroll delta.
+ gfx::ScrollOffset previous_offset =
+ scroll_tree.current_scroll_offset(scroll_node.element_id);
+ scroll_tree.ScrollBy(scroll_node, local_scroll_delta, &ActiveTree());
+ gfx::ScrollOffset scrolled =
+ scroll_tree.current_scroll_offset(scroll_node.element_id) -
+ previous_offset;
+
+ TRACE_EVENT_INSTANT1("cc", "ConsumedDelta", TRACE_EVENT_SCOPE_THREAD, "y",
+ scrolled.y());
+
+ // Get the end point in the layer's content space so we can apply its
+ // ScreenSpaceTransform.
+ gfx::PointF actual_local_end_point =
+ local_start_point + gfx::Vector2dF(scrolled.x(), scrolled.y());
+
+ // Calculate the applied scroll delta in viewport space coordinates.
+ bool end_clipped;
+ const gfx::Transform screen_space_transform =
+ scroll_tree.ScreenSpaceTransform(scroll_node.id);
+ gfx::PointF actual_screen_space_end_point = MathUtil::MapPoint(
+ screen_space_transform, actual_local_end_point, &end_clipped);
+ DCHECK(!end_clipped);
+ if (end_clipped)
+ return gfx::Vector2dF();
+
+ float scale_from_viewport_to_screen_space = host_impl_.DeviceScaleFactor();
+ gfx::PointF actual_viewport_end_point = gfx::ScalePoint(
+ actual_screen_space_end_point, 1.f / scale_from_viewport_to_screen_space);
+ return actual_viewport_end_point - viewport_point;
+}
+
+gfx::Vector2dF ThreadedInputHandler::ScrollNodeWithLocalDelta(
+ const ScrollNode& scroll_node,
+ const gfx::Vector2dF& local_delta) const {
+ bool scrolls_outer_viewport = scroll_node.scrolls_outer_viewport;
+ TRACE_EVENT2("cc", "ScrollNodeWithLocalDelta", "delta_y", local_delta.y(),
+ "is_outer", scrolls_outer_viewport);
+ float page_scale_factor = ActiveTree().page_scale_factor_for_scroll();
+
+ ScrollTree& scroll_tree = GetScrollTree();
+ gfx::ScrollOffset previous_offset =
+ scroll_tree.current_scroll_offset(scroll_node.element_id);
+ gfx::Vector2dF delta = local_delta;
+ delta.Scale(1.f / page_scale_factor);
+ scroll_tree.ScrollBy(scroll_node, delta, &ActiveTree());
+ gfx::ScrollOffset scrolled =
+ scroll_tree.current_scroll_offset(scroll_node.element_id) -
+ previous_offset;
+ gfx::Vector2dF consumed_scroll(scrolled.x(), scrolled.y());
+ consumed_scroll.Scale(page_scale_factor);
+ TRACE_EVENT_INSTANT1("cc", "ConsumedDelta", TRACE_EVENT_SCOPE_THREAD, "y",
+ consumed_scroll.y());
+
+ return consumed_scroll;
+}
+
+// TODO(danakj): Make this into two functions, one with delta, one with
+// viewport_point, no bool required.
+gfx::Vector2dF ThreadedInputHandler::ScrollSingleNode(
+ const ScrollNode& scroll_node,
+ const gfx::Vector2dF& delta,
+ const gfx::Point& viewport_point,
+ bool is_direct_manipulation) {
+ gfx::Vector2dF adjusted_delta = UserScrollableDelta(scroll_node, delta);
+
+ // Events representing direct manipulation of the screen (such as gesture
+ // events) need to be transformed from viewport coordinates to local layer
+ // coordinates so that the scrolling contents exactly follow the user's
+ // finger. In contrast, events not representing direct manipulation of the
+ // screen (such as wheel events) represent a fixed amount of scrolling so we
+ // can just apply them directly, but the page scale factor is applied to the
+ // scroll delta.
+ if (is_direct_manipulation) {
+ // For touch-scroll we need to scale the delta here, as the transform tree
+ // won't know anything about the external page scale factors used by OOPIFs.
+ gfx::Vector2dF scaled_delta(adjusted_delta);
+ scaled_delta.Scale(1 / ActiveTree().external_page_scale_factor());
+ return ScrollNodeWithViewportSpaceDelta(
+ scroll_node, gfx::PointF(viewport_point), scaled_delta);
+ }
+ return ScrollNodeWithLocalDelta(scroll_node, adjusted_delta);
+}
+
+void ThreadedInputHandler::ScrollLatchedScroller(ScrollState* scroll_state,
+ base::TimeDelta delayed_by) {
+ DCHECK(CurrentlyScrollingNode());
+ DCHECK(scroll_state);
+ DCHECK(latched_scroll_type_.has_value());
+
+ ScrollNode& scroll_node = *CurrentlyScrollingNode();
+ const gfx::Vector2dF delta(scroll_state->delta_x(), scroll_state->delta_y());
+ TRACE_EVENT2("cc", "ThreadedInputHandler::ScrollLatchedScroller", "delta_x",
+ delta.x(), "delta_y", delta.y());
+ gfx::Vector2dF applied_delta;
+ gfx::Vector2dF delta_applied_to_content;
+ // TODO(tdresser): Use a more rational epsilon. See crbug.com/510550 for
+ // details.
+ const float kEpsilon = 0.1f;
+
+ if (ShouldAnimateScroll(*scroll_state)) {
+ DCHECK(!scroll_state->is_in_inertial_phase());
+
+ if (ElementId id =
+ host_impl_.mutator_host()->ImplOnlyScrollAnimatingElement()) {
+ TRACE_EVENT_INSTANT0("cc", "UpdateExistingAnimation",
+ TRACE_EVENT_SCOPE_THREAD);
+
+ ScrollNode* animating_scroll_node =
+ GetScrollTree().FindNodeFromElementId(id);
+ DCHECK(animating_scroll_node);
+
+ // Usually the CurrentlyScrollingNode will be the currently animating
+ // one. The one exception is the inner viewport. Scrolling the combined
+ // viewport will always set the outer viewport as the currently scrolling
+ // node. However, if an animation is created on the inner viewport we
+ // must use it when updating the animation curve.
+ DCHECK(animating_scroll_node->id == scroll_node.id ||
+ animating_scroll_node->scrolls_inner_viewport);
+
+ bool animation_updated = ScrollAnimationUpdateTarget(
+ *animating_scroll_node, delta, delayed_by);
+
+ if (animation_updated) {
+ // Because we updated the animation target, consume delta so we notify
+ // the SwapPromiseMonitor to tell it that something happened that will
+ // cause a swap in the future. This will happen within the scope of
+ // the dispatch of a gesture scroll update input event. If we don't
+ // notify during the handling of the input event, the LatencyInfo
+ // associated with the input event will not be added as a swap promise
+ // and we won't get any swap results.
+ applied_delta = delta;
+ } else {
+ TRACE_EVENT_INSTANT0("cc", "Didn't Update Animation",
+ TRACE_EVENT_SCOPE_THREAD);
+ }
+ } else {
+ TRACE_EVENT_INSTANT0("cc", "CreateNewAnimation",
+ TRACE_EVENT_SCOPE_THREAD);
+ if (scroll_node.scrolls_outer_viewport) {
+ applied_delta = GetViewport().ScrollAnimated(delta, delayed_by);
+ } else {
+ applied_delta = ComputeScrollDelta(scroll_node, delta);
+ host_impl_.ScrollAnimationCreate(scroll_node, applied_delta,
+ delayed_by);
+ }
+ }
+
+ // Animated scrolling always applied only to the content (i.e. not to the
+ // browser controls).
+ delta_applied_to_content = delta;
+ } else {
+ gfx::Point viewport_point(scroll_state->position_x(),
+ scroll_state->position_y());
+ if (GetViewport().ShouldScroll(scroll_node)) {
+ // |scrolls_outer_viewport| will only ever be false if the scroll chains
+ // up to the viewport without going through the outer viewport scroll
+ // node. This is because we normally terminate the scroll chain at the
+ // outer viewport node. For example, if we start scrolling from an
+ // element that's not a descendant of the root scroller. In these cases we
+ // want to scroll *only* the inner viewport -- to allow panning while
+ // zoomed -- but still use Viewport::ScrollBy to also move browser
+ // controls if needed.
+ Viewport::ScrollResult result = GetViewport().ScrollBy(
+ delta, viewport_point, scroll_state->is_direct_manipulation(),
+ latched_scroll_type_ != ui::ScrollInputType::kWheel,
+ scroll_node.scrolls_outer_viewport);
+
+ applied_delta = result.consumed_delta;
+ delta_applied_to_content = result.content_scrolled_delta;
+ } else {
+ applied_delta = ScrollSingleNode(scroll_node, delta, viewport_point,
+ scroll_state->is_direct_manipulation());
+ }
+ }
+
+ // If the layer wasn't able to move, try the next one in the hierarchy.
+ bool scrolled = std::abs(applied_delta.x()) > kEpsilon;
+ scrolled = scrolled || std::abs(applied_delta.y()) > kEpsilon;
+ if (!scrolled) {
+ // TODO(bokan): This preserves existing behavior by not allowing tiny
+ // scrolls to produce overscroll but is inconsistent in how delta gets
+ // chained up. We need to clean this up.
+ if (scroll_node.scrolls_outer_viewport)
+ scroll_state->ConsumeDelta(applied_delta.x(), applied_delta.y());
+ return;
+ }
+
+ if (!GetViewport().ShouldScroll(scroll_node)) {
+ // If the applied delta is within 45 degrees of the input
+ // delta, bail out to make it easier to scroll just one layer
+ // in one direction without affecting any of its parents.
+ float angle_threshold = 45;
+ if (MathUtil::SmallestAngleBetweenVectors(applied_delta, delta) <
+ angle_threshold) {
+ applied_delta = delta;
+ } else {
+ // Allow further movement only on an axis perpendicular to the direction
+ // in which the layer moved.
+ applied_delta = MathUtil::ProjectVector(delta, applied_delta);
+ }
+ delta_applied_to_content = applied_delta;
+ }
+
+ scroll_state->set_caused_scroll(
+ std::abs(delta_applied_to_content.x()) > kEpsilon,
+ std::abs(delta_applied_to_content.y()) > kEpsilon);
+ scroll_state->ConsumeDelta(applied_delta.x(), applied_delta.y());
+}
+
+bool ThreadedInputHandler::CanPropagate(ScrollNode* scroll_node,
+ float x,
+ float y) {
+ return (x == 0 || scroll_node->overscroll_behavior.x ==
+ OverscrollBehavior::kOverscrollBehaviorTypeAuto) &&
+ (y == 0 || scroll_node->overscroll_behavior.y ==
+ OverscrollBehavior::kOverscrollBehaviorTypeAuto);
+}
+
+ScrollNode* ThreadedInputHandler::FindNodeToLatch(ScrollState* scroll_state,
+ ScrollNode* starting_node,
+ ui::ScrollInputType type) {
+ ScrollTree& scroll_tree = GetScrollTree();
+ ScrollNode* scroll_node = nullptr;
+ for (ScrollNode* cur_node = starting_node; cur_node;
+ cur_node = scroll_tree.parent(cur_node)) {
+ if (GetViewport().ShouldScroll(*cur_node)) {
+ // Don't chain scrolls past a viewport node. Once we reach that, we
+ // should scroll using the appropriate viewport node which may not be
+ // |cur_node|.
+ scroll_node = GetNodeToScroll(cur_node);
+ break;
+ }
+
+ if (!cur_node->scrollable)
+ continue;
+
+ // For UX reasons, autoscrolling should always latch to the top-most
+ // scroller, even if it can't scroll in the initial direction.
+ if (type == ui::ScrollInputType::kAutoscroll ||
+ CanConsumeDelta(*scroll_state, *cur_node)) {
+ scroll_node = cur_node;
+ break;
+ }
+
+ float delta_x = scroll_state->is_beginning() ? scroll_state->delta_x_hint()
+ : scroll_state->delta_x();
+ float delta_y = scroll_state->is_beginning() ? scroll_state->delta_y_hint()
+ : scroll_state->delta_y();
+
+ if (!CanPropagate(cur_node, delta_x, delta_y)) {
+ // If we reach a node with non-auto overscroll-behavior and we still
+ // haven't latched, we must latch to it. Consider a fully scrolled node
+ // with non-auto overscroll-behavior: we are not allowed to further
+ // chain scroll delta passed to it in the current direction but if we
+ // reverse direction we should scroll it so we must be latched to it.
+ scroll_node = cur_node;
+ scroll_state->set_is_scroll_chain_cut(true);
+ break;
+ }
+ }
+
+ return scroll_node;
+}
+
+void ThreadedInputHandler::UpdateRootLayerStateForSynchronousInputHandler() {
+ if (!input_handler_client_)
+ return;
+ input_handler_client_->UpdateRootLayerStateForSynchronousInputHandler(
+ ActiveTree().TotalScrollOffset(), ActiveTree().TotalMaxScrollOffset(),
+ ActiveTree().ScrollableSize(), ActiveTree().current_page_scale_factor(),
+ ActiveTree().min_page_scale_factor(),
+ ActiveTree().max_page_scale_factor());
+}
+
+void ThreadedInputHandler::DidLatchToScroller(const ScrollState& scroll_state,
+ ui::ScrollInputType type) {
+ DCHECK(CurrentlyScrollingNode());
+ deferred_scroll_end_ = false;
+ host_impl_.browser_controls_manager()->ScrollBegin();
+ host_impl_.mutator_host()->ScrollAnimationAbort();
+
+ scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
+ last_latched_scroller_ = CurrentlyScrollingNode()->element_id;
+ latched_scroll_type_ = type;
+ last_scroll_begin_state_ = scroll_state;
+
+ host_impl_.DidStartScroll();
+ RecordCompositorSlowScrollMetric(type, CC_THREAD);
+
+ UpdateScrollSourceInfo(scroll_state, type);
+}
+
+bool ThreadedInputHandler::CanConsumeDelta(const ScrollState& scroll_state,
+ const ScrollNode& scroll_node) {
+ gfx::Vector2dF delta_to_scroll;
+ if (scroll_state.is_beginning()) {
+ delta_to_scroll = gfx::Vector2dF(scroll_state.delta_x_hint(),
+ scroll_state.delta_y_hint());
+ } else {
+ delta_to_scroll =
+ gfx::Vector2dF(scroll_state.delta_x(), scroll_state.delta_y());
+ }
+
+ if (delta_to_scroll == gfx::Vector2dF())
+ return true;
+
+ if (scroll_state.is_direct_manipulation()) {
+ gfx::Vector2dF local_scroll_delta;
+ if (!CalculateLocalScrollDeltaAndStartPoint(
+ scroll_node,
+ gfx::PointF(scroll_state.position_x(), scroll_state.position_y()),
+ delta_to_scroll, &local_scroll_delta)) {
+ return false;
+ }
+ delta_to_scroll = local_scroll_delta;
+ } else {
+ delta_to_scroll = ResolveScrollGranularityToPixels(
+ scroll_node, delta_to_scroll, scroll_state.delta_granularity());
+ }
+
+ if (ComputeScrollDelta(scroll_node, delta_to_scroll) != gfx::Vector2dF())
+ return true;
+
+ return false;
+}
+
+bool ThreadedInputHandler::ShouldAnimateScroll(
+ const ScrollState& scroll_state) const {
+ if (!Settings().enable_smooth_scroll)
+ return false;
+
+ bool has_precise_scroll_deltas = scroll_state.delta_granularity() ==
+ ui::ScrollGranularity::kScrollByPrecisePixel;
+
+#if defined(OS_MAC)
+ if (has_precise_scroll_deltas)
+ return false;
+
+ // Mac does not smooth scroll wheel events (crbug.com/574283). We allow tests
+ // to force it on.
+ return latched_scroll_type_ == ui::ScrollInputType::kScrollbar
+ ? true
+ : force_smooth_wheel_scrolling_for_testing_;
+#else
+ return !has_precise_scroll_deltas;
+#endif
+}
+
+bool ThreadedInputHandler::SnapAtScrollEnd() {
+ ScrollNode* scroll_node = CurrentlyScrollingNode();
+ if (!scroll_node || !scroll_node->snap_container_data.has_value())
+ return false;
+
+ SnapContainerData& data = scroll_node->snap_container_data.value();
+ gfx::ScrollOffset current_position = GetVisualScrollOffset(*scroll_node);
+
+ // You might think that if a scroll never received a scroll update we could
+ // just drop the snap. However, if the GSB+GSE arrived while we were mid-snap
+ // from a previous gesture, this would leave the scroller at a
+ // non-snap-point.
+ DCHECK(last_scroll_update_state_ || last_scroll_begin_state_);
+ ScrollState& last_scroll_state = last_scroll_update_state_
+ ? *last_scroll_update_state_
+ : *last_scroll_begin_state_;
+
+ bool imprecise_wheel_scrolling =
+ latched_scroll_type_ == ui::ScrollInputType::kWheel &&
+ last_scroll_state.delta_granularity() !=
+ ui::ScrollGranularity::kScrollByPrecisePixel;
+ gfx::ScrollOffset last_scroll_delta = last_scroll_state.DeltaOrHint();
+
+ std::unique_ptr<SnapSelectionStrategy> strategy;
+
+ if (imprecise_wheel_scrolling && !last_scroll_delta.IsZero()) {
+ // This was an imprecise wheel scroll so use direction snapping.
+ strategy = SnapSelectionStrategy::CreateForDirection(
+ current_position, last_scroll_delta, true);
+ } else {
+ strategy = SnapSelectionStrategy::CreateForEndPosition(
+ current_position, did_scroll_x_for_scroll_gesture_,
+ did_scroll_y_for_scroll_gesture_);
+ }
+
+ gfx::ScrollOffset snap_position;
+ TargetSnapAreaElementIds snap_target_ids;
+ if (!data.FindSnapPosition(*strategy, &snap_position, &snap_target_ids))
+ return false;
+
+ if (GetViewport().ShouldScroll(*scroll_node)) {
+ // Flash the overlay scrollbar even if the scroll delta is 0.
+ if (Settings().scrollbar_flash_after_any_scroll_update) {
+ host_impl_.FlashAllScrollbars(false);
+ } else {
+ ScrollbarAnimationController* animation_controller =
+ host_impl_.ScrollbarAnimationControllerForElementId(
+ scroll_node->element_id);
+ if (animation_controller)
+ animation_controller->WillUpdateScroll();
+ }
+ }
+
+ gfx::Vector2dF delta =
+ ScrollOffsetToVector2dF(snap_position - current_position);
+ bool did_animate = false;
+ if (scroll_node->scrolls_outer_viewport) {
+ gfx::Vector2dF scaled_delta(delta);
+ scaled_delta.Scale(ActiveTree().page_scale_factor_for_scroll());
+ gfx::Vector2dF consumed_delta =
+ GetViewport().ScrollAnimated(scaled_delta, base::TimeDelta());
+ did_animate = !consumed_delta.IsZero();
+ } else {
+ did_animate = host_impl_.ScrollAnimationCreate(*scroll_node, delta,
+ base::TimeDelta());
+ }
+ DCHECK(!IsAnimatingForSnap());
+ if (did_animate) {
+ // The snap target will be set when the animation is completed.
+ scroll_animating_snap_target_ids_ = snap_target_ids;
+ } else if (data.SetTargetSnapAreaElementIds(snap_target_ids)) {
+ updated_snapped_elements_.insert(scroll_node->element_id);
+ SetNeedsCommit();
+ }
+ return did_animate;
+}
+
+bool ThreadedInputHandler::IsAnimatingForSnap() const {
+ return scroll_animating_snap_target_ids_ != TargetSnapAreaElementIds();
+}
+
+gfx::ScrollOffset ThreadedInputHandler::GetVisualScrollOffset(
+ const ScrollNode& scroll_node) const {
+ if (scroll_node.scrolls_outer_viewport)
+ return GetViewport().TotalScrollOffset();
+ return GetScrollTree().current_scroll_offset(scroll_node.element_id);
+}
+
+void ThreadedInputHandler::ClearCurrentlyScrollingNode() {
+ TRACE_EVENT0("cc", "ThreadedInputHandler::ClearCurrentlyScrollingNode");
+ ActiveTree().ClearCurrentlyScrollingNode();
+ accumulated_root_overscroll_ = gfx::Vector2dF();
+ did_scroll_x_for_scroll_gesture_ = false;
+ did_scroll_y_for_scroll_gesture_ = false;
+ scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
+ latched_scroll_type_.reset();
+ last_scroll_update_state_.reset();
+ last_scroll_begin_state_.reset();
+}
+
+bool ThreadedInputHandler::ScrollAnimationUpdateTarget(
+ const ScrollNode& scroll_node,
+ const gfx::Vector2dF& scroll_delta,
+ base::TimeDelta delayed_by) {
+ // TODO(bokan): Remove |scroll_node| as a parameter and just use the value
+ // coming from |mutator_host|.
+ DCHECK_EQ(scroll_node.element_id,
+ host_impl_.mutator_host()->ImplOnlyScrollAnimatingElement());
+
+ float scale_factor = ActiveTree().page_scale_factor_for_scroll();
+ gfx::Vector2dF adjusted_delta =
+ gfx::ScaleVector2d(scroll_delta, 1.f / scale_factor);
+ adjusted_delta = UserScrollableDelta(scroll_node, adjusted_delta);
+
+ bool animation_updated =
+ host_impl_.mutator_host()->ImplOnlyScrollAnimationUpdateTarget(
+ adjusted_delta, GetScrollTree().MaxScrollOffset(scroll_node.id),
+ host_impl_.CurrentBeginFrameArgs().frame_time, delayed_by);
+ if (animation_updated) {
+ host_impl_.DidUpdateScrollAnimationCurve();
+
+ // The animation is no longer targeting a snap position. By clearing the
+ // target, this will ensure that we attempt to resnap at the end of this
+ // animation.
+ scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
+ }
+
+ return animation_updated;
+}
+
+void ThreadedInputHandler::UpdateScrollSourceInfo(
+ const ScrollState& scroll_state,
+ ui::ScrollInputType type) {
+ if (type == ui::ScrollInputType::kWheel &&
+ scroll_state.delta_granularity() ==
+ ui::ScrollGranularity::kScrollByPrecisePixel) {
+ has_scrolled_by_precisiontouchpad_ = true;
+ } else if (type == ui::ScrollInputType::kWheel) {
+ has_scrolled_by_wheel_ = true;
+ } else if (type == ui::ScrollInputType::kTouchscreen) {
+ has_scrolled_by_touch_ = true;
+ }
+}
+
+// Return true if scrollable node for 'ancestor' is the same as 'child' or an
+// ancestor along the scroll tree.
+bool ThreadedInputHandler::IsScrolledBy(LayerImpl* child,
+ ScrollNode* ancestor) {
+ DCHECK(ancestor && ancestor->scrollable);
+ if (!child)
+ return false;
+ DCHECK_EQ(child->layer_tree_impl(), &ActiveTree());
+ ScrollTree& scroll_tree = GetScrollTree();
+ for (ScrollNode* scroll_node = scroll_tree.Node(child->scroll_tree_index());
+ scroll_node; scroll_node = scroll_tree.parent(scroll_node)) {
+ if (scroll_node->id == ancestor->id)
+ return true;
+ }
+ return false;
+}
+
+gfx::Vector2dF ThreadedInputHandler::UserScrollableDelta(
+ const ScrollNode& node,
+ const gfx::Vector2dF& delta) const {
+ gfx::Vector2dF adjusted_delta = delta;
+ if (!node.user_scrollable_horizontal)
+ adjusted_delta.set_x(0);
+ if (!node.user_scrollable_vertical)
+ adjusted_delta.set_y(0);
+
+ return adjusted_delta;
+}
+
+} // namespace cc
diff --git a/chromium/cc/input/threaded_input_handler.h b/chromium/cc/input/threaded_input_handler.h
new file mode 100644
index 00000000000..aa42674faad
--- /dev/null
+++ b/chromium/cc/input/threaded_input_handler.h
@@ -0,0 +1,432 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_INPUT_THREADED_INPUT_HANDLER_H_
+#define CC_INPUT_THREADED_INPUT_HANDLER_H_
+
+#include <memory>
+
+#include "base/containers/flat_set.h"
+#include "base/optional.h"
+#include "base/time/time.h"
+#include "cc/input/compositor_input_interfaces.h"
+#include "cc/input/event_listener_properties.h"
+#include "cc/input/input_handler.h"
+#include "cc/input/scroll_snap_data.h"
+#include "cc/input/scroll_state.h"
+#include "cc/input/touch_action.h"
+#include "cc/metrics/events_metrics_manager.h"
+#include "cc/metrics/frame_sequence_metrics.h"
+#include "cc/paint/element_id.h"
+#include "components/viz/common/frame_sinks/begin_frame_args.h"
+#include "ui/events/types/scroll_input_type.h"
+
+namespace gfx {
+class Point;
+class PointF;
+class ScrollOffset;
+} // namespace gfx
+
+namespace cc {
+
+class LayerImpl;
+class LayerTreeHostImpl;
+class LayerTreeSettings;
+class ScrollbarController;
+class ScrollElasticityHelper;
+struct ScrollNode;
+class ScrollTree;
+class SwapPromiseMonitor;
+class Viewport;
+
+class CC_EXPORT ThreadedInputHandler : public InputDelegateForCompositor {
+ public:
+ explicit ThreadedInputHandler(LayerTreeHostImpl* host_impl);
+ ~ThreadedInputHandler();
+
+ // =========== InputHandler "Interface" - will override in a future CL
+ void BindToClient(InputHandlerClient* client);
+ InputHandler::ScrollStatus ScrollBegin(ScrollState* scroll_state,
+ ui::ScrollInputType type);
+ InputHandler::ScrollStatus RootScrollBegin(ScrollState* scroll_state,
+ ui::ScrollInputType type);
+ InputHandlerScrollResult ScrollUpdate(
+ ScrollState* scroll_state,
+ base::TimeDelta delayed_by = base::TimeDelta());
+ void ScrollEnd(bool should_snap = false);
+ void RecordScrollBegin(ui::ScrollInputType input_type,
+ ScrollBeginThreadState scroll_start_state);
+ void RecordScrollEnd(ui::ScrollInputType input_type);
+ InputHandlerPointerResult MouseMoveAt(const gfx::Point& viewport_point);
+ InputHandlerPointerResult MouseDown(const gfx::PointF& viewport_point,
+ bool shift_modifier);
+ InputHandlerPointerResult MouseUp(const gfx::PointF& viewport_point);
+ void MouseLeave();
+ ElementId FindFrameElementIdAtPoint(const gfx::PointF& viewport_point);
+ void RequestUpdateForSynchronousInputHandler();
+ void SetSynchronousInputHandlerRootScrollOffset(
+ const gfx::ScrollOffset& root_content_offset);
+ void PinchGestureBegin();
+ void PinchGestureUpdate(float magnify_delta, const gfx::Point& anchor);
+ void PinchGestureEnd(const gfx::Point& anchor, bool snap_to_min);
+ void SetNeedsAnimateInput();
+ bool IsCurrentlyScrollingViewport() const;
+ EventListenerProperties GetEventListenerProperties(
+ EventListenerClass event_class) const;
+ bool HasBlockingWheelEventHandlerAt(const gfx::Point& viewport_point) const;
+ InputHandler::TouchStartOrMoveEventListenerType
+ EventListenerTypeForTouchStartOrMoveAt(const gfx::Point& viewport_port,
+ TouchAction* out_touch_action);
+ std::unique_ptr<SwapPromiseMonitor> CreateLatencyInfoSwapPromiseMonitor(
+ ui::LatencyInfo* latency);
+ std::unique_ptr<EventsMetricsManager::ScopedMonitor>
+ GetScopedEventMetricsMonitor(std::unique_ptr<EventMetrics> event_metrics);
+ ScrollElasticityHelper* CreateScrollElasticityHelper();
+ bool GetScrollOffsetForLayer(ElementId element_id, gfx::ScrollOffset* offset);
+ bool ScrollLayerTo(ElementId element_id, const gfx::ScrollOffset& offset);
+ bool ScrollingShouldSwitchtoMainThread();
+ bool GetSnapFlingInfoAndSetAnimatingSnapTarget(
+ const gfx::Vector2dF& natural_displacement_in_viewport,
+ gfx::Vector2dF* out_initial_position,
+ gfx::Vector2dF* out_target_position);
+ void ScrollEndForSnapFling(bool did_finish);
+ void NotifyInputEvent();
+
+ // =========== InputDelegateForCompositor Interface - This section implements
+ // the interface that LayerTreeHostImpl uses to communicate with the input
+ // system.
+ void ProcessCommitDeltas(CompositorCommitData* commit_data) override;
+ void TickAnimations(base::TimeTicks monotonic_time) override;
+ void WillShutdown() override;
+ void WillDraw() override;
+ void WillBeginImplFrame(const viz::BeginFrameArgs& args) override;
+ void DidCommit() override;
+ void DidActivatePendingTree() override;
+ void RootLayerStateMayHaveChanged() override;
+ void DidUnregisterScrollbar(ElementId scroll_element_id,
+ ScrollbarOrientation orientation) override;
+ void ScrollOffsetAnimationFinished() override;
+ bool IsCurrentlyScrolling() const override;
+ bool IsActivelyPrecisionScrolling() const override;
+
+ // =========== Public Interface
+
+ bool CanConsumeDelta(const ScrollState& scroll_state,
+ const ScrollNode& scroll_node);
+ // Returns the amount of delta that can be applied to scroll_node, taking
+ // page scale into account.
+ gfx::Vector2dF ComputeScrollDelta(const ScrollNode& scroll_node,
+ const gfx::Vector2dF& delta);
+
+ gfx::Vector2dF ScrollSingleNode(const ScrollNode& scroll_node,
+ const gfx::Vector2dF& delta,
+ const gfx::Point& viewport_point,
+ bool is_direct_manipulation);
+
+ // Resolves a delta in the given granularity for the |scroll_node| into
+ // physical pixels to scroll.
+ gfx::Vector2dF ResolveScrollGranularityToPixels(
+ const ScrollNode& scroll_node,
+ const gfx::Vector2dF& scroll_delta,
+ ui::ScrollGranularity granularity);
+
+ // Used to set the pinch gesture active state when the pinch gesture is
+ // handled on another layer tree. In a page with OOPIFs, only the main
+ // frame's layer tree directly handles pinch events. But layer trees for
+ // sub-frames need to know when pinch gestures are active so they can
+ // throttle the re-rastering. This function allows setting this flag on
+ // OOPIF layer trees using information sent (initially) from the main-frame.
+ void set_external_pinch_gesture_active(bool external_pinch_gesture_active) {
+ external_pinch_gesture_active_ = external_pinch_gesture_active;
+ // Only one of the flags should ever be true at any given time.
+ DCHECK(!pinch_gesture_active_ || !external_pinch_gesture_active_);
+ }
+
+ bool pinch_gesture_active() const {
+ return pinch_gesture_active_ || external_pinch_gesture_active_;
+ }
+
+ void set_force_smooth_wheel_scrolling_for_testing(bool enabled) {
+ force_smooth_wheel_scrolling_for_testing_ = enabled;
+ }
+
+ gfx::Vector2dF accumulated_root_overscroll_for_testing() const {
+ return accumulated_root_overscroll_;
+ }
+
+ bool animating_for_snap_for_testing() const { return IsAnimatingForSnap(); }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest,
+ AnimatedScrollYielding);
+ FRIEND_TEST_ALL_PREFIXES(ScrollUnifiedLayerTreeHostImplTest,
+ AutoscrollOnDeletedScrollbar);
+ FRIEND_TEST_ALL_PREFIXES(LayerTreeHostImplTest, AutoscrollTaskAbort);
+
+ // This method gets the scroll offset for a regular scroller, or the combined
+ // visual and layout offsets of the viewport.
+ gfx::ScrollOffset GetVisualScrollOffset(const ScrollNode& scroll_node) const;
+ bool IsScrolledBy(LayerImpl* child, ScrollNode* ancestor);
+ bool IsAnimatingForSnap() const;
+
+ ScrollNode* CurrentlyScrollingNode();
+ const ScrollNode* CurrentlyScrollingNode() const;
+ void ClearCurrentlyScrollingNode();
+ ScrollTree& GetScrollTree();
+ ScrollTree& GetScrollTree() const;
+ Viewport& GetViewport() const;
+
+ ScrollNode* InnerViewportScrollNode() const;
+ ScrollNode* OuterViewportScrollNode() const;
+
+ void SetNeedsCommit();
+ LayerTreeImpl& ActiveTree();
+ LayerTreeImpl& ActiveTree() const;
+
+ const LayerTreeSettings& Settings() const;
+
+ bool IsMainThreadScrolling(const InputHandler::ScrollStatus& status,
+ const ScrollNode* scroll_node) const;
+
+ bool IsTouchDraggingScrollbar(
+ LayerImpl* first_scrolling_layer_or_drawn_scrollbar,
+ ui::ScrollInputType type);
+
+ void ShowScrollbarsForImplScroll(ElementId element_id);
+
+ void UpdateRootLayerStateForSynchronousInputHandler();
+
+ // Called during ScrollBegin once a scroller was successfully latched to
+ // (i.e. it can and will consume scroll delta on the compositor thread). The
+ // latched scroller is now available in CurrentlyScrollingNode().
+ // TODO(bokan): There's some debate about the name of this method. We should
+ // get consensus on terminology to use and apply it consistently.
+ // https://crrev.com/c/1981336/9/cc/trees/layer_tree_host_impl.cc#4520
+ void DidLatchToScroller(const ScrollState& scroll_state,
+ ui::ScrollInputType type);
+
+ // This function keeps track of sources of scrolls that are handled in the
+ // compositor side. The information gets shared by the main thread as part of
+ // the begin_main_frame_state. Finally Use counters are updated in the main
+ // thread side to keep track of the frequency of scrolling with different
+ // sources per page load. TODO(crbug.com/691886): Use GRC API to plumb the
+ // scroll source info for Use Counters.
+ void UpdateScrollSourceInfo(const ScrollState& scroll_state,
+ ui::ScrollInputType type);
+
+ // Applies the scroll_state to the currently latched scroller. See comment in
+ // InputHandler::ScrollUpdate declaration for the meaning of |delayed_by|.
+ void ScrollLatchedScroller(ScrollState* scroll_state,
+ base::TimeDelta delayed_by);
+
+ // Determines whether the given scroll node can scroll on the compositor
+ // thread or if there are any reasons it must be scrolled on the main thread
+ // or not at all. Note: in general, this is not sufficient to determine if a
+ // scroll can occur on the compositor thread. If hit testing to a scroll
+ // node, the caller must also check whether the hit point intersects a
+ // non-fast-scrolling-region of any ancestor scrolling layers. Can be removed
+ // after scroll unification https://crbug.com/476553.
+ InputHandler::ScrollStatus TryScroll(const ScrollTree& scroll_tree,
+ ScrollNode* scroll_node) const;
+
+ // Creates an animation curve and returns true if we need to update the
+ // scroll position to a snap point. Otherwise returns false.
+ bool SnapAtScrollEnd();
+
+ // |layer| is returned from a regular hit test, and
+ // |first_scrolling_layer_or_drawn_scrollbar| is returned from a hit test
+ // performed only on scrollers and scrollbars. Initial scroll hit testing can
+ // be unreliable if the latter is not the direct scroll ancestor of the
+ // former. In this case, we will fall back to main thread scrolling because
+ // the compositor thread doesn't know which layer to scroll. This happens when
+ // a layer covers a scroller that doesn't scroll the former, or a scroller is
+ // masked by a mask layer for mask image, clip-path, rounded border, etc.
+ //
+ // Note, position: fixed layers use the inner viewport as their ScrollNode
+ // (since they don't scroll with the outer viewport), however, scrolls from
+ // the fixed layer still chain to the outer viewport. It's also possible for a
+ // node to have the inner viewport as its ancestor without going through the
+ // outer viewport; however, it may still scroll using the viewport(). Hence,
+ // this method must use the same scroll chaining logic we use in ApplyScroll.
+ bool IsInitialScrollHitTestReliable(
+ const LayerImpl* layer,
+ const LayerImpl* first_scrolling_layer_or_drawn_scrollbar) const;
+
+ // Similar to above but includes complicated logic to determine whether the
+ // ScrollNode is able to be scrolled on the compositor or requires main
+ // thread scrolling. If main thread scrolling is required
+ // |scroll_on_main_thread| is set to true and the reason is given in
+ // |main_thread_scrolling_reason| to on of the enum values in
+ // main_thread_scrolling_reason.h. Can be removed after scroll unification
+ // https://crbug.com/476553.
+ ScrollNode* FindScrollNodeForCompositedScrolling(
+ const gfx::PointF& device_viewport_point,
+ LayerImpl* layer_hit_by_point,
+ bool* scroll_on_main_thread,
+ uint32_t* main_thread_scrolling_reason);
+
+ // Return all ScrollNode indices that have an associated layer with a non-fast
+ // region that intersects the point.
+ base::flat_set<int> NonFastScrollableNodes(
+ const gfx::PointF& device_viewport_point) const;
+
+ // Returns the ScrollNode we should use to scroll, accounting for viewport
+ // scroll chaining rules.
+ ScrollNode* GetNodeToScroll(ScrollNode* node) const;
+
+ // Given a starting node (determined by hit-test), walks up the scroll tree
+ // looking for the first node that can consume scroll from the given
+ // scroll_state and returns the first such node. If none is found, or if
+ // starting_node is nullptr, returns nullptr;
+ ScrollNode* FindNodeToLatch(ScrollState* scroll_state,
+ ScrollNode* starting_node,
+ ui::ScrollInputType type);
+
+ bool CanPropagate(ScrollNode* scroll_node, float x, float y);
+
+ // Performs a hit test to determine the ScrollNode to use when scrolling at
+ // |viewport_point|. If no layer is hit, this falls back to the inner
+ // viewport scroll node. Returns:
+ // - If |hit_test_sucessful| is false, hit testing has failed and the
+ // compositor cannot determine the correct scroll node (e.g. see comments in
+ // IsInitialScrollHitTestReliable). |scroll_node| is always nullptr in this
+ // case.
+ // - If |hit_test_successful| is true, returns the ScrollNode to use in
+ // |scroll_node|. This can be nullptr if no layer was hit and there are no
+ // viewport nodes (e.g. OOPIF, UI compositor).
+ struct ScrollHitTestResult {
+ ScrollNode* scroll_node;
+ bool hit_test_successful;
+ };
+ ScrollHitTestResult HitTestScrollNode(
+ const gfx::PointF& device_viewport_point) const;
+
+ bool ShouldAnimateScroll(const ScrollState& scroll_state) const;
+
+ bool ScrollAnimationUpdateTarget(const ScrollNode& scroll_node,
+ const gfx::Vector2dF& scroll_delta,
+ base::TimeDelta delayed_by);
+
+ // Transforms viewport start point and scroll delta to local start point and
+ // local delta, respectively. If the transformation of either the start or end
+ // point of a scroll is clipped, the function returns false.
+ bool CalculateLocalScrollDeltaAndStartPoint(
+ const ScrollNode& scroll_node,
+ const gfx::PointF& viewport_point,
+ const gfx::Vector2dF& viewport_delta,
+ gfx::Vector2dF* out_local_scroll_delta,
+ gfx::PointF* out_local_start_point = nullptr);
+ gfx::Vector2dF ScrollNodeWithViewportSpaceDelta(
+ const ScrollNode& scroll_node,
+ const gfx::PointF& viewport_point,
+ const gfx::Vector2dF& viewport_delta);
+ gfx::Vector2dF ScrollNodeWithLocalDelta(
+ const ScrollNode& scroll_node,
+ const gfx::Vector2dF& local_delta) const;
+ // This helper returns an adjusted version of |delta| where the scroll delta
+ // is cleared in any axis in which user scrolling is disabled (e.g. by
+ // |overflow-x: hidden|).
+ gfx::Vector2dF UserScrollableDelta(const ScrollNode& node,
+ const gfx::Vector2dF& delta) const;
+
+ FrameSequenceTrackerType GetTrackerTypeForScroll(
+ ui::ScrollInputType input_type) const;
+
+ ScrollbarController* scrollbar_controller_for_testing() const {
+ return scrollbar_controller_.get();
+ }
+
+ LayerTreeHostImpl& host_impl_;
+
+ InputHandlerClient* input_handler_client_ = nullptr;
+
+ // An object to implement the ScrollElasticityHelper interface and
+ // hold all state related to elasticity. May be nullptr if never requested.
+ std::unique_ptr<ScrollElasticityHelper> scroll_elasticity_helper_;
+
+ // Manages composited scrollbar hit testing.
+ std::unique_ptr<ScrollbarController> scrollbar_controller_;
+
+ // Overscroll delta accumulated on the viewport throughout a scroll gesture;
+ // reset when the gesture ends.
+ gfx::Vector2dF accumulated_root_overscroll_;
+
+ // Unconsumed scroll delta sent to the main thread for firing overscroll DOM
+ // events. Resets after each commit.
+ gfx::Vector2dF overscroll_delta_for_main_thread_;
+
+ // The source device type that started the scroll gesture. Only set between a
+ // ScrollBegin and ScrollEnd.
+ base::Optional<ui::ScrollInputType> latched_scroll_type_;
+
+ // Tracks the last scroll update/begin state received. Used to infer the most
+ // recent scroll type and direction.
+ base::Optional<ScrollState> last_scroll_begin_state_;
+ base::Optional<ScrollState> last_scroll_update_state_;
+
+ // If a scroll snap is being animated, then the value of this will be the
+ // element id(s) of the target(s). Otherwise, the ids will be invalid.
+ // At the end of a scroll animation, the target should be set as the scroll
+ // node's snap target.
+ TargetSnapAreaElementIds scroll_animating_snap_target_ids_;
+
+ // A set of elements that scroll-snapped to a new target since the last
+ // begin main frame. The snap target ids of these elements will be sent to
+ // the main thread in the next begin main frame.
+ base::flat_set<ElementId> updated_snapped_elements_;
+
+ ElementId scroll_element_id_mouse_currently_over_;
+ ElementId scroll_element_id_mouse_currently_captured_;
+
+ // Set in ScrollBegin and outlives the currently scrolling node so it can be
+ // used to send the scrollend and overscroll DOM events from the main thread
+ // when scrolling occurs on the compositor thread. This value is cleared at
+ // the first commit after a GSE.
+ ElementId last_latched_scroller_;
+
+ // Scroll animation can finish either before or after GSE arrival.
+ // deferred_scroll_end_ is set when the GSE has arrvied before scroll
+ // animation completion. ScrollEnd will get called once the animation is
+ // over.
+ bool deferred_scroll_end_ = false;
+
+ // Set to true when a scroll gesture being handled on the compositor has
+ // ended. i.e. When a GSE has arrived and any ongoing scroll animation has
+ // ended.
+ bool scroll_gesture_did_end_ = false;
+
+ // True iff some of the delta has been consumed for the current scroll
+ // sequence on the specific axis.
+ bool did_scroll_x_for_scroll_gesture_ = false;
+ bool did_scroll_y_for_scroll_gesture_ = false;
+
+ // TODO(bokan): Mac doesn't yet have smooth scrolling for wheel; however, to
+ // allow consistency in tests we use this bit to override that decision.
+ // https://crbug.com/574283.
+ bool force_smooth_wheel_scrolling_for_testing_ = false;
+
+ // This value is used to allow the compositor to throttle re-rastering during
+ // pinch gestures, when the page scale factor may be changing frequently. It
+ // is set in one of two ways:
+ // i) In a layer tree serving the root of the frame/compositor tree, it is
+ // directly set during processing of GesturePinch events on the impl thread
+ // (only the root layer tree has access to these).
+ // ii) In a layer tree serving a sub-frame in the frame/compositor tree, it
+ // is set from the main thread during the commit process, using information
+ // sent from the root layer tree via IPC messaging.
+ bool pinch_gesture_active_ = false;
+ bool external_pinch_gesture_active_ = false;
+ bool pinch_gesture_end_should_clear_scrolling_node_ = false;
+
+ // These are used to transfer usage of different types of scrolling to the
+ // main thread.
+ bool has_pinch_zoomed_ = false;
+ bool has_scrolled_by_wheel_ = false;
+ bool has_scrolled_by_touch_ = false;
+ bool has_scrolled_by_precisiontouchpad_ = false;
+};
+
+} // namespace cc
+
+#endif // CC_INPUT_THREADED_INPUT_HANDLER_H_
diff --git a/chromium/cc/ipc/cc_param_traits_macros.h b/chromium/cc/ipc/cc_param_traits_macros.h
index 4b1d723b862..8e3b94983a1 100644
--- a/chromium/cc/ipc/cc_param_traits_macros.h
+++ b/chromium/cc/ipc/cc_param_traits_macros.h
@@ -8,6 +8,7 @@
#include "base/component_export.h"
#include "cc/input/overscroll_behavior.h"
#include "cc/input/touch_action.h"
+#include "cc/trees/browser_controls_params.h"
#include "ipc/ipc_message_macros.h"
#undef IPC_MESSAGE_EXPORT
@@ -24,4 +25,14 @@ IPC_STRUCT_TRAITS_END()
IPC_ENUM_TRAITS_MAX_VALUE(cc::TouchAction, cc::TouchAction::kMax)
+IPC_STRUCT_TRAITS_BEGIN(cc::BrowserControlsParams)
+ IPC_STRUCT_TRAITS_MEMBER(top_controls_height)
+ IPC_STRUCT_TRAITS_MEMBER(top_controls_min_height)
+ IPC_STRUCT_TRAITS_MEMBER(bottom_controls_height)
+ IPC_STRUCT_TRAITS_MEMBER(bottom_controls_min_height)
+ IPC_STRUCT_TRAITS_MEMBER(animate_browser_controls_height_changes)
+ IPC_STRUCT_TRAITS_MEMBER(browser_controls_shrink_blink_size)
+ IPC_STRUCT_TRAITS_MEMBER(only_expand_top_controls_at_page_top)
+IPC_STRUCT_TRAITS_END()
+
#endif // CC_IPC_CC_PARAM_TRAITS_MACROS_H_
diff --git a/chromium/cc/layers/heads_up_display_layer.cc b/chromium/cc/layers/heads_up_display_layer.cc
index 42755c07eea..0c56668a44f 100644
--- a/chromium/cc/layers/heads_up_display_layer.cc
+++ b/chromium/cc/layers/heads_up_display_layer.cc
@@ -17,7 +17,7 @@ scoped_refptr<HeadsUpDisplayLayer> HeadsUpDisplayLayer::Create() {
}
HeadsUpDisplayLayer::HeadsUpDisplayLayer()
- : typeface_(SkTypeface::MakeFromName("times new roman", SkFontStyle())) {
+ : typeface_(SkTypeface::MakeFromName("Arial", SkFontStyle())) {
if (!typeface_) {
typeface_ = SkTypeface::MakeFromName("monospace", SkFontStyle::Bold());
}
@@ -41,7 +41,7 @@ void HeadsUpDisplayLayer::UpdateLocationAndSize(
bounds = device_viewport_in_layout_pixels;
} else {
// If the HUD is not displaying full-viewport rects (e.g., it is showing the
- // FPS meter), use a fixed size.
+ // Frame Rendering Stats), use a fixed size.
constexpr int kDefaultHUDSize = 256;
bounds.SetSize(kDefaultHUDSize, kDefaultHUDSize);
}
diff --git a/chromium/cc/layers/heads_up_display_layer_impl.cc b/chromium/cc/layers/heads_up_display_layer_impl.cc
index b539a5da7c7..49d9f67229d 100644
--- a/chromium/cc/layers/heads_up_display_layer_impl.cc
+++ b/chromium/cc/layers/heads_up_display_layer_impl.cc
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <algorithm>
+#include <utility>
#include <vector>
#include "base/logging.h"
@@ -52,6 +53,7 @@
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkTypeface.h"
+#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/size_conversions.h"
@@ -270,9 +272,10 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture(
}
if (backing->overlay_candidate)
flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
- backing->mailbox =
- sii->CreateSharedImage(pool_resource.format(), pool_resource.size(),
- pool_resource.color_space(), flags);
+ backing->mailbox = sii->CreateSharedImage(
+ pool_resource.format(), pool_resource.size(),
+ pool_resource.color_space(), kTopLeft_GrSurfaceOrigin,
+ kPremul_SkAlphaType, flags, gpu::kNullSurfaceHandle);
if (raster_context_provider) {
auto* ri = raster_context_provider->RasterInterface();
ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
@@ -664,9 +667,9 @@ SkRect HeadsUpDisplayLayerImpl::DrawFrameThroughputDisplay(
SkRect::MakeXYWH(left + kPadding, text_bounds.bottom() + 2 * kPadding,
kGraphWidth, kGraphHeight);
- // Draw the fps meter.
+ // Draw the frame rendering stats.
const std::string title("Frames");
- const std::string value_text = base::StringPrintf("%d %%", throughput_value_);
+ 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(),
@@ -732,7 +735,7 @@ SkRect HeadsUpDisplayLayerImpl::DrawMemoryDisplay(PaintCanvas* canvas,
const int left = 0;
const SkRect area = SkRect::MakeXYWH(left, top, width, height);
- const double kMegabyte = 1024.0 * 1024.0;
+ const double kMegabyte = 1000.0 * 1000.0;
PaintFlags flags;
DrawGraphBackground(canvas, &flags, area);
@@ -745,7 +748,7 @@ SkRect HeadsUpDisplayLayerImpl::DrawMemoryDisplay(PaintCanvas* canvas,
top + 2 * kPadding + 3 * kFontHeight);
flags.setColor(DebugColors::HUDTitleColor());
- DrawText(canvas, flags, "GPU Memory", TextAlign::kLeft, kTitleFontHeight,
+ DrawText(canvas, flags, "GPU memory", TextAlign::kLeft, kTitleFontHeight,
title_pos);
flags.setColor(DebugColors::MemoryDisplayTextColor());
@@ -774,7 +777,7 @@ SkRect HeadsUpDisplayLayerImpl::DrawMemoryDisplay(PaintCanvas* canvas,
int radius = length / 2;
int cx = oval.left() + radius;
int cy = oval.top() + radius;
- double angle = ((double)memory_entry_.total_bytes_used /
+ double angle = (static_cast<double>(memory_entry_.total_bytes_used) /
memory_entry_.total_budget_in_bytes) *
180;
@@ -839,7 +842,7 @@ SkRect HeadsUpDisplayLayerImpl::DrawGpuRasterizationStatus(PaintCanvas* canvas,
SkPoint gpu_status_pos = SkPoint::Make(left + width - kPadding,
top + 2 * kFontHeight + 2 * kPadding);
flags.setColor(DebugColors::HUDTitleColor());
- DrawText(canvas, flags, "GPU Raster", TextAlign::kLeft, kTitleFontHeight,
+ DrawText(canvas, flags, "GPU raster", TextAlign::kLeft, kTitleFontHeight,
left + kPadding, top + kFontHeight + kPadding);
flags.setColor(color);
DrawText(canvas, flags, status, TextAlign::kRight, kFontHeight,
diff --git a/chromium/cc/layers/layer.cc b/chromium/cc/layers/layer.cc
index 7b74ae2da3c..cfff4d33565 100644
--- a/chromium/cc/layers/layer.cc
+++ b/chromium/cc/layers/layer.cc
@@ -52,7 +52,6 @@ struct SameSizeAsLayer : public base::RefCounted<SameSizeAsLayer> {
Region non_fast_scrollable_region;
TouchActionRegion touch_action_region;
ElementId element_id;
- ElementId frame_element_id;
} inputs;
void* layer_tree_inputs;
int int_fields[6];
@@ -962,7 +961,6 @@ void Layer::SetScrollOffsetFromImplSide(
if (inputs.scroll_offset == scroll_offset)
return;
inputs.scroll_offset = scroll_offset;
- SetNeedsPushProperties();
UpdatePropertyTreeScrollOffset();
@@ -1356,7 +1354,6 @@ void Layer::PushPropertiesTo(LayerImpl* layer) {
layer->UnionUpdateRect(inputs_.update_rect);
layer->SetHasWillChangeTransformHint(has_will_change_transform_hint());
- layer->SetFrameElementId(inputs_.frame_element_id);
layer->SetNeedsPushProperties();
// debug_info_->invalidations, if exist, will be cleared in the function.
@@ -1466,13 +1463,6 @@ void Layer::SetHasWillChangeTransformHint(bool has_will_change) {
SetNeedsCommit();
}
-void Layer::SetFrameElementId(ElementId frame_element_id) {
- if (inputs_.frame_element_id == frame_element_id)
- return;
- inputs_.frame_element_id = frame_element_id;
- SetNeedsCommit();
-}
-
void Layer::SetTrilinearFiltering(bool trilinear_filtering) {
auto& inputs = EnsureLayerTreeInputs();
if (inputs.trilinear_filtering == trilinear_filtering)
diff --git a/chromium/cc/layers/layer.h b/chromium/cc/layers/layer.h
index a2da20587d1..8a8d212999c 100644
--- a/chromium/cc/layers/layer.h
+++ b/chromium/cc/layers/layer.h
@@ -557,9 +557,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> {
return inputs_.has_will_change_transform_hint;
}
- void SetFrameElementId(ElementId frame_element_id);
- ElementId frame_element_id() const { return inputs_.frame_element_id; }
-
// For layer tree mode only.
// Sets or gets if trilinear filtering should be used to scaling the contents
// of this layer and its subtree. When set the layer and its subtree will be
@@ -857,8 +854,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> {
TouchActionRegion touch_action_region;
ElementId element_id;
- // ElementId of the document that this layer was created by.
- ElementId frame_element_id;
};
// These inputs are used in layer tree mode (ui compositor) only. Most of them
diff --git a/chromium/cc/layers/layer_impl.cc b/chromium/cc/layers/layer_impl.cc
index 318c2cbc1ee..cda2c0f9517 100644
--- a/chromium/cc/layers/layer_impl.cc
+++ b/chromium/cc/layers/layer_impl.cc
@@ -370,7 +370,6 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) {
// depend on it. Referencing element id on a layer is
// deprecated. http://crbug.com/709137
layer->SetElementId(element_id_);
- layer->SetFrameElementId(frame_element_id_);
layer->has_transform_node_ = has_transform_node_;
layer->offset_to_transform_parent_ = offset_to_transform_parent_;
@@ -515,6 +514,11 @@ bool LayerImpl::IsScrollbarLayer() const {
return false;
}
+bool LayerImpl::IsScrollerOrScrollbar() const {
+ return IsScrollbarLayer() ||
+ GetScrollTree().FindNodeFromElementId(element_id());
+}
+
void LayerImpl::SetDrawsContent(bool draws_content) {
if (draws_content_ == draws_content)
return;
diff --git a/chromium/cc/layers/layer_impl.h b/chromium/cc/layers/layer_impl.h
index 006057b5b12..3df5d1ecf56 100644
--- a/chromium/cc/layers/layer_impl.h
+++ b/chromium/cc/layers/layer_impl.h
@@ -150,6 +150,8 @@ class CC_EXPORT LayerImpl {
virtual bool IsScrollbarLayer() const;
+ bool IsScrollerOrScrollbar() const;
+
// Returns true if this layer has content to draw.
void SetDrawsContent(bool draws_content);
bool DrawsContent() const { return draws_content_; }
@@ -178,11 +180,6 @@ class CC_EXPORT LayerImpl {
void SetElementId(ElementId element_id);
ElementId element_id() const { return element_id_; }
- void SetFrameElementId(ElementId frame_element_id) {
- frame_element_id_ = frame_element_id;
- }
- ElementId frame_element_id() const { return frame_element_id_; }
-
bool IsAffectedByPageScale() const;
bool Is3dSorted() const { return GetSortingContextId() != 0; }
@@ -512,8 +509,6 @@ class CC_EXPORT LayerImpl {
private:
ElementId element_id_;
- // Element ID of the document containing this layer.
- ElementId frame_element_id_;
// Rect indicating what was repainted/updated during update.
// Note that plugin layers bypass this and leave it empty.
// This is in the layer's space.
diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc
index 1d975284495..b483fc7dfb6 100644
--- a/chromium/cc/layers/layer_unittest.cc
+++ b/chromium/cc/layers/layer_unittest.cc
@@ -6,6 +6,8 @@
#include <stddef.h>
+#include <utility>
+
#include "base/bind.h"
#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -1378,38 +1380,45 @@ void ReceiveCopyOutputResult(int* result_count,
++(*result_count);
}
+void ReceiveCopyOutputResultAtomic(
+ std::atomic<int>* result_count,
+ std::unique_ptr<viz::CopyOutputResult> result) {
+ ++(*result_count);
+}
+
TEST_F(LayerTest, DedupesCopyOutputRequestsBySource) {
scoped_refptr<Layer> layer = Layer::Create();
- int result_count = 0;
+ std::atomic<int> result_count{0};
// Create identical requests without the source being set, and expect the
// layer does not abort either one.
std::unique_ptr<viz::CopyOutputRequest> request =
std::make_unique<viz::CopyOutputRequest>(
viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
- base::BindOnce(&ReceiveCopyOutputResult, &result_count));
+ base::BindOnce(&ReceiveCopyOutputResultAtomic,
+ base::Unretained(&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);
+ EXPECT_EQ(0, result_count.load());
request = std::make_unique<viz::CopyOutputRequest>(
viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
- base::BindOnce(&ReceiveCopyOutputResult, &result_count));
+ base::BindOnce(&ReceiveCopyOutputResultAtomic,
+ base::Unretained(&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);
+ EXPECT_EQ(0, result_count.load());
// 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);
+ EXPECT_EQ(2, result_count.load());
layer = Layer::Create();
- result_count = 0;
// Create identical requests, but this time the source is being set. Expect
// the first request using |kArbitrarySourceId1| aborts immediately when
diff --git a/chromium/cc/layers/mirror_layer_impl.cc b/chromium/cc/layers/mirror_layer_impl.cc
index 5751ccb53b2..a8c7d4fb700 100644
--- a/chromium/cc/layers/mirror_layer_impl.cc
+++ b/chromium/cc/layers/mirror_layer_impl.cc
@@ -54,7 +54,7 @@ void MirrorLayerImpl::AppendQuads(viz::RenderPass* render_pass,
auto* mirrored_effect_node = mirrored_render_surface->OwningEffectNode();
auto* quad = render_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>();
quad->SetNew(shared_quad_state, content_rect, unoccluded_content_rect,
- mirrored_layer_id_, mask_resource_id, mask_uv_rect,
+ mirrored_layer_render_pass_id(), mask_resource_id, mask_uv_rect,
mask_texture_size, mirrored_effect_node->surface_contents_scale,
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/mirror_layer_impl.h b/chromium/cc/layers/mirror_layer_impl.h
index b2e62041014..58c07ebdabb 100644
--- a/chromium/cc/layers/mirror_layer_impl.h
+++ b/chromium/cc/layers/mirror_layer_impl.h
@@ -54,6 +54,9 @@ class CC_EXPORT MirrorLayerImpl : public LayerImpl {
private:
const char* LayerTypeAsString() const override;
+ viz::RenderPassId mirrored_layer_render_pass_id() const {
+ return viz::RenderPassId{mirrored_layer_id()};
+ }
int mirrored_layer_id_ = 0;
};
diff --git a/chromium/cc/layers/nine_patch_layer_impl_unittest.cc b/chromium/cc/layers/nine_patch_layer_impl_unittest.cc
index 7b22ec44742..cce728e955d 100644
--- a/chromium/cc/layers/nine_patch_layer_impl_unittest.cc
+++ b/chromium/cc/layers/nine_patch_layer_impl_unittest.cc
@@ -4,6 +4,11 @@
#include <stddef.h>
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "base/numerics/safe_conversions.h"
#include "cc/layers/append_quads_data.h"
#include "cc/layers/nine_patch_layer_impl.h"
#include "cc/resources/ui_resource_bitmap.h"
@@ -18,17 +23,15 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect_conversions.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/transform.h"
namespace cc {
namespace {
gfx::Rect ToRoundedIntRect(const gfx::RectF& rect_f) {
- return gfx::Rect(gfx::ToRoundedInt(rect_f.x()),
- gfx::ToRoundedInt(rect_f.y()),
- gfx::ToRoundedInt(rect_f.width()),
- gfx::ToRoundedInt(rect_f.height()));
+ return gfx::Rect(base::ClampRound(rect_f.x()), base::ClampRound(rect_f.y()),
+ base::ClampRound(rect_f.width()),
+ base::ClampRound(rect_f.height()));
}
void NinePatchLayerLayoutTest(const gfx::Size& bitmap_size,
diff --git a/chromium/cc/layers/painted_scrollbar_layer.cc b/chromium/cc/layers/painted_scrollbar_layer.cc
index 2be635c50e2..1a3e05aa8be 100644
--- a/chromium/cc/layers/painted_scrollbar_layer.cc
+++ b/chromium/cc/layers/painted_scrollbar_layer.cc
@@ -106,7 +106,7 @@ gfx::Size PaintedScrollbarLayer::LayerSizeToContentSize(
return content_size;
}
-void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() {
+bool PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() {
// These properties should never change.
DCHECK_EQ(supports_drag_snap_back_, scrollbar_->SupportsDragSnapBack());
DCHECK_EQ(is_left_side_vertical_scrollbar(),
@@ -114,21 +114,25 @@ 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_);
- UpdateProperty(scrollbar_->HasThumb(), &has_thumb_);
+ bool updated = false;
+ updated |=
+ UpdateProperty(scrollbar_->JumpOnTrackClick(), &jump_on_track_click_);
+ updated |= UpdateProperty(scrollbar_->TrackRect(), &track_rect_);
+ updated |= UpdateProperty(scrollbar_->BackButtonRect(), &back_button_rect_);
+ updated |=
+ UpdateProperty(scrollbar_->ForwardButtonRect(), &forward_button_rect_);
+ updated |= UpdateProperty(scrollbar_->HasThumb(), &has_thumb_);
if (has_thumb_) {
// Ignore ThumbRect's location because the PaintedScrollbarLayerImpl will
// compute it from scroll offset.
- UpdateProperty(scrollbar_->ThumbRect().size(), &thumb_size_);
+ updated |= UpdateProperty(scrollbar_->ThumbRect().size(), &thumb_size_);
} else {
- UpdateProperty(gfx::Size(), &thumb_size_);
+ updated |= UpdateProperty(gfx::Size(), &thumb_size_);
}
+ return updated;
}
-void PaintedScrollbarLayer::UpdateInternalContentScale() {
+bool PaintedScrollbarLayer::UpdateInternalContentScale() {
gfx::Transform transform;
transform = draw_property_utils::ScreenSpaceTransform(
this, layer_tree_host()->property_trees()->transform_tree);
@@ -137,31 +141,24 @@ void PaintedScrollbarLayer::UpdateInternalContentScale() {
transform, layer_tree_host()->device_scale_factor());
float scale = std::max(transform_scales.x(), transform_scales.y());
- bool changed = false;
- changed |= UpdateProperty(scale, &internal_contents_scale_);
- changed |=
+ bool updated = false;
+ updated |= UpdateProperty(scale, &internal_contents_scale_);
+ updated |=
UpdateProperty(gfx::ScaleToCeiledSize(bounds(), internal_contents_scale_),
&internal_content_bounds_);
- if (changed) {
- // If the content scale or bounds change, repaint.
- SetNeedsDisplay();
- }
+ return updated;
}
bool PaintedScrollbarLayer::Update() {
- {
- auto ignore_set_needs_commit = IgnoreSetNeedsCommit();
- ScrollbarLayerBase::Update();
- UpdateInternalContentScale();
- }
+ bool updated = false;
- UpdateThumbAndTrackGeometry();
+ updated |= ScrollbarLayerBase::Update();
+ updated |= UpdateInternalContentScale();
+ updated |= UpdateThumbAndTrackGeometry();
gfx::Size size = bounds();
gfx::Size scaled_size = internal_content_bounds_;
- bool updated = false;
-
if (scaled_size.IsEmpty()) {
if (track_resource_) {
track_resource_ = nullptr;
@@ -178,14 +175,13 @@ bool PaintedScrollbarLayer::Update() {
updated = true;
}
- if (update_rect().IsEmpty() && track_resource_)
- return updated;
-
if (!track_resource_ ||
scrollbar_->NeedsRepaintPart(TRACK_BUTTONS_TICKMARKS)) {
track_resource_ = ScopedUIResource::Create(
layer_tree_host()->GetUIResourceManager(),
RasterizeScrollbarPart(size, scaled_size, TRACK_BUTTONS_TICKMARKS));
+ SetNeedsPushProperties();
+ updated = true;
}
gfx::Size scaled_thumb_size = LayerSizeToContentSize(thumb_size_);
@@ -195,13 +191,12 @@ bool PaintedScrollbarLayer::Update() {
thumb_resource_ = ScopedUIResource::Create(
layer_tree_host()->GetUIResourceManager(),
RasterizeScrollbarPart(thumb_size_, scaled_thumb_size, THUMB));
+ SetNeedsPushProperties();
+ updated = true;
}
- painted_opacity_ = scrollbar_->Opacity();
+ updated |= UpdateProperty(scrollbar_->Opacity(), &painted_opacity_);
}
- // UI resources changed so push properties is needed.
- SetNeedsPushProperties();
- updated = true;
return updated;
}
diff --git a/chromium/cc/layers/painted_scrollbar_layer.h b/chromium/cc/layers/painted_scrollbar_layer.h
index 930c614071e..a449ffc64e7 100644
--- a/chromium/cc/layers/painted_scrollbar_layer.h
+++ b/chromium/cc/layers/painted_scrollbar_layer.h
@@ -52,8 +52,8 @@ class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerBase {
UIResourceId thumb_resource_id() {
return thumb_resource_.get() ? thumb_resource_->id() : 0;
}
- void UpdateInternalContentScale();
- void UpdateThumbAndTrackGeometry();
+ bool UpdateInternalContentScale();
+ bool UpdateThumbAndTrackGeometry();
private:
gfx::Size LayerSizeToContentSize(const gfx::Size& layer_size) const;
diff --git a/chromium/cc/layers/picture_layer.cc b/chromium/cc/layers/picture_layer.cc
index 0baee75a961..a5442b679bf 100644
--- a/chromium/cc/layers/picture_layer.cc
+++ b/chromium/cc/layers/picture_layer.cc
@@ -4,6 +4,9 @@
#include "cc/layers/picture_layer.h"
+#include <memory>
+#include <utility>
+
#include "base/auto_reset.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
@@ -58,8 +61,6 @@ void PictureLayer::PushPropertiesTo(LayerImpl* base_layer) {
DropRecordingSourceContentIfInvalid();
layer_impl->SetNearestNeighbor(picture_layer_inputs_.nearest_neighbor);
- layer_impl->SetUseTransformedRasterization(
- ShouldUseTransformedRasterization());
layer_impl->set_gpu_raster_max_texture_size(
layer_tree_host()->device_viewport_rect().size());
layer_impl->SetIsBackdropFilterMask(is_backdrop_filter_mask());
@@ -212,14 +213,6 @@ void PictureLayer::SetNearestNeighbor(bool nearest_neighbor) {
SetNeedsCommit();
}
-void PictureLayer::SetTransformedRasterizationAllowed(bool allowed) {
- if (picture_layer_inputs_.transformed_rasterization_allowed == allowed)
- return;
-
- picture_layer_inputs_.transformed_rasterization_allowed = allowed;
- SetNeedsCommit();
-}
-
bool PictureLayer::HasDrawableContent() const {
return picture_layer_inputs_.client && Layer::HasDrawableContent();
}
@@ -286,38 +279,6 @@ void PictureLayer::DropRecordingSourceContentIfInvalid() {
}
}
-bool PictureLayer::ShouldUseTransformedRasterization() const {
- if (!picture_layer_inputs_.transformed_rasterization_allowed)
- return false;
-
- const TransformTree& transform_tree =
- layer_tree_host()->property_trees()->transform_tree;
- DCHECK(!transform_tree.needs_update());
- auto* transform_node = transform_tree.Node(transform_tree_index());
- DCHECK(transform_node);
- // TODO(pdr): This is a workaround for https://crbug.com/708951 to avoid
- // crashing when there's no transform node. This workaround should be removed.
- if (!transform_node)
- return false;
-
- if (transform_node->to_screen_is_potentially_animated)
- return false;
-
- const gfx::Transform& to_screen =
- transform_tree.ToScreen(transform_tree_index());
- if (!to_screen.IsScaleOrTranslation())
- return false;
-
- float origin_x =
- to_screen.matrix().getFloat(0, 3) + offset_to_transform_parent().x();
- float origin_y =
- to_screen.matrix().getFloat(1, 3) + offset_to_transform_parent().y();
- if (origin_x - floorf(origin_x) == 0.f && origin_y - floorf(origin_y) == 0.f)
- return false;
-
- return true;
-}
-
const DisplayItemList* PictureLayer::GetDisplayItemList() {
return picture_layer_inputs_.display_list.get();
}
diff --git a/chromium/cc/layers/picture_layer.h b/chromium/cc/layers/picture_layer.h
index 8fdfba82e85..878e287bc9c 100644
--- a/chromium/cc/layers/picture_layer.h
+++ b/chromium/cc/layers/picture_layer.h
@@ -5,6 +5,7 @@
#ifndef CC_LAYERS_PICTURE_LAYER_H_
#define CC_LAYERS_PICTURE_LAYER_H_
+#include <memory>
#include <vector>
#include "cc/base/devtools_instrumentation.h"
@@ -32,11 +33,6 @@ class CC_EXPORT PictureLayer : public Layer {
return picture_layer_inputs_.nearest_neighbor;
}
- void SetTransformedRasterizationAllowed(bool allowed);
- bool transformed_rasterization_allowed() const {
- return picture_layer_inputs_.transformed_rasterization_allowed;
- }
-
void SetIsBackdropFilterMask(bool is_backdrop_filter_mask);
bool is_backdrop_filter_mask() const {
return picture_layer_inputs_.is_backdrop_filter_mask;
@@ -69,7 +65,6 @@ class CC_EXPORT PictureLayer : public Layer {
ContentLayerClient* client = nullptr;
bool nearest_neighbor = false;
- bool transformed_rasterization_allowed = false;
bool is_backdrop_filter_mask = false;
scoped_refptr<DisplayItemList> display_list;
base::Optional<gfx::Size> directly_composited_image_size = base::nullopt;
@@ -91,8 +86,6 @@ class CC_EXPORT PictureLayer : public Layer {
void DropRecordingSourceContentIfInvalid();
- bool ShouldUseTransformedRasterization() const;
-
std::unique_ptr<RecordingSource> recording_source_;
devtools_instrumentation::
ScopedLayerObjectTracker instrumentation_object_tracker_;
diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc
index 0f030bd4ff3..06fa222c84a 100644
--- a/chromium/cc/layers/picture_layer_impl.cc
+++ b/chromium/cc/layers/picture_layer_impl.cc
@@ -10,7 +10,9 @@
#include <algorithm>
#include <cmath>
#include <limits>
+#include <memory>
#include <set>
+#include <utility>
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
@@ -96,7 +98,6 @@ PictureLayerImpl::PictureLayerImpl(LayerTreeImpl* tree_impl, int id)
was_screen_space_transform_animating_(false),
only_used_low_res_last_append_quads_(false),
nearest_neighbor_(false),
- use_transformed_rasterization_(false),
lcd_text_disallowed_reason_(LCDTextDisallowedReason::kNone),
directly_composited_image_size_(base::nullopt),
directly_composited_image_initial_raster_scale_(0.f),
@@ -139,7 +140,6 @@ std::unique_ptr<LayerImpl> PictureLayerImpl::CreateLayerImpl(
void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) {
PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer);
-
LayerImpl::PushPropertiesTo(base_layer);
// Twin relationships should never change once established.
@@ -153,7 +153,6 @@ void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) {
layer_impl->twin_layer_ = this;
layer_impl->SetNearestNeighbor(nearest_neighbor_);
- layer_impl->SetUseTransformedRasterization(use_transformed_rasterization_);
layer_impl->SetDirectlyCompositedImageSize(directly_composited_image_size_);
layer_impl->SetIsBackdropFilterMask(is_backdrop_filter_mask_);
@@ -273,7 +272,7 @@ void PictureLayerImpl::AppendQuads(viz::RenderPass* render_pass,
// Validate that the tile and bounds size are always within one pixel.
PictureLayerTiling* high_res =
tilings_->FindTilingWithResolution(HIGH_RESOLUTION);
- if (high_res) {
+ if (raster_contents_scale_ >= 1.f && high_res) {
const float epsilon = 1.f;
gfx::SizeF scaled_tiling_size(high_res->tiling_size());
scaled_tiling_size.Scale(1 / raster_contents_scale_);
@@ -600,14 +599,11 @@ bool PictureLayerImpl::UpdateTiles() {
const float old_ideal_contents_scale = ideal_contents_scale_;
UpdateIdealScales();
- const bool ideal_contents_scale_changed =
- old_ideal_contents_scale != 0 &&
- old_ideal_contents_scale != ideal_contents_scale_;
- if (!raster_contents_scale_ ||
- ShouldAdjustRasterScale(ideal_contents_scale_changed)) {
+ const bool should_adjust_raster_scale =
+ ShouldAdjustRasterScale(old_ideal_contents_scale);
+ if (should_adjust_raster_scale)
RecalculateRasterScales();
- AddTilingsForRasterScale();
- }
+ UpdateTilingsForRasterScaleAndTranslation(should_adjust_raster_scale);
if (layer_tree_impl()->IsActiveTree())
AddLowResolutionTilingIfNeeded();
@@ -754,10 +750,12 @@ void PictureLayerImpl::UpdateRasterSource(
layer_tree_impl()->GetMSAASampleCountForRaster(
new_display_item_list);
needs_full_invalidation |=
- current_display_item_list->discardable_image_map()
- .contains_only_srgb_images() !=
- new_display_item_list->discardable_image_map()
- .contains_only_srgb_images();
+ layer_tree_impl()->GetRasterColorSpace(
+ current_display_item_list->discardable_image_map()
+ .content_color_usage()) !=
+ layer_tree_impl()->GetRasterColorSpace(
+ new_display_item_list->discardable_image_map()
+ .content_color_usage());
if (needs_full_invalidation)
new_invalidation->Union(gfx::Rect(raster_source->GetSize()));
}
@@ -814,28 +812,24 @@ void PictureLayerImpl::UpdateRasterSource(
}
}
-bool PictureLayerImpl::UpdateCanUseLCDTextAfterCommit() {
- DCHECK(layer_tree_impl()->IsSyncTree());
-
- // Once we disable lcd text, we don't re-enable it.
- if (!can_use_lcd_text())
+bool PictureLayerImpl::UpdateCanUseLCDText(
+ bool raster_translation_aligns_pixels) {
+ // If we have pending/active trees, the active tree doesn't update lcd text
+ // status but copies it from the pending tree.
+ if (!layer_tree_impl()->IsSyncTree())
return false;
- auto new_lcd_text_disallowed_reason = ComputeLCDTextDisallowedReason();
+ auto new_lcd_text_disallowed_reason =
+ ComputeLCDTextDisallowedReason(raster_translation_aligns_pixels);
if (lcd_text_disallowed_reason_ == new_lcd_text_disallowed_reason)
return false;
lcd_text_disallowed_reason_ = new_lcd_text_disallowed_reason;
- // Synthetically invalidate everything.
- gfx::Rect bounds_rect(bounds());
- invalidation_ = Region(bounds_rect);
- tilings_->Invalidate(invalidation_);
- UnionUpdateRect(bounds_rect);
return true;
}
-LCDTextDisallowedReason PictureLayerImpl::ComputeLCDTextDisallowedReason()
- const {
+LCDTextDisallowedReason PictureLayerImpl::ComputeLCDTextDisallowedReason(
+ bool raster_translation_aligns_pixels) const {
if (layer_tree_impl()->settings().layers_always_allowed_lcd_text)
return LCDTextDisallowedReason::kNone;
if (!layer_tree_impl()->settings().can_use_lcd_text)
@@ -846,25 +840,38 @@ LCDTextDisallowedReason PictureLayerImpl::ComputeLCDTextDisallowedReason()
return LCDTextDisallowedReason::kContentsNotOpaque;
}
- if (!use_transformed_rasterization_) {
- if (!GetTransformTree()
- .Node(transform_tree_index())
- ->node_and_ancestors_have_only_integer_translation)
- return LCDTextDisallowedReason::kNonIntegralTranslation;
+ // If raster translation aligns pixels, we can ignore fractional layer offset
+ // and transform for LCD text.
+ if (!raster_translation_aligns_pixels) {
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;
+ return LCDTextDisallowedReason::kNonIntegralTranslation;
}
if (has_will_change_transform_hint())
return LCDTextDisallowedReason::kWillChangeTransform;
+ if (screen_space_transform_is_animating())
+ return LCDTextDisallowedReason::kTransformAnimation;
+
+ EffectNode* effect_node = GetEffectTree().Node(effect_tree_index());
+ if (effect_node->node_or_ancestor_has_filters)
+ return LCDTextDisallowedReason::kPixelOrColorEffect;
+
return LCDTextDisallowedReason::kNone;
}
+LCDTextDisallowedReason
+PictureLayerImpl::ComputeLCDTextDisallowedReasonForTesting() const {
+ gfx::Vector2dF raster_translation;
+ return ComputeLCDTextDisallowedReason(
+ CalculateRasterTranslation(raster_translation));
+}
+
void PictureLayerImpl::NotifyTileStateChanged(const Tile* tile) {
if (layer_tree_impl()->IsActiveTree())
damage_rect_.Union(tile->enclosing_layer_rect());
@@ -928,8 +935,7 @@ std::unique_ptr<Tile> PictureLayerImpl::CreateTile(
flags |= Tile::IS_OPAQUE;
return layer_tree_impl()->tile_manager()->CreateTile(
- info, id(), layer_tree_impl()->source_frame_number(), flags,
- can_use_lcd_text());
+ info, id(), layer_tree_impl()->source_frame_number(), flags);
}
const Region* PictureLayerImpl::GetPendingInvalidation() {
@@ -965,6 +971,10 @@ const PaintWorkletRecordMap& PictureLayerImpl::GetPaintWorkletRecords() const {
return paint_worklet_records_;
}
+bool PictureLayerImpl::IsDirectlyCompositedImage() const {
+ return directly_composited_image_size_.has_value();
+}
+
gfx::Rect PictureLayerImpl::GetEnclosingRectInTargetSpace() const {
return GetScaledEnclosingRectInTargetSpace(MaximumTilingContentsScale());
}
@@ -1071,19 +1081,6 @@ void PictureLayerImpl::SetNearestNeighbor(bool nearest_neighbor) {
NoteLayerPropertyChanged();
}
-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;
-
- use_transformed_rasterization_ = use;
- NoteLayerPropertyChanged();
-}
-
void PictureLayerImpl::SetDirectlyCompositedImageSize(
base::Optional<gfx::Size> size) {
if (directly_composited_image_size_ == size)
@@ -1094,6 +1091,11 @@ void PictureLayerImpl::SetDirectlyCompositedImageSize(
}
bool PictureLayerImpl::ShouldDirectlyCompositeImage(float raster_scale) const {
+ // Even if there are minor rendering differences, we want to apply directly
+ // compositing images in cases where doing so is going to save memory.
+ if (raster_scale < 0.1f)
+ return true;
+
// If the results of scaling the bounds by the expected raster scale
// would end up with a content rect whose width/height are more than one
// pixel different from the layer bounds, don't directly composite the image
@@ -1179,12 +1181,15 @@ void PictureLayerImpl::LogDirectlyCompositedImageRasterScaleUMAs() const {
}
PictureLayerTiling* PictureLayerImpl::AddTiling(
- const gfx::AxisTransform2d& contents_transform) {
+ const gfx::AxisTransform2d& raster_transform) {
DCHECK(CanHaveTilings());
- DCHECK_GE(contents_transform.scale(), MinimumContentsScale());
- DCHECK_LE(contents_transform.scale(), MaximumContentsScale());
+ DCHECK_GE(raster_transform.scale(), MinimumContentsScale());
+ DCHECK_LE(raster_transform.scale(), MaximumContentsScale());
DCHECK(raster_source_->HasRecordings());
- return tilings_->AddTiling(contents_transform, raster_source_);
+ bool tiling_can_use_lcd_text =
+ can_use_lcd_text() && raster_transform.scale() == raster_contents_scale_;
+ return tilings_->AddTiling(raster_transform, raster_source_,
+ tiling_can_use_lcd_text);
}
void PictureLayerImpl::RemoveAllTilings() {
@@ -1193,24 +1198,56 @@ void PictureLayerImpl::RemoveAllTilings() {
ResetRasterScale();
}
-void PictureLayerImpl::AddTilingsForRasterScale() {
- // Reset all resolution enums on tilings, we'll be setting new values in this
- // function.
- tilings_->MarkAllTilingsNonIdeal();
+bool PictureLayerImpl::CanRecreateHighResTilingForLCDTextAndRasterTranslation(
+ const PictureLayerTiling& high_res) const {
+ // This is for the sync tree only to avoid flickering.
+ if (!layer_tree_impl()->IsSyncTree())
+ return false;
+ // We can recreate the tiling if we would invalidate all of its tiles.
+ if (high_res.may_contain_low_resolution_tiles())
+ return true;
+ // Keep the non-ideal raster translation unchanged for transform animations
+ // to avoid re-rasterization during animation.
+ if (draw_properties().screen_space_transform_is_animating ||
+ has_will_change_transform_hint())
+ return false;
+ // Also avoid re-rasterization during pinch-zoom.
+ if (layer_tree_impl()->PinchGestureActive())
+ return false;
+ return true;
+}
+void PictureLayerImpl::UpdateTilingsForRasterScaleAndTranslation(
+ bool has_adjusted_raster_scale) {
PictureLayerTiling* high_res =
tilings_->FindTilingWithScaleKey(raster_contents_scale_);
- // Note: This function is always invoked when raster scale is recomputed,
- // but not necessarily changed. This means raster translation update is also
- // always done when there are significant changes that triggered raster scale
- // recomputation.
- gfx::Vector2dF raster_translation =
- CalculateRasterTranslation(raster_contents_scale_);
- if (high_res &&
- high_res->raster_transform().translation() != raster_translation) {
- tilings_->Remove(high_res);
- high_res = nullptr;
+
+ gfx::Vector2dF raster_translation;
+ bool raster_translation_aligns_pixels =
+ CalculateRasterTranslation(raster_translation);
+ bool can_use_lcd_text_changed =
+ UpdateCanUseLCDText(raster_translation_aligns_pixels);
+ if (high_res) {
+ bool raster_translation_is_not_ideal =
+ high_res->raster_transform().translation() != raster_translation;
+ bool should_recreate_high_res =
+ (raster_translation_is_not_ideal || can_use_lcd_text_changed) &&
+ CanRecreateHighResTilingForLCDTextAndRasterTranslation(*high_res);
+ if (should_recreate_high_res) {
+ tilings_->Remove(high_res);
+ high_res = nullptr;
+ } else if (!has_adjusted_raster_scale) {
+ // Nothing changed, no need to update tilings.
+ DCHECK_EQ(HIGH_RESOLUTION, high_res->resolution());
+ SanityCheckTilingState();
+ return;
+ }
}
+
+ // Reset all resolution enums on tilings, we'll be setting new values in this
+ // function.
+ tilings_->MarkAllTilingsNonIdeal();
+
if (!high_res) {
// We always need a high res tiling, so create one if it doesn't exist.
high_res = AddTiling(
@@ -1244,7 +1281,10 @@ void PictureLayerImpl::AddTilingsForRasterScale() {
}
bool PictureLayerImpl::ShouldAdjustRasterScale(
- bool ideal_contents_scale_changed) const {
+ float old_ideal_contents_scale) const {
+ if (!raster_contents_scale_)
+ return true;
+
if (directly_composited_image_size_) {
// If we have a directly composited image size, but previous raster scale
// calculations did not set an initial raster scale, we must recalcluate.
@@ -1269,6 +1309,9 @@ bool PictureLayerImpl::ShouldAdjustRasterScale(
// changed. We should recalculate in order to raster at the intrinsic image
// size. Note that this is not a comparison of the used raster_source_scale_
// and desired because of the adjustments in RecalculateRasterScales.
+ bool ideal_contents_scale_changed =
+ old_ideal_contents_scale != 0 &&
+ old_ideal_contents_scale != ideal_contents_scale_;
bool default_raster_scale_changed =
default_raster_scale != directly_composited_image_initial_raster_scale_;
if (ideal_contents_scale_changed && !default_raster_scale_changed) {
@@ -1567,43 +1610,58 @@ void PictureLayerImpl::CleanUpTilingsOnActiveLayer(
SanityCheckTilingState();
}
-gfx::Vector2dF PictureLayerImpl::CalculateRasterTranslation(
- float raster_scale) {
- if (!use_transformed_rasterization_)
- return gfx::Vector2dF();
-
- DCHECK(!contents_opaque());
+bool PictureLayerImpl::CalculateRasterTranslation(
+ gfx::Vector2dF& raster_translation) const {
+ // If this setting is set, the client (e.g. the Chromium UI) is sure that it
+ // can almost always align raster pixels to physical pixels, and doesn't care
+ // about temporary misalignment, so don't bother raster translation.
+ if (layer_tree_impl()->settings().layers_always_allowed_lcd_text)
+ return true;
+ const gfx::Transform& screen_transform = ScreenSpaceTransform();
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
- // avoid this elsewhere but it still happens: http://crbug.com/778440
- // TODO(enne): Also, we shouldn't ever get here if the draw transform is not
- // just a scale + translation, but we do sometimes: http://crbug.com/740113
- if (draw_properties().screen_space_transform_is_animating ||
+
+ if (!screen_transform.IsScaleOrTranslation() ||
!draw_transform.IsScaleOrTranslation()) {
- // For now, while these problems are not well understood, avoid changing
- // the raster scale in these cases.
- return gfx::Vector2dF();
+ return false;
}
// It is only useful to align the content space to the target space if their
// relative pixel ratio is some small rational number. Currently we only
- // align if the relative pixel ratio is 1:1.
- // Good match if the maximum alignment error on a layer of size 10000px
- // does not exceed 0.001px.
- static constexpr float kErrorThreshold = 0.0000001f;
- if (std::abs(draw_transform.matrix().getFloat(0, 0) - raster_scale) >
- kErrorThreshold ||
- std::abs(draw_transform.matrix().getFloat(1, 1) - raster_scale) >
- kErrorThreshold)
- return gfx::Vector2dF();
-
- // Extract the fractional part of layer origin in the target space.
- float origin_x = draw_transform.matrix().getFloat(0, 3);
- float origin_y = draw_transform.matrix().getFloat(1, 3);
- return gfx::Vector2dF(origin_x - floorf(origin_x),
- origin_y - floorf(origin_y));
+ // align if the relative pixel ratio is 1:1 (i.e. the scale components of
+ // both the screen transform and the draw transform are approximately the same
+ // as |raster_contents_scale_|). Good match if the maximum alignment error on
+ // a layer of size 10000px does not exceed 0.001px.
+ static constexpr float kPixelErrorThreshold = 0.001f;
+ static constexpr float kScaleErrorThreshold = kPixelErrorThreshold / 10000;
+ auto is_raster_scale = [this](float scale) -> bool {
+ return std::abs(scale - raster_contents_scale_) <= kScaleErrorThreshold;
+ };
+ if (!is_raster_scale(screen_transform.matrix().getFloat(0, 0)) ||
+ !is_raster_scale(screen_transform.matrix().getFloat(1, 1)) ||
+ !is_raster_scale(draw_transform.matrix().getFloat(0, 0)) ||
+ !is_raster_scale(draw_transform.matrix().getFloat(1, 1))) {
+ return false;
+ }
+
+ // Extract the fractional part of layer origin in the screen space and in the
+ // target space.
+ auto fraction = [](float f) -> float { return f - floorf(f); };
+ float screen_x_fraction = fraction(screen_transform.matrix().getFloat(0, 3));
+ float screen_y_fraction = fraction(screen_transform.matrix().getFloat(1, 3));
+ float target_x_fraction = fraction(draw_transform.matrix().getFloat(0, 3));
+ float target_y_fraction = fraction(draw_transform.matrix().getFloat(1, 3));
+
+ // If the origin is different in the screen space and in the target space,
+ // it means the render target is not aligned to physical pixels, and the
+ // text content will be blurry regardless of raster translation.
+ if (std::abs(screen_x_fraction - target_x_fraction) > kPixelErrorThreshold ||
+ std::abs(screen_y_fraction - target_y_fraction) > kPixelErrorThreshold) {
+ return false;
+ }
+
+ raster_translation = gfx::Vector2dF(target_x_fraction, target_y_fraction);
+ return true;
}
float PictureLayerImpl::MinimumContentsScale() const {
@@ -1713,26 +1771,34 @@ void PictureLayerImpl::UpdateIdealScales() {
DCHECK_GT(min_contents_scale, 0.f);
ideal_device_scale_ = layer_tree_impl()->device_scale_factor();
+ ideal_page_scale_ = 1.f;
+ ideal_contents_scale_ = GetIdealContentsScale();
+
if (layer_tree_impl()->PageScaleTransformNode()) {
+ DCHECK(!layer_tree_impl()->settings().is_layer_tree_for_subframe);
ideal_page_scale_ = IsAffectedByPageScale()
? layer_tree_impl()->current_page_scale_factor()
: 1.f;
- ideal_contents_scale_ = GetIdealContentsScale();
- } else {
- // This layer may be in a layer tree embedded in a hierarchy that has its
- // own page scale factor. We represent that here as
- // 'external_page_scale_factor', a value that affects raster scale in the
- // same way that page_scale_factor does, but doesn't affect any geometry
- // calculations.
- float external_page_scale_factor =
- layer_tree_impl() ? layer_tree_impl()->external_page_scale_factor()
- : 1.f;
- DCHECK(!layer_tree_impl() || external_page_scale_factor == 1.f ||
- layer_tree_impl()->current_page_scale_factor() == 1.f);
- ideal_page_scale_ = external_page_scale_factor;
- ideal_contents_scale_ =
- GetIdealContentsScale() * external_page_scale_factor;
}
+
+ // This layer may be in a layer tree embedded in a hierarchy that has its own
+ // page scale factor. We represent that here as 'external_page_scale_factor',
+ // a value that affects raster scale in the same way that page_scale_factor
+ // does, but doesn't affect any geometry calculations. In a normal main frame
+ // or OOPIF, only one of current or external page scale factor is ever used
+ // but not both. The only exception to this is a main frame in a portal. It
+ // may have a current_page_scale_factor (e.g. due to a viewport <meta> tag)
+ // as well as an external_page_scale_factor coming from the page scale of its
+ // embedder page.
+ float external_page_scale_factor =
+ layer_tree_impl() ? layer_tree_impl()->external_page_scale_factor() : 1.f;
+ DCHECK(!layer_tree_impl() ||
+ !layer_tree_impl()->settings().is_layer_tree_for_subframe ||
+ external_page_scale_factor == 1.f ||
+ layer_tree_impl()->current_page_scale_factor() == 1.f);
+ ideal_page_scale_ *= external_page_scale_factor;
+ ideal_contents_scale_ *= external_page_scale_factor;
+
ideal_contents_scale_ = base::ClampToRange(
ideal_contents_scale_, min_contents_scale, kMaxIdealContentsScale);
ideal_source_scale_ =
@@ -1968,17 +2034,10 @@ void PictureLayerImpl::InvalidatePaintWorklets(
gfx::ContentColorUsage PictureLayerImpl::GetContentColorUsage() const {
auto display_item_list = raster_source_->GetDisplayItemList();
- bool contains_only_srgb_images = true;
- if (display_item_list) {
- contains_only_srgb_images =
- display_item_list->discardable_image_map().contains_only_srgb_images();
- }
-
- if (contains_only_srgb_images)
+ if (!display_item_list)
return gfx::ContentColorUsage::kSRGB;
- // TODO(cblume) This assumes only wide color gamut and not HDR
- return gfx::ContentColorUsage::kWideColorGamut;
+ return display_item_list->discardable_image_map().content_color_usage();
}
} // namespace cc
diff --git a/chromium/cc/layers/picture_layer_impl.h b/chromium/cc/layers/picture_layer_impl.h
index 0be0cf1c68a..ed55a5d9270 100644
--- a/chromium/cc/layers/picture_layer_impl.h
+++ b/chromium/cc/layers/picture_layer_impl.h
@@ -8,6 +8,7 @@
#include <stddef.h>
#include <map>
+#include <memory>
#include <string>
#include <vector>
@@ -76,6 +77,7 @@ class CC_EXPORT PictureLayerImpl
bool HasValidTilePriorities() const override;
bool RequiresHighResToDraw() const override;
const PaintWorkletRecordMap& GetPaintWorkletRecords() const override;
+ bool IsDirectlyCompositedImage() const override;
// ImageAnimationController::AnimationDriver overrides.
bool ShouldAnimate(PaintImage::Id paint_image_id) const override;
@@ -94,8 +96,6 @@ class CC_EXPORT PictureLayerImpl
const PictureLayerTilingSet* pending_set,
const PaintWorkletRecordMap* pending_paint_worklet_records);
bool UpdateTiles();
- // Returns true if the LCD state changed.
- bool UpdateCanUseLCDTextAfterCommit();
// Mask-related functions.
void GetContentsResourceId(viz::ResourceId* resource_id,
@@ -104,8 +104,6 @@ class CC_EXPORT PictureLayerImpl
void SetNearestNeighbor(bool nearest_neighbor);
- void SetUseTransformedRasterization(bool use);
-
void SetDirectlyCompositedImageSize(base::Optional<gfx::Size>);
size_t GPUMemoryUsageInBytes() const override;
@@ -143,9 +141,7 @@ class CC_EXPORT PictureLayerImpl
LCDTextDisallowedReason lcd_text_disallowed_reason() const {
return lcd_text_disallowed_reason_;
}
- LCDTextDisallowedReason ComputeLCDTextDisallowedReasonForTesting() const {
- return ComputeLCDTextDisallowedReason();
- }
+ LCDTextDisallowedReason ComputeLCDTextDisallowedReasonForTesting() const;
const Region& InvalidationForTesting() const { return invalidation_; }
@@ -167,15 +163,22 @@ class CC_EXPORT PictureLayerImpl
// an animation, at which point the PaintWorklet must be re-painted.
void InvalidatePaintWorklets(const PaintWorkletInput::PropertyKey& key);
+ void SetContentsScaleForTesting(float scale) {
+ ideal_contents_scale_ = raster_contents_scale_ = scale;
+ }
+
protected:
PictureLayerImpl(LayerTreeImpl* tree_impl, int id);
PictureLayerTiling* AddTiling(const gfx::AxisTransform2d& contents_transform);
void RemoveAllTilings();
- void AddTilingsForRasterScale();
+ bool CanRecreateHighResTilingForLCDTextAndRasterTranslation(
+ const PictureLayerTiling& high_res) const;
+ void UpdateTilingsForRasterScaleAndTranslation(bool adjusted_raster_scale);
void AddLowResolutionTilingIfNeeded();
- bool ShouldAdjustRasterScale(bool ideal_contents_scale_changed) const;
+ bool ShouldAdjustRasterScale(float old_ideal_contents_scale) const;
void RecalculateRasterScales();
- gfx::Vector2dF CalculateRasterTranslation(float raster_scale);
+ // Returns false if raster translation is not applicable.
+ bool CalculateRasterTranslation(gfx::Vector2dF& raster_translation) const;
void CleanUpTilingsOnActiveLayer(
const std::vector<PictureLayerTiling*>& used_tilings);
float MinimumContentsScale() const;
@@ -218,7 +221,10 @@ class CC_EXPORT PictureLayerImpl
const std::vector<DiscardableImageMap::PaintWorkletInputWithImageId>&
inputs);
- LCDTextDisallowedReason ComputeLCDTextDisallowedReason() const;
+ LCDTextDisallowedReason ComputeLCDTextDisallowedReason(
+ bool raster_translation_aligns_pixels) const;
+ // Returns true if the LCD state changed.
+ bool UpdateCanUseLCDText(bool raster_translation_aligns_pixels);
PictureLayerImpl* twin_layer_;
@@ -252,7 +258,6 @@ class CC_EXPORT PictureLayerImpl
bool only_used_low_res_last_append_quads_ : 1;
bool nearest_neighbor_ : 1;
- bool use_transformed_rasterization_ : 1;
LCDTextDisallowedReason lcd_text_disallowed_reason_;
diff --git a/chromium/cc/layers/picture_layer_impl_unittest.cc b/chromium/cc/layers/picture_layer_impl_unittest.cc
index 38609980105..87a612b6fe5 100644
--- a/chromium/cc/layers/picture_layer_impl_unittest.cc
+++ b/chromium/cc/layers/picture_layer_impl_unittest.cc
@@ -8,6 +8,7 @@
#include <algorithm>
#include <limits>
+#include <memory>
#include <set>
#include <utility>
@@ -161,6 +162,7 @@ class PictureLayerImplTest : public TestLayerTreeHostBase {
gfx::Transform scale_transform;
scale_transform.Scale(ideal_contents_scale, ideal_contents_scale);
layer->draw_properties().screen_space_transform = scale_transform;
+ layer->draw_properties().target_space_transform = scale_transform;
layer->set_contributes_to_drawn_render_surface(true);
DCHECK_EQ(layer->GetIdealContentsScale(), ideal_contents_scale);
layer->layer_tree_impl()->property_trees()->SetAnimationScalesForTesting(
@@ -4031,6 +4033,8 @@ class OcclusionTrackingPictureLayerImplTest
EXPECT_EQ(expected_occluded_tile_count, occluded_tile_count)
<< "line: " << source_line;
}
+
+ void TestOcclusionForScale(float scale, int expected_occluded_count);
};
TEST_F(OcclusionTrackingPictureLayerImplTest,
@@ -4240,7 +4244,9 @@ TEST_F(OcclusionTrackingPictureLayerImplTest,
}
}
-TEST_F(OcclusionTrackingPictureLayerImplTest, OcclusionForDifferentScales) {
+void OcclusionTrackingPictureLayerImplTest::TestOcclusionForScale(
+ float scale,
+ int expected_occluded_count) {
host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(1));
gfx::Size tile_size(102, 102);
@@ -4254,42 +4260,38 @@ TEST_F(OcclusionTrackingPictureLayerImplTest, OcclusionForDifferentScales) {
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(viewport_size));
SetupPendingTreeWithFixedTileSize(pending_raster_source, tile_size, Region());
- ASSERT_TRUE(pending_layer()->CanHaveTilings());
- LayerImpl* layer1 = AddLayer<LayerImpl>(host_impl()->pending_tree());
+ ActivateTree();
+
+ LayerImpl* layer1 = AddLayer<LayerImpl>(host_impl()->active_tree());
layer1->SetBounds(layer_bounds);
layer1->SetDrawsContent(true);
layer1->SetContentsOpaque(true);
- CopyProperties(pending_layer(), layer1);
+ CopyProperties(active_layer(), layer1);
layer1->SetOffsetToTransformParent(occluding_layer_position);
- pending_layer()->tilings()->RemoveAllTilings();
+ ASSERT_TRUE(active_layer()->CanHaveTilings());
+ active_layer()->SetContentsScaleForTesting(scale);
+ active_layer()->tilings()->RemoveAllTilings();
float low_res_factor = host_impl()->settings().low_res_contents_scale_factor;
- pending_layer()
+ active_layer()
->AddTiling(gfx::AxisTransform2d(low_res_factor, gfx::Vector2dF()))
->set_resolution(LOW_RESOLUTION);
- pending_layer()
- ->AddTiling(gfx::AxisTransform2d(0.3f, gfx::Vector2dF()))
- ->set_resolution(HIGH_RESOLUTION);
- pending_layer()
- ->AddTiling(gfx::AxisTransform2d(0.7f, gfx::Vector2dF()))
- ->set_resolution(HIGH_RESOLUTION);
- pending_layer()
- ->AddTiling(gfx::AxisTransform2d(1.0f, gfx::Vector2dF()))
- ->set_resolution(HIGH_RESOLUTION);
- pending_layer()
- ->AddTiling(gfx::AxisTransform2d(2.0f, gfx::Vector2dF()))
+ active_layer()
+ ->AddTiling(gfx::AxisTransform2d(scale, gfx::Vector2dF()))
->set_resolution(HIGH_RESOLUTION);
+ ASSERT_EQ(2u, active_layer()->num_tilings());
+
host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(1));
// UpdateDrawProperties with the occluding layer.
- UpdateDrawProperties(host_impl()->pending_tree());
+ UpdateDrawProperties(host_impl()->active_tree());
- EXPECT_EQ(5u, pending_layer()->num_tilings());
+ ASSERT_EQ(2u, active_layer()->num_tilings());
int occluded_tile_count = 0;
- for (size_t i = 0; i < pending_layer()->num_tilings(); ++i) {
- PictureLayerTiling* tiling = pending_layer()->tilings()->tiling_at(i);
+ for (size_t i = 0; i < active_layer()->num_tilings(); ++i) {
+ PictureLayerTiling* tiling = active_layer()->tilings()->tiling_at(i);
auto prioritized_tiles =
tiling->UpdateAndGetAllPrioritizedTilesForTesting();
std::vector<Tile*> tiles = tiling->AllTilesForTesting();
@@ -4304,26 +4306,32 @@ TEST_F(OcclusionTrackingPictureLayerImplTest, OcclusionForDifferentScales) {
}
}
- switch (i) {
- case 0:
- EXPECT_EQ(occluded_tile_count, 30);
- break;
- case 1:
- EXPECT_EQ(occluded_tile_count, 5);
- break;
- case 2:
- EXPECT_EQ(occluded_tile_count, 4);
- break;
- case 4:
- case 3:
- EXPECT_EQ(occluded_tile_count, 2);
- break;
- default:
- NOTREACHED();
+ if (i == 0) {
+ EXPECT_EQ(scale, tiling->contents_scale_key());
+ EXPECT_EQ(occluded_tile_count, expected_occluded_count);
+ } else {
+ ASSERT_EQ(1u, i);
+ EXPECT_EQ(occluded_tile_count, 2);
}
}
}
+TEST_F(OcclusionTrackingPictureLayerImplTest, OcclusionForScale0_3) {
+ TestOcclusionForScale(0.3f, 2);
+}
+
+TEST_F(OcclusionTrackingPictureLayerImplTest, OcclusionForScale0_7) {
+ TestOcclusionForScale(0.7f, 4);
+}
+
+TEST_F(OcclusionTrackingPictureLayerImplTest, OcclusionForScale1) {
+ TestOcclusionForScale(1.0f, 5);
+}
+
+TEST_F(OcclusionTrackingPictureLayerImplTest, OcclusionForScale2) {
+ TestOcclusionForScale(2.0f, 30);
+}
+
TEST_F(OcclusionTrackingPictureLayerImplTest, DifferentOcclusionOnTrees) {
gfx::Size layer_bounds(1000, 1000);
gfx::Size viewport_size(1000, 1000);
@@ -4877,6 +4885,51 @@ TEST_F(LegacySWPictureLayerImplTest, CloneMissingRecordings) {
EXPECT_EQ(tile22, active_tiling->TileAt(2, 2)->id());
}
+TEST_F(LegacySWPictureLayerImplTest,
+ DirectlyCompositedImageRasterSourceCoverage) {
+ gfx::Size tile_size(100, 100);
+ gfx::Size layer_bounds(400, 400);
+
+ scoped_refptr<FakeRasterSource> filled_raster_source =
+ FakeRasterSource::CreateFilled(layer_bounds);
+
+ scoped_refptr<FakeRasterSource> partial_raster_source =
+ FakeRasterSource::CreatePartiallyFilled(layer_bounds,
+ gfx::Rect(150, 150, 100, 100));
+
+ SetupPendingTreeWithFixedTileSize(filled_raster_source, tile_size, Region());
+ pending_layer()->SetDirectlyCompositedImageSize(layer_bounds);
+ ActivateTree();
+
+ PictureLayerTiling* pending_tiling = old_pending_layer()->HighResTiling();
+ PictureLayerTiling* active_tiling = active_layer()->HighResTiling();
+
+ // We should have all tiles on active, and none on pending.
+ EXPECT_EQ(0u, pending_tiling->AllTilesForTesting().size());
+ EXPECT_EQ(5u * 5u, active_tiling->AllTilesForTesting().size());
+
+ // Now put a partially-recorded raster source on the pending tree (and
+ // invalidate everything, since the main thread recording will invalidate
+ // dropped recordings). Because the layer is a directly composited image, all
+ // tiles should be created.
+ SetupPendingTreeWithFixedTileSize(partial_raster_source, tile_size,
+ Region(gfx::Rect(layer_bounds)));
+ EXPECT_EQ(5u * 5u, pending_tiling->AllTilesForTesting().size());
+
+ // Activate the tree. The same tiles should have been moved to active tree.
+ EXPECT_EQ(5u * 5u, pending_tiling->AllTilesForTesting().size());
+ Tile::Id tile00 = pending_tiling->TileAt(0, 0)->id();
+ Tile::Id tile11 = pending_tiling->TileAt(1, 1)->id();
+ Tile::Id tile22 = pending_tiling->TileAt(2, 2)->id();
+
+ // Activate the tree. The tiles are moved to the active tree.
+ ActivateTree();
+ EXPECT_EQ(5u * 5u, active_tiling->AllTilesForTesting().size());
+ EXPECT_EQ(tile00, active_tiling->TileAt(0, 0)->id());
+ EXPECT_EQ(tile11, active_tiling->TileAt(1, 1)->id());
+ EXPECT_EQ(tile22, active_tiling->TileAt(2, 2)->id());
+}
+
TEST_F(LegacySWPictureLayerImplTest, ScrollPastLiveTilesRectAndBack) {
host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(1));
@@ -4954,35 +5007,68 @@ TEST_F(LegacySWPictureLayerImplTest, ScrollPropagatesToPending) {
.ToString());
}
-TEST_F(LegacySWPictureLayerImplTest, UpdateLCDInvalidatesPendingTree) {
- host_impl()->AdvanceToNextFrame(base::TimeDelta::FromMilliseconds(1));
-
- gfx::Size tile_size(102, 102);
+TEST_F(LegacySWPictureLayerImplTest, UpdateLCDTextInvalidatesPendingTree) {
gfx::Size layer_bounds(100, 100);
- gfx::Size viewport_size(100, 100);
-
- host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(viewport_size));
- SetInitialDeviceScaleFactor(1.f);
-
- scoped_refptr<FakeRasterSource> pending_raster_source =
- FakeRasterSource::CreateFilledLCD(layer_bounds);
- SetupPendingTreeWithFixedTileSize(pending_raster_source, tile_size, Region());
+ SetupPendingTree(FakeRasterSource::CreateFilled(layer_bounds));
EXPECT_TRUE(pending_layer()->can_use_lcd_text());
EXPECT_TRUE(pending_layer()->HighResTiling()->has_tiles());
std::vector<Tile*> tiles =
pending_layer()->HighResTiling()->AllTilesForTesting();
+ for (Tile* tile : tiles)
+ EXPECT_TRUE(tile->can_use_lcd_text());
pending_layer()->SetContentsOpaque(false);
- pending_layer()->UpdateCanUseLCDTextAfterCommit();
+ pending_layer()->UpdateTiles();
EXPECT_FALSE(pending_layer()->can_use_lcd_text());
EXPECT_TRUE(pending_layer()->HighResTiling()->has_tiles());
- std::vector<Tile*> new_tiles =
- pending_layer()->HighResTiling()->AllTilesForTesting();
- ASSERT_EQ(tiles.size(), new_tiles.size());
- for (size_t i = 0; i < tiles.size(); ++i)
- EXPECT_NE(tiles[i], new_tiles[i]);
+ for (Tile* tile : pending_layer()->HighResTiling()->AllTilesForTesting())
+ EXPECT_FALSE(tile->can_use_lcd_text());
+
+ pending_layer()->SetContentsOpaque(true);
+ pending_layer()->UpdateTiles();
+ EXPECT_TRUE(pending_layer()->can_use_lcd_text());
+ EXPECT_TRUE(pending_layer()->HighResTiling()->has_tiles());
+ for (Tile* tile : pending_layer()->HighResTiling()->AllTilesForTesting())
+ EXPECT_TRUE(tile->can_use_lcd_text());
+}
+
+TEST_F(LegacySWPictureLayerImplTest, UpdateLCDTextPushToActiveTree) {
+ SetupPendingTree(FakeRasterSource::CreateFilled(gfx::Size(200, 200)));
+ float page_scale = 4.f;
+ SetupDrawPropertiesAndUpdateTiles(pending_layer(), page_scale, 1.0f,
+ page_scale, 1.0f, 0.f, false);
+ EXPECT_TRUE(pending_layer()->can_use_lcd_text());
+ EXPECT_TRUE(pending_layer()->HighResTiling()->can_use_lcd_text());
+ ActivateTree();
+
+ EXPECT_TRUE(active_layer()->can_use_lcd_text());
+ ASSERT_EQ(2u, active_layer()->tilings()->num_tilings());
+ ASSERT_TRUE(active_layer()->HighResTiling()->has_tiles());
+ std::vector<Tile*> tiles =
+ active_layer()->HighResTiling()->AllTilesForTesting();
+ for (Tile* tile : tiles)
+ EXPECT_TRUE(tile->can_use_lcd_text());
+ for (Tile* tile : active_layer()->LowResTiling()->AllTilesForTesting())
+ EXPECT_FALSE(tile->can_use_lcd_text());
+
+ SetupPendingTree(FakeRasterSource::CreateFilled(gfx::Size(200, 200)));
+ SetupDrawPropertiesAndUpdateTiles(pending_layer(), page_scale, 1.0f,
+ page_scale, 1.0f, 0.f, false);
+ pending_layer()->SetContentsOpaque(false);
+ pending_layer()->UpdateTiles();
+ EXPECT_FALSE(pending_layer()->can_use_lcd_text());
+ EXPECT_FALSE(pending_layer()->HighResTiling()->can_use_lcd_text());
+ ActivateTree();
+
+ EXPECT_FALSE(active_layer()->can_use_lcd_text());
+ ASSERT_EQ(2u, active_layer()->tilings()->num_tilings());
+ ASSERT_TRUE(active_layer()->HighResTiling()->has_tiles());
+ for (Tile* tile : active_layer()->HighResTiling()->AllTilesForTesting())
+ EXPECT_FALSE(tile->can_use_lcd_text());
+ for (Tile* tile : active_layer()->LowResTiling()->AllTilesForTesting())
+ EXPECT_FALSE(tile->can_use_lcd_text());
}
TEST_F(LegacySWPictureLayerImplTest, TilingAllTilesDone) {
@@ -5717,7 +5803,6 @@ TEST_F(LegacySWPictureLayerImplTest,
gfx::Size layer_bounds(200, 200);
gfx::Size tile_size(256, 256);
SetupDefaultTreesWithFixedTileSize(layer_bounds, tile_size, Region());
- pending_layer()->SetUseTransformedRasterization(true);
// Start with scale & translation of * 2.25 + (0.25, 0.5).
SetupDrawProperties(pending_layer(), 2.25f, 1.5f, 1.f, 2.25f, 2.25f, false);
@@ -5739,8 +5824,7 @@ TEST_F(LegacySWPictureLayerImplTest,
}
// Change to scale & translation of * 2.25 + (0.75, 0.25).
- // Verifies there is a hysteresis that simple layer movement doesn't update
- // raster translation.
+ // Verifies that layer movement updates raster translation.
SetupDrawProperties(pending_layer(), 2.25f, 1.5f, 1.f, 2.25f, 2.25f, false);
gfx::Transform translate2;
translate2.Translate(0.75f, 0.25f);
@@ -5752,19 +5836,15 @@ TEST_F(LegacySWPictureLayerImplTest,
ASSERT_EQ(1u, pending_layer()->tilings()->num_tilings());
{
PictureLayerTiling* tiling = pending_layer()->tilings()->tiling_at(0);
- EXPECT_EQ(gfx::AxisTransform2d(2.25f, gfx::Vector2dF(0.25f, 0.5f)),
+ EXPECT_EQ(gfx::AxisTransform2d(2.25f, gfx::Vector2dF(0.75f, 0.25f)),
tiling->raster_transform());
EXPECT_EQ(4u, tiling->AllTilesForTesting().size());
for (auto* tile : tiling->AllTilesForTesting())
EXPECT_EQ(tile->raster_transform(), tiling->raster_transform());
}
- // Now change the device scale factor but keep the same total scale.
- // Our policy recomputes raster translation only if raster scale is
- // recomputed. Even if the recomputed scale remains the same, we still
- // updates to new translation value. Old tiles with the same scale but
- // different translation would become non-ideal and deleted on pending
- // layers (in fact, delete ahead due to slot conflict with the new tiling).
+ // Now change the device scale factor but keep the same total scale. Old tiles
+ // with the same scale would become non-ideal and deleted on pending layers.
SetupDrawProperties(pending_layer(), 2.25f, 1.0f, 1.f, 2.25f, 2.25f, false);
pending_layer()->draw_properties().screen_space_transform.ConcatTransform(
translate2);
@@ -5787,8 +5867,6 @@ TEST_F(LegacySWPictureLayerImplTest,
gfx::Size layer_bounds(200, 200);
gfx::Size tile_size(256, 256);
SetupDefaultTreesWithFixedTileSize(layer_bounds, tile_size, Region());
- active_layer()->SetUseTransformedRasterization(true);
- pending_layer()->SetUseTransformedRasterization(true);
// Start with scale & translation of * 2.25 + (0.25, 0.5) on the active layer.
SetupDrawProperties(active_layer(), 2.25f, 1.5f, 1.f, 2.25f, 2.25f, false);
@@ -5989,24 +6067,303 @@ TEST_F(LegacySWPictureLayerImplTest,
pending_layer()->SetBackgroundColor(SK_ColorWHITE);
pending_layer()->SetContentsOpaque(true);
pending_layer()->SetOffsetToTransformParent(gfx::Vector2dF(0.2, 0.3));
+ host_impl()->pending_tree()->set_needs_update_draw_properties();
+ UpdateDrawProperties(host_impl()->pending_tree());
EXPECT_TRUE(pending_layer()->contents_opaque());
EXPECT_TRUE(pending_layer()->contents_opaque_for_text());
- EXPECT_EQ(LCDTextDisallowedReason::kNonIntegralXOffset,
+ EXPECT_EQ(LCDTextDisallowedReason::kNone,
pending_layer()->ComputeLCDTextDisallowedReasonForTesting());
+ ASSERT_TRUE(pending_layer()->HighResTiling());
+ EXPECT_EQ(gfx::Vector2dF(0.2, 0.3),
+ pending_layer()->HighResTiling()->raster_transform().translation());
- pending_layer()->SetUseTransformedRasterization(true);
- EXPECT_FALSE(pending_layer()->contents_opaque());
- EXPECT_FALSE(pending_layer()->contents_opaque_for_text());
- EXPECT_EQ(LCDTextDisallowedReason::kContentsNotOpaque,
+ // Adding will-change:transform will keep the current raster translation.
+ pending_layer()->SetHasWillChangeTransformHint(true);
+ host_impl()->pending_tree()->set_needs_update_draw_properties();
+ UpdateDrawProperties(host_impl()->pending_tree());
+ EXPECT_TRUE(pending_layer()->contents_opaque());
+ EXPECT_TRUE(pending_layer()->contents_opaque_for_text());
+ EXPECT_EQ(LCDTextDisallowedReason::kWillChangeTransform,
+ pending_layer()->ComputeLCDTextDisallowedReasonForTesting());
+ ASSERT_TRUE(pending_layer()->HighResTiling());
+ EXPECT_EQ(gfx::Vector2dF(0.2, 0.3),
+ pending_layer()->HighResTiling()->raster_transform().translation());
+
+ // We should not update raster translation when there is
+ // will-change:transform.
+ pending_layer()->SetOffsetToTransformParent(gfx::Vector2dF(0.4, 0.5));
+ host_impl()->pending_tree()->set_needs_update_draw_properties();
+ UpdateDrawProperties(host_impl()->pending_tree());
+ EXPECT_TRUE(pending_layer()->contents_opaque());
+ EXPECT_TRUE(pending_layer()->contents_opaque_for_text());
+ EXPECT_EQ(LCDTextDisallowedReason::kWillChangeTransform,
pending_layer()->ComputeLCDTextDisallowedReasonForTesting());
+ ASSERT_TRUE(pending_layer()->HighResTiling());
+ EXPECT_EQ(gfx::Vector2dF(0.2, 0.3),
+ pending_layer()->HighResTiling()->raster_transform().translation());
- // 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,
+ // Removing will-change:transform will update raster translation.
+ pending_layer()->SetHasWillChangeTransformHint(false);
+ host_impl()->pending_tree()->set_needs_update_draw_properties();
+ UpdateDrawProperties(host_impl()->pending_tree());
+ EXPECT_TRUE(pending_layer()->contents_opaque());
+ EXPECT_TRUE(pending_layer()->contents_opaque_for_text());
+ EXPECT_EQ(LCDTextDisallowedReason::kNone,
pending_layer()->ComputeLCDTextDisallowedReasonForTesting());
+ ASSERT_TRUE(pending_layer()->HighResTiling());
+ EXPECT_EQ(gfx::Vector2dF(0.4, 0.5),
+ pending_layer()->HighResTiling()->raster_transform().translation());
+}
+
+enum {
+ kCanUseLCDText = 1 << 0,
+ kLayersAlwaysAllowedLCDText = 1 << 1,
+};
+
+class LCDTextTest : public PictureLayerImplTest,
+ public testing::WithParamInterface<unsigned> {
+ protected:
+ LayerTreeSettings CreateSettings() override {
+ auto settings = PictureLayerImplTest::CreateSettings();
+ settings.can_use_lcd_text = GetParam() & kCanUseLCDText;
+ settings.layers_always_allowed_lcd_text =
+ GetParam() & kLayersAlwaysAllowedLCDText;
+ return settings;
+ }
+
+ void SetUp() override {
+ PictureLayerImplTest::SetUp();
+
+ SetupPendingTree(FakeRasterSource::CreateFilled(gfx::Size(200, 200)));
+
+ tree_ = host_impl()->pending_tree();
+ layer_ = pending_layer();
+ descendant_ = AddLayer<PictureLayerImpl>(tree_);
+
+ layer_->SetContentsOpaque(true);
+ layer_->SetDrawsContent(true);
+ layer_->SetBounds(gfx::Size(200, 200));
+ ASSERT_TRUE(layer_->CanHaveTilings());
+
+ descendant_->SetContentsOpaque(true);
+ descendant_->SetDrawsContent(true);
+ descendant_->SetBounds(gfx::Size(200, 200));
+ Region invalidation;
+ descendant_->UpdateRasterSource(
+ FakeRasterSource::CreateFilled(gfx::Size(200, 200)), &invalidation,
+ nullptr, nullptr);
+ ASSERT_TRUE(layer_->CanHaveTilings());
+
+ CreateTransformNode(layer_);
+ CreateEffectNode(layer_);
+ CopyProperties(layer_, descendant_);
+ CreateTransformNode(descendant_);
+ CreateEffectNode(descendant_);
+ }
+
+ void CheckCanUseLCDText(LCDTextDisallowedReason expected_disallowed_reason,
+ const char* description,
+ PictureLayerImpl* layer = nullptr) {
+ UpdateDrawProperties(tree_);
+
+ if (GetParam() & kLayersAlwaysAllowedLCDText)
+ expected_disallowed_reason = LCDTextDisallowedReason::kNone;
+ else if (!(GetParam() & kCanUseLCDText))
+ expected_disallowed_reason = LCDTextDisallowedReason::kSetting;
+
+ SCOPED_TRACE(description);
+ if (layer) {
+ EXPECT_EQ(expected_disallowed_reason,
+ layer->ComputeLCDTextDisallowedReasonForTesting());
+ } else {
+ EXPECT_EQ(expected_disallowed_reason,
+ layer_->ComputeLCDTextDisallowedReasonForTesting());
+ EXPECT_EQ(expected_disallowed_reason,
+ descendant_->ComputeLCDTextDisallowedReasonForTesting());
+ }
+ }
+
+ LayerTreeImpl* tree_ = nullptr;
+ PictureLayerImpl* layer_ = nullptr;
+ PictureLayerImpl* descendant_ = nullptr;
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+ LCDTextTest,
+ testing::Values(0,
+ kCanUseLCDText,
+ kLayersAlwaysAllowedLCDText,
+ kCanUseLCDText |
+ kLayersAlwaysAllowedLCDText));
+
+TEST_P(LCDTextTest, IdentityTransform) {
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, "identity transform");
+}
+
+TEST_P(LCDTextTest, IntegralTransform) {
+ gfx::Transform integral_translation;
+ integral_translation.Translate(1.0, 2.0);
+ SetTransform(layer_, integral_translation);
+
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, "integral transform");
+}
+
+TEST_P(LCDTextTest, NonIntegralTranslation) {
+ // Non-integral translation.
+ gfx::Transform non_integral_translation;
+ non_integral_translation.Translate(1.5, 2.5);
+ SetTransform(layer_, non_integral_translation);
+ // We can use LCD-text as raster translation can align the text to physical
+ // pixels for fragtional transform in the render target.
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone,
+ "non-integeral translation");
+
+ SetTransform(layer_, gfx::Transform());
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, "identity transform");
+}
+
+TEST_P(LCDTextTest, NonIntegralTranslationAboveRenderTarget) {
+ // Non-integral translation above render target.
+ gfx::Transform non_integral_translation;
+ non_integral_translation.Translate(1.5, 2.5);
+ SetTransform(layer_, non_integral_translation);
+ SetRenderSurfaceReason(layer_, RenderSurfaceReason::kTest);
+ // Raster translation can't handle fractional transform above the render
+ // target, so LCD text is not allowed.
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation,
+ "non-integeral translation above render target");
+ SetTransform(layer_, gfx::Transform());
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, "identity transform");
+}
+
+TEST_P(LCDTextTest, NonTranslation) {
+ // Rotation.
+ gfx::Transform rotation;
+ rotation.Rotate(10.0);
+ SetTransform(layer_, rotation);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation,
+ "Rotation transform");
+
+ // Scale. LCD text is allowed.
+ gfx::Transform scale;
+ scale.Scale(2.0, 2.0);
+ SetTransform(layer_, scale);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, "Scale transform");
+
+ // Skew.
+ gfx::Transform skew;
+ skew.Skew(10.0, 0.0);
+ SetTransform(layer_, skew);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation,
+ "Skew transform");
+
+ SetTransform(layer_, gfx::Transform());
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, "identity transform");
+}
+
+TEST_P(LCDTextTest, NonTranslationAboveRenderTarget) {
+ SetRenderSurfaceReason(layer_, RenderSurfaceReason::kTest);
+
+ // Rotation.
+ gfx::Transform rotation;
+ rotation.Rotate(10.0);
+ SetTransform(layer_, rotation);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation,
+ "rotation transform above render target");
+
+ // Scale. LCD-text is allowed.
+ gfx::Transform scale;
+ scale.Scale(2.0, 2.0);
+ // Apply perspective to prevent the scale from applying to the layers below
+ // the render target.
+ scale.ApplyPerspectiveDepth(10);
+ SetTransform(layer_, scale);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation,
+ "scale transform above render target");
+
+ // Skew.
+ gfx::Transform skew;
+ skew.Skew(10.0, 0.0);
+ SetTransform(layer_, skew);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation,
+ "skew transform above render target");
+
+ SetTransform(layer_, gfx::Transform());
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, "identity transform");
+}
+
+TEST_P(LCDTextTest, Opacity) {
+ // LCD-text is allowed with opacity paint property.
+ SetOpacity(layer_, 0.5f);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, "opacity: 0.5");
+ SetOpacity(layer_, 1.f);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, "opacity: 1.0");
+}
+
+TEST_P(LCDTextTest, ContentsNotOpaque) {
+ // Non-opaque content and opaque background.
+ layer_->SetContentsOpaque(false);
+ layer_->SetBackgroundColor(SK_ColorGREEN);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kContentsNotOpaque,
+ "contents not opaque", layer_);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone,
+ "descedant of contents not opaque", descendant_);
+
+ // Non-opaque content and non-opaque background.
+ layer_->SetBackgroundColor(SkColorSetARGB(128, 255, 255, 255));
+ CheckCanUseLCDText(LCDTextDisallowedReason::kBackgroundColorNotOpaque,
+ "background not opaque", layer_);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone,
+ "descendant of contents not opaque", descendant_);
+
+ layer_->SetContentsOpaque(true);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, "contents opaque");
+}
+
+TEST_P(LCDTextTest, WillChangeTransform) {
+ layer_->SetHasWillChangeTransformHint(true);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kWillChangeTransform,
+ "will-change:transform", layer_);
+ // TODO(crbug.com/1114504): will-change:transform should apply to descendants.
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone,
+ "descendant of will-change: transform", descendant_);
+
+ layer_->SetHasWillChangeTransformHint(false);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone,
+ "no will-change: transform");
+}
+
+TEST_P(LCDTextTest, Filter) {
+ FilterOperations blur_filter;
+ blur_filter.Append(FilterOperation::CreateBlurFilter(4.0f));
+ SetFilter(layer_, blur_filter);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kPixelOrColorEffect, "filter");
+
+ SetFilter(layer_, FilterOperations());
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, "no filter");
+}
+
+TEST_P(LCDTextTest, ContentsOpaqueForText) {
+ layer_->SetContentsOpaque(false);
+ layer_->SetBackgroundColor(SK_ColorGREEN);
+ layer_->SetContentsOpaqueForText(true);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, "contents opaque for text",
+ layer_);
+
+ layer_->SetContentsOpaqueForText(false);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kContentsNotOpaque,
+ "contents not opaque for text", layer_);
+}
+
+TEST_P(LCDTextTest, TransformAnimation) {
+ GetTransformNode(layer_)->has_potential_animation = true;
+ SetLocalTransformChanged(layer_);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kTransformAnimation,
+ "transform animation");
+
+ GetTransformNode(layer_)->has_potential_animation = false;
+ SetLocalTransformChanged(layer_);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, "no transform animation");
}
} // namespace
diff --git a/chromium/cc/layers/render_surface_impl.cc b/chromium/cc/layers/render_surface_impl.cc
index f3cd766fdd5..48421224424 100644
--- a/chromium/cc/layers/render_surface_impl.cc
+++ b/chromium/cc/layers/render_surface_impl.cc
@@ -370,7 +370,7 @@ std::unique_ptr<viz::RenderPass> RenderSurfaceImpl::CreateRenderPass() {
viz::RenderPass::Create(num_contributors_);
gfx::Rect damage_rect = GetDamageRect();
damage_rect.Intersect(content_rect());
- pass->SetNew(id(), content_rect(), damage_rect,
+ pass->SetNew(render_pass_id(), content_rect(), damage_rect,
draw_properties_.screen_space_transform);
pass->filters = Filters();
pass->backdrop_filters = BackdropFilters();
@@ -451,9 +451,9 @@ void RenderSurfaceImpl::AppendQuads(DrawMode draw_mode,
gfx::RectF tex_coord_rect(gfx::Rect(content_rect().size()));
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, gfx::PointF(),
- tex_coord_rect,
+ /*needs_blending=*/true, render_pass_id(), mask_resource_id,
+ mask_uv_rect, mask_texture_size, surface_contents_scale,
+ gfx::PointF(), tex_coord_rect,
!layer_tree_impl_->settings().enable_edge_anti_aliasing,
OwningEffectNode()->backdrop_filter_quality,
can_use_cached_backdrop_filtered_result_);
diff --git a/chromium/cc/layers/render_surface_impl.h b/chromium/cc/layers/render_surface_impl.h
index fac0274a79d..9ce6bd5a591 100644
--- a/chromium/cc/layers/render_surface_impl.h
+++ b/chromium/cc/layers/render_surface_impl.h
@@ -161,6 +161,7 @@ class CC_EXPORT RenderSurfaceImpl {
}
uint64_t id() const { return stable_id_; }
+ viz::RenderPassId render_pass_id() const { return viz::RenderPassId{id()}; }
bool HasMaskingContributingSurface() const;
diff --git a/chromium/cc/layers/render_surface_unittest.cc b/chromium/cc/layers/render_surface_unittest.cc
index 30e41c9f3dd..ab5672282f9 100644
--- a/chromium/cc/layers/render_surface_unittest.cc
+++ b/chromium/cc/layers/render_surface_unittest.cc
@@ -205,7 +205,7 @@ TEST(RenderSurfaceTest, SanityCheckSurfaceCreatesCorrectRenderPass) {
auto pass = render_surface->CreateRenderPass();
- EXPECT_EQ(2u, pass->id);
+ EXPECT_EQ(viz::RenderPassId{2}, pass->id);
EXPECT_EQ(content_rect, pass->output_rect);
EXPECT_EQ(origin, pass->transform_to_root_target);
}
diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.h b/chromium/cc/layers/scrollbar_layer_impl_base.h
index a887ab47ed2..bc3648e55d0 100644
--- a/chromium/cc/layers/scrollbar_layer_impl_base.h
+++ b/chromium/cc/layers/scrollbar_layer_impl_base.h
@@ -127,6 +127,11 @@ inline ScrollbarLayerImplBase* ToScrollbarLayer(LayerImpl* layer) {
return static_cast<ScrollbarLayerImplBase*>(layer);
}
+inline const ScrollbarLayerImplBase* ToScrollbarLayer(const LayerImpl* layer) {
+ DCHECK(layer->IsScrollbarLayer());
+ return static_cast<const ScrollbarLayerImplBase*>(layer);
+}
+
using ScrollbarSet = base::flat_set<ScrollbarLayerImplBase*>;
} // namespace cc
diff --git a/chromium/cc/layers/scrollbar_layer_unittest.cc b/chromium/cc/layers/scrollbar_layer_unittest.cc
index 4bf6b64a851..6da184e247e 100644
--- a/chromium/cc/layers/scrollbar_layer_unittest.cc
+++ b/chromium/cc/layers/scrollbar_layer_unittest.cc
@@ -227,6 +227,66 @@ TEST_F(ScrollbarLayerTest, RepaintOverlayWhenResourceDisposed) {
}
}
+TEST_F(ScrollbarLayerTest, SetNeedsDisplayDoesNotRequireUpdate) {
+ scoped_refptr<Layer> layer_tree_root = Layer::Create();
+ scoped_refptr<Layer> content_layer = Layer::Create();
+ scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer =
+ FakePaintedScrollbarLayer::Create(true, true,
+ layer_tree_root->element_id());
+
+ // Setup.
+ {
+ layer_tree_root->AddChild(content_layer);
+ layer_tree_root->AddChild(scrollbar_layer);
+ layer_tree_host_->SetRootLayer(layer_tree_root);
+ scrollbar_layer->SetIsDrawable(true);
+ scrollbar_layer->SetBounds(gfx::Size(100, 100));
+ layer_tree_root->SetBounds(gfx::Size(100, 200));
+ content_layer->SetBounds(gfx::Size(100, 200));
+ }
+
+ layer_tree_host_->UpdateLayers();
+
+ // Simulate commit to compositor thread.
+ scrollbar_layer->PushPropertiesTo(
+ scrollbar_layer->CreateLayerImpl(layer_tree_host_->active_tree()).get());
+ scrollbar_layer->fake_scrollbar()->set_needs_repaint_thumb(false);
+ scrollbar_layer->fake_scrollbar()->set_needs_repaint_track(false);
+
+ EXPECT_FALSE(scrollbar_layer->Update());
+
+ // Opacity changes should cause an update.
+ {
+ scrollbar_layer->fake_scrollbar()->set_thumb_opacity(0.3f);
+ EXPECT_TRUE(scrollbar_layer->Update());
+ }
+
+ // Needing a thumb repaint should cause an update.
+ {
+ scrollbar_layer->fake_scrollbar()->set_needs_repaint_thumb(true);
+ EXPECT_TRUE(scrollbar_layer->Update());
+ scrollbar_layer->fake_scrollbar()->set_needs_repaint_thumb(false);
+ EXPECT_FALSE(scrollbar_layer->Update());
+ }
+
+ // Needing a track repaint should cause an update.
+ {
+ scrollbar_layer->fake_scrollbar()->set_needs_repaint_track(true);
+ EXPECT_TRUE(scrollbar_layer->Update());
+ scrollbar_layer->fake_scrollbar()->set_needs_repaint_track(false);
+ EXPECT_FALSE(scrollbar_layer->Update());
+ }
+
+ // A scroll will cause |SetNeedsDisplay| to be called, but the scrollbar parts
+ // are used for invalidation, rather than the scrollbar layer itself. This
+ // should not cause an update. This is important for performance as an update
+ // will cause a commit on every scroll offset change.
+ {
+ scrollbar_layer->SetNeedsDisplay();
+ EXPECT_FALSE(scrollbar_layer->Update());
+ }
+}
+
class FakeNinePatchScrollbar : public FakeScrollbar {
public:
FakeNinePatchScrollbar() {
@@ -1263,6 +1323,8 @@ TEST_F(ScrollbarLayerTestResourceCreationAndRelease, TestResourceUpdate) {
// Simulate commit to compositor thread.
scrollbar_layer->PushPropertiesTo(
scrollbar_layer->CreateLayerImpl(layer_tree_host_->active_tree()).get());
+ scrollbar_layer->fake_scrollbar()->set_needs_repaint_thumb(false);
+ scrollbar_layer->fake_scrollbar()->set_needs_repaint_track(false);
EXPECT_FALSE(scrollbar_layer->Update());
EXPECT_NE(0, scrollbar_layer->track_resource_id());
diff --git a/chromium/cc/layers/solid_color_layer_impl_unittest.cc b/chromium/cc/layers/solid_color_layer_impl_unittest.cc
index 1e5bb9af082..5978391c5bb 100644
--- a/chromium/cc/layers/solid_color_layer_impl_unittest.cc
+++ b/chromium/cc/layers/solid_color_layer_impl_unittest.cc
@@ -177,6 +177,7 @@ TEST_F(SolidColorLayerImplTest, VerifyNeedsBlending) {
auto animation_host = AnimationHost::CreateForTesting(ThreadInstance::MAIN);
std::unique_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(
&client, &task_graph_runner, animation_host.get());
+ host->CreateFakeLayerTreeHostImpl();
host->SetRootLayer(root);
UpdateDrawProperties(host.get());
diff --git a/chromium/cc/layers/texture_layer_unittest.cc b/chromium/cc/layers/texture_layer_unittest.cc
index 3ef6ef05af0..5ac82c82139 100644
--- a/chromium/cc/layers/texture_layer_unittest.cc
+++ b/chromium/cc/layers/texture_layer_unittest.cc
@@ -595,8 +595,9 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest {
!layer_tree_host()->GetSettings().single_thread_proxy_scheduler;
return std::make_unique<TestLayerTreeFrameSink>(
compositor_context_provider, std::move(worker_context_provider),
- gpu_memory_buffer_manager(), renderer_settings, ImplThreadTaskRunner(),
- synchronous_composite, disable_display_vsync, refresh_rate);
+ gpu_memory_buffer_manager(), renderer_settings, &debug_settings_,
+ ImplThreadTaskRunner(), synchronous_composite, disable_display_vsync,
+ refresh_rate);
}
void AdvanceTestCase() {
@@ -1495,8 +1496,8 @@ class SoftwareTextureLayerTest : public LayerTreeTest {
!layer_tree_host()->GetSettings().single_thread_proxy_scheduler;
auto sink = std::make_unique<TestLayerTreeFrameSink>(
nullptr, nullptr, gpu_memory_buffer_manager(), renderer_settings,
- ImplThreadTaskRunner(), synchronous_composite, disable_display_vsync,
- refresh_rate);
+ &debug_settings_, ImplThreadTaskRunner(), synchronous_composite,
+ disable_display_vsync, refresh_rate);
frame_sink_ = sink.get();
num_frame_sinks_created_++;
return sink;
diff --git a/chromium/cc/layers/viewport.cc b/chromium/cc/layers/viewport.cc
index 46b5f15dd64..0a9a9da33cc 100644
--- a/chromium/cc/layers/viewport.cc
+++ b/chromium/cc/layers/viewport.cc
@@ -4,6 +4,8 @@
#include "cc/layers/viewport.h"
+#include <algorithm>
+
#include "base/check.h"
#include "base/memory/ptr_util.h"
#include "cc/input/browser_controls_offset_manager.h"
@@ -52,15 +54,15 @@ Viewport::ScrollResult Viewport::ScrollBy(const gfx::Vector2dF& physical_delta,
gfx::Vector2dF pending_scroll_node_delta = scroll_node_delta;
// Attempt to scroll inner viewport first.
- pending_scroll_node_delta -= host_impl_->ScrollSingleNode(
+ pending_scroll_node_delta -= host_impl_->GetInputHandler().ScrollSingleNode(
*InnerScrollNode(), pending_scroll_node_delta, viewport_point,
- is_direct_manipulation, &scroll_tree());
+ is_direct_manipulation);
// Now attempt to scroll the outer viewport.
if (scroll_outer_viewport) {
- pending_scroll_node_delta -= host_impl_->ScrollSingleNode(
+ pending_scroll_node_delta -= host_impl_->GetInputHandler().ScrollSingleNode(
*OuterScrollNode(), pending_scroll_node_delta, viewport_point,
- is_direct_manipulation, &scroll_tree());
+ is_direct_manipulation);
}
ScrollResult result;
@@ -74,7 +76,8 @@ bool Viewport::CanScroll(const ScrollNode& node,
const ScrollState& scroll_state) const {
DCHECK(ShouldScroll(node));
- bool result = host_impl_->CanConsumeDelta(scroll_state, *InnerScrollNode());
+ bool result = host_impl_->GetInputHandler().CanConsumeDelta(
+ scroll_state, *InnerScrollNode());
// If the passed in node is the inner viewport, we're not interested in the
// scrollability of the outer viewport. See LTHI::GetNodeToScroll for how the
@@ -82,10 +85,58 @@ bool Viewport::CanScroll(const ScrollNode& node,
if (node.scrolls_inner_viewport)
return result;
- result |= host_impl_->CanConsumeDelta(scroll_state, *OuterScrollNode());
+ result |= host_impl_->GetInputHandler().CanConsumeDelta(scroll_state,
+ *OuterScrollNode());
return result;
}
+gfx::Vector2dF Viewport::ComputeClampedDelta(
+ const gfx::Vector2dF& scroll_delta) const {
+ // When clamping for the outer viewport, we need to distribute the scroll
+ // between inner and outer to get the clamped value. The returned values
+ // from ComputeScrollDelta are unscaled, so we have to do scaling
+ // conversions each step of the way.
+ ScrollNode* inner_node = InnerScrollNode();
+ gfx::Vector2dF inner_delta = host_impl_->GetInputHandler().ComputeScrollDelta(
+ *inner_node, scroll_delta);
+
+ float page_scale = host_impl_->active_tree()->page_scale_factor_for_scroll();
+ gfx::Vector2dF unscaled_delta = scroll_delta;
+ unscaled_delta.Scale(1.f / page_scale);
+
+ gfx::Vector2dF remaining_delta = unscaled_delta - inner_delta;
+ remaining_delta.Scale(page_scale);
+
+ const ScrollNode* outer_node = OuterScrollNode();
+ gfx::Vector2dF outer_delta = host_impl_->GetInputHandler().ComputeScrollDelta(
+ *outer_node, remaining_delta);
+
+ gfx::Vector2dF combined_delta = inner_delta + outer_delta;
+ combined_delta.Scale(page_scale);
+
+ return combined_delta;
+}
+
+gfx::SizeF Viewport::GetInnerViewportSizeExcludingScrollbars() const {
+ DCHECK(InnerScrollNode());
+ ScrollNode* inner_node = InnerScrollNode();
+ gfx::SizeF inner_bounds(inner_node->container_bounds);
+ ScrollNode* outer_node = OuterScrollNode();
+ ScrollbarSet scrollbars = host_impl_->ScrollbarsFor(outer_node->element_id);
+ gfx::SizeF scrollbars_size;
+ for (const auto* scrollbar : scrollbars) {
+ if (scrollbar->orientation() == ScrollbarOrientation::VERTICAL) {
+ scrollbars_size.set_width(scrollbar->bounds().width());
+ } else {
+ DCHECK(scrollbar->orientation() == ScrollbarOrientation::HORIZONTAL);
+ scrollbars_size.set_height(scrollbar->bounds().height());
+ }
+ }
+
+ inner_bounds.Enlarge(-scrollbars_size.width(), -scrollbars_size.height());
+ return inner_bounds;
+}
+
void Viewport::ScrollByInnerFirst(const gfx::Vector2dF& delta) {
DCHECK(InnerScrollNode());
gfx::Vector2dF unused_delta = scroll_tree().ScrollBy(
@@ -119,13 +170,13 @@ gfx::Vector2dF Viewport::ScrollAnimated(const gfx::Vector2dF& delta,
ScrollNode* inner_node = InnerScrollNode();
gfx::Vector2dF inner_delta =
- host_impl_->ComputeScrollDelta(*inner_node, delta);
+ host_impl_->GetInputHandler().ComputeScrollDelta(*inner_node, delta);
gfx::Vector2dF pending_delta = scaled_delta - inner_delta;
pending_delta.Scale(scale_factor);
- gfx::Vector2dF outer_delta =
- host_impl_->ComputeScrollDelta(*outer_node, pending_delta);
+ gfx::Vector2dF outer_delta = host_impl_->GetInputHandler().ComputeScrollDelta(
+ *outer_node, pending_delta);
if (inner_delta.IsZero() && outer_delta.IsZero())
return gfx::Vector2dF(0, 0);
diff --git a/chromium/cc/layers/viewport.h b/chromium/cc/layers/viewport.h
index d57963371d3..ed6b861fcd7 100644
--- a/chromium/cc/layers/viewport.h
+++ b/chromium/cc/layers/viewport.h
@@ -92,6 +92,18 @@ class CC_EXPORT Viewport {
// method.
bool CanScroll(const ScrollNode& node, const ScrollState& scroll_state) const;
+ // Takes |scroll_delta| and returns a clamped delta based on distributing
+ // |scroll_delta| first to the inner viewport, then the outer viewport.
+ // The passed delta must be scaled by the page scale factor and the returned
+ // delta will also be scaled.
+ gfx::Vector2dF ComputeClampedDelta(const gfx::Vector2dF& scroll_delta) const;
+
+ // Returns the innert viewport size, excluding scrollbars. This is not the
+ // same as the inner viewport container_bounds size, which does not account
+ // for scrollbars. This method can be useful for calculating the area of the
+ // inner viewport where content is visible.
+ gfx::SizeF GetInnerViewportSizeExcludingScrollbars() const;
+
private:
explicit Viewport(LayerTreeHostImpl* host_impl);
diff --git a/chromium/cc/metrics/average_lag_tracker.cc b/chromium/cc/metrics/average_lag_tracker.cc
new file mode 100644
index 00000000000..05e7e41c3a1
--- /dev/null
+++ b/chromium/cc/metrics/average_lag_tracker.cc
@@ -0,0 +1,230 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/metrics/average_lag_tracker.h"
+
+#include <algorithm>
+
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/string_util.h"
+
+namespace cc {
+
+AverageLagTracker::AverageLagTracker(FinishTimeType finish_time_type)
+ : finish_time_type_(finish_time_type) {}
+AverageLagTracker::~AverageLagTracker() = default;
+
+void AverageLagTracker::AddScrollEventInFrame(const EventInfo& event_info) {
+ if (event_info.event_type == EventType::ScrollBegin) {
+ AddScrollBeginInFrame(event_info);
+ } else if (!last_event_timestamp_.is_null()) {
+ AddScrollUpdateInFrame(event_info);
+ }
+
+ last_event_timestamp_ = event_info.event_timestamp;
+ last_event_accumulated_delta_ += event_info.event_scroll_delta;
+ last_rendered_accumulated_delta_ += event_info.predicted_scroll_delta;
+}
+
+std::string AverageLagTracker::GetAverageLagMetricName(EventType event) const {
+ std::string metric_name = finish_time_type_ == FinishTimeType::GpuSwapBegin
+ ? "AverageLag"
+ : "AverageLagPresentation";
+
+ std::string event_name =
+ event == EventType::ScrollBegin ? "ScrollBegin" : "ScrollUpdate";
+
+ return base::JoinString(
+ {"Event", "Latency", event_name, "Touch", metric_name}, ".");
+}
+
+void AverageLagTracker::AddScrollBeginInFrame(const EventInfo& event_info) {
+ DCHECK_EQ(event_info.event_type, EventType::ScrollBegin);
+
+ // Flush all unfinished frames.
+ while (!frame_lag_infos_.empty()) {
+ frame_lag_infos_.front().lag_area += LagForUnfinishedFrame(
+ frame_lag_infos_.front().rendered_accumulated_delta);
+ frame_lag_infos_.front().lag_area_no_prediction += LagForUnfinishedFrame(
+ frame_lag_infos_.front().rendered_accumulated_delta_no_prediction);
+
+ // Record UMA when it's the last item in queue.
+ CalculateAndReportAverageLagUma(frame_lag_infos_.size() == 1);
+ }
+ // |accumulated_lag_| should be cleared/reset.
+ DCHECK_EQ(accumulated_lag_, 0);
+
+ // Create ScrollBegin report, with report time equals to the frame
+ // timestamp.
+ LagAreaInFrame first_frame(event_info.finish_timestamp);
+ frame_lag_infos_.push_back(first_frame);
+
+ // Reset fields.
+ last_reported_time_ = event_info.event_timestamp;
+ last_finished_frame_time_ = event_info.event_timestamp;
+ last_event_accumulated_delta_ = 0;
+ last_rendered_accumulated_delta_ = 0;
+ is_begin_ = true;
+}
+
+void AverageLagTracker::AddScrollUpdateInFrame(const EventInfo& event_info) {
+ DCHECK_EQ(event_info.event_type, EventType::ScrollUpdate);
+
+ // Only accept events in nondecreasing order.
+ if ((event_info.event_timestamp - last_event_timestamp_).InMilliseconds() < 0)
+ return;
+
+ // Pop all frames where frame_time <= event_timestamp.
+ while (!frame_lag_infos_.empty() &&
+ frame_lag_infos_.front().frame_time <= event_info.event_timestamp) {
+ base::TimeTicks front_time =
+ std::max(last_event_timestamp_, last_finished_frame_time_);
+ base::TimeTicks back_time = frame_lag_infos_.front().frame_time;
+ frame_lag_infos_.front().lag_area +=
+ LagBetween(front_time, back_time, event_info.event_scroll_delta,
+ event_info.event_timestamp,
+ frame_lag_infos_.front().rendered_accumulated_delta);
+ frame_lag_infos_.front().lag_area_no_prediction += LagBetween(
+ front_time, back_time, event_info.event_scroll_delta,
+ event_info.event_timestamp,
+ frame_lag_infos_.front().rendered_accumulated_delta_no_prediction);
+
+ CalculateAndReportAverageLagUma();
+ }
+
+ // Initialize a new LagAreaInFrame when current_frame_time > frame_time.
+ if (frame_lag_infos_.empty() ||
+ event_info.finish_timestamp > frame_lag_infos_.back().frame_time) {
+ LagAreaInFrame new_frame(event_info.finish_timestamp,
+ last_rendered_accumulated_delta_,
+ last_event_accumulated_delta_);
+ frame_lag_infos_.push_back(new_frame);
+ }
+
+ // last_frame_time <= event_timestamp < frame_time
+ if (!frame_lag_infos_.empty()) {
+ // The front element in queue (if any) must satisfy frame_time >
+ // event_timestamp, otherwise it would be popped in the while loop.
+ DCHECK_LE(last_finished_frame_time_, event_info.event_timestamp);
+ DCHECK_LE(event_info.event_timestamp, frame_lag_infos_.front().frame_time);
+ base::TimeTicks front_time =
+ std::max(last_finished_frame_time_, last_event_timestamp_);
+ base::TimeTicks back_time = event_info.event_timestamp;
+
+ frame_lag_infos_.front().lag_area +=
+ LagBetween(front_time, back_time, event_info.event_scroll_delta,
+ event_info.event_timestamp,
+ frame_lag_infos_.front().rendered_accumulated_delta);
+
+ frame_lag_infos_.front().lag_area_no_prediction += LagBetween(
+ front_time, back_time, event_info.event_scroll_delta,
+ event_info.event_timestamp,
+ frame_lag_infos_.front().rendered_accumulated_delta_no_prediction);
+ }
+}
+
+float AverageLagTracker::LagBetween(base::TimeTicks front_time,
+ base::TimeTicks back_time,
+ const float scroll_delta,
+ base::TimeTicks event_timestamp,
+ float rendered_accumulated_delta) {
+ // In some tests, we use const event time. return 0 to avoid divided by 0.
+ if (event_timestamp == last_event_timestamp_)
+ return 0;
+
+ float front_delta =
+ (last_event_accumulated_delta_ +
+ (scroll_delta * ((front_time - last_event_timestamp_) /
+ (event_timestamp - last_event_timestamp_)))) -
+ rendered_accumulated_delta;
+
+ float back_delta =
+ (last_event_accumulated_delta_ +
+ scroll_delta * ((back_time - last_event_timestamp_) /
+ (event_timestamp - last_event_timestamp_))) -
+ rendered_accumulated_delta;
+
+ // Calculate the trapezoid area.
+ if (front_delta * back_delta >= 0) {
+ return 0.5f * std::abs(front_delta + back_delta) *
+ (back_time - front_time).InMillisecondsF();
+ }
+
+ // Corner case that rendered_accumulated_delta is in between of front_pos
+ // and back_pos.
+ return 0.5f *
+ std::abs((front_delta * front_delta + back_delta * back_delta) /
+ (back_delta - front_delta)) *
+ (back_time - front_time).InMillisecondsF();
+}
+
+float AverageLagTracker::LagForUnfinishedFrame(
+ float rendered_accumulated_delta) {
+ base::TimeTicks last_time =
+ std::max(last_event_timestamp_, last_finished_frame_time_);
+ return std::abs(last_event_accumulated_delta_ - rendered_accumulated_delta) *
+ (frame_lag_infos_.front().frame_time - last_time).InMillisecondsF();
+}
+
+void AverageLagTracker::CalculateAndReportAverageLagUma(bool send_anyway) {
+ DCHECK(!frame_lag_infos_.empty());
+ const LagAreaInFrame& frame_lag = frame_lag_infos_.front();
+
+ DCHECK_GE(frame_lag.lag_area, 0.f);
+ DCHECK_GE(frame_lag.lag_area_no_prediction, 0.f);
+ accumulated_lag_ += frame_lag.lag_area;
+ accumulated_lag_no_prediction_ += frame_lag.lag_area_no_prediction;
+
+ if (is_begin_) {
+ DCHECK_EQ(accumulated_lag_, accumulated_lag_no_prediction_);
+ }
+
+ // |send_anyway| is true when we are flush all remaining frames on next
+ // |ScrollBegin|. Otherwise record UMA when it's ScrollBegin, or when
+ // reaching the 1 second gap.
+ if (send_anyway || is_begin_ ||
+ (frame_lag.frame_time - last_reported_time_) >=
+ base::TimeDelta::FromSeconds(1)) {
+ const EventType event_type =
+ is_begin_ ? EventType::ScrollBegin : EventType::ScrollUpdate;
+
+ const float time_delta =
+ (frame_lag.frame_time - last_reported_time_).InMillisecondsF();
+ const float scaled_lag = accumulated_lag_ / time_delta;
+ base::UmaHistogramCounts1000(GetAverageLagMetricName(event_type),
+ scaled_lag);
+
+ const float prediction_effect =
+ (accumulated_lag_no_prediction_ - accumulated_lag_) / time_delta;
+ // Log positive and negative prediction effects. ScrollBegin currently
+ // doesn't take prediction into account so don't log for it.
+ // Positive effect means that the prediction reduced the perceived lag,
+ // where negative means prediction made lag worse (most likely due to
+ // misprediction).
+ if (event_type == EventType::ScrollUpdate) {
+ if (prediction_effect >= 0.f) {
+ base::UmaHistogramCounts1000(
+ base::JoinString(
+ {GetAverageLagMetricName(event_type), "PredictionPositive"},
+ "."),
+ prediction_effect);
+ } else {
+ base::UmaHistogramCounts1000(
+ base::JoinString(
+ {GetAverageLagMetricName(event_type), "PredictionNegative"},
+ "."),
+ -prediction_effect);
+ }
+ }
+ accumulated_lag_ = 0;
+ accumulated_lag_no_prediction_ = 0;
+ last_reported_time_ = frame_lag.frame_time;
+ is_begin_ = false;
+ }
+
+ last_finished_frame_time_ = frame_lag.frame_time;
+ frame_lag_infos_.pop_front();
+}
+
+} // namespace cc
diff --git a/chromium/cc/metrics/average_lag_tracker.h b/chromium/cc/metrics/average_lag_tracker.h
new file mode 100644
index 00000000000..3877a773eff
--- /dev/null
+++ b/chromium/cc/metrics/average_lag_tracker.h
@@ -0,0 +1,146 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_METRICS_AVERAGE_LAG_TRACKER_H_
+#define CC_METRICS_AVERAGE_LAG_TRACKER_H_
+
+#include <deque>
+#include <string>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "cc/cc_export.h"
+
+namespace cc {
+
+// A class for reporting AverageLag metrics. See
+// https://docs.google.com/document/d/1e8NuzPblIv2B9bz01oSj40rmlse7_PHq5oFS3lqz6N4/
+class CC_EXPORT AverageLagTracker {
+ public:
+ enum class FinishTimeType { GpuSwapBegin, PresentationFeedback };
+ enum class EventType { ScrollBegin, ScrollUpdate };
+
+ struct EventInfo {
+ EventInfo(int trace_id,
+ float event_scroll_delta,
+ float predicted_scroll_delta,
+ base::TimeTicks event_timestamp,
+ EventType event_type)
+ : trace_id(trace_id),
+ event_scroll_delta(event_scroll_delta),
+ predicted_scroll_delta(predicted_scroll_delta),
+ event_timestamp(event_timestamp),
+ event_type(event_type) {}
+ // Id from the original LatencyInfo.
+ int trace_id;
+ // Delta reported by the scroll event (begin or update).
+ float event_scroll_delta;
+ // Delta predicted (when prediction is on, otherwise should be equals to
+ // |event_scroll_delta|).
+ float predicted_scroll_delta;
+ // Timestamp when the scroll event happened.
+ base::TimeTicks event_timestamp;
+ // Timestamp when the scroll event's frame finished, which is currently
+ // when the frame swap completed.
+ base::TimeTicks finish_timestamp;
+ // Scroll event type (begin or update).
+ EventType event_type;
+ };
+
+ explicit AverageLagTracker(FinishTimeType);
+ ~AverageLagTracker();
+
+ // Disallow copy and assign.
+ AverageLagTracker(const AverageLagTracker&) = delete;
+ AverageLagTracker& operator=(AverageLagTracker const&) = delete;
+
+ // Adds a scroll event defined by |event_info|.
+ void AddScrollEventInFrame(const EventInfo& event_info);
+
+ protected:
+ std::string GetAverageLagMetricName(EventType) const;
+
+ private:
+ typedef struct LagAreaInFrame {
+ LagAreaInFrame(base::TimeTicks time,
+ float rendered_pos = 0,
+ float rendered_pos_no_prediction = 0)
+ : frame_time(time),
+ rendered_accumulated_delta(rendered_pos),
+ lag_area(0),
+ rendered_accumulated_delta_no_prediction(rendered_pos_no_prediction),
+ lag_area_no_prediction(0) {}
+ base::TimeTicks frame_time;
+ // |rendered_accumulated_delta| is the cumulative delta that was swapped for
+ // this frame; this is based on the predicted delta, if prediction is
+ // enabled.
+ float rendered_accumulated_delta;
+ // |lag_area| is computed once a future input is processed that occurs after
+ // the swap timestamp (so that we can compute how far the rendered delta
+ // was from the actual position at the swap time).
+ float lag_area;
+ // |rendered_accumulated_delta_no_prediction| is the what would have been
+ // rendered if prediction was not taken into account, i.e., the actual delta
+ // from the input event.
+ float rendered_accumulated_delta_no_prediction;
+ // |lag_area_no_prediction| is computed the same as |lag_area| but using
+ // rendered_accumulated_delta_no_prediction as the rendered delta.
+ float lag_area_no_prediction;
+ } LagAreaInFrame;
+
+ // Processes |event_info| as a ScrollBegin event and add it to the Lag.
+ void AddScrollBeginInFrame(const EventInfo& event_info);
+ // Processes |event_info| as a ScrollUpdate event and add it to the Lag.
+ void AddScrollUpdateInFrame(const EventInfo& event_info);
+
+ // Calculate lag in 1 seconds intervals and report UMA.
+ void CalculateAndReportAverageLagUma(bool send_anyway = false);
+
+ // Helper function to calculate lag area between |front_time| to
+ // |back_time|.
+ float LagBetween(base::TimeTicks front_time,
+ base::TimeTicks back_time,
+ float scroll_delta,
+ base::TimeTicks event_time,
+ float rendered_accumulated_delta);
+
+ float LagForUnfinishedFrame(float rendered_accumulated_delta);
+
+ // Time in the frame lifecycle that EventInfo::finish_timestamp represents
+ // and defines which metric is tracked by this AverageLagTracker.
+ const FinishTimeType finish_time_type_;
+
+ std::deque<LagAreaInFrame> frame_lag_infos_;
+
+ // Last scroll event's timestamp in the sequence, reset on ScrollBegin.
+ base::TimeTicks last_event_timestamp_;
+ // Timestamp of the last frame popped from |frame_lag_infos_| queue.
+ base::TimeTicks last_finished_frame_time_;
+
+ // Accumulated scroll delta for actual scroll update events. Cumulated from
+ // event_scroll_delta. Reset on ScrollBegin.
+ float last_event_accumulated_delta_ = 0;
+ // Accumulated scroll delta got rendered on gpu swap. Cumulated from
+ // predicted_scroll_delta. It always has same value as
+ // |last_event_accumulated_delta_| when scroll prediction is disabled.
+ float last_rendered_accumulated_delta_ = 0;
+
+ // This keeps track of the last report_time when we report to UMA, so we can
+ // calculate the report's duration by current - last. Reset on ScrollBegin.
+ base::TimeTicks last_reported_time_;
+
+ // True if the first element of |frame_lag_infos_| is for ScrollBegin.
+ // For ScrollBegin, we don't wait for the 1 second interval but record the
+ // UMA once the frame is finished.
+ bool is_begin_ = false;
+
+ // Accumulated lag area in the 1 second intervals.
+ float accumulated_lag_ = 0;
+ // Accumulated lag not taking into account the predicted deltas.
+ float accumulated_lag_no_prediction_ = 0;
+};
+
+} // namespace cc
+
+#endif // CC_METRICS_AVERAGE_LAG_TRACKER_H_
diff --git a/chromium/cc/metrics/average_lag_tracker_unittest.cc b/chromium/cc/metrics/average_lag_tracker_unittest.cc
new file mode 100644
index 00000000000..d836f75c759
--- /dev/null
+++ b/chromium/cc/metrics/average_lag_tracker_unittest.cc
@@ -0,0 +1,539 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/metrics/average_lag_tracker.h"
+
+#include <memory>
+
+#include "base/test/metrics/histogram_tester.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Bucket;
+using testing::ElementsAre;
+
+namespace cc {
+namespace {
+
+class AverageLagTrackerTest : public testing::Test {
+ public:
+ AverageLagTrackerTest() { ResetHistograms(); }
+
+ void ResetHistograms() {
+ histogram_tester_ = std::make_unique<base::HistogramTester>();
+ }
+
+ const base::HistogramTester& histogram_tester() { return *histogram_tester_; }
+
+ void SetUp() override {
+ average_lag_tracker_gpu_swap_ = std::make_unique<AverageLagTracker>(
+ AverageLagTracker::FinishTimeType::GpuSwapBegin);
+ average_lag_tracker_presentation_ = std::make_unique<AverageLagTracker>(
+ AverageLagTracker::FinishTimeType::PresentationFeedback);
+ }
+
+ void SyntheticTouchScrollBegin(base::TimeTicks event_time,
+ base::TimeTicks frame_time,
+ float delta,
+ float predicted_delta = 0) {
+ AverageLagTracker::EventInfo event_info(
+ 0, delta, predicted_delta != 0 ? predicted_delta : delta, event_time,
+ AverageLagTracker::EventType::ScrollBegin);
+ event_info.finish_timestamp = frame_time;
+
+ // Always add the events to both and test if they have the same behavior
+ // regardless of the metrics names.
+ average_lag_tracker_gpu_swap_->AddScrollEventInFrame(event_info);
+ average_lag_tracker_presentation_->AddScrollEventInFrame(event_info);
+ }
+
+ void SyntheticTouchScrollUpdate(base::TimeTicks event_time,
+ base::TimeTicks frame_time,
+ float delta,
+ float predicted_delta = 0) {
+ AverageLagTracker::EventInfo event_info(
+ 0, delta, predicted_delta != 0 ? predicted_delta : delta, event_time,
+ AverageLagTracker::EventType::ScrollUpdate);
+ event_info.finish_timestamp = frame_time;
+
+ average_lag_tracker_gpu_swap_->AddScrollEventInFrame(event_info);
+ average_lag_tracker_presentation_->AddScrollEventInFrame(event_info);
+ }
+
+ void CheckScrollBeginHistograms(int bucket_value, int count) {
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "Event.Latency.ScrollBegin.Touch.AverageLag"),
+ ElementsAre(Bucket(bucket_value, count)));
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "Event.Latency.ScrollBegin.Touch.AverageLagPresentation"),
+ ElementsAre(Bucket(bucket_value, count)));
+ }
+
+ void CheckScrollUpdateHistograms(int bucket_value, int count) {
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "Event.Latency.ScrollUpdate.Touch.AverageLag"),
+ ElementsAre(Bucket(bucket_value, count)));
+
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation"),
+ ElementsAre(Bucket(bucket_value, count)));
+ }
+
+ void CheckPredictionPositiveHistograms(int bucket_value, int count) {
+ EXPECT_THAT(
+ histogram_tester().GetAllSamples(
+ "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"),
+ ElementsAre(Bucket(bucket_value, count)));
+
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation."
+ "PredictionPositive"),
+ ElementsAre(Bucket(bucket_value, count)));
+ }
+
+ void CheckPredictionNegativeHistograms(int bucket_value, int count) {
+ EXPECT_THAT(
+ histogram_tester().GetAllSamples(
+ "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative"),
+ ElementsAre(Bucket(bucket_value, count)));
+
+ EXPECT_THAT(histogram_tester().GetAllSamples(
+ "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation."
+ "PredictionNegative"),
+ ElementsAre(Bucket(bucket_value, count)));
+ }
+
+ void CheckScrollUpdateHistogramsTotalCount(int count) {
+ histogram_tester().ExpectTotalCount(
+ "Event.Latency.ScrollUpdate.Touch.AverageLag", count);
+ histogram_tester().ExpectTotalCount(
+ "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation", count);
+ }
+
+ void CheckPredictionPositiveHistogramsTotalCount(int count) {
+ histogram_tester().ExpectTotalCount(
+ "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive",
+ count);
+ histogram_tester().ExpectTotalCount(
+ "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation."
+ "PredictionPositive",
+ count);
+ }
+
+ void CheckPredictionNegativeHistogramsTotalCount(int count) {
+ histogram_tester().ExpectTotalCount(
+ "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative",
+ count);
+ histogram_tester().ExpectTotalCount(
+ "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation."
+ "PredictionNegative",
+ count);
+ }
+
+ protected:
+ std::unique_ptr<AverageLagTracker> average_lag_tracker_gpu_swap_;
+ std::unique_ptr<AverageLagTracker> average_lag_tracker_presentation_;
+
+ std::unique_ptr<base::HistogramTester> histogram_tester_;
+};
+
+base::TimeTicks MillisecondsToTimeTicks(float t_ms) {
+ return base::TimeTicks() + base::TimeDelta::FromMilliseconds(t_ms);
+}
+
+// Simulate a simple situation that events at every 10ms and start at t=15ms,
+// frame swaps at every 10ms too and start at t=20ms and test we record one
+// UMA for ScrollUpdate in one second.
+TEST_F(AverageLagTrackerTest, OneSecondInterval) {
+ base::TimeTicks event_time =
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(5);
+ base::TimeTicks frame_time =
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(10);
+ float scroll_delta = 10;
+
+ // ScrollBegin
+ event_time += base::TimeDelta::FromMilliseconds(10); // 15ms
+ frame_time += base::TimeDelta::FromMilliseconds(10); // 20ms
+ SyntheticTouchScrollBegin(event_time, frame_time, scroll_delta);
+
+ // Send 101 ScrollUpdate events to verify that there is 1 AverageLag record
+ // per 1 second.
+ const int kUpdates = 101;
+ for (int i = 0; i < kUpdates; i++) {
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ frame_time += base::TimeDelta::FromMilliseconds(10);
+ // First 50 has positive delta, others negetive delta.
+ const int sign = (i < kUpdates / 2) ? 1 : -1;
+ SyntheticTouchScrollUpdate(event_time, frame_time, sign * scroll_delta);
+ }
+
+ // ScrollBegin report_time is at 20ms, so the next ScrollUpdate report_time is
+ // at 1020ms. The last event_time that finish this report should be later than
+ // 1020ms.
+ EXPECT_EQ(event_time,
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1025));
+ EXPECT_EQ(frame_time,
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(1030));
+
+ // ScrollBegin AverageLag are the area between the event original component
+ // (time=15ms, delta=10px) to the frame swap time (time=20ms, expect finger
+ // position at delta=15px). The AverageLag scaled to 1 second is
+ // (0.5*(10px+15px)*5ms)/5ms = 12.5px.
+ CheckScrollBeginHistograms(12, 1);
+
+ // This ScrollUpdate AverageLag are calculated as the finger uniformly scroll
+ // 10px each frame. For scroll up/down frame, the Lag at the last frame swap
+ // is 5px, and Lag at this frame swap is 15px. For the one changing direction,
+ // the Lag is from 5 to 10 and down to 5 again. So total LagArea is 99 * 100,
+ // plus 75. the AverageLag in 1 second is 9.975px.
+ CheckScrollUpdateHistograms(9, 1);
+ CheckPredictionPositiveHistograms(0, 1);
+ CheckPredictionNegativeHistogramsTotalCount(0);
+
+ ResetHistograms();
+
+ // Send another ScrollBegin to end the unfinished ScrollUpdate report.
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ frame_time += base::TimeDelta::FromMilliseconds(10);
+ SyntheticTouchScrollBegin(event_time, frame_time, scroll_delta);
+
+ // The last ScrollUpdate's lag is 8.75px and truncated to 8.
+ CheckScrollUpdateHistograms(8, 1);
+ CheckPredictionPositiveHistograms(0, 1);
+ CheckPredictionNegativeHistogramsTotalCount(0);
+}
+
+// Test the case that event's frame swap time is later than next event's
+// creation time. (i.e, event at t=10ms will be dispatch at t=30ms, while next
+// event is at t=20ms).
+TEST_F(AverageLagTrackerTest, LargerLatency) {
+ base::TimeTicks event_time = MillisecondsToTimeTicks(10);
+ base::TimeTicks frame_time =
+ event_time + base::TimeDelta::FromMilliseconds(20);
+ float scroll_delta = 10;
+
+ SyntheticTouchScrollBegin(event_time, frame_time, scroll_delta);
+
+ // Send 2 ScrollUpdate. The second one will record AverageLag.ScrollBegin as
+ // it's event_time is larger or equal to ScrollBegin's frame_time.
+ for (int i = 0; i < 2; i++) {
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ frame_time = event_time + base::TimeDelta::FromMilliseconds(20);
+ SyntheticTouchScrollUpdate(event_time, frame_time, scroll_delta);
+ }
+
+ // ScrollBegin AveragLag are from t=10ms to t=30ms, with absolute scroll
+ // position from 10 to 30. The AverageLag should be:
+ // (0.5*(10px + 30px)*20ms/20ms) = 20px.
+ CheckScrollBeginHistograms(20, 1);
+
+ // Another ScrollBegin to flush unfinished frames.
+ // event_time doesn't matter here because the previous frames' lag are
+ // compute from their frame_time.
+ event_time = MillisecondsToTimeTicks(1000);
+ frame_time = MillisecondsToTimeTicks(1000);
+ SyntheticTouchScrollBegin(event_time, frame_time, scroll_delta);
+ // The to unfinished frames' lag are (finger_positon-rendered_position)*time,
+ // AverageLag is ((30px-10px)*10ms+(30px-20px)*10ms)/20ms = 15px.
+ CheckScrollUpdateHistograms(14, 1);
+}
+
+// Test that multiple latency being flush in the same frame swap.
+TEST_F(AverageLagTrackerTest, TwoLatencyInfoInSameFrame) {
+ // ScrollBegin
+ base::TimeTicks event_time = MillisecondsToTimeTicks(10);
+ base::TimeTicks frame_time = MillisecondsToTimeTicks(20);
+ SyntheticTouchScrollBegin(event_time, frame_time, -10 /* scroll_delta */);
+
+ // ScrollUpdate with event_time >= ScrollBegin frame_time will generate
+ // a histogram for AverageLag.ScrollBegin.
+ event_time = MillisecondsToTimeTicks(20);
+ frame_time = MillisecondsToTimeTicks(30);
+ SyntheticTouchScrollUpdate(event_time, frame_time, -10 /* scroll_delta */);
+
+ // Absolute position from -10 to -20. The AverageLag should be:
+ // (0.5*(10px + 20px)*10ms/10ms) = 15px.
+ CheckScrollBeginHistograms(14, 1);
+
+ event_time = MillisecondsToTimeTicks(25);
+ frame_time = MillisecondsToTimeTicks(30);
+ SyntheticTouchScrollUpdate(event_time, frame_time, 5 /* scroll_delta */);
+
+ // Another ScrollBegin to flush unfinished frames.
+ event_time = MillisecondsToTimeTicks(1000);
+ frame_time = MillisecondsToTimeTicks(1000);
+ SyntheticTouchScrollBegin(event_time, frame_time, 0);
+
+ // The ScrollUpdates are at t=20ms, finger_pos=-20px, rendered_pos=-10px,
+ // at t=25ms, finger_pos=-15px, rendered_pos=-10px;
+ // To t=30ms both events get flush.
+ // AverageLag is (0.5*(10px+5px)*5ms + 5px*5ms)/10ms = 6.25px
+ CheckScrollUpdateHistograms(6, 1);
+}
+
+// Test the case that switching direction causes lag at current frame
+// time and previous frame time are in different direction.
+TEST_F(AverageLagTrackerTest, ChangeDirectionInFrame) {
+ // ScrollBegin
+ base::TimeTicks event_time = MillisecondsToTimeTicks(10);
+ base::TimeTicks frame_time = MillisecondsToTimeTicks(20);
+ SyntheticTouchScrollBegin(event_time, frame_time, 10 /* scroll_delta */);
+
+ // At t=20, lag = 10px.
+ event_time = MillisecondsToTimeTicks(20);
+ frame_time = MillisecondsToTimeTicks(30);
+ SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */);
+
+ // At t=30, lag = -10px.
+ event_time = MillisecondsToTimeTicks(30);
+ frame_time = MillisecondsToTimeTicks(40);
+ SyntheticTouchScrollUpdate(event_time, frame_time, -20 /* scroll_delta */);
+
+ // Another ScrollBegin to flush unfinished frames.
+ event_time = MillisecondsToTimeTicks(1000);
+ frame_time = MillisecondsToTimeTicks(1000);
+ SyntheticTouchScrollBegin(event_time, frame_time, 0);
+
+ // From t=20 to t=30, lag_area=2*(0.5*10px*5ms)=50px*ms.
+ // From t=30 to t=40, lag_area=20px*10ms=200px*ms
+ // AverageLag = (50+200)/20 = 12.5px.
+ CheckScrollUpdateHistograms(12, 1);
+}
+
+// A simple case without scroll prediction to compare with the two with
+// prediction cases below.
+TEST_F(AverageLagTrackerTest, NoScrollPrediction) {
+ // ScrollBegin, at t=5, finger_pos=5px.
+ base::TimeTicks event_time = MillisecondsToTimeTicks(5);
+ base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
+ SyntheticTouchScrollBegin(event_time, frame_time, 5 /* scroll_delta */);
+
+ // ScrollUpdate, at t=15, finger_pos=15px.
+ event_time = MillisecondsToTimeTicks(15);
+ frame_time = MillisecondsToTimeTicks(20);
+ SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */);
+
+ // ScrollUpdate, at t=25, finger_pos=25px.
+ event_time = MillisecondsToTimeTicks(25);
+ frame_time = MillisecondsToTimeTicks(30);
+ SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */);
+
+ // Another ScrollBegin to flush unfinished frames.
+ event_time = MillisecondsToTimeTicks(1000);
+ frame_time = MillisecondsToTimeTicks(1000);
+ SyntheticTouchScrollBegin(event_time, frame_time, 0);
+
+ // Prediction hasn't take affect on ScrollBegin so it'll stay the same.
+ CheckScrollBeginHistograms(7, 1);
+ // At t=10, finger_pos = 10px, rendered_pos = 5px.
+ // At t=20, finger_pos = 20px, rendered_pos = 15px.
+ // At t=30, finger_pos = 25px, rendered_pos = 25px.
+ // AverageLag = ((5px+15px)*10ms/2 + (5px+10px)*5ms/2 + 10px*5ms)/20ms
+ // = 9.375
+ CheckScrollUpdateHistograms(9, 1);
+}
+
+// Test AverageLag with perfect scroll prediction.
+TEST_F(AverageLagTrackerTest, ScrollPrediction) {
+ // ScrollBegin, at t=5, finger_pos=5px.
+ // Predict frame_time=10, predicted_pos = 10px.
+ base::TimeTicks event_time = MillisecondsToTimeTicks(5);
+ base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
+ SyntheticTouchScrollBegin(event_time, frame_time, 5 /* scroll_delta */,
+ 10 /* predicted_delta */);
+
+ // ScrollUpdate, at t=15, finger_pos=15px.
+ // Predict frame_time=20, predicted_pos = 20px.
+ event_time = MillisecondsToTimeTicks(15);
+ frame_time = MillisecondsToTimeTicks(20);
+ SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+ 10 /* predicted_delta */);
+
+ // ScrollUpdate, at t=25, finger_pos=25px.
+ // Predict frame_time=30, predicted_pos = 30px.
+ event_time = MillisecondsToTimeTicks(25);
+ frame_time = MillisecondsToTimeTicks(30);
+ SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+ 10 /* predicted_delta */);
+
+ // Another ScrollBegin to flush unfinished frames.
+ event_time = MillisecondsToTimeTicks(1000);
+ frame_time = MillisecondsToTimeTicks(1000);
+ SyntheticTouchScrollBegin(event_time, frame_time, 0);
+
+ // Prediction hasn't take affect on ScrollBegin so it'll stay the same.
+ CheckScrollBeginHistograms(7, 1);
+ // At t=10, finger_pos = 10px, rendered_pos = 10px.
+ // At t=20, finger_pos = 20px, rendered_pos = 20px.
+ // At t=30, finger_pos = 25px, rendered_pos = 30px.
+ // AverageLag = ((0px+10px)*10ms/2 + (0px+5px)*10ms/2 + 5px*5ms)/20ms
+ // = 4.375px
+ // AverageLag (w/o prediction)
+ // ((5px+15px)*10ms/2 + (5px+10px)*5ms/2 + 10px*5ms)/20ms
+ // = 9.375px
+ // Positive effect of prediction = 5px
+ CheckScrollUpdateHistograms(4, 1);
+ CheckPredictionPositiveHistograms(5, 1);
+ CheckPredictionNegativeHistogramsTotalCount(0);
+}
+
+// Test AverageLag with imperfect scroll prediction.
+TEST_F(AverageLagTrackerTest, ImperfectScrollPrediction) {
+ // ScrollBegin, at t=5, finger_pos=5px.
+ // Predict frame_time=10, predicted_pos(over) = 12px.
+ base::TimeTicks event_time = MillisecondsToTimeTicks(5);
+ base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
+ SyntheticTouchScrollBegin(event_time, frame_time, 5 /* scroll_delta */,
+ 12 /* predicted_delta */);
+
+ // ScrollUpdate, at t=15, finger_pos=15px.
+ // Predict frame_time=20, predicted_pos(under) = 17px.
+ event_time = MillisecondsToTimeTicks(15);
+ frame_time = MillisecondsToTimeTicks(20);
+ SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+ 5 /* predicted_delta */);
+
+ // ScrollUpdate, at t=25, finger_pos=25px.
+ // Predict frame_time=30, predicted_pos(over) = 31px.
+ event_time = MillisecondsToTimeTicks(25);
+ frame_time = MillisecondsToTimeTicks(30);
+ SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+ 14 /* predicted_delta */);
+
+ // Another ScrollBegin to flush unfinished frames.
+ event_time = MillisecondsToTimeTicks(1000);
+ frame_time = MillisecondsToTimeTicks(1000);
+ SyntheticTouchScrollBegin(event_time, frame_time, 0);
+
+ CheckScrollBeginHistograms(7, 1);
+ // AverageLag = ((2px*2ms/2+8px*8ms/2)+ ((3px+8px)*5ms/2+8px*5ms))/20ms
+ // = 5.075px
+ CheckScrollUpdateHistograms(5, 1);
+ // AverageLag (w/o prediction =
+ // ((5px+15px)*10ms/2 + (5px+10px)*5ms/2 + 10px*5ms)/20ms
+ // = 9.375px
+ // Positive effect of prediction = 4.3px
+ CheckPredictionPositiveHistograms(4, 1);
+ CheckPredictionNegativeHistogramsTotalCount(0);
+}
+
+TEST_F(AverageLagTrackerTest, NegativePredictionEffect) {
+ // ScrollBegin, at t=5, finger_pos=5px.
+ // Predict frame_time=10, predicted_pos(over) = 20px.
+ base::TimeTicks event_time = MillisecondsToTimeTicks(5);
+ base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
+ SyntheticTouchScrollBegin(event_time, frame_time, 5 /* scroll_delta */,
+ 20 /* predicted_delta */);
+
+ // ScrollUpdate, at t=15, finger_pos=15px.
+ // Predict frame_time=20, predicted_pos(over) = 60px.
+ event_time = MillisecondsToTimeTicks(15);
+ frame_time = MillisecondsToTimeTicks(20);
+ SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+ 40 /* predicted_delta */);
+
+ // ScrollUpdate, at t=25, finger_pos=25px.
+ // Predict frame_time=30, predicted_pos(over) = 60px.
+ event_time = MillisecondsToTimeTicks(25);
+ frame_time = MillisecondsToTimeTicks(30);
+ SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+ 0 /* predicted_delta */);
+
+ // Another ScrollBegin to flush unfinished frames.
+ event_time = MillisecondsToTimeTicks(1000);
+ frame_time = MillisecondsToTimeTicks(1000);
+ SyntheticTouchScrollBegin(event_time, frame_time, 0);
+
+ CheckScrollBeginHistograms(7, 1);
+ // AverageLag = ((10px+0px)*10ms/2)+ ((40px+35px)*5ms/2+35px*5ms))/20ms
+ // = 20.625px
+ CheckScrollUpdateHistograms(20, 1);
+ // AverageLag (w/o prediction =
+ // ((5px+15px)*10ms/2 + (5px+10px)*5ms/2 + 10px*5ms)/20ms
+ // = 9.375px
+ // Negative effect of prediction = 11.25
+ CheckPredictionPositiveHistogramsTotalCount(0);
+ CheckPredictionNegativeHistograms(11, 1);
+}
+
+TEST_F(AverageLagTrackerTest, NoPredictionEffect) {
+ // ScrollBegin, at t=5, finger_pos=5px.
+ // Predict frame_time=10, predicted_pos(over) = 25px.
+ base::TimeTicks event_time = MillisecondsToTimeTicks(5);
+ base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
+ SyntheticTouchScrollBegin(event_time, frame_time, 5 /* scroll_delta */,
+ 25 /* predicted_delta */);
+
+ // ScrollUpdate, at t=15, finger_pos=15px.
+ // Predict frame_time=20, predicted_pos(over) = 32px.
+ event_time = MillisecondsToTimeTicks(15);
+ frame_time = MillisecondsToTimeTicks(20);
+ SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+ 7 /* predicted_delta */);
+
+ // ScrollUpdate, at t=25, finger_pos=25px.
+ // Predict frame_time=30, predicted_pos(over) = 37px.
+ event_time = MillisecondsToTimeTicks(25);
+ frame_time = MillisecondsToTimeTicks(30);
+ SyntheticTouchScrollUpdate(event_time, frame_time, 10 /* scroll_delta */,
+ 5 /* predicted_delta */);
+
+ // Another ScrollBegin to flush unfinished frames.
+ event_time = MillisecondsToTimeTicks(1000);
+ frame_time = MillisecondsToTimeTicks(1000);
+ SyntheticTouchScrollBegin(event_time, frame_time, 0);
+
+ CheckScrollBeginHistograms(7, 1);
+ // AverageLag = ((15px+5px)*10ms/2 + (12px+7px)*5ms/2 + 7px*5ms)/20ms
+ // = 9.125px
+ CheckScrollUpdateHistograms(9, 1);
+ // AverageLag (w/o prediction) =
+ // ((5px+15px)*10ms/2 + (5px+10px)*5ms/2 + 10px*5ms)/20ms
+ // = 9.375px
+ // Prediction slightly positive, we should see a 0 bucket in
+ // PredictionPositive UMA
+ CheckPredictionPositiveHistograms(0, 1);
+ CheckPredictionNegativeHistogramsTotalCount(0);
+}
+
+// Tests that when an event arrives out-of-order, the average lag tracker
+// properly ignores it.
+TEST_F(AverageLagTrackerTest, EventOutOfOrder) {
+ base::TimeTicks event_time = MillisecondsToTimeTicks(5);
+ base::TimeTicks frame_time = MillisecondsToTimeTicks(10);
+ float scroll_delta = 5.f;
+ SyntheticTouchScrollBegin(event_time, frame_time, scroll_delta);
+
+ event_time = MillisecondsToTimeTicks(15);
+ frame_time = MillisecondsToTimeTicks(20);
+ SyntheticTouchScrollUpdate(event_time, frame_time, scroll_delta);
+
+ event_time = MillisecondsToTimeTicks(25);
+ frame_time = MillisecondsToTimeTicks(30);
+ SyntheticTouchScrollUpdate(event_time, frame_time, scroll_delta);
+
+ // A ScrollBegin to flush unfinished frames.
+ event_time = MillisecondsToTimeTicks(1000);
+ frame_time = MillisecondsToTimeTicks(1000);
+ SyntheticTouchScrollBegin(event_time, frame_time, 0);
+
+ CheckScrollUpdateHistogramsTotalCount(1);
+
+ // Send an event whose timestamp is earlier than the most recent event,
+ // representing an event that gets process out of order.
+ base::TimeTicks earlier_event_time = MillisecondsToTimeTicks(15);
+ frame_time = MillisecondsToTimeTicks(1010);
+ SyntheticTouchScrollUpdate(earlier_event_time, frame_time, scroll_delta);
+
+ // Another ScrollBegin to flush unfinished frames.
+ event_time = MillisecondsToTimeTicks(2000);
+ frame_time = MillisecondsToTimeTicks(2000);
+ SyntheticTouchScrollBegin(event_time, frame_time, 0);
+
+ // Ensure that the event was ignored.
+ CheckScrollUpdateHistogramsTotalCount(1);
+}
+
+} // namespace
+} // namespace cc
diff --git a/chromium/cc/metrics/average_lag_tracking_manager.cc b/chromium/cc/metrics/average_lag_tracking_manager.cc
new file mode 100644
index 00000000000..efca9693ca7
--- /dev/null
+++ b/chromium/cc/metrics/average_lag_tracking_manager.cc
@@ -0,0 +1,103 @@
+// 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/average_lag_tracking_manager.h"
+
+#include <algorithm>
+
+#include "components/viz/common/frame_timing_details.h"
+#include "components/viz/common/quads/compositor_frame_metadata.h"
+#include "ui/latency/latency_info.h"
+
+namespace cc {
+
+AverageLagTrackingManager::AverageLagTrackingManager() = default;
+
+AverageLagTrackingManager::~AverageLagTrackingManager() {
+ // The map must contain only frames that haven't been presented (i.e. did not
+ // get a presentation feedback yet). Thus, at a given point in time, more than
+ // a handful (actually around 2) of frames without feedback is unexpected.
+ DCHECK_LE(frame_token_to_info_.size(), 20u);
+}
+
+void AverageLagTrackingManager::CollectScrollEventsFromFrame(
+ uint32_t frame_token,
+ const std::vector<ui::LatencyInfo>& latency_infos) {
+ std::vector<AverageLagTracker::EventInfo> event_infos;
+
+ for (ui::LatencyInfo latency_info : latency_infos) {
+ if (latency_info.source_event_type() != ui::SourceEventType::TOUCH)
+ continue;
+
+ bool found_scroll_begin = latency_info.FindLatency(
+ ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT,
+ nullptr);
+ bool found_scroll_update = latency_info.FindLatency(
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, nullptr);
+
+ if (!found_scroll_begin && !found_scroll_update)
+ continue;
+
+ base::TimeTicks event_timestamp;
+ bool found_event = latency_info.FindLatency(
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT,
+ &event_timestamp);
+ DCHECK(found_event);
+
+ AverageLagTracker::EventInfo event_info(
+ latency_info.trace_id(), latency_info.scroll_update_delta(),
+ latency_info.predicted_scroll_update_delta(), event_timestamp,
+ found_scroll_begin == true
+ ? AverageLagTracker::EventType::ScrollBegin
+ : AverageLagTracker::EventType::ScrollUpdate);
+
+ event_infos.push_back(event_info);
+ }
+
+ if (event_infos.size() > 0)
+ frame_token_to_info_.push_back(std::make_pair(frame_token, event_infos));
+}
+
+void AverageLagTrackingManager::DidPresentCompositorFrame(
+ uint32_t frame_token,
+ const viz::FrameTimingDetails& frame_details) {
+ // Erase all previous frames that haven't received a feedback and get the
+ // current |frame_token| list of events.
+ std::vector<AverageLagTracker::EventInfo> infos;
+ while (!frame_token_to_info_.empty() &&
+ !viz::FrameTokenGT(frame_token_to_info_.front().first, frame_token)) {
+ if (frame_token_to_info_.front().first == frame_token)
+ infos = frame_token_to_info_.front().second;
+
+ frame_token_to_info_.pop_front();
+ }
+
+ if (infos.size() == 0)
+ return;
+
+ if (!frame_details.presentation_feedback.failed()) {
+ DCHECK(!frame_details.swap_timings.is_null());
+
+ // Sorts data by trace_id because |infos| can be in non-asceding order
+ // (ascending order of trace_id/time is required by AverageLagTracker).
+ std::sort(infos.begin(), infos.end(),
+ [](const AverageLagTracker::EventInfo& a,
+ const AverageLagTracker::EventInfo& b) {
+ return a.trace_id < b.trace_id;
+ });
+
+ for (AverageLagTracker::EventInfo info : infos) {
+ info.finish_timestamp = frame_details.swap_timings.swap_start;
+ lag_tracker_gpu_swap_.AddScrollEventInFrame(info);
+
+ info.finish_timestamp = frame_details.presentation_feedback.timestamp;
+ lag_tracker_presentation_.AddScrollEventInFrame(info);
+ }
+ }
+}
+
+void AverageLagTrackingManager::Clear() {
+ frame_token_to_info_.clear();
+}
+} // namespace cc
diff --git a/chromium/cc/metrics/average_lag_tracking_manager.h b/chromium/cc/metrics/average_lag_tracking_manager.h
new file mode 100644
index 00000000000..d694fe789ad
--- /dev/null
+++ b/chromium/cc/metrics/average_lag_tracking_manager.h
@@ -0,0 +1,72 @@
+// 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_AVERAGE_LAG_TRACKING_MANAGER_H_
+#define CC_METRICS_AVERAGE_LAG_TRACKING_MANAGER_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/containers/circular_deque.h"
+#include "cc/cc_export.h"
+#include "cc/metrics/average_lag_tracker.h"
+
+namespace ui {
+class LatencyInfo;
+} // namespace ui
+
+namespace viz {
+struct FrameTimingDetails;
+} // namespace viz
+
+namespace cc {
+
+// A helper to decouple the LatencyInfos and the AverageLagTracker
+class CC_EXPORT AverageLagTrackingManager {
+ public:
+ AverageLagTrackingManager();
+ ~AverageLagTrackingManager();
+
+ // Disallow copy and assign.
+ AverageLagTrackingManager(const AverageLagTrackingManager&) = delete;
+ AverageLagTrackingManager& operator=(AverageLagTrackingManager const&) =
+ delete;
+
+ // Adds all the eligible events in the collection |infos| to the |frame_token|
+ // wait list.
+ void CollectScrollEventsFromFrame(uint32_t frame_token,
+ const std::vector<ui::LatencyInfo>& infos);
+
+ // Sends all pending events in the |frame_token| list to the
+ // AverageLagTracker, given its |frame_details|.
+ void DidPresentCompositorFrame(uint32_t frame_token,
+ const viz::FrameTimingDetails& frame_details);
+
+ // Clears the list of events |frame_token_to_info_| if the current frames wont
+ // receive a presentation feedback (e.g. LayerTreeFrameSink loses context)
+ void Clear();
+
+ private:
+ // TODO(https://crbug.com/1101005): Remove GpuSwap implementation after M86.
+ // Tracker for the AverageLag metrics that uses the gpu swap begin timing as
+ // an approximation for the time the users sees the frame on the screen.
+ AverageLagTracker lag_tracker_gpu_swap_{
+ AverageLagTracker::FinishTimeType::GpuSwapBegin};
+
+ // Tracker for the AverageLagPresentation metrics that uses the presentation
+ // feedback time as an approximation for the time the users sees the frame on
+ // the screen.
+ AverageLagTracker lag_tracker_presentation_{
+ AverageLagTracker::FinishTimeType::PresentationFeedback};
+
+ // List of events (vector) per frame (uint32_t |frame_token|) to submit to the
+ // lag trackers when DidPresentCompositorFrame is called for a |frame_token|.
+ base::circular_deque<
+ std::pair<uint32_t, std::vector<AverageLagTracker::EventInfo>>>
+ frame_token_to_info_;
+};
+
+} // namespace cc
+
+#endif // CC_METRICS_AVERAGE_LAG_TRACKING_MANAGER_H_
diff --git a/chromium/cc/metrics/average_lag_tracking_manager_unittest.cc b/chromium/cc/metrics/average_lag_tracking_manager_unittest.cc
new file mode 100644
index 00000000000..69931f0a010
--- /dev/null
+++ b/chromium/cc/metrics/average_lag_tracking_manager_unittest.cc
@@ -0,0 +1,285 @@
+// 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/average_lag_tracking_manager.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "base/test/metrics/histogram_tester.h"
+#include "components/viz/common/frame_timing_details.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/latency/latency_info.h"
+
+namespace cc {
+namespace {
+
+using base::Bucket;
+using testing::ElementsAre;
+
+// Helper for TimeTicks usage
+base::TimeTicks MillisecondsToTimeTicks(float t_ms) {
+ return base::TimeTicks() + base::TimeDelta::FromMilliseconds(t_ms);
+}
+
+// Helper for FrameTimingDetails usage in DidPresentCompositorFrame
+viz::FrameTimingDetails PrepareFrameDetails(base::TimeTicks swap_time,
+ base::TimeTicks presentation_time) {
+ gfx::SwapTimings timings;
+ timings.swap_start = swap_time;
+ viz::FrameTimingDetails details;
+ details.presentation_feedback.timestamp = presentation_time;
+ details.swap_timings = timings;
+ return details;
+}
+
+class AverageLagTrackingManagerTest : public testing::Test {
+ protected:
+ AverageLagTrackingManagerTest() = default;
+
+ void SetUp() override { ResetHistograms(); }
+
+ void ResetHistograms() {
+ histogram_tester_ = std::make_unique<base::HistogramTester>();
+ }
+
+ // Creates a scroll event each |scroll_rate| (in ms) of |scroll_delta| px.
+ // Collect events at the expected |gpu_swap_times|.
+ void SimulateConstantScroll(const std::vector<unsigned int>& gpu_swap_times,
+ float scroll_delta,
+ unsigned int scroll_rate) {
+ if (gpu_swap_times.size() == 0 || gpu_swap_times[0] < scroll_rate)
+ return;
+
+ // Creates 1st frame with scroll begin
+ std::vector<ui::LatencyInfo> events(gpu_swap_times[0] / scroll_rate);
+ base::TimeTicks event_time = MillisecondsToTimeTicks(scroll_rate);
+ events[0] = PrepareScrollEvent(AverageLagTracker::EventType::ScrollBegin,
+ event_time, 0, scroll_delta);
+ for (size_t i = 1; i < events.size(); i++) {
+ event_time += base::TimeDelta::FromMilliseconds(scroll_rate);
+ events[i] = PrepareScrollEvent(AverageLagTracker::EventType::ScrollUpdate,
+ event_time, i, scroll_delta);
+ }
+ average_lag_tracking_manager_.CollectScrollEventsFromFrame(0, events);
+
+ // Creates remaining frames
+ for (size_t frame = 1; frame < gpu_swap_times.size(); frame++) {
+ unsigned int time_delta =
+ gpu_swap_times[frame] - gpu_swap_times[frame - 1];
+ events = std::vector<ui::LatencyInfo>(time_delta / scroll_rate);
+ for (size_t i = 0; i < events.size(); i++) {
+ event_time += base::TimeDelta::FromMilliseconds(scroll_rate);
+ events[i] =
+ PrepareScrollEvent(AverageLagTracker::EventType::ScrollUpdate,
+ event_time, i, scroll_delta);
+ }
+ average_lag_tracking_manager_.CollectScrollEventsFromFrame(frame, events);
+ }
+ }
+
+ // Prepares a ui::LatencyInfo object for a ScrollEvent
+ ui::LatencyInfo PrepareScrollEvent(AverageLagTracker::EventType event_type,
+ base::TimeTicks event_time,
+ int trace_id,
+ float delta,
+ float predicted_delta = 0) {
+ ui::LatencyInfo info;
+ info.set_trace_id(trace_id);
+ info.set_source_event_type(ui::SourceEventType::TOUCH);
+
+ info.AddLatencyNumberWithTimestamp(
+ event_type == AverageLagTracker::EventType::ScrollBegin
+ ? ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT
+ : ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT,
+ event_time);
+
+ info.AddLatencyNumberWithTimestamp(
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT, event_time);
+
+ info.set_scroll_update_delta(delta);
+ info.set_predicted_scroll_update_delta(
+ predicted_delta != 0 ? predicted_delta : delta);
+
+ return info;
+ }
+
+ AverageLagTrackingManager average_lag_tracking_manager_;
+ std::unique_ptr<base::HistogramTester> histogram_tester_;
+};
+
+// Simulate a simple situation that events at every 10ms and start at t=15ms,
+// frame swaps at every 10ms too and start at t=20ms and test we record one
+// UMA for ScrollUpdate in one second. Tests using CollectScrollEventAtFrame
+// (1 event per collection)
+TEST_F(AverageLagTrackingManagerTest, OneSecondInterval) {
+ base::TimeTicks event_time = MillisecondsToTimeTicks(5);
+ base::TimeTicks gpu_swap_time = MillisecondsToTimeTicks(10);
+ base::TimeTicks presentation_time = MillisecondsToTimeTicks(13);
+ float scroll_delta = 10;
+ int frame_id = 1;
+
+ // ScrollBegin
+ event_time += base::TimeDelta::FromMilliseconds(10); // 15ms
+ gpu_swap_time += base::TimeDelta::FromMilliseconds(10); // 20ms
+ presentation_time += base::TimeDelta::FromMilliseconds(10); // 23ms
+ ui::LatencyInfo evt = PrepareScrollEvent(
+ AverageLagTracker::EventType::ScrollBegin, event_time, 1, scroll_delta);
+ average_lag_tracking_manager_.CollectScrollEventsFromFrame(
+ frame_id, std::vector<ui::LatencyInfo>{evt});
+ average_lag_tracking_manager_.DidPresentCompositorFrame(
+ frame_id, PrepareFrameDetails(gpu_swap_time, presentation_time));
+
+ // Send 101 ScrollUpdate events to verify that there is 1 AverageLag record
+ // per 1 second.
+ const int kUpdates = 101;
+ for (int i = 0; i < kUpdates; i++) {
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ gpu_swap_time += base::TimeDelta::FromMilliseconds(10);
+ presentation_time += base::TimeDelta::FromMilliseconds(10);
+ // First 50 has positive delta, others negative delta.
+ const int sign = (i < kUpdates / 2) ? 1 : -1;
+
+ evt = PrepareScrollEvent(AverageLagTracker::EventType::ScrollUpdate,
+ event_time, 1, sign * scroll_delta);
+ average_lag_tracking_manager_.CollectScrollEventsFromFrame(
+ frame_id, std::vector<ui::LatencyInfo>{evt});
+ average_lag_tracking_manager_.DidPresentCompositorFrame(
+ frame_id, PrepareFrameDetails(gpu_swap_time, presentation_time));
+ }
+
+ // ScrollBegin report_time is at 20ms, so the next ScrollUpdate report_time is
+ // at 1020ms. The last event_time that finish this report should be later than
+ // 1020ms.
+ EXPECT_EQ(event_time, MillisecondsToTimeTicks(1025));
+ EXPECT_EQ(gpu_swap_time, MillisecondsToTimeTicks(1030));
+ EXPECT_EQ(presentation_time, MillisecondsToTimeTicks(1033));
+
+ // ScrollBegin AverageLag are the area between the event original component
+ // (time=15ms, delta=10px) to the gpu swap time (time=20ms, expect finger
+ // position at delta=15px). The AverageLag scaled to 1 second is
+ // (0.5*(10px+15px)*5ms)/5ms = 12.5px.
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "Event.Latency.ScrollBegin.Touch.AverageLag"),
+ ElementsAre(Bucket(12, 1)));
+
+ // Using the presentation time (25ms) instead of gpu swap (20ms) the expected
+ // finger position is delta = 16px. Then (0.5*(10px+18px)*10ms)/10ms = 14px.
+ // UmaHistogramCounts1000's binning will round it to 12.
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "Event.Latency.ScrollBegin.Touch.AverageLagPresentation"),
+ ElementsAre(Bucket(14, 1)));
+
+ // This ScrollUpdate AverageLag are calculated as the finger uniformly scroll
+ // 10px each frame. For scroll up/down frame, the Lag at the last frame swap
+ // is 5px, and Lag at this frame swap is 15px. For the one changing direction,
+ // the Lag is from 5 to 10 and down to 5 again. So total LagArea is 99 * 100,
+ // plus 75. the AverageLag in 1 second is 9.975px.
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "Event.Latency.ScrollUpdate.Touch.AverageLag"),
+ ElementsAre(Bucket(9, 1)));
+ EXPECT_THAT(
+ histogram_tester_->GetAllSamples(
+ "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"),
+ ElementsAre(Bucket(0, 1)));
+ histogram_tester_->ExpectTotalCount(
+ "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative", 0);
+
+ // As the presentation times are at 80% of the gap between 2 scroll events,
+ // the Lag Area between 2 frames is define by the trapezoids: (time=event-2,
+ // delta=8px), (time=event, delta=10px), (time=event+8, delta=18). This makes
+ // 99 trapezois with an area of 0.5*2*(8+10) + 0.5*8*(10+18) = 130px.
+ // For scroll up/down frame, the Lag at the last frame swap is 2px, and Lag
+ // at this frame swap is 12px. For the one changing direction, the Lag is
+ // from 8 to 10 and down to 8 again. So total LagArea is 99 * 130, plus
+ // 0.5*8*(10+2) + 0.5*2*(8+10) = 66. This makes 12,936. Scaled by 1 sec
+ // and binned: 12.
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation"),
+ ElementsAre(Bucket(12, 1)));
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation."
+ "PredictionPositive"),
+ ElementsAre(Bucket(0, 1)));
+ histogram_tester_->ExpectTotalCount(
+ "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation."
+ "PredictionNegative",
+ 0);
+
+ ResetHistograms();
+
+ // Send another ScrollBegin to end the unfinished ScrollUpdate report.
+ event_time += base::TimeDelta::FromMilliseconds(10);
+ gpu_swap_time += base::TimeDelta::FromMilliseconds(10);
+ presentation_time += base::TimeDelta::FromMilliseconds(10);
+ evt = PrepareScrollEvent(AverageLagTracker::EventType::ScrollBegin,
+ event_time, 1, scroll_delta);
+ average_lag_tracking_manager_.CollectScrollEventsFromFrame(
+ frame_id, std::vector<ui::LatencyInfo>{evt});
+ average_lag_tracking_manager_.DidPresentCompositorFrame(
+ frame_id, PrepareFrameDetails(gpu_swap_time, presentation_time));
+
+ // The last ScrollUpdate's lag is 8.75px and truncated to 8.
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "Event.Latency.ScrollUpdate.Touch.AverageLag"),
+ ElementsAre(Bucket(8, 1)));
+ EXPECT_THAT(
+ histogram_tester_->GetAllSamples(
+ "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionPositive"),
+ ElementsAre(Bucket(0, 1)));
+ histogram_tester_->ExpectTotalCount(
+ "Event.Latency.ScrollUpdate.Touch.AverageLag.PredictionNegative", 0);
+}
+
+// This test creates 3 frames in order to check the submission of ScrollBegin
+// and ScrollUpdate events sent using CollectScrollEventsAtFrame (multiple
+// events per collection)
+TEST_F(AverageLagTrackingManagerTest, MultipleEventsInSameFrame) {
+ std::vector<unsigned int> gpu_swap_times = {400, 1400, 1600};
+ std::vector<unsigned int> presentation_times = {500, 1500, 1700};
+ SimulateConstantScroll(gpu_swap_times, 10, 100);
+ for (size_t frame = 0; frame < gpu_swap_times.size(); frame++) {
+ average_lag_tracking_manager_.DidPresentCompositorFrame(
+ frame, PrepareFrameDetails(
+ MillisecondsToTimeTicks(gpu_swap_times[frame]),
+ MillisecondsToTimeTicks(presentation_times[frame])));
+ }
+
+ // As the first frame is the ScrollBegin frame, the average lag is, using the
+ // gpu swap time, 0.5*(10 + 40) * 30 / 30 = 25. But UmaHistogramCounts1000's
+ // binning will round it to 23.
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "Event.Latency.ScrollBegin.Touch.AverageLag"),
+ ElementsAre(Bucket(23, 1)));
+
+ // As the first frame is the ScrollBegin frame, the average lag is, using the
+ // presentation time, 0.5*(10 + 50) * 40 / 40 = 30. But
+ // UmaHistogramCounts1000's binning will round it to 29.
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "Event.Latency.ScrollBegin.Touch.AverageLagPresentation"),
+ ElementsAre(Bucket(29, 1)));
+
+ // Only the ScrollUpdate events from frame 2 are sent (as the frame 3 is
+ // waiting for the next frame for sumission).
+ // As there is a scroll update right at the same time as the frame submission,
+ // using gpu swap time, frame 2 starts with 0 lag at 0.4s and finishes with
+ // 100 at 1.4, thus: 0.5 * (0 + 100) / 2 = 50. It gets into the same bin as
+ // 47.
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "Event.Latency.ScrollUpdate.Touch.AverageLag"),
+ ElementsAre(Bucket(47, 1)));
+
+ // Only the ScrollUpdate events from frame 2 are sent (as the frame 3 is
+ // waiting for the next frame for sumission).
+ // As there is a scroll update right at the same time as the frame submission,
+ // using presentation time, frame 2 starts with 10 lag at 0.5s and finishes
+ // with 110 at 1.5, thus: 0.5 * (10 + 110) / 2 = 60.
+ EXPECT_THAT(histogram_tester_->GetAllSamples(
+ "Event.Latency.ScrollUpdate.Touch.AverageLagPresentation"),
+ ElementsAre(Bucket(60, 1)));
+}
+
+} // namespace
+} // namespace cc
diff --git a/chromium/cc/metrics/begin_main_frame_metrics.h b/chromium/cc/metrics/begin_main_frame_metrics.h
index bcd40714215..24062d4dc6f 100644
--- a/chromium/cc/metrics/begin_main_frame_metrics.h
+++ b/chromium/cc/metrics/begin_main_frame_metrics.h
@@ -21,7 +21,8 @@ struct CC_EXPORT BeginMainFrameMetrics {
base::TimeDelta style_update;
base::TimeDelta layout_update;
base::TimeDelta prepaint;
- base::TimeDelta composite;
+ base::TimeDelta compositing_assignments;
+ base::TimeDelta compositing_inputs;
base::TimeDelta paint;
base::TimeDelta scrolling_coordinator;
base::TimeDelta composite_commit;
diff --git a/chromium/cc/metrics/compositor_frame_reporter.cc b/chromium/cc/metrics/compositor_frame_reporter.cc
index c5b037edf4d..afe42621d69 100644
--- a/chromium/cc/metrics/compositor_frame_reporter.cc
+++ b/chromium/cc/metrics/compositor_frame_reporter.cc
@@ -123,6 +123,22 @@ constexpr const char* GetStageName(int stage_type_index,
kVizBreakdownInitialIndex:
return "SubmitCompositorFrameToPresentationCompositorFrame."
"SwapEndToPresentationCompositorFrame";
+ case static_cast<int>(VizBreakdown::kSwapStartToBufferAvailable) +
+ kVizBreakdownInitialIndex:
+ return "SubmitCompositorFrameToPresentationCompositorFrame."
+ "SwapStartToBufferAvailable";
+ case static_cast<int>(VizBreakdown::kBufferAvailableToBufferReady) +
+ kVizBreakdownInitialIndex:
+ return "SubmitCompositorFrameToPresentationCompositorFrame."
+ "BufferAvailableToBufferReady";
+ case static_cast<int>(VizBreakdown::kBufferReadyToLatch) +
+ kVizBreakdownInitialIndex:
+ return "SubmitCompositorFrameToPresentationCompositorFrame."
+ "BufferReadyToLatch";
+ case static_cast<int>(VizBreakdown::kLatchToSwapEnd) +
+ kVizBreakdownInitialIndex:
+ return "SubmitCompositorFrameToPresentationCompositorFrame."
+ "LatchToSwapEnd";
case static_cast<int>(BlinkBreakdown::kHandleInputEvents) +
kBlinkBreakdownInitialIndex:
return "SendBeginMainFrameToCommit.HandleInputEvents";
@@ -138,9 +154,12 @@ constexpr const char* GetStageName(int stage_type_index,
case static_cast<int>(BlinkBreakdown::kPrepaint) +
kBlinkBreakdownInitialIndex:
return "SendBeginMainFrameToCommit.Prepaint";
- case static_cast<int>(BlinkBreakdown::kComposite) +
+ case static_cast<int>(BlinkBreakdown::kCompositingInputs) +
+ kBlinkBreakdownInitialIndex:
+ return "SendBeginMainFrameToCommit.CompositingInputs";
+ case static_cast<int>(BlinkBreakdown::kCompositingAssignments) +
kBlinkBreakdownInitialIndex:
- return "SendBeginMainFrameToCommit.Composite";
+ return "SendBeginMainFrameToCommit.CompositingAssignments";
case static_cast<int>(BlinkBreakdown::kPaint) + kBlinkBreakdownInitialIndex:
return "SendBeginMainFrameToCommit.Paint";
case static_cast<int>(BlinkBreakdown::kScrollingCoordinator) +
@@ -156,6 +175,7 @@ constexpr const char* GetStageName(int stage_type_index,
kBlinkBreakdownInitialIndex:
return "SendBeginMainFrameToCommit.BeginMainSentToStarted";
default:
+ NOTREACHED();
return "";
}
}
@@ -174,8 +194,10 @@ static_assert(base::size(kReportTypeNames) == kFrameReportTypeCount,
constexpr int kMaxCompositorLatencyHistogramIndex =
kFrameReportTypeCount * kFrameSequenceTrackerTypeCount *
(kStageTypeCount + kAllBreakdownCount);
-constexpr int kCompositorLatencyHistogramMin = 1;
-constexpr int kCompositorLatencyHistogramMax = 350000;
+constexpr base::TimeDelta kCompositorLatencyHistogramMin =
+ base::TimeDelta::FromMicroseconds(1);
+constexpr base::TimeDelta kCompositorLatencyHistogramMax =
+ base::TimeDelta::FromMilliseconds(350);
constexpr int kCompositorLatencyHistogramBucketCount = 50;
constexpr int kEventLatencyEventTypeCount =
@@ -186,8 +208,10 @@ constexpr int kMaxEventLatencyHistogramBaseIndex =
kEventLatencyEventTypeCount * kEventLatencyScrollTypeCount;
constexpr int kMaxEventLatencyHistogramIndex =
kMaxEventLatencyHistogramBaseIndex * (kStageTypeCount + kAllBreakdownCount);
-constexpr int kEventLatencyHistogramMin = 1;
-constexpr int kEventLatencyHistogramMax = 5000000;
+constexpr base::TimeDelta kEventLatencyHistogramMin =
+ base::TimeDelta::FromMicroseconds(1);
+constexpr base::TimeDelta kEventLatencyHistogramMax =
+ base::TimeDelta::FromSeconds(5);
constexpr int kEventLatencyHistogramBucketCount = 100;
bool ShouldReportLatencyMetricsForSequenceType(
@@ -609,12 +633,15 @@ void CompositorFrameReporter::ReportCompositorLatencyHistogram(
CHECK_LT(histogram_index, kMaxCompositorLatencyHistogramIndex);
CHECK_GE(histogram_index, 0);
+ // Note: There's a 1:1 mapping between `histogram_index` and the name
+ // returned by `GetCompositorLatencyHistogramName()` which allows the use of
+ // `STATIC_HISTOGRAM_POINTER_GROUP()` to cache histogram objects.
STATIC_HISTOGRAM_POINTER_GROUP(
GetCompositorLatencyHistogramName(
report_type_index, frame_sequence_tracker_type, stage_type_index),
histogram_index, kMaxCompositorLatencyHistogramIndex,
AddTimeMicrosecondsGranularity(time_delta),
- base::Histogram::FactoryGet(
+ base::Histogram::FactoryMicrosecondsTimeGet(
GetCompositorLatencyHistogramName(report_type_index,
frame_sequence_tracker_type,
stage_type_index),
@@ -636,20 +663,23 @@ void CompositorFrameReporter::ReportEventLatencyHistograms() const {
const int histogram_base_index =
event_type_index * kEventLatencyScrollTypeCount + scroll_type_index;
- // For scroll events, report total latency up to gpu-swap-end. This is
+ // For scroll events, report total latency up to gpu-swap-begin. This is
// useful in comparing new EventLatency metrics with LatencyInfo-based
// scroll event latency metrics.
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 =
- histogram_base_name + ".TotalLatencyToSwapEnd";
+ const base::TimeDelta swap_begin_latency =
+ viz_breakdown_.swap_timings.swap_start - event_metrics.time_stamp();
+ const std::string swap_begin_histogram_name =
+ histogram_base_name + ".TotalLatencyToSwapBegin";
+ // Note: There's a 1:1 mapping between `histogram_base_index` and
+ // `swap_begin_histogram_name` which allows the use of
+ // `STATIC_HISTOGRAM_POINTER_GROUP()` to cache histogram objects.
STATIC_HISTOGRAM_POINTER_GROUP(
- swap_end_histogram_name, histogram_base_index,
+ swap_begin_histogram_name, histogram_base_index,
kMaxEventLatencyHistogramBaseIndex,
- AddTimeMicrosecondsGranularity(swap_end_latency),
- base::Histogram::FactoryGet(
- swap_end_histogram_name, kEventLatencyHistogramMin,
+ AddTimeMicrosecondsGranularity(swap_begin_latency),
+ base::Histogram::FactoryMicrosecondsTimeGet(
+ swap_begin_histogram_name, kEventLatencyHistogramMin,
kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount,
base::HistogramBase::kUmaTargetedHistogramFlag));
}
@@ -678,11 +708,14 @@ void CompositorFrameReporter::ReportEventLatencyHistograms() const {
stage_it->start_time - event_metrics.time_stamp();
const std::string b2r_histogram_name =
histogram_base_name + ".BrowserToRendererCompositor";
+ // Note: There's a 1:1 mapping between `histogram_base_index` and
+ // `b2r_histogram_name` which allows the use of
+ // `STATIC_HISTOGRAM_POINTER_GROUP()` to cache histogram objects.
STATIC_HISTOGRAM_POINTER_GROUP(
b2r_histogram_name, histogram_base_index,
kMaxEventLatencyHistogramBaseIndex,
AddTimeMicrosecondsGranularity(b2r_latency),
- base::Histogram::FactoryGet(
+ base::Histogram::FactoryMicrosecondsTimeGet(
b2r_histogram_name, kEventLatencyHistogramMin,
kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount,
base::HistogramBase::kUmaTargetedHistogramFlag));
@@ -707,6 +740,11 @@ void CompositorFrameReporter::ReportEventLatencyHistograms() const {
ReportEventLatencyVizBreakdowns(histogram_base_index,
histogram_base_name);
break;
+ case StageType::kTotalLatency:
+ UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+ "EventLatency.TotalLatency", latency, kEventLatencyHistogramMin,
+ kEventLatencyHistogramMax, kEventLatencyHistogramBucketCount);
+ break;
default:
break;
}
@@ -759,10 +797,13 @@ void CompositorFrameReporter::ReportEventLatencyHistogram(
const int histogram_index =
histogram_base_index * (kStageTypeCount + kAllBreakdownCount) +
stage_type_index;
+ // Note: There's a 1:1 mapping between `histogram_index` and `histogram_name`
+ // which allows the use of `STATIC_HISTOGRAM_POINTER_GROUP()` to cache
+ // histogram objects.
STATIC_HISTOGRAM_POINTER_GROUP(
histogram_name, histogram_index, kMaxEventLatencyHistogramIndex,
AddTimeMicrosecondsGranularity(latency),
- base::Histogram::FactoryGet(
+ base::Histogram::FactoryMicrosecondsTimeGet(
histogram_name, kEventLatencyHistogramMin, kEventLatencyHistogramMax,
kEventLatencyHistogramBucketCount,
base::HistogramBase::kUmaTargetedHistogramFlag));
@@ -906,8 +947,11 @@ void CompositorFrameReporter::PopulateBlinkBreakdownList() {
blink_breakdown_.layout_update;
blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kPrepaint)] =
blink_breakdown_.prepaint;
- blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kComposite)] =
- blink_breakdown_.composite;
+ blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kCompositingInputs)] =
+ blink_breakdown_.compositing_inputs;
+ blink_breakdown_list_[static_cast<int>(
+ BlinkBreakdown::kCompositingAssignments)] =
+ blink_breakdown_.compositing_assignments;
blink_breakdown_list_[static_cast<int>(BlinkBreakdown::kPaint)] =
blink_breakdown_.paint;
blink_breakdown_list_[static_cast<int>(
diff --git a/chromium/cc/metrics/compositor_frame_reporter.h b/chromium/cc/metrics/compositor_frame_reporter.h
index 2ff3b986489..85344d29d1d 100644
--- a/chromium/cc/metrics/compositor_frame_reporter.h
+++ b/chromium/cc/metrics/compositor_frame_reporter.h
@@ -111,12 +111,13 @@ class CC_EXPORT CompositorFrameReporter {
kStyleUpdate = 2,
kLayoutUpdate = 3,
kPrepaint = 4,
- kComposite = 5,
- kPaint = 6,
- kScrollingCoordinator = 7,
- kCompositeCommit = 8,
- kUpdateLayers = 9,
- kBeginMainSentToStarted = 10,
+ kCompositingInputs = 5,
+ kCompositingAssignments = 6,
+ kPaint = 7,
+ kScrollingCoordinator = 8,
+ kCompositeCommit = 9,
+ kUpdateLayers = 10,
+ kBeginMainSentToStarted = 11,
kBreakdownCount
};
diff --git a/chromium/cc/metrics/compositor_frame_reporter_unittest.cc b/chromium/cc/metrics/compositor_frame_reporter_unittest.cc
index 414655412dd..dd0bde4e7c7 100644
--- a/chromium/cc/metrics/compositor_frame_reporter_unittest.cc
+++ b/chromium/cc/metrics/compositor_frame_reporter_unittest.cc
@@ -47,19 +47,20 @@ class CompositorFrameReporterTest : public testing::Test {
std::unique_ptr<BeginMainFrameMetrics> BuildBlinkBreakdown() {
auto breakdown = std::make_unique<BeginMainFrameMetrics>();
- breakdown->handle_input_events = base::TimeDelta::FromMicroseconds(10);
- breakdown->animate = base::TimeDelta::FromMicroseconds(9);
- breakdown->style_update = base::TimeDelta::FromMicroseconds(8);
- breakdown->layout_update = base::TimeDelta::FromMicroseconds(7);
+ breakdown->handle_input_events = base::TimeDelta::FromMicroseconds(11);
+ breakdown->animate = base::TimeDelta::FromMicroseconds(10);
+ breakdown->style_update = base::TimeDelta::FromMicroseconds(9);
+ breakdown->layout_update = base::TimeDelta::FromMicroseconds(8);
+ breakdown->compositing_inputs = base::TimeDelta::FromMicroseconds(7);
breakdown->prepaint = base::TimeDelta::FromMicroseconds(6);
- breakdown->composite = base::TimeDelta::FromMicroseconds(5);
+ breakdown->compositing_assignments = base::TimeDelta::FromMicroseconds(5);
breakdown->paint = base::TimeDelta::FromMicroseconds(4);
breakdown->scrolling_coordinator = base::TimeDelta::FromMicroseconds(3);
breakdown->composite_commit = base::TimeDelta::FromMicroseconds(2);
breakdown->update_layers = base::TimeDelta::FromMicroseconds(1);
// Advance now by the sum of the breakdowns.
- AdvanceNowByMs(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1);
+ AdvanceNowByMs(11 + 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1);
return breakdown;
}
@@ -269,10 +270,13 @@ TEST_F(CompositorFrameReporterTest,
histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency",
1);
histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 2);
+ histogram_tester.ExpectTotalCount("EventLatency.TotalLatency", 3);
histogram_tester.ExpectBucketCount("EventLatency.TouchPressed.TotalLatency",
latency_ms, 1);
histogram_tester.ExpectBucketCount("EventLatency.TouchMoved.TotalLatency",
latency_ms, 2);
+ histogram_tester.ExpectBucketCount("EventLatency.TotalLatency", latency_ms,
+ 3);
}
// Tests that when a frame is presented to the user, event latency breakdown
@@ -357,10 +361,13 @@ TEST_F(CompositorFrameReporterTest,
blink_breakdown_copy.style_update},
{"EventLatency.TouchPressed.SendBeginMainFrameToCommit.LayoutUpdate",
blink_breakdown_copy.layout_update},
+ {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.CompositingInputs",
+ blink_breakdown_copy.compositing_inputs},
{"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Prepaint",
blink_breakdown_copy.prepaint},
- {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Composite",
- blink_breakdown_copy.composite},
+ {"EventLatency.TouchPressed.SendBeginMainFrameToCommit"
+ ".CompositingAssignments",
+ blink_breakdown_copy.compositing_assignments},
{"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Paint",
blink_breakdown_copy.paint},
{"EventLatency.TouchPressed.SendBeginMainFrameToCommit."
@@ -408,6 +415,8 @@ TEST_F(CompositorFrameReporterTest,
viz_breakdown.swap_timings.swap_end},
{"EventLatency.TouchPressed.TotalLatency",
viz_breakdown.presentation_feedback.timestamp - event_time},
+ {"EventLatency.TotalLatency",
+ viz_breakdown.presentation_feedback.timestamp - event_time},
};
for (const auto& expected_latency : expected_latencies) {
@@ -465,8 +474,8 @@ TEST_F(CompositorFrameReporterTest,
const int total_latency_ms =
(viz_breakdown.presentation_feedback.timestamp - event_time)
.InMicroseconds();
- const int swap_end_latency_ms =
- (viz_breakdown.swap_timings.swap_end - event_time).InMicroseconds();
+ const int swap_begin_latency_ms =
+ (viz_breakdown.swap_timings.swap_start - event_time).InMicroseconds();
struct {
const char* name;
const int64_t latency_ms;
@@ -474,12 +483,12 @@ TEST_F(CompositorFrameReporterTest,
} expected_counts[] = {
{"EventLatency.GestureScrollBegin.Wheel.TotalLatency", total_latency_ms,
1},
- {"EventLatency.GestureScrollBegin.Wheel.TotalLatencyToSwapEnd",
- swap_end_latency_ms, 1},
+ {"EventLatency.GestureScrollBegin.Wheel.TotalLatencyToSwapBegin",
+ swap_begin_latency_ms, 1},
{"EventLatency.GestureScrollUpdate.Wheel.TotalLatency", total_latency_ms,
2},
- {"EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapEnd",
- swap_end_latency_ms, 2},
+ {"EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapBegin",
+ swap_begin_latency_ms, 2},
};
for (const auto& expected_count : expected_counts) {
histogram_tester.ExpectTotalCount(expected_count.name,
@@ -487,6 +496,7 @@ TEST_F(CompositorFrameReporterTest,
histogram_tester.ExpectBucketCount(
expected_count.name, expected_count.latency_ms, expected_count.count);
}
+ histogram_tester.ExpectTotalCount("EventLatency.TotalLatency", 3);
}
// Tests that when the frame is not presented to the user, event latency metrics
diff --git a/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc
index 68bab0a45c1..4c895fe174e 100644
--- a/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc
+++ b/chromium/cc/metrics/compositor_frame_reporting_controller_unittest.cc
@@ -142,19 +142,20 @@ class CompositorFrameReportingControllerTest : public testing::Test {
std::unique_ptr<BeginMainFrameMetrics> BuildBlinkBreakdown() {
auto breakdown = std::make_unique<BeginMainFrameMetrics>();
- breakdown->handle_input_events = base::TimeDelta::FromMicroseconds(10);
- breakdown->animate = base::TimeDelta::FromMicroseconds(9);
- breakdown->style_update = base::TimeDelta::FromMicroseconds(8);
- breakdown->layout_update = base::TimeDelta::FromMicroseconds(7);
+ breakdown->handle_input_events = base::TimeDelta::FromMicroseconds(11);
+ breakdown->animate = base::TimeDelta::FromMicroseconds(10);
+ breakdown->style_update = base::TimeDelta::FromMicroseconds(9);
+ breakdown->layout_update = base::TimeDelta::FromMicroseconds(8);
+ breakdown->compositing_inputs = base::TimeDelta::FromMicroseconds(7);
breakdown->prepaint = base::TimeDelta::FromMicroseconds(6);
- breakdown->composite = base::TimeDelta::FromMicroseconds(5);
+ breakdown->compositing_assignments = base::TimeDelta::FromMicroseconds(5);
breakdown->paint = base::TimeDelta::FromMicroseconds(4);
breakdown->scrolling_coordinator = base::TimeDelta::FromMicroseconds(3);
breakdown->composite_commit = base::TimeDelta::FromMicroseconds(2);
breakdown->update_layers = base::TimeDelta::FromMicroseconds(1);
// Advance now by the sum of the breakdowns.
- AdvanceNowByMs(10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1);
+ AdvanceNowByMs(11 + 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1);
return breakdown;
}
@@ -874,10 +875,13 @@ TEST_F(CompositorFrameReportingControllerTest, BlinkBreakdown) {
"CompositorLatency.SendBeginMainFrameToCommit.LayoutUpdate",
base::TimeDelta::FromMicroseconds(7).InMilliseconds(), 1);
histogram_tester.ExpectUniqueSample(
+ "CompositorLatency.SendBeginMainFrameToCommit.CompositingInputs",
+ base::TimeDelta::FromMicroseconds(5).InMilliseconds(), 1);
+ histogram_tester.ExpectUniqueSample(
"CompositorLatency.SendBeginMainFrameToCommit.Prepaint",
base::TimeDelta::FromMicroseconds(6).InMilliseconds(), 1);
histogram_tester.ExpectUniqueSample(
- "CompositorLatency.SendBeginMainFrameToCommit.Composite",
+ "CompositorLatency.SendBeginMainFrameToCommit.CompositingAssignments",
base::TimeDelta::FromMicroseconds(5).InMilliseconds(), 1);
histogram_tester.ExpectUniqueSample(
"CompositorLatency.SendBeginMainFrameToCommit.Paint",
@@ -1003,10 +1007,13 @@ TEST_F(CompositorFrameReportingControllerTest,
histogram_tester.ExpectTotalCount("EventLatency.TouchPressed.TotalLatency",
1);
histogram_tester.ExpectTotalCount("EventLatency.TouchMoved.TotalLatency", 2);
+ histogram_tester.ExpectTotalCount("EventLatency.TotalLatency", 3);
histogram_tester.ExpectBucketCount("EventLatency.TouchPressed.TotalLatency",
latency_ms, 1);
histogram_tester.ExpectBucketCount("EventLatency.TouchMoved.TotalLatency",
latency_ms, 2);
+ histogram_tester.ExpectBucketCount("EventLatency.TotalLatency", latency_ms,
+ 3);
}
// Tests that EventLatency breakdown histograms are reported properly when a
@@ -1058,10 +1065,13 @@ TEST_F(CompositorFrameReportingControllerTest,
blink_breakdown_copy.style_update},
{"EventLatency.TouchPressed.SendBeginMainFrameToCommit.LayoutUpdate",
blink_breakdown_copy.layout_update},
+ {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.CompositingInputs",
+ blink_breakdown_copy.compositing_inputs},
{"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Prepaint",
blink_breakdown_copy.prepaint},
- {"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Composite",
- blink_breakdown_copy.composite},
+ {"EventLatency.TouchPressed.SendBeginMainFrameToCommit."
+ "CompositingAssignments",
+ blink_breakdown_copy.compositing_assignments},
{"EventLatency.TouchPressed.SendBeginMainFrameToCommit.Paint",
blink_breakdown_copy.paint},
{"EventLatency.TouchPressed.SendBeginMainFrameToCommit."
@@ -1110,6 +1120,8 @@ TEST_F(CompositorFrameReportingControllerTest,
viz_breakdown.swap_timings.swap_end},
{"EventLatency.TouchPressed.TotalLatency",
viz_breakdown.presentation_feedback.timestamp - event_time},
+ {"EventLatency.TotalLatency",
+ viz_breakdown.presentation_feedback.timestamp - event_time},
};
for (const auto& expected_latency : expected_latencies) {
@@ -1155,8 +1167,8 @@ TEST_F(CompositorFrameReportingControllerTest,
// Verify that EventLatency histograms are recorded.
const int64_t total_latency_ms =
(details.presentation_feedback.timestamp - event_time).InMicroseconds();
- const int64_t swap_end_latency_ms =
- (details.swap_timings.swap_end - event_time).InMicroseconds();
+ const int64_t swap_begin_latency_ms =
+ (details.swap_timings.swap_start - event_time).InMicroseconds();
struct {
const char* name;
const int64_t latency_ms;
@@ -1164,12 +1176,12 @@ TEST_F(CompositorFrameReportingControllerTest,
} expected_counts[] = {
{"EventLatency.GestureScrollBegin.Wheel.TotalLatency", total_latency_ms,
1},
- {"EventLatency.GestureScrollBegin.Wheel.TotalLatencyToSwapEnd",
- swap_end_latency_ms, 1},
+ {"EventLatency.GestureScrollBegin.Wheel.TotalLatencyToSwapBegin",
+ swap_begin_latency_ms, 1},
{"EventLatency.GestureScrollUpdate.Wheel.TotalLatency", total_latency_ms,
2},
- {"EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapEnd",
- swap_end_latency_ms, 2},
+ {"EventLatency.GestureScrollUpdate.Wheel.TotalLatencyToSwapBegin",
+ swap_begin_latency_ms, 2},
};
for (const auto& expected_count : expected_counts) {
histogram_tester.ExpectTotalCount(expected_count.name,
@@ -1177,6 +1189,7 @@ TEST_F(CompositorFrameReportingControllerTest,
histogram_tester.ExpectBucketCount(
expected_count.name, expected_count.latency_ms, expected_count.count);
}
+ histogram_tester.ExpectTotalCount("EventLatency.TotalLatency", 3);
}
// Tests that EventLatency histograms are not reported when the frame is dropped
diff --git a/chromium/cc/metrics/compositor_timing_history.cc b/chromium/cc/metrics/compositor_timing_history.cc
index 99873a0152f..3fd47516b7d 100644
--- a/chromium/cc/metrics/compositor_timing_history.cc
+++ b/chromium/cc/metrics/compositor_timing_history.cc
@@ -25,7 +25,6 @@ class CompositorTimingHistory::UMAReporter {
virtual ~UMAReporter() = default;
// Throughput measurements
- virtual void AddBeginMainFrameIntervalCritical(base::TimeDelta interval) = 0;
virtual void AddDrawInterval(base::TimeDelta interval) = 0;
// Latency measurements
@@ -35,11 +34,7 @@ class CompositorTimingHistory::UMAReporter {
virtual void AddInvalidationToReadyToActivateDuration(
base::TimeDelta duration,
TreePriority priority) = 0;
- virtual void AddReadyToActivateToWillActivateDuration(
- base::TimeDelta duration,
- bool pending_tree_is_impl_side) = 0;
virtual void AddPrepareTilesDuration(base::TimeDelta duration) = 0;
- virtual void AddActivateDuration(base::TimeDelta duration) = 0;
virtual void AddDrawDuration(base::TimeDelta duration) = 0;
virtual void AddSubmitToAckLatency(base::TimeDelta duration) = 0;
@@ -296,11 +291,6 @@ class RendererUMAReporter : public CompositorTimingHistory::UMAReporter {
public:
~RendererUMAReporter() override = default;
- void AddBeginMainFrameIntervalCritical(base::TimeDelta interval) override {
- UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED(
- "Scheduling.Renderer.BeginMainFrameIntervalCritical", interval);
- }
-
void AddDrawInterval(base::TimeDelta interval) override {
UMA_HISTOGRAM_CUSTOM_TIMES_VSYNC_ALIGNED("Scheduling.Renderer.DrawInterval",
interval);
@@ -333,30 +323,11 @@ class RendererUMAReporter : public CompositorTimingHistory::UMAReporter {
priority);
}
- void AddReadyToActivateToWillActivateDuration(
- base::TimeDelta duration,
- bool pending_tree_is_impl_side) override {
- if (pending_tree_is_impl_side) {
- UMA_HISTOGRAM_CUSTOM_TIMES_DURATION_SUFFIX(
- "Scheduling.Renderer.ReadyToActivateToActivationDuration", ".Impl",
- duration);
- } else {
- UMA_HISTOGRAM_CUSTOM_TIMES_DURATION_SUFFIX(
- "Scheduling.Renderer.ReadyToActivateToActivationDuration", ".Main",
- duration);
- }
- }
-
void AddPrepareTilesDuration(base::TimeDelta duration) override {
UMA_HISTOGRAM_CUSTOM_TIMES_DURATION(
"Scheduling.Renderer.PrepareTilesDuration", duration);
}
- void AddActivateDuration(base::TimeDelta duration) override {
- UMA_HISTOGRAM_CUSTOM_TIMES_DURATION("Scheduling.Renderer.ActivateDuration",
- duration);
- }
-
void AddDrawDuration(base::TimeDelta duration) override {
UMA_HISTOGRAM_CUSTOM_TIMES_DURATION("Scheduling.Renderer.DrawDuration",
duration);
@@ -372,10 +343,6 @@ class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter {
public:
~BrowserUMAReporter() override = default;
- // BeginMainFrameIntervalCritical is not meaningful to measure on browser
- // side because browser rendering fps is not at 60.
- void AddBeginMainFrameIntervalCritical(base::TimeDelta interval) override {}
-
// DrawInterval is not meaningful to measure on browser side because
// browser rendering fps is not at 60.
void AddDrawInterval(base::TimeDelta interval) override {}
@@ -403,21 +370,11 @@ class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter {
priority);
}
- void AddReadyToActivateToWillActivateDuration(
- base::TimeDelta duration,
- bool pending_tree_is_impl_side) override {
- UMA_HISTOGRAM_CUSTOM_TIMES_DURATION_SUFFIX(
- "Scheduling.Browser.ReadyToActivateToActivationDuration", ".Main",
- duration);
- }
-
void AddPrepareTilesDuration(base::TimeDelta duration) override {
UMA_HISTOGRAM_CUSTOM_TIMES_DURATION(
"Scheduling.Browser.PrepareTilesDuration", duration);
}
- void AddActivateDuration(base::TimeDelta duration) override {}
-
void AddDrawDuration(base::TimeDelta duration) override {
UMA_HISTOGRAM_CUSTOM_TIMES_DURATION("Scheduling.Browser.DrawDuration",
duration);
@@ -432,7 +389,6 @@ class BrowserUMAReporter : public CompositorTimingHistory::UMAReporter {
class NullUMAReporter : public CompositorTimingHistory::UMAReporter {
public:
~NullUMAReporter() override = default;
- void AddBeginMainFrameIntervalCritical(base::TimeDelta interval) override {}
void AddDrawInterval(base::TimeDelta interval) override {}
void AddDrawIntervalWithCustomPropertyAnimations(
base::TimeDelta inverval) override {}
@@ -442,11 +398,7 @@ class NullUMAReporter : public CompositorTimingHistory::UMAReporter {
void AddInvalidationToReadyToActivateDuration(
base::TimeDelta duration,
TreePriority priority) override {}
- void AddReadyToActivateToWillActivateDuration(
- base::TimeDelta duration,
- bool pending_tree_is_impl_side) override {}
void AddPrepareTilesDuration(base::TimeDelta duration) override {}
- void AddActivateDuration(base::TimeDelta duration) override {}
void AddDrawDuration(base::TimeDelta duration) override {}
void AddSubmitToAckLatency(base::TimeDelta duration) override {}
};
@@ -462,7 +414,6 @@ CompositorTimingHistory::CompositorTimingHistory(
using_synchronous_renderer_compositor),
enabled_(false),
did_send_begin_main_frame_(false),
- begin_main_frame_needed_continuously_(false),
compositor_drawing_continuously_(false),
begin_main_frame_queue_duration_history_(kDurationHistorySize),
begin_main_frame_queue_duration_critical_history_(kDurationHistorySize),
@@ -522,13 +473,6 @@ void CompositorTimingHistory::SetRecordingEnabled(bool enabled) {
enabled_ = enabled;
}
-void CompositorTimingHistory::SetBeginMainFrameNeededContinuously(bool active) {
- if (active == begin_main_frame_needed_continuously_)
- return;
- begin_main_frame_end_time_prev_ = base::TimeTicks();
- begin_main_frame_needed_continuously_ = active;
-}
-
void CompositorTimingHistory::SetCompositorDrawingContinuously(bool active) {
if (active == compositor_drawing_continuously_)
return;
@@ -599,23 +543,12 @@ void CompositorTimingHistory::DidCreateAndInitializeLayerTreeFrameSink() {
void CompositorTimingHistory::WillBeginImplFrame(
const viz::BeginFrameArgs& args,
- bool new_active_tree_is_likely,
base::TimeTicks now) {
viz::BeginFrameArgs::BeginFrameArgsType frame_type = args.type;
base::TimeTicks frame_time = args.frame_time;
compositor_frame_reporting_controller_->WillBeginImplFrame(args);
- // The check for whether a BeginMainFrame was sent anytime between two
- // BeginImplFrames protects us from not detecting a fast main thread that
- // does all it's work and goes idle in between BeginImplFrames.
- // For example, this may happen if an animation is being driven with
- // setInterval(17) or if input events just happen to arrive in the
- // middle of every frame.
- if (!new_active_tree_is_likely && !did_send_begin_main_frame_) {
- SetBeginMainFrameNeededContinuously(false);
- }
-
if (frame_type == viz::BeginFrameArgs::NORMAL)
uma_reporter_->AddBeginImplFrameLatency(now - frame_time);
@@ -631,7 +564,6 @@ void CompositorTimingHistory::WillFinishImplFrame(bool needs_redraw,
}
void CompositorTimingHistory::BeginImplFrameNotExpectedSoon() {
- SetBeginMainFrameNeededContinuously(false);
SetCompositorDrawingContinuously(false);
compositor_frame_reporting_controller_->OnStoppedRequestingBeginFrames();
}
@@ -646,7 +578,6 @@ void CompositorTimingHistory::WillBeginMainFrame(
begin_main_frame_sent_time_ = Now();
did_send_begin_main_frame_ = true;
- SetBeginMainFrameNeededContinuously(true);
}
void CompositorTimingHistory::BeginMainFrameStarted(
@@ -725,16 +656,6 @@ void CompositorTimingHistory::DidBeginMainFrame(
}
}
- if (begin_main_frame_needed_continuously_) {
- if (!begin_main_frame_end_time_prev_.is_null()) {
- base::TimeDelta commit_interval =
- begin_main_frame_end_time - begin_main_frame_end_time_prev_;
- if (begin_main_frame_on_critical_path_)
- uma_reporter_->AddBeginMainFrameIntervalCritical(commit_interval);
- }
- begin_main_frame_end_time_prev_ = begin_main_frame_end_time;
- }
-
begin_main_frame_sent_time_ = base::TimeTicks();
begin_main_frame_start_time_ = base::TimeTicks();
}
@@ -802,16 +723,6 @@ void CompositorTimingHistory::WillActivate() {
compositor_frame_reporting_controller_->WillActivate();
activate_start_time_ = Now();
- // Its possible to activate the pending tree before it is ready for
- // activation, for instance in the case of a context loss or visibility
- // changes.
- if (pending_tree_ready_to_activate_time_ != base::TimeTicks()) {
- base::TimeDelta time_since_ready =
- activate_start_time_ - pending_tree_ready_to_activate_time_;
- uma_reporter_->AddReadyToActivateToWillActivateDuration(
- time_since_ready, pending_tree_is_impl_side_);
- }
-
pending_tree_is_impl_side_ = false;
pending_tree_creation_time_ = base::TimeTicks();
pending_tree_ready_to_activate_time_ = base::TimeTicks();
@@ -822,7 +733,6 @@ void CompositorTimingHistory::DidActivate() {
compositor_frame_reporting_controller_->DidActivate();
base::TimeDelta activate_duration = Now() - activate_start_time_;
- uma_reporter_->AddActivateDuration(activate_duration);
if (enabled_)
activate_duration_history_.InsertSample(activate_duration);
diff --git a/chromium/cc/metrics/compositor_timing_history.h b/chromium/cc/metrics/compositor_timing_history.h
index a4ff2503787..277295e945a 100644
--- a/chromium/cc/metrics/compositor_timing_history.h
+++ b/chromium/cc/metrics/compositor_timing_history.h
@@ -74,7 +74,6 @@ class CC_EXPORT CompositorTimingHistory {
// Events to be timed.
void WillBeginImplFrame(const viz::BeginFrameArgs& args,
- bool new_active_tree_is_likely,
base::TimeTicks now);
void WillFinishImplFrame(bool needs_redraw, const viz::BeginFrameId& id);
void BeginImplFrameNotExpectedSoon();
@@ -121,7 +120,6 @@ class CC_EXPORT CompositorTimingHistory {
protected:
void DidBeginMainFrame(base::TimeTicks begin_main_frame_end_time);
- void SetBeginMainFrameNeededContinuously(bool active);
void SetCompositorDrawingContinuously(bool active);
static std::unique_ptr<UMAReporter> CreateUMAReporter(UMACategory category);
@@ -132,9 +130,7 @@ class CC_EXPORT CompositorTimingHistory {
// Used to calculate frame rates of Main and Impl threads.
bool did_send_begin_main_frame_;
- bool begin_main_frame_needed_continuously_;
bool compositor_drawing_continuously_;
- base::TimeTicks begin_main_frame_end_time_prev_;
base::TimeTicks new_active_tree_draw_end_time_prev_;
base::TimeTicks draw_end_time_prev_;
diff --git a/chromium/cc/metrics/dropped_frame_counter.cc b/chromium/cc/metrics/dropped_frame_counter.cc
index 56564a2415f..1493adf55af 100644
--- a/chromium/cc/metrics/dropped_frame_counter.cc
+++ b/chromium/cc/metrics/dropped_frame_counter.cc
@@ -9,7 +9,6 @@
#include <limits>
#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
namespace cc {
diff --git a/chromium/cc/metrics/dropped_frame_counter.h b/chromium/cc/metrics/dropped_frame_counter.h
index 4929295ddce..22980a03b07 100644
--- a/chromium/cc/metrics/dropped_frame_counter.h
+++ b/chromium/cc/metrics/dropped_frame_counter.h
@@ -10,7 +10,6 @@
#include <memory>
#include "base/containers/ring_buffer.h"
-#include "base/time/time.h"
namespace cc {
@@ -23,6 +22,7 @@ class DroppedFrameCounter {
kFrameStatePartial,
kFrameStateComplete
};
+
DroppedFrameCounter();
DroppedFrameCounter(const DroppedFrameCounter&) = delete;
diff --git a/chromium/cc/metrics/event_metrics.cc b/chromium/cc/metrics/event_metrics.cc
index 951cdfc2ceb..ba4d5b368dc 100644
--- a/chromium/cc/metrics/event_metrics.cc
+++ b/chromium/cc/metrics/event_metrics.cc
@@ -18,7 +18,7 @@ constexpr struct {
EventMetrics::EventType metrics_event_type;
ui::EventType ui_event_type;
const char* name;
-} kWhitelistedEvents[] = {
+} kInterestingEvents[] = {
#define EVENT_TYPE(name, ui_type) \
{ EventMetrics::EventType::k##name, ui_type, #name }
EVENT_TYPE(MousePressed, ui::ET_MOUSE_PRESSED),
@@ -43,7 +43,7 @@ constexpr struct {
EVENT_TYPE(GestureTwoFingerTap, ui::ET_GESTURE_TWO_FINGER_TAP),
#undef EVENT_TYPE
};
-static_assert(base::size(kWhitelistedEvents) ==
+static_assert(base::size(kInterestingEvents) ==
static_cast<int>(EventMetrics::EventType::kMaxValue) + 1,
"EventMetrics::EventType has changed.");
@@ -64,13 +64,13 @@ static_assert(base::size(kScrollTypes) ==
static_cast<int>(EventMetrics::ScrollType::kMaxValue) + 1,
"EventMetrics::ScrollType has changed.");
-base::Optional<EventMetrics::EventType> ToWhitelistedEventType(
+base::Optional<EventMetrics::EventType> ToInterestingEventType(
ui::EventType ui_event_type) {
- for (size_t i = 0; i < base::size(kWhitelistedEvents); i++) {
- if (ui_event_type == kWhitelistedEvents[i].ui_event_type) {
+ for (size_t i = 0; i < base::size(kInterestingEvents); i++) {
+ if (ui_event_type == kInterestingEvents[i].ui_event_type) {
EventMetrics::EventType metrics_event_type =
static_cast<EventMetrics::EventType>(i);
- DCHECK_EQ(metrics_event_type, kWhitelistedEvents[i].metrics_event_type);
+ DCHECK_EQ(metrics_event_type, kInterestingEvents[i].metrics_event_type);
return metrics_event_type;
}
}
@@ -100,10 +100,10 @@ std::unique_ptr<EventMetrics> EventMetrics::Create(
ui::EventType type,
base::TimeTicks time_stamp,
base::Optional<ui::ScrollInputType> scroll_input_type) {
- base::Optional<EventType> whitelisted_type = ToWhitelistedEventType(type);
- if (!whitelisted_type)
+ base::Optional<EventType> interesting_type = ToInterestingEventType(type);
+ if (!interesting_type)
return nullptr;
- return base::WrapUnique(new EventMetrics(*whitelisted_type, time_stamp,
+ return base::WrapUnique(new EventMetrics(*interesting_type, time_stamp,
ToScrollType(scroll_input_type)));
}
@@ -116,7 +116,7 @@ EventMetrics::EventMetrics(const EventMetrics&) = default;
EventMetrics& EventMetrics::operator=(const EventMetrics&) = default;
const char* EventMetrics::GetTypeName() const {
- return kWhitelistedEvents[static_cast<int>(type_)].name;
+ return kInterestingEvents[static_cast<int>(type_)].name;
}
const char* EventMetrics::GetScrollTypeName() const {
diff --git a/chromium/cc/metrics/event_metrics.h b/chromium/cc/metrics/event_metrics.h
index df37365b7ed..13fac55f2c8 100644
--- a/chromium/cc/metrics/event_metrics.h
+++ b/chromium/cc/metrics/event_metrics.h
@@ -20,8 +20,8 @@ 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.
+ // Event types we are interested in. This list should be in the same order as
+ // values of EventLatencyEventType enum from enums.xml file.
enum class EventType {
kMousePressed,
kMouseReleased,
@@ -59,8 +59,8 @@ class CC_EXPORT EventMetrics {
kMaxValue = kWheel,
};
- // Returns a new instance if |type| is a whitelisted event type. Otherwise,
- // returns nullptr.
+ // Returns a new instance if |type| is an event type we are interested in.
+ // Otherwise, returns nullptr.
static std::unique_ptr<EventMetrics> Create(
ui::EventType type,
base::TimeTicks time_stamp,
diff --git a/chromium/cc/metrics/events_metrics_manager.h b/chromium/cc/metrics/events_metrics_manager.h
index 6c44f62d212..ea2c49ffe74 100644
--- a/chromium/cc/metrics/events_metrics_manager.h
+++ b/chromium/cc/metrics/events_metrics_manager.h
@@ -53,6 +53,10 @@ class CC_EXPORT EventsMetricsManager {
// caller.
std::vector<EventMetrics> TakeSavedEventsMetrics();
+ size_t saved_events_metrics_count_for_testing() const {
+ return saved_events_.size();
+ }
+
private:
void OnScopedMonitorEnded();
diff --git a/chromium/cc/metrics/events_metrics_manager_unittest.cc b/chromium/cc/metrics/events_metrics_manager_unittest.cc
index ba162cc3283..376861d2d83 100644
--- a/chromium/cc/metrics/events_metrics_manager_unittest.cc
+++ b/chromium/cc/metrics/events_metrics_manager_unittest.cc
@@ -29,9 +29,9 @@ class EventsMetricsManagerTest : public testing::Test {
EventsMetricsManager manager_;
};
-// Tests that EventMetrics are saved only if they have a whitelisted event type
-// and SaveActiveEventMetrics() is called inside their corresponding monitor's
-// scope.
+// Tests that EventMetrics are saved only if they have an event type we are
+// interested in, and SaveActiveEventMetrics() is called inside their
+// corresponding monitor's scope.
TEST_F(EventsMetricsManagerTest, EventsMetricsSaved) {
enum class Behavior {
kDoNotSave,
@@ -40,23 +40,23 @@ TEST_F(EventsMetricsManagerTest, EventsMetricsSaved) {
};
std::pair<std::unique_ptr<EventMetrics>, Behavior> events[] = {
- // A whitelisted event type for which SaveActiveEventMetrics() is not
+ // An interesting event type for which SaveActiveEventMetrics() is not
// called.
{EventMetrics::Create(ui::ET_MOUSE_PRESSED, TimeAtMs(0), base::nullopt),
Behavior::kDoNotSave},
- // A whitelisted event type for which SaveActiveEventMetrics() is called
+ // An interesting event type for which SaveActiveEventMetrics() is called
// inside its monitor scope.
{EventMetrics::Create(ui::ET_MOUSE_PRESSED, TimeAtMs(1), base::nullopt),
Behavior::kSaveInsideScope},
- // A whitelisted event type for which SaveActiveEventMetrics() is called
+ // An interesting event type for which SaveActiveEventMetrics() is called
// after
// its monitor scope is finished.
{EventMetrics::Create(ui::ET_MOUSE_PRESSED, TimeAtMs(2), base::nullopt),
Behavior::kSaveOutsideScope},
- // A non-whitelisted event type for which SaveActiveEventMetrics() is
+ // A non-interesting event type for which SaveActiveEventMetrics() is
// called inside its monitor scope.
{EventMetrics::Create(ui::ET_MOUSE_MOVED, TimeAtMs(3), base::nullopt),
Behavior::kSaveInsideScope},
@@ -66,7 +66,7 @@ TEST_F(EventsMetricsManagerTest, EventsMetricsSaved) {
EXPECT_NE(events[2].first, nullptr);
EXPECT_EQ(events[3].first, nullptr);
- // Out of the above events, only those with a whitelisted event type, for
+ // Out of the above events, only those with an interesting event type, for
// which SaveActiveEventMetrics() is called inside its monitor scope, are
// expected to be saved.
std::vector<EventMetrics> expected_saved_events = {
diff --git a/chromium/cc/metrics/frame_sequence_metrics.cc b/chromium/cc/metrics/frame_sequence_metrics.cc
index 5933096b855..56c615719f4 100644
--- a/chromium/cc/metrics/frame_sequence_metrics.cc
+++ b/chromium/cc/metrics/frame_sequence_metrics.cc
@@ -14,6 +14,7 @@
#include "base/trace_event/trace_event.h"
#include "base/trace_event/traced_value.h"
#include "cc/metrics/frame_sequence_tracker.h"
+#include "cc/metrics/jank_metrics.h"
#include "cc/metrics/throughput_ukm_reporter.h"
namespace cc {
@@ -72,9 +73,10 @@ bool ShouldReportForInteraction(FrameSequenceMetrics* metrics,
FrameSequenceMetrics::ThreadType thread_type) {
const auto sequence_type = metrics->type();
- // For touch/wheel scroll, the slower thread is the one we want to report. For
- // pinch-zoom, it's the compositor-thread.
- if (sequence_type == FrameSequenceTrackerType::kTouchScroll ||
+ // For scrollbar/touch/wheel scroll, the slower thread is the one we want to
+ // report. For pinch-zoom, it's the compositor-thread.
+ if (sequence_type == FrameSequenceTrackerType::kScrollbarScroll ||
+ sequence_type == FrameSequenceTrackerType::kTouchScroll ||
sequence_type == FrameSequenceTrackerType::kWheelScroll)
return thread_type == metrics->GetEffectiveThread();
@@ -85,7 +87,8 @@ bool ShouldReportForInteraction(FrameSequenceMetrics* metrics,
}
bool IsInteractionType(FrameSequenceTrackerType sequence_type) {
- return sequence_type == FrameSequenceTrackerType::kTouchScroll ||
+ return sequence_type == FrameSequenceTrackerType::kScrollbarScroll ||
+ sequence_type == FrameSequenceTrackerType::kTouchScroll ||
sequence_type == FrameSequenceTrackerType::kWheelScroll ||
sequence_type == FrameSequenceTrackerType::kPinchZoom;
}
@@ -95,6 +98,16 @@ bool IsInteractionType(FrameSequenceTrackerType sequence_type) {
FrameSequenceMetrics::FrameSequenceMetrics(FrameSequenceTrackerType type,
ThroughputUkmReporter* ukm_reporter)
: type_(type), throughput_ukm_reporter_(ukm_reporter) {
+ ThreadType thread_type = GetEffectiveThread();
+
+ // Only construct |jank_reporter_| if it has a valid tracker and thread type.
+ // For scrolling tracker types, |jank_reporter_| may be constructed later in
+ // SetScrollingThread().
+ if ((thread_type == ThreadType::kCompositor ||
+ thread_type == ThreadType::kMain) &&
+ type != FrameSequenceTrackerType::kUniversal &&
+ type != FrameSequenceTrackerType::kCustom)
+ jank_reporter_ = std::make_unique<JankMetrics>(type, thread_type);
}
FrameSequenceMetrics::~FrameSequenceMetrics() = default;
@@ -119,6 +132,11 @@ void FrameSequenceMetrics::SetScrollingThread(ThreadType scrolling_thread) {
type_ == FrameSequenceTrackerType::kScrollbarScroll);
DCHECK_EQ(scrolling_thread_, ThreadType::kUnknown);
scrolling_thread_ = scrolling_thread;
+
+ DCHECK(!jank_reporter_);
+ DCHECK_NE(scrolling_thread, ThreadType::kSlower);
+ DCHECK_NE(scrolling_thread, ThreadType::kUnknown);
+ jank_reporter_ = std::make_unique<JankMetrics>(type_, scrolling_thread);
}
void FrameSequenceMetrics::SetCustomReporter(CustomReporter custom_reporter) {
@@ -131,11 +149,11 @@ FrameSequenceMetrics::ThreadType FrameSequenceMetrics::GetEffectiveThread()
switch (type_) {
case FrameSequenceTrackerType::kCompositorAnimation:
case FrameSequenceTrackerType::kPinchZoom:
+ case FrameSequenceTrackerType::kVideo:
return ThreadType::kCompositor;
case FrameSequenceTrackerType::kMainThreadAnimation:
case FrameSequenceTrackerType::kRAF:
- case FrameSequenceTrackerType::kVideo:
return ThreadType::kMain;
case FrameSequenceTrackerType::kTouchScroll:
@@ -166,6 +184,9 @@ void FrameSequenceMetrics::Merge(
aggregated_throughput_.Merge(metrics->aggregated_throughput_);
frames_checkerboarded_ += metrics->frames_checkerboarded_;
+ if (jank_reporter_)
+ jank_reporter_->Merge(std::move(metrics->jank_reporter_));
+
// Reset the state of |metrics| before destroying it, so that it doesn't end
// up reporting the metrics.
metrics->impl_throughput_ = {};
@@ -194,17 +215,6 @@ 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
- // to be produced, and when that main frame is presented, we are expecting 3
- // impl frames, then the number of expected frames is 3 for the aggregated
- // throughput.
- aggregated_throughput_.frames_expected = impl_throughput_.frames_expected;
- DCHECK_LE(aggregated_throughput_.frames_produced,
- aggregated_throughput_.frames_expected);
-}
-
void FrameSequenceMetrics::ReportMetrics() {
DCHECK_LE(impl_throughput_.frames_produced, impl_throughput_.frames_expected);
DCHECK_LE(main_throughput_.frames_produced, main_throughput_.frames_expected);
@@ -305,6 +315,18 @@ void FrameSequenceMetrics::ReportMetrics() {
frames_checkerboarded_ = 0;
}
+ // Report the jank metrics
+ if (jank_reporter_) {
+ if (jank_reporter_->thread_type() ==
+ FrameSequenceMetrics::ThreadType::kCompositor &&
+ impl_throughput_.frames_expected >= kMinFramesForThroughputMetric)
+ jank_reporter_->ReportJankMetrics(impl_throughput_.frames_expected);
+ else if (jank_reporter_->thread_type() ==
+ FrameSequenceMetrics::ThreadType::kMain &&
+ main_throughput_.frames_expected >= kMinFramesForThroughputMetric)
+ jank_reporter_->ReportJankMetrics(main_throughput_.frames_expected);
+ }
+
// Reset the metrics that reach reporting threshold.
if (impl_throughput_.frames_expected >= kMinFramesForThroughputMetric) {
impl_throughput_ = {};
@@ -314,6 +336,17 @@ void FrameSequenceMetrics::ReportMetrics() {
main_throughput_ = {};
}
+void FrameSequenceMetrics::ComputeJank(
+ FrameSequenceMetrics::ThreadType thread_type,
+ base::TimeTicks presentation_time,
+ base::TimeDelta frame_interval) {
+ if (!jank_reporter_)
+ return;
+
+ if (thread_type == jank_reporter_->thread_type())
+ jank_reporter_->AddPresentedFrame(presentation_time, frame_interval);
+}
+
base::Optional<int> FrameSequenceMetrics::ThroughputData::ReportHistogram(
FrameSequenceMetrics* metrics,
ThreadType thread_type,
@@ -322,14 +355,21 @@ base::Optional<int> FrameSequenceMetrics::ThroughputData::ReportHistogram(
const auto sequence_type = metrics->type();
DCHECK_LT(sequence_type, FrameSequenceTrackerType::kMaxType);
- STATIC_HISTOGRAM_POINTER_GROUP(
- GetFrameSequenceLengthHistogramName(sequence_type),
- static_cast<int>(sequence_type),
- static_cast<int>(FrameSequenceTrackerType::kMaxType),
- Add(data.frames_expected),
- base::Histogram::FactoryGet(
- GetFrameSequenceLengthHistogramName(sequence_type), 1, 1000, 50,
- base::HistogramBase::kUmaTargetedHistogramFlag));
+ // All video frames are compositor thread only.
+ if (sequence_type == FrameSequenceTrackerType::kVideo &&
+ thread_type == ThreadType::kMain)
+ return base::nullopt;
+
+ if (metrics->GetEffectiveThread() == thread_type) {
+ STATIC_HISTOGRAM_POINTER_GROUP(
+ GetFrameSequenceLengthHistogramName(sequence_type),
+ static_cast<int>(sequence_type),
+ static_cast<int>(FrameSequenceTrackerType::kMaxType),
+ Add(data.frames_expected),
+ base::Histogram::FactoryGet(
+ GetFrameSequenceLengthHistogramName(sequence_type), 1, 1000, 50,
+ base::HistogramBase::kUmaTargetedHistogramFlag));
+ }
if (data.frames_expected < kMinFramesForThroughputMetric)
return base::nullopt;
diff --git a/chromium/cc/metrics/frame_sequence_metrics.h b/chromium/cc/metrics/frame_sequence_metrics.h
index 3a77fc57775..8b0d56ba08c 100644
--- a/chromium/cc/metrics/frame_sequence_metrics.h
+++ b/chromium/cc/metrics/frame_sequence_metrics.h
@@ -14,6 +14,7 @@
namespace cc {
class ThroughputUkmReporter;
+class JankMetrics;
enum class FrameSequenceTrackerType {
// Used as an enum for metrics. DO NOT reorder or delete values. Rather,
@@ -106,9 +107,6 @@ class CC_EXPORT FrameSequenceMetrics {
bool HasDataLeftForReporting() const;
// Report related metrics: throughput, checkboarding...
void ReportMetrics();
- void ComputeAggregatedThroughputForTesting() {
- ComputeAggregatedThroughput();
- }
ThroughputData& impl_throughput() { return impl_throughput_; }
ThroughputData& main_throughput() { return main_throughput_; }
@@ -129,9 +127,11 @@ class CC_EXPORT FrameSequenceMetrics {
void AdoptTrace(FrameSequenceMetrics* adopt_from);
void AdvanceTrace(base::TimeTicks timestamp);
- private:
- void ComputeAggregatedThroughput();
+ void ComputeJank(FrameSequenceMetrics::ThreadType thread_type,
+ base::TimeTicks presentation_time,
+ base::TimeDelta frame_interval);
+ private:
const FrameSequenceTrackerType type_;
// Tracks some data to generate useful trace events.
@@ -164,6 +164,8 @@ class CC_EXPORT FrameSequenceMetrics {
// Callback invoked to report metrics for kCustom typed sequence.
CustomReporter custom_reporter_;
+
+ std::unique_ptr<JankMetrics> jank_reporter_;
};
} // namespace cc
diff --git a/chromium/cc/metrics/frame_sequence_metrics_unittest.cc b/chromium/cc/metrics/frame_sequence_metrics_unittest.cc
index c2a1f76babb..e99209a727e 100644
--- a/chromium/cc/metrics/frame_sequence_metrics_unittest.cc
+++ b/chromium/cc/metrics/frame_sequence_metrics_unittest.cc
@@ -14,18 +14,6 @@
namespace cc {
-TEST(FrameSequenceMetricsTest, AggregatedThroughput) {
- FrameSequenceMetrics first(FrameSequenceTrackerType::kTouchScroll, nullptr);
- first.impl_throughput().frames_expected = 200u;
- first.impl_throughput().frames_produced = 190u;
- first.main_throughput().frames_expected = 100u;
- first.main_throughput().frames_produced = 50u;
-
- // The aggregated throughput is computed at ReportMetrics().
- first.ComputeAggregatedThroughputForTesting();
- EXPECT_EQ(first.aggregated_throughput().frames_expected, 200u);
-}
-
TEST(FrameSequenceMetricsTest, AggregatedThroughputClearedAfterReport) {
FrameSequenceMetrics first(FrameSequenceTrackerType::kCompositorAnimation,
nullptr);
@@ -99,6 +87,19 @@ TEST(FrameSequenceMetricsTest, ScrollingThreadMergeMetrics) {
}
#endif // DCHECK_IS_ON()
+TEST(FrameSequenceMetricsTest, VideoReportsOnImplOnly) {
+ base::HistogramTester histograms;
+
+ FrameSequenceMetrics first(FrameSequenceTrackerType::kVideo, nullptr);
+ first.impl_throughput().frames_expected = 120;
+ first.impl_throughput().frames_produced = 80;
+ first.main_throughput().frames_expected = 0;
+ first.main_throughput().frames_produced = 0;
+ first.ReportMetrics();
+ histograms.ExpectTotalCount("Graphics.Smoothness.FrameSequenceLength.Video",
+ 1u);
+}
+
TEST(FrameSequenceMetricsTest, AllMetricsReported) {
base::HistogramTester histograms;
diff --git a/chromium/cc/metrics/frame_sequence_tracker.cc b/chromium/cc/metrics/frame_sequence_tracker.cc
index dec83686b65..3d2f484c81c 100644
--- a/chromium/cc/metrics/frame_sequence_tracker.cc
+++ b/chromium/cc/metrics/frame_sequence_tracker.cc
@@ -189,6 +189,7 @@ void FrameSequenceTracker::ReportBeginMainFrame(
begin_main_frame_data_.previous_sequence_delta;
previous_begin_main_sequence_ = current_begin_main_sequence_;
current_begin_main_sequence_ = args.frame_id.sequence_number;
+ no_damage_impl_frames_while_expecting_main_ = 0;
}
void FrameSequenceTracker::ReportMainFrameProcessed(
@@ -376,6 +377,8 @@ void FrameSequenceTracker::ReportFrameEnd(
begin_impl_frame_data_.previous_sequence = 0;
if (!IsExpectingMainFrame())
--aggregated_throughput().frames_expected;
+ else
+ ++no_damage_impl_frames_while_expecting_main_;
}
// last_submitted_frame_ == 0 means the last impl frame has been presented.
if (termination_status_ == TerminationStatus::kScheduledForTermination &&
@@ -447,6 +450,9 @@ void FrameSequenceTracker::ReportFramePresented(
if (metrics()->GetEffectiveThread() == ThreadType::kCompositor) {
metrics()->AdvanceTrace(feedback.timestamp);
}
+
+ metrics()->ComputeJank(FrameSequenceMetrics::ThreadType::kCompositor,
+ feedback.timestamp, feedback.interval);
}
if (was_presented) {
@@ -466,6 +472,9 @@ void FrameSequenceTracker::ReportFramePresented(
if (metrics()->GetEffectiveThread() == ThreadType::kMain) {
metrics()->AdvanceTrace(feedback.timestamp);
}
+
+ metrics()->ComputeJank(FrameSequenceMetrics::ThreadType::kMain,
+ feedback.timestamp, feedback.interval);
}
if (impl_frames_produced > 0) {
@@ -522,7 +531,7 @@ void FrameSequenceTracker::ReportFramePresented(
: feedback.interval;
DCHECK(!interval.is_zero()) << TRACKER_DCHECK_MSG;
constexpr base::TimeDelta kEpsilon = base::TimeDelta::FromMilliseconds(1);
- int64_t frames = (difference + kEpsilon) / interval;
+ int64_t frames = (difference + kEpsilon).IntDiv(interval);
metrics_->add_checkerboarded_frames(frames);
}
@@ -603,6 +612,16 @@ void FrameSequenceTracker::ReportMainFrameCausedNoDamage(
<< TRACKER_DCHECK_MSG;
last_no_main_damage_sequence_ = args.frame_id.sequence_number;
--main_throughput().frames_expected;
+ // Compute the number of actually expected compositor frames during this main
+ // frame, which produced no damage..
+ DCHECK_GE(aggregated_throughput().frames_expected,
+ no_damage_impl_frames_while_expecting_main_)
+ << TRACKER_DCHECK_MSG;
+ aggregated_throughput().frames_expected -=
+ no_damage_impl_frames_while_expecting_main_;
+ DCHECK_GE(aggregated_throughput().frames_expected,
+ aggregated_throughput().frames_produced)
+ << TRACKER_DCHECK_MSG;
DCHECK_GE(main_throughput().frames_expected, main_frames_.size())
<< TRACKER_DCHECK_MSG;
diff --git a/chromium/cc/metrics/frame_sequence_tracker.h b/chromium/cc/metrics/frame_sequence_tracker.h
index 86deb91b3bd..5d1d92e5a8f 100644
--- a/chromium/cc/metrics/frame_sequence_tracker.h
+++ b/chromium/cc/metrics/frame_sequence_tracker.h
@@ -260,6 +260,11 @@ class CC_EXPORT FrameSequenceTracker {
// only when the last impl-frame is ended (ReportFrameEnd).
bool is_inside_frame_ = false;
+ // The number of no damage impl frames accumulated while expecting main. This
+ // main frame could report no damage eventually, then we need to account for
+ // that in the aggregated throughput.
+ uint32_t no_damage_impl_frames_while_expecting_main_ = 0;
+
#if DCHECK_IS_ON()
// This stringstream represents a sequence of frame reporting activities on
// the current tracker. Each letter can be one of the following:
diff --git a/chromium/cc/metrics/frame_sequence_tracker_collection.cc b/chromium/cc/metrics/frame_sequence_tracker_collection.cc
index 1efd3a37159..7c97a48f869 100644
--- a/chromium/cc/metrics/frame_sequence_tracker_collection.cc
+++ b/chromium/cc/metrics/frame_sequence_tracker_collection.cc
@@ -4,6 +4,9 @@
#include "cc/metrics/frame_sequence_tracker_collection.h"
+#include <utility>
+#include <vector>
+
#include "base/memory/ptr_util.h"
#include "cc/metrics/compositor_frame_reporting_controller.h"
#include "cc/metrics/frame_sequence_tracker.h"
@@ -34,6 +37,8 @@ FrameSequenceTrackerCollection::~FrameSequenceTrackerCollection() {
CleanUp();
frame_trackers_.clear();
removal_trackers_.clear();
+ custom_frame_trackers_.clear();
+ accumulated_metrics_.clear();
}
FrameSequenceTracker* FrameSequenceTrackerCollection::StartSequenceInternal(
diff --git a/chromium/cc/metrics/frame_sequence_tracker_unittest.cc b/chromium/cc/metrics/frame_sequence_tracker_unittest.cc
index 963cf1045f4..d93147afef8 100644
--- a/chromium/cc/metrics/frame_sequence_tracker_unittest.cc
+++ b/chromium/cc/metrics/frame_sequence_tracker_unittest.cc
@@ -1120,8 +1120,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame2) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1148,8 +1149,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame3) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1176,8 +1178,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame4) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1245,8 +1248,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame7) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1273,8 +1277,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame8) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1301,8 +1306,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame9) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1362,8 +1368,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame12) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1390,8 +1397,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame13) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1411,8 +1419,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame14) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1446,8 +1455,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame15) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1474,8 +1484,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame16) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1502,8 +1513,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame17) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1531,8 +1543,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame18) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1559,8 +1572,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame19) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1587,8 +1601,9 @@ TEST_F(FrameSequenceTrackerTest, TrackLastImplFrame20) {
std::string metric = "Graphics.Smoothness.FrameSequenceLength.TouchScroll";
// 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);
+ // The main thread does not submit a report because it is not the effective
+ // thread.
+ EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 0);
metric =
"Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll";
EXPECT_EQ(histogram_tester.GetBucketCount(metric, 0), 1);
@@ -1907,6 +1922,33 @@ TEST_F(FrameSequenceTrackerTest, UniversalTrackerSubmitThroughput) {
EXPECT_FALSE(collection_.HasThroughputData());
}
+// Test that when an impl frame caused no damage is due to waiting on main, the
+// computation of aggregated throughput is correct.
+TEST_F(FrameSequenceTrackerTest, ImplFrameNoDamageWaitingOnMain1) {
+ GenerateSequence("b(1)B(0,1)n(1)N(1,1)e(1,0)");
+ EXPECT_EQ(AggregatedThroughput().frames_expected, 0u);
+}
+
+TEST_F(FrameSequenceTrackerTest, ImplFrameNoDamageWaitingOnMain2) {
+ GenerateSequence("b(1)B(0,1)n(1)e(1,0)N(1,1)");
+ EXPECT_EQ(AggregatedThroughput().frames_expected, 0u);
+}
+
+TEST_F(FrameSequenceTrackerTest, ImplFrameNoDamageWaitingOnMain3) {
+ GenerateSequence("b(1)n(1)B(0,1)Re(1,0)N(0,1)");
+ EXPECT_EQ(AggregatedThroughput().frames_expected, 0u);
+}
+
+TEST_F(FrameSequenceTrackerTest, ImplFrameNoDamageWaitingOnMain4) {
+ GenerateSequence("b(1)B(0,1)n(1)e(1,0)b(2)n(2)e(2,0)N(1,1)");
+ EXPECT_EQ(AggregatedThroughput().frames_expected, 0u);
+}
+
+TEST_F(FrameSequenceTrackerTest, ImplFrameNoDamageWaitingOnMain5) {
+ GenerateSequence("b(2)B(0,2)n(2)e(2,0)b(3)s(1)S(1)e(3,0)N(2,2)");
+ EXPECT_EQ(AggregatedThroughput().frames_expected, 1u);
+}
+
TEST_F(FrameSequenceTrackerTest, CustomTrackers) {
// Start custom tracker 1.
collection_.StartCustomSequence(1);
diff --git a/chromium/cc/metrics/jank_metrics.cc b/chromium/cc/metrics/jank_metrics.cc
new file mode 100644
index 00000000000..fba24b14e25
--- /dev/null
+++ b/chromium/cc/metrics/jank_metrics.cc
@@ -0,0 +1,124 @@
+// 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/jank_metrics.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/strcat.h"
+#include "base/trace_event/trace_event.h"
+#include "cc/metrics/frame_sequence_tracker.h"
+
+namespace cc {
+
+namespace {
+
+constexpr int kBuiltinSequenceNum =
+ static_cast<int>(FrameSequenceTrackerType::kMaxType) + 1;
+constexpr int kMaximumJankHistogramIndex = 2 * kBuiltinSequenceNum;
+
+constexpr bool IsValidJankThreadType(FrameSequenceMetrics::ThreadType type) {
+ return type == FrameSequenceMetrics::ThreadType::kCompositor ||
+ type == FrameSequenceMetrics::ThreadType::kMain;
+}
+
+const char* GetJankThreadTypeName(FrameSequenceMetrics::ThreadType type) {
+ DCHECK(IsValidJankThreadType(type));
+
+ switch (type) {
+ case FrameSequenceMetrics::ThreadType::kCompositor:
+ return "Compositor";
+ case FrameSequenceMetrics::ThreadType::kMain:
+ return "Main";
+ default:
+ NOTREACHED();
+ return "";
+ }
+}
+
+int GetIndexForJankMetric(FrameSequenceMetrics::ThreadType thread_type,
+ FrameSequenceTrackerType type) {
+ DCHECK(IsValidJankThreadType(thread_type));
+ if (thread_type == FrameSequenceMetrics::ThreadType::kMain)
+ return static_cast<int>(type);
+
+ DCHECK_EQ(thread_type, FrameSequenceMetrics::ThreadType::kCompositor);
+ return static_cast<int>(type) + kBuiltinSequenceNum;
+}
+
+std::string GetJankHistogramName(FrameSequenceTrackerType type,
+ const char* thread_name) {
+ return base::StrCat(
+ {"Graphics.Smoothness.Jank.", thread_name, ".",
+ FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type)});
+}
+
+} // namespace
+
+JankMetrics::JankMetrics(FrameSequenceTrackerType tracker_type,
+ FrameSequenceMetrics::ThreadType effective_thread)
+ : tracker_type_(tracker_type), effective_thread_(effective_thread) {
+ DCHECK(IsValidJankThreadType(effective_thread));
+}
+JankMetrics::~JankMetrics() = default;
+
+void JankMetrics::AddPresentedFrame(
+ base::TimeTicks current_presentation_timestamp,
+ base::TimeDelta frame_interval) {
+ base::TimeDelta current_frame_delta =
+ current_presentation_timestamp - last_presentation_timestamp_;
+
+ // Only start tracking jank if this function has been called (so that
+ // |last_presentation_timestamp_| and |prev_frame_delta_| have been set).
+ //
+ // The presentation interval is typically a multiple of VSync intervals (i.e.
+ // 16.67ms, 33.33ms, 50ms ... on a 60Hz display) with small fluctuations. The
+ // 0.5 * |frame_interval| criterion is chosen so that the jank detection is
+ // robust to those fluctuations.
+ if (!last_presentation_timestamp_.is_null() && !prev_frame_delta_.is_zero() &&
+ current_frame_delta > prev_frame_delta_ + 0.5 * frame_interval) {
+ jank_count_++;
+
+ TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1(
+ "cc,benchmark", "Jank", TRACE_ID_LOCAL(this),
+ last_presentation_timestamp_, "thread-type",
+ GetJankThreadTypeName(effective_thread_));
+ TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP1(
+ "cc,benchmark", "Jank", TRACE_ID_LOCAL(this),
+ current_presentation_timestamp, "tracker-type",
+ FrameSequenceTracker::GetFrameSequenceTrackerTypeName(tracker_type_));
+ }
+ last_presentation_timestamp_ = current_presentation_timestamp;
+
+ prev_frame_delta_ = current_frame_delta;
+}
+
+void JankMetrics::ReportJankMetrics(int frames_expected) {
+ if (tracker_type_ == FrameSequenceTrackerType::kUniversal ||
+ tracker_type_ == FrameSequenceTrackerType::kCustom)
+ return;
+
+ int jank_percent = static_cast<int>(100 * jank_count_ / frames_expected);
+
+ const char* jank_thread_name = GetJankThreadTypeName(effective_thread_);
+
+ STATIC_HISTOGRAM_POINTER_GROUP(
+ GetJankHistogramName(tracker_type_, jank_thread_name),
+ GetIndexForJankMetric(effective_thread_, tracker_type_),
+ kMaximumJankHistogramIndex, Add(jank_percent),
+ base::LinearHistogram::FactoryGet(
+ GetJankHistogramName(tracker_type_, jank_thread_name), 1, 100, 101,
+ base::HistogramBase::kUmaTargetedHistogramFlag));
+}
+
+void JankMetrics::Merge(std::unique_ptr<JankMetrics> jank_metrics) {
+ if (jank_metrics)
+ jank_count_ += jank_metrics->jank_count_;
+}
+
+} // namespace cc
diff --git a/chromium/cc/metrics/jank_metrics.h b/chromium/cc/metrics/jank_metrics.h
new file mode 100644
index 00000000000..71aca6b58e8
--- /dev/null
+++ b/chromium/cc/metrics/jank_metrics.h
@@ -0,0 +1,57 @@
+// 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_JANK_METRICS_H_
+#define CC_METRICS_JANK_METRICS_H_
+
+#include <memory>
+
+#include "cc/metrics/frame_sequence_metrics.h"
+
+namespace cc {
+class CC_EXPORT JankMetrics {
+ public:
+ JankMetrics(FrameSequenceTrackerType tracker_type,
+ FrameSequenceMetrics::ThreadType effective_thread);
+ ~JankMetrics();
+
+ JankMetrics(const JankMetrics&) = delete;
+ JankMetrics& operator=(const JankMetrics&) = delete;
+
+ // Check if a jank occurs based on the timestamps of recent presentations.
+ // If there is a jank, increment |jank_count_| and log a trace event.
+ void AddPresentedFrame(base::TimeTicks current_presentation_timestamp,
+ base::TimeDelta frame_interval);
+
+ // Report the occurrence rate of janks as a UMA metric.
+ void ReportJankMetrics(int frames_expected);
+
+ // Merge the current jank count with a previously unreported jank metrics.
+ void Merge(std::unique_ptr<JankMetrics> jank_metrics);
+
+ FrameSequenceMetrics::ThreadType thread_type() const {
+ return effective_thread_;
+ }
+
+ private:
+ // The type of the tracker this JankMetrics object is attached to.
+ const FrameSequenceTrackerType tracker_type_;
+
+ // The thread that contributes to the janks detected by the current
+ // JankMetrics object.
+ const FrameSequenceMetrics::ThreadType effective_thread_;
+
+ // Number of janks detected.
+ int jank_count_ = 0;
+
+ // The time when the last presentation occurs
+ base::TimeTicks last_presentation_timestamp_;
+
+ // The interval before the previous frame presentation.
+ base::TimeDelta prev_frame_delta_;
+};
+
+} // namespace cc
+
+#endif // CC_METRICS_JANK_METRICS_H_
diff --git a/chromium/cc/metrics/jank_metrics_unittest.cc b/chromium/cc/metrics/jank_metrics_unittest.cc
new file mode 100644
index 00000000000..687a8358045
--- /dev/null
+++ b/chromium/cc/metrics/jank_metrics_unittest.cc
@@ -0,0 +1,274 @@
+// 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/jank_metrics.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/strings/strcat.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "cc/metrics/frame_sequence_tracker.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"
+
+namespace cc {
+
+class JankMetricsTest : public testing::Test {
+ public:
+ JankMetricsTest() = default;
+ ~JankMetricsTest() override = default;
+
+ // Create a sequence of PresentationFeedback for testing based on the provided
+ // sequence of actual frame intervals and the expected frame interval. The
+ // size of the returned sequence is |actual_intervals_ms|.size() + 1
+ static std::vector<gfx::PresentationFeedback> CreateFeedbackSequence(
+ const std::vector<double>& actual_intervals_ms,
+ double expected_interval_ms) {
+ std::vector<gfx::PresentationFeedback> feedbacks;
+
+ // The timestamp of the first presentation.
+ base::TimeTicks start_time = base::TimeTicks::Now();
+ double accum_interval = 0.0;
+ base::TimeDelta expected_interval =
+ base::TimeDelta::FromMillisecondsD(expected_interval_ms);
+
+ feedbacks.emplace_back(
+ gfx::PresentationFeedback(start_time, expected_interval, 0));
+ for (auto interval : actual_intervals_ms) {
+ accum_interval += interval;
+ feedbacks.emplace_back(gfx::PresentationFeedback{
+ start_time + base::TimeDelta::FromMillisecondsD(accum_interval),
+ expected_interval, 0});
+ }
+ return feedbacks;
+ }
+
+ // Notify |jank_reporter| of all presentations in |feedbacks|.
+ void AddPresentedFramesToJankReporter(
+ JankMetrics* jank_reporter,
+ const std::vector<gfx::PresentationFeedback>& feedbacks) {
+ for (auto feedback : feedbacks) {
+ jank_reporter->AddPresentedFrame(feedback.timestamp, feedback.interval);
+ }
+ }
+};
+
+TEST_F(JankMetricsTest, CompositorAnimationMildFluctuationNoJank) {
+ base::HistogramTester histogram_tester;
+ FrameSequenceTrackerType tracker_type =
+ FrameSequenceTrackerType::kCompositorAnimation;
+ FrameSequenceMetrics::ThreadType thread_type =
+ FrameSequenceMetrics::ThreadType::kCompositor;
+ JankMetrics jank_reporter{tracker_type, thread_type};
+
+ // No jank. Small upticks such as 15->17 or 14->18 do not qualify as janks.
+ auto feedbacks =
+ CreateFeedbackSequence({16.67, 16.67, 15, 17, 14, 18, 15, 16.67}, 16.67);
+
+ AddPresentedFramesToJankReporter(&jank_reporter, feedbacks);
+ jank_reporter.ReportJankMetrics(100u);
+
+ // One sample of 0 janks reported for "Compositor".
+ const char* metric =
+ "Graphics.Smoothness.Jank.Compositor.CompositorAnimation";
+ const char* invalid_metric =
+ "Graphics.Smoothness.Jank.Main.CompositorAnimation";
+
+ histogram_tester.ExpectTotalCount(metric, 1u);
+ EXPECT_THAT(histogram_tester.GetAllSamples(metric),
+ testing::ElementsAre(base::Bucket(0, 1)));
+
+ // No reporting for "Main".
+ histogram_tester.ExpectTotalCount(invalid_metric, 0u);
+}
+
+TEST_F(JankMetricsTest, MainThreadAnimationOneJank) {
+ base::HistogramTester histogram_tester;
+ FrameSequenceTrackerType tracker_type =
+ FrameSequenceTrackerType::kMainThreadAnimation;
+ FrameSequenceMetrics::ThreadType thread_type =
+ FrameSequenceMetrics::ThreadType::kMain;
+ JankMetrics jank_reporter{tracker_type, thread_type};
+
+ // One Main thread jank from 15 to 24, since 24 - 15 = 9, which is greater
+ // then 0.5 * frame_interval = 8.33. The jank occurrence is visually marked
+ // with a "+" sign.
+ auto feedbacks =
+ CreateFeedbackSequence({48, 15, +24, 14, 18, 15, 16.67}, 16.67);
+
+ AddPresentedFramesToJankReporter(&jank_reporter, feedbacks);
+ jank_reporter.ReportJankMetrics(100u);
+
+ // One jank is reported for "Main".
+ const char* metric = "Graphics.Smoothness.Jank.Main.MainThreadAnimation";
+ const char* invalid_metric =
+ "Graphics.Smoothness.Jank.Compositor.MainThreadAnimation";
+
+ histogram_tester.ExpectTotalCount(metric, 1u);
+ EXPECT_THAT(histogram_tester.GetAllSamples(metric),
+ testing::ElementsAre(base::Bucket(1, 1)));
+
+ // No jank is reported for "Compositor"
+ histogram_tester.ExpectTotalCount(invalid_metric, 0u);
+}
+
+TEST_F(JankMetricsTest, VideoManyJanksOver300ExpectedFrames) {
+ base::HistogramTester histogram_tester;
+ FrameSequenceTrackerType tracker_type = FrameSequenceTrackerType::kVideo;
+ FrameSequenceMetrics::ThreadType thread_type =
+ FrameSequenceMetrics::ThreadType::kCompositor;
+ JankMetrics jank_reporter{tracker_type, thread_type};
+
+ // 7 janks.
+ auto feedbacks = CreateFeedbackSequence(
+ {15, +33, +50, 33, 16, +33, +50, +100, +120, +180}, 16.67);
+
+ AddPresentedFramesToJankReporter(&jank_reporter, feedbacks);
+ jank_reporter.ReportJankMetrics(300u);
+
+ // Report in the 7/300 ~= 2% bucket for "Compositor"
+ const char* metric = "Graphics.Smoothness.Jank.Compositor.Video";
+ const char* invalid_metric = "Graphics.Smoothness.Jank.Main.Video";
+
+ histogram_tester.ExpectTotalCount(metric, 1u);
+ EXPECT_THAT(histogram_tester.GetAllSamples(metric),
+ testing::ElementsAre(base::Bucket(2, 1)));
+
+ // No jank is reported for "Main"
+ histogram_tester.ExpectTotalCount(invalid_metric, 0u);
+}
+
+TEST_F(JankMetricsTest, WheelScrollMainThreadTwoJanks) {
+ base::HistogramTester histogram_tester;
+ FrameSequenceTrackerType tracker_type =
+ FrameSequenceTrackerType::kWheelScroll;
+ FrameSequenceMetrics::ThreadType thread_type =
+ FrameSequenceMetrics::ThreadType::kMain;
+
+ JankMetrics jank_reporter{tracker_type, thread_type};
+
+ auto feedbacks = CreateFeedbackSequence({33, 16, +33, +48, 33}, 16.67);
+ AddPresentedFramesToJankReporter(&jank_reporter, feedbacks);
+ jank_reporter.ReportJankMetrics(100u);
+
+ // Expect 2 janks for "Main" and no jank for "Compositor"
+ const char* metric = "Graphics.Smoothness.Jank.Main.WheelScroll";
+ const char* invalid_metric =
+ "Graphics.Smoothness.Jank.Compositor.WheelScroll";
+
+ histogram_tester.ExpectTotalCount(metric, 1u);
+ EXPECT_THAT(histogram_tester.GetAllSamples(metric),
+ testing::ElementsAre(base::Bucket(2, 1)));
+
+ histogram_tester.ExpectTotalCount(invalid_metric, 0u);
+}
+
+TEST_F(JankMetricsTest, TouchScrollCompositorThreadManyJanks) {
+ base::HistogramTester histogram_tester;
+ FrameSequenceTrackerType tracker_type =
+ FrameSequenceTrackerType::kTouchScroll;
+ FrameSequenceMetrics::ThreadType thread_type =
+ FrameSequenceMetrics::ThreadType::kCompositor;
+
+ JankMetrics jank_reporter{tracker_type, thread_type};
+
+ auto feedbacks =
+ CreateFeedbackSequence({33, 16, +33, +48, +100, 16, +48, +100}, 16.67);
+
+ AddPresentedFramesToJankReporter(&jank_reporter, feedbacks);
+ jank_reporter.ReportJankMetrics(120u);
+
+ // Expect janks in the 5/120 ~= 4% bucket for "Compositor", and no jank for
+ // "Main"
+ const char* metric = "Graphics.Smoothness.Jank.Compositor.TouchScroll";
+ const char* invalid_metric = "Graphics.Smoothness.Jank.Main.TouchScroll";
+
+ histogram_tester.ExpectTotalCount(metric, 1u);
+ EXPECT_THAT(histogram_tester.GetAllSamples(metric),
+ testing::ElementsAre(base::Bucket(4, 1)));
+
+ histogram_tester.ExpectTotalCount(invalid_metric, 0u);
+}
+
+// Test if the jank reporter can correctly merge janks from another jank
+// reporter.
+TEST_F(JankMetricsTest, RAFMergeJanks) {
+ base::HistogramTester histogram_tester;
+ FrameSequenceTrackerType tracker_type = FrameSequenceTrackerType::kRAF;
+ FrameSequenceMetrics::ThreadType thread_type =
+ FrameSequenceMetrics::ThreadType::kMain;
+
+ JankMetrics jank_reporter{tracker_type, thread_type};
+ std::unique_ptr<JankMetrics> other_reporter =
+ std::make_unique<JankMetrics>(tracker_type, thread_type);
+
+ auto feedbacks = CreateFeedbackSequence({33, +50, 16, +33, 33, +48}, 16.67);
+ AddPresentedFramesToJankReporter(other_reporter.get(), feedbacks);
+ AddPresentedFramesToJankReporter(&jank_reporter, feedbacks);
+
+ jank_reporter.Merge(std::move(other_reporter));
+ jank_reporter.ReportJankMetrics(100u);
+
+ // Expect 6 janks for "Main" (3 from each reporter)
+ const char* metric = "Graphics.Smoothness.Jank.Main.RAF";
+ const char* invalid_metric = "Graphics.Smoothness.Jank.Compositor.RAF";
+
+ histogram_tester.ExpectTotalCount(metric, 1u);
+ EXPECT_THAT(histogram_tester.GetAllSamples(metric),
+ testing::ElementsAre(base::Bucket(6, 1)));
+
+ histogram_tester.ExpectTotalCount(invalid_metric, 0u);
+}
+
+// Test if jank reporting is correctly disabled for Universal trackers.
+TEST_F(JankMetricsTest, UniversalNotReported) {
+ base::HistogramTester histogram_tester;
+ FrameSequenceTrackerType tracker_type = FrameSequenceTrackerType::kUniversal;
+ FrameSequenceMetrics::ThreadType thread_type =
+ FrameSequenceMetrics::ThreadType::kCompositor;
+ JankMetrics jank_reporter{tracker_type, thread_type};
+
+ // There should be 4 janks, but the jank reporter does not track or report
+ // them.
+ auto feedbacks = CreateFeedbackSequence({16, +33, +48, 16, +33, +48}, 16.67);
+
+ AddPresentedFramesToJankReporter(&jank_reporter, feedbacks);
+ jank_reporter.ReportJankMetrics(100u);
+
+ // Expect no jank reports even though the sequence contains jank
+ histogram_tester.ExpectTotalCount("Graphics.Smoothness.Jank.Main.Universal",
+ 0u);
+ histogram_tester.ExpectTotalCount(
+ "Graphics.Smoothness.Jank.Compositor.Universal", 0u);
+}
+
+// Test if jank reporting is correctly disabled for Custom trackers.
+TEST_F(JankMetricsTest, CustomNotReported) {
+ base::HistogramTester histogram_tester;
+ FrameSequenceTrackerType tracker_type = FrameSequenceTrackerType::kCustom;
+ FrameSequenceMetrics::ThreadType thread_type =
+ FrameSequenceMetrics::ThreadType::kMain;
+ JankMetrics jank_reporter{tracker_type, thread_type};
+
+ // There should be 4 janks, but the jank reporter does not track or report
+ // them.
+ auto feedbacks = CreateFeedbackSequence({16, +33, +48, 16, +33, +48}, 16.67);
+
+ AddPresentedFramesToJankReporter(&jank_reporter, feedbacks);
+ jank_reporter.ReportJankMetrics(100u);
+
+ // Expect no jank reports even though the sequence contains jank
+ histogram_tester.ExpectTotalCount("Graphics.Smoothness.Jank.Main.Custom", 0u);
+ histogram_tester.ExpectTotalCount(
+ "Graphics.Smoothness.Jank.Compositor.Custom", 0u);
+}
+
+} // namespace cc
diff --git a/chromium/cc/metrics/total_frame_counter.cc b/chromium/cc/metrics/total_frame_counter.cc
new file mode 100644
index 00000000000..c73caac9a96
--- /dev/null
+++ b/chromium/cc/metrics/total_frame_counter.cc
@@ -0,0 +1,63 @@
+// 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/total_frame_counter.h"
+
+#include "base/logging.h"
+#include "components/viz/common/frame_sinks/begin_frame_args.h"
+
+namespace cc {
+
+TotalFrameCounter::TotalFrameCounter() = default;
+
+void TotalFrameCounter::OnShow(base::TimeTicks timestamp) {
+ DCHECK(last_shown_timestamp_.is_null());
+ DCHECK(latest_interval_.is_zero());
+ last_shown_timestamp_ = timestamp;
+}
+
+void TotalFrameCounter::OnHide(base::TimeTicks timestamp) {
+ // It is possible to hide right after being shown before receiving any
+ // BeginFrameArgs.
+ if (!latest_interval_.is_zero())
+ UpdateTotalFramesSinceLastVisible(timestamp);
+ last_shown_timestamp_ = base::TimeTicks();
+ latest_interval_ = base::TimeDelta();
+}
+
+void TotalFrameCounter::OnBeginFrame(const viz::BeginFrameArgs& args) {
+ // In tests, it is possible to receive begin-frames when invisible. Ignore
+ // these.
+ if (last_shown_timestamp_.is_null())
+ return;
+
+ if (!latest_interval_.is_zero() && latest_interval_ != args.interval) {
+ UpdateTotalFramesSinceLastVisible(args.frame_time);
+ last_shown_timestamp_ = args.frame_time;
+ }
+
+ latest_interval_ = args.interval;
+}
+
+void TotalFrameCounter::Reset() {
+ total_frames_ = 0;
+ latest_interval_ = {};
+ // If the compositor is visible, then update the visible timestamp to current
+ // time.
+ if (!last_shown_timestamp_.is_null())
+ last_shown_timestamp_ = base::TimeTicks::Now();
+}
+
+void TotalFrameCounter::UpdateTotalFramesSinceLastVisible(
+ base::TimeTicks until) {
+ DCHECK(!until.is_null());
+ DCHECK(!last_shown_timestamp_.is_null());
+ DCHECK_GE(until, last_shown_timestamp_);
+ DCHECK(!latest_interval_.is_zero());
+ auto frames_since =
+ std::round((until - last_shown_timestamp_) / latest_interval_);
+ total_frames_ += frames_since;
+}
+
+} // namespace cc
diff --git a/chromium/cc/metrics/total_frame_counter.h b/chromium/cc/metrics/total_frame_counter.h
new file mode 100644
index 00000000000..9550f630550
--- /dev/null
+++ b/chromium/cc/metrics/total_frame_counter.h
@@ -0,0 +1,50 @@
+// 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_TOTAL_FRAME_COUNTER_H_
+#define CC_METRICS_TOTAL_FRAME_COUNTER_H_
+
+#include "base/time/time.h"
+#include "cc/cc_export.h"
+
+namespace viz {
+struct BeginFrameArgs;
+}
+
+namespace cc {
+
+// This class keeps track of how many vsyncs (frames) the compositor was visible
+// for.
+class CC_EXPORT TotalFrameCounter {
+ public:
+ TotalFrameCounter();
+
+ TotalFrameCounter(const TotalFrameCounter&) = delete;
+ TotalFrameCounter& operator=(const TotalFrameCounter&) = delete;
+
+ void Reset();
+
+ void OnShow(base::TimeTicks timestamp);
+ void OnHide(base::TimeTicks timestamp);
+ void OnBeginFrame(const viz::BeginFrameArgs& args);
+
+ size_t total_frames() const { return total_frames_; }
+
+ private:
+ void UpdateTotalFramesSinceLastVisible(base::TimeTicks until);
+
+ size_t total_frames_ = 0;
+
+ // The most recent vsync-interval set by the display compositor.
+ // Set only if the compositor is currently visible, otherwise not set.
+ base::TimeDelta latest_interval_;
+
+ // The time the compositor was made visible.
+ // Set only if the compositor is currently visible, otherwise not set.
+ base::TimeTicks last_shown_timestamp_;
+};
+
+} // namespace cc
+
+#endif // CC_METRICS_TOTAL_FRAME_COUNTER_H_
diff --git a/chromium/cc/metrics/total_frame_counter_unittest.cc b/chromium/cc/metrics/total_frame_counter_unittest.cc
new file mode 100644
index 00000000000..fce7a6da1d4
--- /dev/null
+++ b/chromium/cc/metrics/total_frame_counter_unittest.cc
@@ -0,0 +1,93 @@
+// 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/total_frame_counter.h"
+#include "components/viz/common/frame_sinks/begin_frame_args.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+const uint64_t kSourceId = 1;
+
+TEST(TotalFrameCounterTest, Basic) {
+ TotalFrameCounter counter;
+ uint64_t sequence_number = 1;
+ auto frame_time = base::TimeTicks::Now();
+ const auto interval = base::TimeDelta::FromMillisecondsD(16.67);
+
+ auto args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, kSourceId, sequence_number++, frame_time,
+ frame_time + interval, interval, viz::BeginFrameArgs::NORMAL);
+ counter.OnShow(frame_time);
+ counter.OnBeginFrame(args);
+
+ auto advance = base::TimeDelta::FromSeconds(1);
+ frame_time += advance;
+ counter.OnHide(frame_time);
+ EXPECT_EQ(counter.total_frames(), 60u);
+}
+
+TEST(TotalFrameCounterTest, BeginFrameIntervalChange) {
+ TotalFrameCounter counter;
+ uint64_t sequence_number = 1;
+ auto frame_time = base::TimeTicks::Now();
+ auto interval = base::TimeDelta::FromMillisecondsD(16.67);
+
+ // Make the page visible at the default frame rate.
+ auto args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, kSourceId, sequence_number++, frame_time,
+ frame_time + interval, interval, viz::BeginFrameArgs::NORMAL);
+ counter.OnShow(frame_time);
+ counter.OnBeginFrame(args);
+
+ // After 10 seconds, change the frame rate to be 120fps.
+ interval = base::TimeDelta::FromMillisecondsD(8.33);
+ frame_time += base::TimeDelta::FromSeconds(10);
+ args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, kSourceId, sequence_number++, frame_time,
+ frame_time + interval, interval, viz::BeginFrameArgs::NORMAL);
+ counter.OnBeginFrame(args);
+
+ // Hide the page after 10 more seconds.
+ auto advance = base::TimeDelta::FromSeconds(10);
+ frame_time += advance;
+ counter.OnHide(frame_time);
+ EXPECT_EQ(counter.total_frames(), 1800u);
+}
+
+TEST(TotalFrameCounterTest, VisibilityChange) {
+ TotalFrameCounter counter;
+ uint64_t sequence_number = 1;
+ auto frame_time = base::TimeTicks::Now();
+ auto interval = base::TimeDelta::FromMillisecondsD(16.67);
+
+ // Make the page visible at the default frame rate.
+ auto args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, kSourceId, sequence_number++, frame_time,
+ frame_time + interval, interval, viz::BeginFrameArgs::NORMAL);
+ counter.OnShow(frame_time);
+ counter.OnBeginFrame(args);
+
+ // Hide the page after 10 seconds.
+ frame_time += base::TimeDelta::FromSeconds(10);
+ counter.OnHide(frame_time);
+ EXPECT_EQ(counter.total_frames(), 600u);
+
+ // After 20 more seconds, make the page visible again and keep it visible for
+ // 5 more seconds.
+ frame_time += base::TimeDelta::FromSeconds(20);
+ counter.OnShow(frame_time);
+ args = viz::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, kSourceId, sequence_number++, frame_time,
+ frame_time + interval, interval, viz::BeginFrameArgs::NORMAL);
+ counter.OnBeginFrame(args);
+
+ frame_time += base::TimeDelta::FromSeconds(5);
+ counter.OnHide(frame_time);
+ EXPECT_EQ(counter.total_frames(), 900u);
+}
+
+} // namespace
+} // namespace cc
diff --git a/chromium/cc/metrics/video_playback_roughness_reporter.cc b/chromium/cc/metrics/video_playback_roughness_reporter.cc
index a7b77e99a65..ac5e4d7298d 100644
--- a/chromium/cc/metrics/video_playback_roughness_reporter.cc
+++ b/chromium/cc/metrics/video_playback_roughness_reporter.cc
@@ -8,6 +8,8 @@
#include "base/bind_helpers.h"
#include "base/metrics/histogram_macros.h"
+#include "base/numerics/ranges.h"
+#include "base/numerics/safe_conversions.h"
#include "components/viz/common/quads/compositor_frame_metadata.h"
namespace {
@@ -30,7 +32,6 @@ 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)
@@ -59,6 +60,8 @@ void VideoPlaybackRoughnessReporter::FrameSubmitted(
FrameInfo info;
info.token = token;
info.decode_time = frame.metadata()->decode_end_time;
+ info.refresh_rate_hz = int{std::round(1.0 / render_interval.InSecondsF())};
+ info.size = frame.natural_size();
info.intended_duration = frame.metadata()->wallclock_frame_duration;
if (info.intended_duration) {
@@ -70,10 +73,10 @@ void VideoPlaybackRoughnessReporter::FrameSubmitted(
}
// 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);
+ const int win_size =
+ base::ClampRound(info.intended_duration.value().ToHz());
+ frames_window_size_ =
+ base::ClampToRange(win_size, kMinWindowSize, kMaxWindowSize);
}
frames_.push_back(info);
@@ -108,7 +111,8 @@ void VideoPlaybackRoughnessReporter::SubmitPlaybackRoughness() {
}
auto it = worst_windows_.begin() + index_to_submit;
- reporting_cb_.Run(it->size, it->intended_duration, it->roughness());
+ reporting_cb_.Run(it->size, it->intended_duration, it->roughness(),
+ it->refresh_rate_hz, it->frame_size);
worst_windows_.clear();
windows_seen_ = 0;
@@ -163,15 +167,26 @@ void VideoPlaybackRoughnessReporter::ProcessFrameWindow() {
// let's calculate window metrics and report it.
if (next_frame_it - frames_.begin() > frames_window_size_) {
ConsecutiveFramesWindow win;
+ bool observed_change_in_parameters = false;
double mean_square_error_ms2 = 0.0;
base::TimeDelta total_error;
- if (frames_.front().presentation_time.has_value())
- win.first_frame_time = frames_.front().presentation_time.value();
+ auto& first_frame = frames_.front();
+ if (first_frame.presentation_time.has_value()) {
+ win.first_frame_time = first_frame.presentation_time.value();
+ win.refresh_rate_hz = first_frame.refresh_rate_hz;
+ win.frame_size = first_frame.size;
+ }
for (auto i = 0; i < frames_window_size_; i++) {
FrameInfo& frame = frames_[i];
base::TimeDelta error;
+ if (win.frame_size != frame.size ||
+ win.refresh_rate_hz != frame.refresh_rate_hz) {
+ observed_change_in_parameters = true;
+ break;
+ }
+
if (frame.actual_duration.has_value() &&
frame.intended_duration.has_value()) {
error = frame.actual_duration.value() - frame.intended_duration.value();
@@ -187,7 +202,20 @@ void VideoPlaybackRoughnessReporter::ProcessFrameWindow() {
win.root_mean_square_error = base::TimeDelta::FromMillisecondsD(
std::sqrt(mean_square_error_ms2 / frames_window_size_));
- ReportWindow(win);
+ if (observed_change_in_parameters) {
+ // There has been a change in the frame size or the screen refresh rate,
+ // whatever roughness stats were accumulated up to this point need to be
+ // reported or discarded, because there is no point in mixing together
+ // roughess for different resolutions or refresh rates.
+ if (windows_seen_ >= kMinWindowsBeforeSubmit) {
+ SubmitPlaybackRoughness();
+ } else {
+ worst_windows_.clear();
+ windows_seen_ = 0;
+ }
+ } else {
+ ReportWindow(win);
+ }
// The frames in the window have been reported,
// no need to keep them around any longer.
diff --git a/chromium/cc/metrics/video_playback_roughness_reporter.h b/chromium/cc/metrics/video_playback_roughness_reporter.h
index d001df91d7c..ebced5e2fdb 100644
--- a/chromium/cc/metrics/video_playback_roughness_reporter.h
+++ b/chromium/cc/metrics/video_playback_roughness_reporter.h
@@ -13,6 +13,7 @@
#include "cc/cc_export.h"
#include "media/base/video_frame.h"
#include "media/base/video_types.h"
+#include "ui/gfx/geometry/size.h"
namespace cc {
@@ -20,8 +21,14 @@ namespace cc {
// |frames| - number of frames in the interval
// |duration| - intended wallclock duration of the interval
// |roughness| - roughness of the interval
-using PlaybackRoughnessReportingCallback = base::RepeatingCallback<
- void(int frames, base::TimeDelta duration, double roughness)>;
+// |refresh_rate_hz| - display refresh rate, usually 60Hz
+// |frame_size| - size of the video frames in the interval
+using PlaybackRoughnessReportingCallback =
+ base::RepeatingCallback<void(int frames,
+ base::TimeDelta duration,
+ double roughness,
+ int refresh_rate_hz,
+ gfx::Size frame_size)>;
// This class tracks moments when each frame was submitted
// and when it was displayed. Then series of frames split into groups
@@ -74,13 +81,6 @@ 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 {
@@ -91,12 +91,16 @@ class CC_EXPORT VideoPlaybackRoughnessReporter {
base::Optional<base::TimeTicks> presentation_time;
base::Optional<base::TimeDelta> actual_duration;
base::Optional<base::TimeDelta> intended_duration;
+ int refresh_rate_hz = 60;
+ gfx::Size size;
};
struct ConsecutiveFramesWindow {
int size;
base::TimeTicks first_frame_time;
base::TimeDelta intended_duration;
+ int refresh_rate_hz = 60;
+ gfx::Size frame_size;
// Worst case difference between a frame's intended duration and
// actual duration, calculated for all frames in the window.
diff --git a/chromium/cc/metrics/video_playback_roughness_reporter_unittest.cc b/chromium/cc/metrics/video_playback_roughness_reporter_unittest.cc
index 053c1e77d35..3d7acf8db9e 100644
--- a/chromium/cc/metrics/video_playback_roughness_reporter_unittest.cc
+++ b/chromium/cc/metrics/video_playback_roughness_reporter_unittest.cc
@@ -22,6 +22,8 @@ namespace cc {
class VideoPlaybackRoughnessReporterTest : public ::testing::Test {
protected:
std::unique_ptr<VideoPlaybackRoughnessReporter> reporter_;
+ base::TimeTicks time_;
+ int token_ = 0;
template <class T>
void SetReportingCallabck(T cb) {
@@ -34,9 +36,10 @@ class VideoPlaybackRoughnessReporterTest : public ::testing::Test {
return reporter_.get();
}
- scoped_refptr<VideoFrame> MakeFrame(base::TimeDelta duration) {
+ scoped_refptr<VideoFrame> MakeFrame(base::TimeDelta duration,
+ int frame_size = 100) {
scoped_refptr<VideoFrame> result = media::VideoFrame::CreateColorFrame(
- gfx::Size(4, 4), 0x80, 0x80, 0x80, base::TimeDelta());
+ gfx::Size(frame_size, frame_size), 0x80, 0x80, 0x80, base::TimeDelta());
result->metadata()->wallclock_frame_duration = duration;
return result;
}
@@ -57,18 +60,21 @@ class VideoPlaybackRoughnessReporterTest : public ::testing::Test {
return ::testing::AssertionSuccess();
}
- void NormalRun(double fps, double hz, std::vector<int> cadence, int frames) {
+ void NormalRun(double fps,
+ double hz,
+ std::vector<int> cadence,
+ int frames,
+ int frame_size = 100) {
base::TimeDelta vsync = base::TimeDelta::FromSecondsD(1 / hz);
base::TimeDelta ideal_duration = base::TimeDelta::FromSecondsD(1 / fps);
- base::TimeTicks time;
for (int idx = 0; idx < frames; idx++) {
int frame_cadence = cadence[idx % cadence.size()];
base::TimeDelta duration = vsync * frame_cadence;
- auto frame = MakeFrame(ideal_duration);
- reporter()->FrameSubmitted(idx, *frame, vsync);
- reporter()->FramePresented(idx, time, true);
+ auto frame = MakeFrame(ideal_duration, frame_size);
+ reporter()->FrameSubmitted(token_, *frame, vsync);
+ reporter()->FramePresented(token_++, time_, true);
reporter()->ProcessFrameWindow();
- time += duration;
+ time_ += duration;
}
}
@@ -78,19 +84,17 @@ class VideoPlaybackRoughnessReporterTest : public ::testing::Test {
int frames) {
base::TimeDelta vsync = base::TimeDelta::FromSecondsD(1 / hz);
base::TimeDelta ideal_duration = base::TimeDelta::FromSecondsD(1 / fps);
- base::TimeTicks time;
constexpr int batch_size = 3;
for (int idx = 0; idx < frames; idx++) {
auto frame = MakeFrame(ideal_duration);
reporter()->FrameSubmitted(idx, *frame, vsync);
-
if (idx % batch_size == batch_size - 1) {
for (int i = batch_size - 1; i >= 0; i--) {
int presented_idx = idx - i;
int frame_cadence = cadence[presented_idx % cadence.size()];
base::TimeDelta duration = vsync * frame_cadence;
- reporter()->FramePresented(presented_idx, time, true);
- time += duration;
+ reporter()->FramePresented(presented_idx, time_, true);
+ time_ += duration;
}
}
@@ -102,13 +106,14 @@ class VideoPlaybackRoughnessReporterTest : public ::testing::Test {
TEST_F(VideoPlaybackRoughnessReporterTest, BestCase24fps) {
int call_count = 0;
int fps = 24;
- SetReportingCallabck(
- [&](int size, base::TimeDelta duration, double roughness) {
- ASSERT_EQ(size, fps);
- ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
- ASSERT_NEAR(roughness, 5.9, 0.1);
- call_count++;
- });
+ SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) {
+ ASSERT_EQ(size, fps);
+ ASSERT_EQ(hz, 60);
+ ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+ ASSERT_NEAR(roughness, 5.9, 0.1);
+ call_count++;
+ });
int frames_to_run =
VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 10;
NormalRun(fps, 60, {2, 3}, frames_to_run);
@@ -118,13 +123,14 @@ TEST_F(VideoPlaybackRoughnessReporterTest, BestCase24fps) {
TEST_F(VideoPlaybackRoughnessReporterTest, BestCase24fpsOn120Hz) {
int call_count = 0;
int fps = 24;
- 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++;
- });
+ SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) {
+ ASSERT_EQ(size, fps);
+ ASSERT_EQ(hz, 120);
+ ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
+ ASSERT_NEAR(roughness, 0.0, 0.1);
+ call_count++;
+ });
int frames_to_run =
VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit * fps + 10;
NormalRun(fps, 120, {5}, frames_to_run);
@@ -134,13 +140,13 @@ TEST_F(VideoPlaybackRoughnessReporterTest, BestCase24fpsOn120Hz) {
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++;
- });
+ SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) {
+ 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);
@@ -156,13 +162,13 @@ TEST_F(VideoPlaybackRoughnessReporterTest, BestCase30fps) {
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++;
- });
+ SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) {
+ 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},
@@ -178,13 +184,13 @@ TEST_F(VideoPlaybackRoughnessReporterTest, UserStudyOkay) {
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++;
- });
+ SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) {
+ 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},
@@ -195,13 +201,13 @@ TEST_F(VideoPlaybackRoughnessReporterTest, UserStudyBad) {
TEST_F(VideoPlaybackRoughnessReporterTest, Glitchy24fps) {
int call_count = 0;
int fps = 24;
- SetReportingCallabck(
- [&](int size, base::TimeDelta duration, double roughness) {
- ASSERT_EQ(size, fps);
- ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
- ASSERT_NEAR(roughness, 14.8, 0.1);
- call_count++;
- });
+ SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) {
+ 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 + 1;
NormalRun(fps, 60, {2, 3, 1, 3, 2, 4, 2, 3, 2, 3, 3, 3}, frames_to_run);
@@ -211,13 +217,13 @@ TEST_F(VideoPlaybackRoughnessReporterTest, Glitchy24fps) {
TEST_F(VideoPlaybackRoughnessReporterTest, BestCase60fps) {
int call_count = 0;
int fps = 60;
- 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++;
- });
+ SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) {
+ 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, {1}, frames_to_run);
@@ -227,13 +233,13 @@ TEST_F(VideoPlaybackRoughnessReporterTest, BestCase60fps) {
TEST_F(VideoPlaybackRoughnessReporterTest, BestCase50fps) {
int call_count = 0;
int fps = 50;
- SetReportingCallabck(
- [&](int size, base::TimeDelta duration, double roughness) {
- ASSERT_EQ(size, fps);
- ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
- ASSERT_NEAR(roughness, 8.1, 01);
- call_count++;
- });
+ SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) {
+ 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 + 1;
NormalRun(fps, 60, {1, 1, 1, 1, 2}, frames_to_run);
@@ -253,7 +259,8 @@ TEST_F(VideoPlaybackRoughnessReporterTest, PredictableRoughnessValue) {
base::TimeDelta error = base::TimeDelta::FromMillisecondsD(
std::sqrt(intended_roughness * intended_roughness * frames_in_window));
- auto callback = [&](int size, base::TimeDelta duration, double roughness) {
+ auto callback = [&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) {
ASSERT_EQ(frames_in_window, size);
ASSERT_NEAR(roughness, intended_roughness, 0.1);
call_count++;
@@ -295,7 +302,8 @@ TEST_F(VideoPlaybackRoughnessReporterTest, TakingPercentile) {
std::mt19937 rnd(1);
std::shuffle(targets.begin(), targets.end(), rnd);
- auto callback = [&](int size, base::TimeDelta duration, double roughness) {
+ auto callback = [&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) {
ASSERT_EQ(frames_in_window, size);
ASSERT_NEAR(roughness, expected_roughness, 0.05);
call_count++;
@@ -330,8 +338,8 @@ TEST_F(VideoPlaybackRoughnessReporterTest, TakingPercentile) {
TEST_F(VideoPlaybackRoughnessReporterTest, LongRunWithoutWindows) {
int call_count = 0;
base::TimeDelta vsync = base::TimeDelta::FromMilliseconds(1);
- SetReportingCallabck([&](int size, base::TimeDelta duration,
- double roughness) { call_count++; });
+ SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) { call_count++; });
for (int i = 0; i < 10000; i++) {
auto frame = MakeFrame(vsync);
reporter()->FrameSubmitted(i, *frame, vsync);
@@ -348,8 +356,8 @@ TEST_F(VideoPlaybackRoughnessReporterTest, LongRunWithoutWindows) {
TEST_F(VideoPlaybackRoughnessReporterTest, PresentingUnknownFrames) {
int call_count = 0;
base::TimeDelta vsync = base::TimeDelta::FromMilliseconds(1);
- SetReportingCallabck([&](int size, base::TimeDelta duration,
- double roughness) { call_count++; });
+ SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) { call_count++; });
for (int i = 0; i < 10000; i++) {
auto frame = MakeFrame(vsync);
reporter()->FrameSubmitted(i, *frame, vsync);
@@ -365,8 +373,8 @@ TEST_F(VideoPlaybackRoughnessReporterTest, PresentingUnknownFrames) {
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++; });
+ SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) { call_count++; });
for (int i = 0; i < 10000; i++) {
auto frame = MakeFrame(vsync);
reporter()->FrameSubmitted(i, *frame, vsync);
@@ -382,9 +390,8 @@ TEST_F(VideoPlaybackRoughnessReporterTest, IgnoringUnreliableTimings) {
TEST_F(VideoPlaybackRoughnessReporterTest, ReportingInReset) {
int call_count = 0;
int fps = 60;
- auto callback = [&](int size, base::TimeDelta duration, double roughness) {
- call_count++;
- };
+ auto callback = [&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) { call_count++; };
SetReportingCallabck(callback);
// Set number of frames insufficient for reporting in Reset()
@@ -411,6 +418,50 @@ TEST_F(VideoPlaybackRoughnessReporterTest, ReportingInReset) {
EXPECT_EQ(call_count, 1);
}
+// Test that a change of display refresh rate or frame size causes reporting
+// iff there is sufficient number of windows accumulated.
+TEST_F(VideoPlaybackRoughnessReporterTest, ReportingAfterParameterChange) {
+ struct Report {
+ int hz;
+ int height;
+ double roughness;
+ };
+ std::vector<Report> reports;
+ int fps = 60;
+ auto callback = [&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) {
+ reports.push_back({hz, frame_size.height(), roughness});
+ };
+ SetReportingCallabck(callback);
+
+ int frames_to_run =
+ (VideoPlaybackRoughnessReporter::kMinWindowsBeforeSubmit - 1) * fps + 3;
+ NormalRun(fps, 59, {1}, frames_to_run, 480);
+ ASSERT_TRUE(reports.empty());
+
+ frames_to_run =
+ (VideoPlaybackRoughnessReporter::kMinWindowsBeforeSubmit + 1) * fps + 3;
+ NormalRun(fps, 60, {1}, frames_to_run, 480);
+ // Check that if parameters change after only a few windows, nothing gets
+ // reported.
+ ASSERT_TRUE(reports.empty());
+
+ frames_to_run =
+ (VideoPlaybackRoughnessReporter::kMaxWindowsBeforeSubmit + 1) * fps + 3;
+ NormalRun(fps, 120, {2}, frames_to_run, 481);
+
+ // Check that if parameters change after sufficient number of windows
+ // roughness is reported. The second report is done normally after max
+ // number of windows is seen.
+ ASSERT_EQ(reports.size(), 2u);
+ EXPECT_EQ(reports[0].hz, 60);
+ EXPECT_EQ(reports[0].height, 480);
+ EXPECT_EQ(reports[0].roughness, 0.0);
+ EXPECT_EQ(reports[1].hz, 120);
+ EXPECT_EQ(reports[1].height, 481);
+ EXPECT_EQ(reports[1].roughness, 0.0);
+}
+
// Test that reporting works even if frame presentation signal come out of
// order.
TEST_F(VideoPlaybackRoughnessReporterTest, BatchPresentation) {
@@ -418,26 +469,26 @@ TEST_F(VideoPlaybackRoughnessReporterTest, BatchPresentation) {
int fps = 60;
// Try 60 fps
- 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++;
- });
+ SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) {
+ 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 + 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);
- ASSERT_NEAR(duration.InMillisecondsF(), 1000.0, 1.0);
- ASSERT_NEAR(roughness, 5.9, 0.1);
- call_count++;
- });
+ SetReportingCallabck([&](int size, base::TimeDelta duration, double roughness,
+ int hz, gfx::Size frame_size) {
+ 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 + 10;
diff --git a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc
index f5de6a66a82..469a4f18d5a 100644
--- a/chromium/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc
+++ b/chromium/cc/mojo_embedder/async_layer_tree_frame_sink_unittest.cc
@@ -5,6 +5,7 @@
#include "cc/mojo_embedder/async_layer_tree_frame_sink.h"
#include <memory>
+#include <utility>
#include "base/bind.h"
#include "base/memory/scoped_refptr.h"
@@ -176,7 +177,7 @@ class AsyncLayerTreeFrameSinkSimpleTest : public testing::Test {
TEST_F(AsyncLayerTreeFrameSinkSimpleTest, HitTestRegionListEmpty) {
viz::RenderPassList pass_list;
auto pass = viz::RenderPass::Create();
- pass->id = 1;
+ pass->id = viz::RenderPassId{1};
pass->output_rect = display_rect_;
pass_list.push_back(move(pass));
@@ -191,7 +192,7 @@ TEST_F(AsyncLayerTreeFrameSinkSimpleTest, HitTestRegionListDuplicate) {
viz::RenderPassList pass_list;
// Initial submission.
auto pass1 = viz::RenderPass::Create();
- pass1->id = 1;
+ pass1->id = viz::RenderPassId{1};
pass1->output_rect = display_rect_;
pass_list.push_back(move(pass1));
@@ -206,7 +207,7 @@ TEST_F(AsyncLayerTreeFrameSinkSimpleTest, HitTestRegionListDuplicate) {
// Identical submission.
auto pass2 = viz::RenderPass::Create();
- pass2->id = 2;
+ pass2->id = viz::RenderPassId{2};
pass2->output_rect = display_rect_;
pass_list.push_back(move(pass2));
@@ -218,7 +219,7 @@ TEST_F(AsyncLayerTreeFrameSinkSimpleTest, HitTestRegionListDuplicate) {
// Different submission.
auto pass3 = viz::RenderPass::Create();
- pass3->id = 3;
+ pass3->id = viz::RenderPassId{3};
pass3->output_rect = display_rect_;
pass_list.push_back(move(pass3));
@@ -239,7 +240,7 @@ TEST_F(AsyncLayerTreeFrameSinkSimpleTest,
// Initial submission.
auto pass1 = viz::RenderPass::Create();
- pass1->id = 1;
+ pass1->id = viz::RenderPassId{1};
pass1->output_rect = display_rect_;
pass_list.push_back(move(pass1));
@@ -254,7 +255,7 @@ TEST_F(AsyncLayerTreeFrameSinkSimpleTest,
// Different submission with |hit_test_data_changed| set to true.
auto pass2 = viz::RenderPass::Create();
- pass2->id = 2;
+ pass2->id = viz::RenderPassId{2};
pass2->output_rect = display_rect_;
pass_list.push_back(std::move(pass2));
@@ -273,7 +274,7 @@ TEST_F(AsyncLayerTreeFrameSinkSimpleTest,
// Different submission with |hit_test_data_changed| set back to false. We
// expect the hit-data to still have been sent.
auto pass3 = viz::RenderPass::Create();
- pass3->id = 3;
+ pass3->id = viz::RenderPassId{3};
pass3->output_rect = display_rect_;
pass_list.push_back(move(pass3));
diff --git a/chromium/cc/mojom/BUILD.gn b/chromium/cc/mojom/BUILD.gn
index 12d79257d5d..7c0b5ab0237 100644
--- a/chromium/cc/mojom/BUILD.gn
+++ b/chromium/cc/mojom/BUILD.gn
@@ -7,40 +7,46 @@ import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") {
generate_java = true
sources = [
+ "browser_controls_params.mojom",
"overscroll_behavior.mojom",
"touch_action.mojom",
]
public_deps = [ "//mojo/public/mojom/base" ]
- touch_action_typemap = {
- types = [
- {
- mojom = "cc.mojom.TouchAction"
- cpp = "::cc::TouchAction"
- },
- ]
- traits_headers = [ "//cc/ipc/cc_param_traits_macros.h" ]
- traits_public_deps = [ "//cc/ipc" ]
- }
-
- overscroll_behavior_typemap = {
- types = [
- {
- mojom = "cc.mojom.OverscrollBehavior"
- cpp = "::cc::OverscrollBehavior"
- },
- ]
- traits_headers = [ "//cc/ipc/cc_param_traits_macros.h" ]
- traits_public_deps = [ "//cc/ipc" ]
- }
-
- cpp_typemaps = [
- overscroll_behavior_typemap,
- touch_action_typemap,
- ]
- blink_cpp_typemaps = [
- overscroll_behavior_typemap,
- touch_action_typemap,
+ shared_typemap = [
+ {
+ types = [
+ {
+ mojom = "cc.mojom.TouchAction"
+ cpp = "::cc::TouchAction"
+ },
+ ]
+ traits_headers = [ "//cc/ipc/cc_param_traits_macros.h" ]
+ traits_public_deps = [ "//cc/ipc" ]
+ },
+ {
+ types = [
+ {
+ mojom = "cc.mojom.OverscrollBehavior"
+ cpp = "::cc::OverscrollBehavior"
+ },
+ ]
+ traits_headers = [ "//cc/ipc/cc_param_traits_macros.h" ]
+ traits_public_deps = [ "//cc/ipc" ]
+ },
+ {
+ types = [
+ {
+ mojom = "cc.mojom.BrowserControlsParams"
+ cpp = "::cc::BrowserControlsParams"
+ },
+ ]
+ traits_headers = [ "//cc/ipc/cc_param_traits_macros.h" ]
+ traits_public_deps = [ "//cc/ipc" ]
+ },
]
+
+ cpp_typemaps = shared_typemap
+ blink_cpp_typemaps = shared_typemap
}
diff --git a/chromium/cc/mojom/browser_controls_params.mojom b/chromium/cc/mojom/browser_controls_params.mojom
new file mode 100644
index 00000000000..0fbf83c43c7
--- /dev/null
+++ b/chromium/cc/mojom/browser_controls_params.mojom
@@ -0,0 +1,8 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module cc.mojom;
+
+[Native]
+struct BrowserControlsParams;
diff --git a/chromium/cc/paint/BUILD.gn b/chromium/cc/paint/BUILD.gn
index 0a9161d4e66..3c750b5c4f0 100644
--- a/chromium/cc/paint/BUILD.gn
+++ b/chromium/cc/paint/BUILD.gn
@@ -8,6 +8,8 @@ import("//testing/libfuzzer/fuzzer_test.gni")
cc_component("paint") {
output_name = "cc_paint"
sources = [
+ "clear_for_opaque_raster.cc",
+ "clear_for_opaque_raster.h",
"decode_stashing_image_provider.cc",
"decode_stashing_image_provider.h",
"decoded_draw_image.cc",
@@ -100,6 +102,7 @@ cc_component("paint") {
"//cc/base",
"//cc/debug",
"//skia",
+ "//skia:skcms",
"//ui/gfx:color_space",
"//ui/gfx:geometry_skia",
"//ui/gfx/geometry",
diff --git a/chromium/cc/paint/clear_for_opaque_raster.cc b/chromium/cc/paint/clear_for_opaque_raster.cc
new file mode 100644
index 00000000000..9ae3ea07ad5
--- /dev/null
+++ b/chromium/cc/paint/clear_for_opaque_raster.cc
@@ -0,0 +1,79 @@
+// 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/paint/clear_for_opaque_raster.h"
+
+#include <cmath>
+
+#include "base/check_op.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size_f.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace cc {
+
+bool CalculateClearForOpaqueRasterRects(const gfx::Vector2dF& translation,
+ const gfx::SizeF& scale,
+ const gfx::Size& content_size,
+ const gfx::Rect& canvas_bitmap_rect,
+ const gfx::Rect& canvas_playback_rect,
+ gfx::Rect& outer_rect,
+ gfx::Rect& inner_rect) {
+ // If there is translation, the top and/or left texels are not guaranteed to
+ // be fully opaque.
+ DCHECK_GE(translation.x(), 0.0f);
+ DCHECK_GE(translation.y(), 0.0f);
+ DCHECK_LT(translation.x(), 1.0f);
+ DCHECK_LT(translation.y(), 1.0f);
+ bool left_opaque = translation.x() == 0.0f;
+ bool top_opaque = translation.y() == 0.0f;
+ // If there is scale, the right and/or bottom texels are not guaranteed to be
+ // fully opaque.
+ bool right_opaque = scale.width() == 1.0f;
+ bool bottom_opaque = scale.height() == 1.0f;
+ if (left_opaque && top_opaque && right_opaque && bottom_opaque)
+ return false;
+
+ // |outer_rect| is the bounds of all texels affected by content.
+ outer_rect = gfx::Rect(content_size);
+ // |inner_rect| is the opaque coverage of the content.
+ inner_rect = outer_rect;
+ // If not fully covered, one texel inside the content rect may not be opaque
+ // (because of blending during raster) and, for scale, one texel outside
+ // (because of bilinear filtering during draw) may not be opaque.
+ outer_rect.Inset(0, 0, right_opaque ? 0 : -1, bottom_opaque ? 0 : -1);
+ inner_rect.Inset(left_opaque ? 0 : 1, top_opaque ? 0 : 1,
+ right_opaque ? 0 : 1, bottom_opaque ? 0 : 1);
+
+ // If the playback rect is touching either edge of the content rect, extend it
+ // by one to include the extra texel outside that was added to outer_rect
+ // above.
+ bool touches_left_edge = !left_opaque && !canvas_playback_rect.x();
+ bool touches_top_edge = !top_opaque && !canvas_playback_rect.y();
+ bool touches_right_edge =
+ !right_opaque && content_size.width() == canvas_playback_rect.right();
+ bool touches_bottom_edge =
+ !bottom_opaque && content_size.height() == canvas_playback_rect.bottom();
+ gfx::Rect adjusted_playback_rect = canvas_playback_rect;
+ adjusted_playback_rect.Inset(
+ touches_left_edge ? -1 : 0, touches_top_edge ? -1 : 0,
+ touches_right_edge ? -1 : 0, touches_bottom_edge ? -1 : 0);
+
+ // No need to clear if the playback area is fully covered by the opaque
+ // content.
+ if (inner_rect.Contains(adjusted_playback_rect))
+ return false;
+
+ outer_rect.Intersect(adjusted_playback_rect);
+ DCHECK(!outer_rect.IsEmpty());
+ inner_rect.Intersect(adjusted_playback_rect);
+ // inner_rect can be empty if the content is very small.
+
+ // Move the rects into the device space.
+ outer_rect.Offset(-canvas_bitmap_rect.OffsetFromOrigin());
+ inner_rect.Offset(-canvas_bitmap_rect.OffsetFromOrigin());
+ return inner_rect != outer_rect;
+}
+
+} // namespace cc
diff --git a/chromium/cc/paint/clear_for_opaque_raster.h b/chromium/cc/paint/clear_for_opaque_raster.h
new file mode 100644
index 00000000000..92be18ce309
--- /dev/null
+++ b/chromium/cc/paint/clear_for_opaque_raster.h
@@ -0,0 +1,36 @@
+// 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_PAINT_CLEAR_FOR_OPAQUE_RASTER_H_
+#define CC_PAINT_CLEAR_FOR_OPAQUE_RASTER_H_
+
+#include "cc/paint/paint_export.h"
+
+namespace gfx {
+class Rect;
+class Size;
+class SizeF;
+class Vector2dF;
+} // namespace gfx
+
+namespace cc {
+
+// Called when we are drawing opaque content with |translation| and |scale|.
+// Calculates |outer_rect| and |inner_rect| between which the drawn content
+// would not be opaque due to |translation| and/or |scale| and should be cleared
+// with an opaque color before drawing the original contents, to ensure all
+// texels are fully opaque. The output rects are in the device space.
+// Returns false if no clearing for opaque is needed.
+bool CC_PAINT_EXPORT
+CalculateClearForOpaqueRasterRects(const gfx::Vector2dF& translation,
+ const gfx::SizeF& scale,
+ const gfx::Size& content_size,
+ const gfx::Rect& canvas_bitmap_rect,
+ const gfx::Rect& canvas_playback_rect,
+ gfx::Rect& outer_rect,
+ gfx::Rect& inner_rect);
+
+} // namespace cc
+
+#endif // CC_PAINT_CLEAR_FOR_OPAQUE_RASTER_H_
diff --git a/chromium/cc/paint/clear_for_opaque_raster_unittest.cc b/chromium/cc/paint/clear_for_opaque_raster_unittest.cc
new file mode 100644
index 00000000000..50fcae78d37
--- /dev/null
+++ b/chromium/cc/paint/clear_for_opaque_raster_unittest.cc
@@ -0,0 +1,175 @@
+// 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/paint/clear_for_opaque_raster.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size_f.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+
+namespace cc {
+
+TEST(ClearForOpaqueRasterTest, NoTransform) {
+ const gfx::Vector2dF translation;
+ const gfx::SizeF scale(1, 1);
+ const gfx::Size content_size(100, 100);
+ const gfx::Rect bitmap_rect(content_size);
+ gfx::Rect inner_rect;
+ gfx::Rect outer_rect;
+ EXPECT_FALSE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, bitmap_rect, outer_rect,
+ inner_rect));
+ EXPECT_FALSE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(25, 25, 50, 50),
+ outer_rect, inner_rect));
+}
+
+TEST(ClearForOpaqueRasterTest, WithTranslation) {
+ const gfx::Vector2dF translation(0.3f, 0.7f);
+ const gfx::SizeF scale(1, 1);
+ const gfx::Size content_size(100, 100);
+ const gfx::Rect bitmap_rect(content_size);
+ gfx::Rect inner_rect;
+ gfx::Rect outer_rect;
+
+ // Full playback (touching all edges).
+ EXPECT_TRUE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, bitmap_rect, outer_rect,
+ inner_rect));
+ EXPECT_EQ(gfx::Rect(0, 0, 100, 100), outer_rect);
+ EXPECT_EQ(gfx::Rect(1, 1, 99, 99), inner_rect);
+
+ // Touches the left edge only.
+ EXPECT_TRUE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(0, 25, 50, 50),
+ outer_rect, inner_rect));
+ EXPECT_EQ(gfx::Rect(0, 25, 50, 50), outer_rect);
+ EXPECT_EQ(gfx::Rect(1, 25, 49, 50), inner_rect);
+
+ // Touches the top edge only.
+ EXPECT_TRUE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(25, 0, 50, 50),
+ outer_rect, inner_rect));
+ EXPECT_EQ(gfx::Rect(25, 0, 50, 50), outer_rect);
+ EXPECT_EQ(gfx::Rect(25, 1, 50, 49), inner_rect);
+
+ // Touches the right edge only.
+ EXPECT_FALSE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(50, 25, 50, 50),
+ outer_rect, inner_rect));
+
+ // Touches the bottom edge only.
+ EXPECT_FALSE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(25, 50, 50, 50),
+ outer_rect, inner_rect));
+
+ // Touches no edges.
+ EXPECT_FALSE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(1, 1, 98, 98),
+ outer_rect, inner_rect));
+}
+
+TEST(ClearForOpaqueRasterTest, WithScale) {
+ const gfx::Vector2dF translation;
+ const gfx::SizeF scale(1.5f, 1.5f);
+ const gfx::Size content_size(100, 100);
+ const gfx::Rect bitmap_rect(content_size);
+ gfx::Rect inner_rect;
+ gfx::Rect outer_rect;
+
+ // Full playback (touching all edges).
+ EXPECT_TRUE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, bitmap_rect, outer_rect,
+ inner_rect));
+ EXPECT_EQ(gfx::Rect(0, 0, 101, 101), outer_rect);
+ EXPECT_EQ(gfx::Rect(0, 0, 99, 99), inner_rect);
+
+ // Touches the left edge only.
+ EXPECT_FALSE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(0, 25, 50, 50),
+ outer_rect, inner_rect));
+
+ // Touches the top edge only.
+ EXPECT_FALSE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(25, 0, 50, 50),
+ outer_rect, inner_rect));
+
+ // Touches the right edge only.
+ EXPECT_TRUE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(50, 25, 50, 50),
+ outer_rect, inner_rect));
+ EXPECT_EQ(gfx::Rect(50, 25, 51, 50), outer_rect);
+ EXPECT_EQ(gfx::Rect(50, 25, 49, 50), inner_rect);
+
+ // Touches the bottom edge only.
+ EXPECT_TRUE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(25, 50, 50, 50),
+ outer_rect, inner_rect));
+ EXPECT_EQ(gfx::Rect(25, 50, 50, 51), outer_rect);
+ EXPECT_EQ(gfx::Rect(25, 50, 50, 49), inner_rect);
+
+ // Touches no edges.
+ EXPECT_FALSE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(1, 1, 98, 98),
+ outer_rect, inner_rect));
+}
+
+TEST(ClearForOpaqueRasterTest, WithTranslationAndScale) {
+ const gfx::Vector2dF translation(0.3f, 0.7f);
+ const gfx::SizeF scale(1.5f, 1.5f);
+ const gfx::Size content_size(100, 100);
+ const gfx::Rect bitmap_rect(content_size);
+ gfx::Rect inner_rect;
+ gfx::Rect outer_rect;
+
+ // Full playback (touching all edges).
+ EXPECT_TRUE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, bitmap_rect, outer_rect,
+ inner_rect));
+ EXPECT_EQ(gfx::Rect(0, 0, 101, 101), outer_rect);
+ EXPECT_EQ(gfx::Rect(1, 1, 98, 98), inner_rect);
+
+ // Touches the left edge only.
+ EXPECT_TRUE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(0, 25, 50, 50),
+ outer_rect, inner_rect));
+ EXPECT_EQ(gfx::Rect(0, 25, 50, 50), outer_rect);
+ EXPECT_EQ(gfx::Rect(1, 25, 49, 50), inner_rect);
+
+ // Touches the top edge only.
+ EXPECT_TRUE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(25, 0, 50, 50),
+ outer_rect, inner_rect));
+ EXPECT_EQ(gfx::Rect(25, 0, 50, 50), outer_rect);
+ EXPECT_EQ(gfx::Rect(25, 1, 50, 49), inner_rect);
+
+ // Touches the right edge only.
+ EXPECT_TRUE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(50, 25, 50, 50),
+ outer_rect, inner_rect));
+ EXPECT_EQ(gfx::Rect(50, 25, 51, 50), outer_rect);
+ EXPECT_EQ(gfx::Rect(50, 25, 49, 50), inner_rect);
+
+ // Touches the bottom edge only.
+ EXPECT_TRUE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(25, 50, 50, 50),
+ outer_rect, inner_rect));
+ EXPECT_EQ(gfx::Rect(25, 50, 50, 51), outer_rect);
+ EXPECT_EQ(gfx::Rect(25, 50, 50, 49), inner_rect);
+
+ // Touches no edges.
+ EXPECT_FALSE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, bitmap_rect, gfx::Rect(1, 1, 98, 98),
+ outer_rect, inner_rect));
+
+ // With bitmap_rect non-zero offset.
+ EXPECT_TRUE(CalculateClearForOpaqueRasterRects(
+ translation, scale, content_size, gfx::Rect(25, 25, 75, 75),
+ gfx::Rect(50, 50, 50, 50), outer_rect, inner_rect));
+ EXPECT_EQ(gfx::Rect(25, 25, 51, 51), outer_rect);
+ EXPECT_EQ(gfx::Rect(25, 25, 49, 49), inner_rect);
+}
+
+} // namespace cc
diff --git a/chromium/cc/paint/decoded_draw_image.h b/chromium/cc/paint/decoded_draw_image.h
index a3e47d30fc0..40eaabc1be1 100644
--- a/chromium/cc/paint/decoded_draw_image.h
+++ b/chromium/cc/paint/decoded_draw_image.h
@@ -60,7 +60,10 @@ class CC_PAINT_EXPORT DecodedDrawImage {
return transfer_cache_entry_needs_mips_;
}
bool is_budgeted() const { return is_budgeted_; }
- operator bool() const { return image_ || transfer_cache_entry_id_; }
+ const gpu::Mailbox& mailbox() const { return mailbox_; }
+ explicit operator bool() const {
+ return image_ || transfer_cache_entry_id_ || !mailbox_.IsZero();
+ }
private:
sk_sp<const SkImage> image_;
diff --git a/chromium/cc/paint/discardable_image_map.cc b/chromium/cc/paint/discardable_image_map.cc
index 6284df45242..f417621d9cb 100644
--- a/chromium/cc/paint/discardable_image_map.cc
+++ b/chromium/cc/paint/discardable_image_map.cc
@@ -17,6 +17,7 @@
#include "cc/paint/paint_filter.h"
#include "cc/paint/paint_op_buffer.h"
#include "third_party/skia/include/utils/SkNoDrawCanvas.h"
+#include "ui/gfx/display_color_spaces.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/skia_util.h"
@@ -54,25 +55,10 @@ class DiscardableImageGenerator {
return std::move(paint_worklet_inputs_);
}
- void RecordColorHistograms() const {
- if (color_stats_total_image_count_ > 0) {
- int srgb_image_percent = (100 * color_stats_srgb_image_count_) /
- color_stats_total_image_count_;
- UMA_HISTOGRAM_PERCENTAGE("Renderer4.ImagesPercentSRGB",
- srgb_image_percent);
- }
-
- base::CheckedNumeric<int> srgb_pixel_percent =
- 100 * color_stats_srgb_pixel_count_ / color_stats_total_pixel_count_;
- if (srgb_pixel_percent.IsValid()) {
- UMA_HISTOGRAM_PERCENTAGE("Renderer4.ImagePixelsPercentSRGB",
- srgb_pixel_percent.ValueOrDie());
- }
- }
-
- bool contains_only_srgb_images() const {
- return color_stats_srgb_image_count_ == color_stats_total_image_count_;
+ gfx::ContentColorUsage content_color_usage() const {
+ return content_color_usage_;
}
+ bool contains_hbd_images() const { return contains_hbd_images_; }
private:
class ImageGatheringProvider : public ImageProvider {
@@ -259,15 +245,11 @@ class DiscardableImageGenerator {
paint_worklet_inputs_.push_back(std::make_pair(
paint_image.paint_worklet_input(), paint_image.stable_id()));
} else {
- // Make a note if any image was originally specified in a non-sRGB color
- // space. PaintWorklets do not have the concept of a color space, so
- // should not be used to accumulate either counter.
- color_stats_total_pixel_count_ += image_rect.size().GetCheckedArea();
- color_stats_total_image_count_++;
- if (paint_image.isSRGB()) {
- color_stats_srgb_pixel_count_ += image_rect.size().GetCheckedArea();
- color_stats_srgb_image_count_++;
- }
+ const auto image_color_usage = paint_image.GetContentColorUsage();
+ content_color_usage_ = std::max(content_color_usage_, image_color_usage);
+
+ if (paint_image.is_high_bit_depth())
+ contains_hbd_images_ = true;
}
auto& rects = image_id_to_rects_[paint_image.stable_id()];
@@ -327,12 +309,8 @@ class DiscardableImageGenerator {
base::flat_map<PaintImage::Id, PaintImage::DecodingMode> decoding_mode_map_;
bool only_gather_animated_images_ = false;
- // Statistics about the number of images and pixels that will require color
- // conversion if the target color space is not sRGB.
- int color_stats_srgb_image_count_ = 0;
- int color_stats_total_image_count_ = 0;
- base::CheckedNumeric<int64_t> color_stats_srgb_pixel_count_ = 0;
- base::CheckedNumeric<int64_t> color_stats_total_pixel_count_ = 0;
+ gfx::ContentColorUsage content_color_usage_ = gfx::ContentColorUsage::kSRGB;
+ bool contains_hbd_images_ = false;
};
} // namespace
@@ -349,12 +327,12 @@ void DiscardableImageMap::Generate(const PaintOpBuffer* paint_op_buffer,
DiscardableImageGenerator generator(bounds.right(), bounds.bottom(),
paint_op_buffer);
- generator.RecordColorHistograms();
image_id_to_rects_ = generator.TakeImageIdToRectsMap();
animated_images_metadata_ = generator.TakeAnimatedImagesMetadata();
paint_worklet_inputs_ = generator.TakePaintWorkletInputs();
decoding_mode_map_ = generator.TakeDecodingModeMap();
- contains_only_srgb_images_ = generator.contains_only_srgb_images();
+ contains_hbd_images_ = generator.contains_hbd_images();
+ content_color_usage_ = generator.content_color_usage();
auto images = generator.TakeImages();
images_rtree_.Build(
images,
diff --git a/chromium/cc/paint/discardable_image_map.h b/chromium/cc/paint/discardable_image_map.h
index b14b92aade1..1d5ed402532 100644
--- a/chromium/cc/paint/discardable_image_map.h
+++ b/chromium/cc/paint/discardable_image_map.h
@@ -5,6 +5,7 @@
#ifndef CC_PAINT_DISCARDABLE_IMAGE_MAP_H_
#define CC_PAINT_DISCARDABLE_IMAGE_MAP_H_
+#include <memory>
#include <utility>
#include <vector>
@@ -21,6 +22,7 @@
#include "cc/paint/paint_worklet_input.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkRefCnt.h"
+#include "ui/gfx/display_color_spaces.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
@@ -59,7 +61,10 @@ class CC_PAINT_EXPORT DiscardableImageMap {
void GetDiscardableImagesInRect(const gfx::Rect& rect,
std::vector<const DrawImage*>* images) const;
const Rects& GetRectsForImage(PaintImage::Id image_id) const;
- bool contains_only_srgb_images() const { return contains_only_srgb_images_; }
+ gfx::ContentColorUsage content_color_usage() const {
+ return content_color_usage_;
+ }
+ bool contains_hbd_images() const { return contains_hbd_images_; }
const std::vector<AnimatedImageMetadata>& animated_images_metadata() const {
return animated_images_metadata_;
}
@@ -91,7 +96,8 @@ class CC_PAINT_EXPORT DiscardableImageMap {
base::flat_map<PaintImage::Id, Rects> image_id_to_rects_;
std::vector<AnimatedImageMetadata> animated_images_metadata_;
base::flat_map<PaintImage::Id, PaintImage::DecodingMode> decoding_mode_map_;
- bool contains_only_srgb_images_ = true;
+ gfx::ContentColorUsage content_color_usage_ = gfx::ContentColorUsage::kSRGB;
+ bool contains_hbd_images_ = false;
RTree<DrawImage> images_rtree_;
diff --git a/chromium/cc/paint/discardable_image_map_unittest.cc b/chromium/cc/paint/discardable_image_map_unittest.cc
index 380ee6d6ae2..92b1fbd5e7b 100644
--- a/chromium/cc/paint/discardable_image_map_unittest.cc
+++ b/chromium/cc/paint/discardable_image_map_unittest.cc
@@ -6,6 +6,8 @@
#include <stddef.h>
+#include <algorithm>
+#include <limits>
#include <memory>
#include "base/memory/ref_counted.h"
@@ -1131,6 +1133,88 @@ TEST_F(DiscardableImageMapTest, TracksImageRegions) {
expected_region);
}
+TEST_F(DiscardableImageMapTest, HighBitDepth) {
+ gfx::Rect visible_rect(500, 500);
+
+ SkBitmap bitmap;
+ auto info = SkImageInfo::Make(visible_rect.width(), visible_rect.height(),
+ kRGBA_F16_SkColorType, kPremul_SkAlphaType,
+ nullptr /* color_space */);
+ bitmap.allocPixels(info);
+ bitmap.eraseColor(SK_AlphaTRANSPARENT);
+ PaintImage discardable_image = PaintImageBuilder::WithDefault()
+ .set_id(PaintImage::GetNextId())
+ .set_is_high_bit_depth(true)
+ .set_image(SkImage::MakeFromBitmap(bitmap),
+ PaintImage::GetNextContentId())
+ .TakePaintImage();
+
+ FakeContentLayerClient content_layer_client;
+ content_layer_client.set_bounds(visible_rect.size());
+
+ scoped_refptr<DisplayItemList> display_list =
+ content_layer_client.PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+ display_list->GenerateDiscardableImagesMetadata();
+ const DiscardableImageMap& image_map = display_list->discardable_image_map();
+ EXPECT_FALSE(image_map.contains_hbd_images());
+
+ content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0),
+ PaintFlags());
+ display_list = content_layer_client.PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+ display_list->GenerateDiscardableImagesMetadata();
+ const DiscardableImageMap& image_map2 = display_list->discardable_image_map();
+ EXPECT_TRUE(image_map2.contains_hbd_images());
+}
+
+TEST_F(DiscardableImageMapTest, ContentColorUsage) {
+ constexpr gfx::Size kSize(25, 25);
+ constexpr gfx::Rect kVisibleRect(500, 500);
+ FakeContentLayerClient content_layer_client;
+ content_layer_client.set_bounds(kVisibleRect.size());
+
+ // Empty map should report a color usage of SRGB.
+ auto display_list = content_layer_client.PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+ display_list->GenerateDiscardableImagesMetadata();
+ EXPECT_EQ(display_list->discardable_image_map().content_color_usage(),
+ gfx::ContentColorUsage::kSRGB);
+
+ // Adding a SRGB image should remain SRGB.
+ PaintImage discardable_image_srgb = CreateDiscardablePaintImage(
+ kSize, gfx::ColorSpace::CreateSRGB().ToSkColorSpace());
+ content_layer_client.add_draw_image(discardable_image_srgb, gfx::Point(0, 0),
+ PaintFlags());
+ display_list = content_layer_client.PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+ display_list->GenerateDiscardableImagesMetadata();
+ EXPECT_EQ(display_list->discardable_image_map().content_color_usage(),
+ gfx::ContentColorUsage::kSRGB);
+
+ // Adding a WCG image should switch to WCG.
+ PaintImage discardable_image_wcg = CreateDiscardablePaintImage(
+ kSize, gfx::ColorSpace::CreateDisplayP3D65().ToSkColorSpace());
+ content_layer_client.add_draw_image(discardable_image_wcg, gfx::Point(0, 0),
+ PaintFlags());
+ display_list = content_layer_client.PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+ display_list->GenerateDiscardableImagesMetadata();
+ EXPECT_EQ(display_list->discardable_image_map().content_color_usage(),
+ gfx::ContentColorUsage::kWideColorGamut);
+
+ // Adding a HDR image should switch to HDR.
+ PaintImage discardable_image_hdr = CreateDiscardablePaintImage(
+ kSize, gfx::ColorSpace::CreateHDR10().ToSkColorSpace());
+ content_layer_client.add_draw_image(discardable_image_hdr, gfx::Point(0, 0),
+ PaintFlags());
+ display_list = content_layer_client.PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+ display_list->GenerateDiscardableImagesMetadata();
+ EXPECT_EQ(display_list->discardable_image_map().content_color_usage(),
+ gfx::ContentColorUsage::kHDR);
+}
+
class DiscardableImageMapColorSpaceTest
: public DiscardableImageMapTest,
public testing::WithParamInterface<gfx::ColorSpace> {};
@@ -1150,7 +1234,8 @@ TEST_P(DiscardableImageMapColorSpaceTest, ColorSpace) {
display_list->GenerateDiscardableImagesMetadata();
const DiscardableImageMap& image_map = display_list->discardable_image_map();
- EXPECT_TRUE(image_map.contains_only_srgb_images());
+ EXPECT_EQ(image_map.content_color_usage(), gfx::ContentColorUsage::kSRGB);
+ EXPECT_FALSE(image_map.contains_hbd_images());
content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0),
PaintFlags());
@@ -1159,18 +1244,22 @@ TEST_P(DiscardableImageMapColorSpaceTest, ColorSpace) {
display_list->GenerateDiscardableImagesMetadata();
const DiscardableImageMap& image_map2 = display_list->discardable_image_map();
- if (!image_color_space.IsValid())
- EXPECT_TRUE(image_map2.contains_only_srgb_images());
- else if (image_color_space == gfx::ColorSpace::CreateSRGB())
- EXPECT_TRUE(image_map2.contains_only_srgb_images());
- else
- EXPECT_FALSE(image_map2.contains_only_srgb_images());
+ if (!image_color_space.IsValid()) {
+ EXPECT_EQ(image_map2.content_color_usage(), gfx::ContentColorUsage::kSRGB);
+ } else if (image_color_space == gfx::ColorSpace::CreateSRGB()) {
+ EXPECT_EQ(image_map2.content_color_usage(), gfx::ContentColorUsage::kSRGB);
+ } else if (image_color_space.IsHDR()) {
+ EXPECT_EQ(image_map2.content_color_usage(), gfx::ContentColorUsage::kHDR);
+ } else {
+ EXPECT_EQ(image_map2.content_color_usage(),
+ gfx::ContentColorUsage::kWideColorGamut);
+ }
}
gfx::ColorSpace test_color_spaces[] = {
gfx::ColorSpace(), gfx::ColorSpace::CreateSRGB(),
- gfx::ColorSpace::CreateDisplayP3D65(),
-};
+ gfx::ColorSpace::CreateDisplayP3D65(), gfx::ColorSpace::CreateHDR10(),
+ gfx::ColorSpace::CreateHLG()};
INSTANTIATE_TEST_SUITE_P(ColorSpace,
DiscardableImageMapColorSpaceTest,
diff --git a/chromium/cc/paint/draw_image.cc b/chromium/cc/paint/draw_image.cc
index 8e920f6cce8..afa344d5ed3 100644
--- a/chromium/cc/paint/draw_image.cc
+++ b/chromium/cc/paint/draw_image.cc
@@ -4,6 +4,8 @@
#include "cc/paint/draw_image.h"
+#include <utility>
+
namespace cc {
namespace {
@@ -41,19 +43,22 @@ DrawImage::DrawImage(PaintImage image,
SkFilterQuality filter_quality,
const SkMatrix& matrix,
base::Optional<size_t> frame_index,
- const base::Optional<gfx::ColorSpace>& color_space)
+ const base::Optional<gfx::ColorSpace>& color_space,
+ float sdr_white_level)
: paint_image_(std::move(image)),
src_rect_(src_rect),
filter_quality_(filter_quality),
frame_index_(frame_index),
- target_color_space_(color_space) {
+ target_color_space_(color_space),
+ sdr_white_level_(sdr_white_level) {
matrix_is_decomposable_ = ExtractScale(matrix, &scale_);
}
DrawImage::DrawImage(const DrawImage& other,
float scale_adjustment,
size_t frame_index,
- const gfx::ColorSpace& color_space)
+ const gfx::ColorSpace& color_space,
+ float sdr_white_level)
: paint_image_(other.paint_image_),
src_rect_(other.src_rect_),
filter_quality_(other.filter_quality_),
@@ -61,7 +66,11 @@ DrawImage::DrawImage(const DrawImage& other,
other.scale_.height() * scale_adjustment)),
matrix_is_decomposable_(other.matrix_is_decomposable_),
frame_index_(frame_index),
- target_color_space_(color_space) {}
+ target_color_space_(color_space),
+ sdr_white_level_(sdr_white_level) {
+ if (sdr_white_level_ == gfx::ColorSpace::kDefaultSDRWhiteLevel)
+ sdr_white_level_ = other.sdr_white_level_;
+}
DrawImage::DrawImage(const DrawImage& other) = default;
DrawImage::DrawImage(DrawImage&& other) = default;
@@ -74,7 +83,8 @@ bool DrawImage::operator==(const DrawImage& other) const {
return paint_image_ == other.paint_image_ && src_rect_ == other.src_rect_ &&
filter_quality_ == other.filter_quality_ && scale_ == other.scale_ &&
matrix_is_decomposable_ == other.matrix_is_decomposable_ &&
- target_color_space_ == other.target_color_space_;
+ target_color_space_ == other.target_color_space_ &&
+ sdr_white_level_ == other.sdr_white_level_;
}
} // namespace cc
diff --git a/chromium/cc/paint/draw_image.h b/chromium/cc/paint/draw_image.h
index c1c6957baa8..984d85add06 100644
--- a/chromium/cc/paint/draw_image.h
+++ b/chromium/cc/paint/draw_image.h
@@ -31,13 +31,15 @@ class CC_PAINT_EXPORT DrawImage {
SkFilterQuality filter_quality,
const SkMatrix& matrix,
base::Optional<size_t> frame_index = base::nullopt,
- const base::Optional<gfx::ColorSpace>& color_space = base::nullopt);
+ const base::Optional<gfx::ColorSpace>& color_space = base::nullopt,
+ float sdr_white_level = gfx::ColorSpace::kDefaultSDRWhiteLevel);
// Constructs a DrawImage from |other| by adjusting its scale and setting a
// new color_space.
DrawImage(const DrawImage& other,
float scale_adjustment,
size_t frame_index,
- const gfx::ColorSpace& color_space);
+ const gfx::ColorSpace& color_space,
+ float sdr_white_level = gfx::ColorSpace::kDefaultSDRWhiteLevel);
DrawImage(const DrawImage& other);
DrawImage(DrawImage&& other);
~DrawImage();
@@ -63,6 +65,7 @@ class CC_PAINT_EXPORT DrawImage {
DCHECK(frame_index_.has_value());
return frame_index_.value();
}
+ float sdr_white_level() const { return sdr_white_level_; }
private:
PaintImage paint_image_;
@@ -72,6 +75,12 @@ class CC_PAINT_EXPORT DrawImage {
bool matrix_is_decomposable_;
base::Optional<size_t> frame_index_;
base::Optional<gfx::ColorSpace> target_color_space_;
+
+ // The SDR white level in nits for the display. Only if |target_color_space_|
+ // is HDR will this have a value other than kDefaultSDRWhiteLevel. Used by the
+ // ImageDecodeCache to prevent HDR images from being affected by variable SDR
+ // white levels since rasterization is always treated as SDR at present.
+ float sdr_white_level_ = gfx::ColorSpace::kDefaultSDRWhiteLevel;
};
} // namespace cc
diff --git a/chromium/cc/paint/image_transfer_cache_entry.cc b/chromium/cc/paint/image_transfer_cache_entry.cc
index 1f669ebefb4..90a0cf13bdd 100644
--- a/chromium/cc/paint/image_transfer_cache_entry.cc
+++ b/chromium/cc/paint/image_transfer_cache_entry.cc
@@ -19,7 +19,7 @@
#include "third_party/skia/include/core/SkPixmap.h"
#include "third_party/skia/include/core/SkYUVAIndex.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "third_party/skia/include/gpu/GrTypes.h"
namespace cc {
@@ -46,7 +46,7 @@ void ReleaseContext(SkImage::ReleaseContext context) {
// returned. On failure, nullptr is returned (e.g., if one of the backend
// textures is invalid or a Skia error occurs).
sk_sp<SkImage> MakeYUVImageFromUploadedPlanes(
- GrContext* context,
+ GrDirectContext* context,
const std::vector<sk_sp<SkImage>>& plane_images,
YUVDecodeFormat plane_images_format,
SkYUVColorSpace yuv_color_space,
@@ -104,7 +104,7 @@ sk_sp<SkImage> MakeYUVImageFromUploadedPlanes(
// TODO(ericrk): Replace calls to this with calls to SkImage::makeTextureImage,
// once that function handles colorspaces. https://crbug.com/834837
-sk_sp<SkImage> MakeTextureImage(GrContext* context,
+sk_sp<SkImage> MakeTextureImage(GrDirectContext* context,
sk_sp<SkImage> source_image,
sk_sp<SkColorSpace> target_color_space,
GrMipMapped mip_mapped) {
@@ -121,7 +121,8 @@ sk_sp<SkImage> MakeTextureImage(GrContext* context,
// Step 2: Apply a color-space conversion if necessary.
if (uploaded_image && target_color_space) {
- uploaded_image = uploaded_image->makeColorSpace(target_color_space);
+ uploaded_image =
+ uploaded_image->makeColorSpace(target_color_space, context);
}
// Step 3: If we had a colorspace conversion, we couldn't mipmap in step 1, so
@@ -218,6 +219,7 @@ ClientImageTransferCacheEntry::ClientImageTransferCacheEntry(
safe_size += sizeof(uint32_t); // num_planes
safe_size += sizeof(uint32_t); // has mips
safe_size += sizeof(uint32_t); // yuv_color_space
+ safe_size += sizeof(uint32_t); // yuv_color_type
safe_size += decoded_color_space_size + align;
safe_size += num_planes_ * sizeof(uint64_t); // plane widths
safe_size += num_planes_ * sizeof(uint64_t); // plane heights
@@ -274,6 +276,7 @@ bool ClientImageTransferCacheEntry::Serialize(base::span<uint8_t> data) const {
writer.Write(static_cast<uint32_t>(needs_mips_ ? 1 : 0));
writer.Write(yuv_color_space_);
writer.Write(decoded_color_space_);
+ writer.Write(yuv_pixmaps_->at(0)->colorType());
for (uint32_t i = 0; i < num_planes_; ++i) {
DCHECK(yuv_pixmaps_->at(i));
const SkPixmap* plane = yuv_pixmaps_->at(i);
@@ -329,7 +332,7 @@ ServiceImageTransferCacheEntry& ServiceImageTransferCacheEntry::operator=(
ServiceImageTransferCacheEntry&& other) = default;
bool ServiceImageTransferCacheEntry::BuildFromHardwareDecodedImage(
- GrContext* context,
+ GrDirectContext* context,
std::vector<sk_sp<SkImage>> plane_images,
YUVDecodeFormat plane_images_format,
SkYUVColorSpace yuv_color_space,
@@ -382,7 +385,7 @@ size_t ServiceImageTransferCacheEntry::CachedSize() const {
}
bool ServiceImageTransferCacheEntry::Deserialize(
- GrContext* context,
+ GrDirectContext* context,
base::span<const uint8_t> data) {
context_ = context;
@@ -390,7 +393,7 @@ bool ServiceImageTransferCacheEntry::Deserialize(
// only used for de-serializing primitives.
std::vector<uint8_t> scratch_buffer;
PaintOp::DeserializeOptions options(nullptr, nullptr, nullptr,
- &scratch_buffer, false);
+ &scratch_buffer, false, nullptr);
PaintOpReader reader(data.data(), data.size(), options);
uint32_t image_is_yuv = 0;
reader.Read(&image_is_yuv);
@@ -411,6 +414,8 @@ bool ServiceImageTransferCacheEntry::Deserialize(
yuv_color_space_ = yuv_color_space;
sk_sp<SkColorSpace> decoded_color_space;
reader.Read(&decoded_color_space);
+ SkColorType yuv_plane_color_type = kUnknown_SkColorType;
+ reader.Read(&yuv_plane_color_type);
// Match GrTexture::onGpuMemorySize so that memory traces agree.
auto gr_mips = has_mips_ ? GrMipMapped::kYes : GrMipMapped::kNo;
@@ -437,7 +442,6 @@ bool ServiceImageTransferCacheEntry::Deserialize(
size_t plane_bytes;
reader.ReadSize(&plane_bytes);
- constexpr SkColorType yuv_plane_color_type = kGray_8_SkColorType;
SkImageInfo plane_pixmap_info =
SkImageInfo::Make(plane_width, plane_height, yuv_plane_color_type,
kPremul_SkAlphaType, decoded_color_space);
@@ -570,7 +574,7 @@ sk_sp<SkImage> ServiceImageTransferCacheEntry::MakeSkImage(
if (!original)
return nullptr;
if (target_color_space) {
- image = original->makeColorSpace(target_color_space);
+ image = original->makeColorSpace(target_color_space, nullptr);
// If color space conversion is a noop, use original data.
if (image == original)
image = SkImage::MakeRasterCopy(pixmap);
diff --git a/chromium/cc/paint/image_transfer_cache_entry.h b/chromium/cc/paint/image_transfer_cache_entry.h
index 12eb917e91c..c68810d1ac8 100644
--- a/chromium/cc/paint/image_transfer_cache_entry.h
+++ b/chromium/cc/paint/image_transfer_cache_entry.h
@@ -18,7 +18,7 @@
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkYUVASizeInfo.h"
-class GrContext;
+class GrDirectContext;
class SkColorSpace;
class SkImage;
class SkPixmap;
@@ -113,7 +113,7 @@ class CC_PAINT_EXPORT ServiceImageTransferCacheEntry
// - The colorspace of the resulting RGB image is sRGB.
//
// Returns true if the entry can be built, false otherwise.
- bool BuildFromHardwareDecodedImage(GrContext* context,
+ bool BuildFromHardwareDecodedImage(GrDirectContext* context,
std::vector<sk_sp<SkImage>> plane_images,
YUVDecodeFormat plane_images_format,
SkYUVColorSpace yuv_color_space,
@@ -122,7 +122,8 @@ class CC_PAINT_EXPORT ServiceImageTransferCacheEntry
// ServiceTransferCacheEntry implementation:
size_t CachedSize() const final;
- bool Deserialize(GrContext* context, base::span<const uint8_t> data) final;
+ bool Deserialize(GrDirectContext* context,
+ base::span<const uint8_t> data) final;
bool fits_on_gpu() const { return fits_on_gpu_; }
const std::vector<sk_sp<SkImage>>& plane_images() const {
@@ -148,7 +149,7 @@ class CC_PAINT_EXPORT ServiceImageTransferCacheEntry
uint32_t height,
sk_sp<SkColorSpace> target_color_space);
- GrContext* context_ = nullptr;
+ GrDirectContext* context_ = nullptr;
std::vector<sk_sp<SkImage>> plane_images_;
YUVDecodeFormat plane_images_format_ = YUVDecodeFormat::kUnknown;
std::vector<size_t> plane_sizes_;
diff --git a/chromium/cc/paint/image_transfer_cache_entry_unittest.cc b/chromium/cc/paint/image_transfer_cache_entry_unittest.cc
index 0da5db2f946..de074e8fea9 100644
--- a/chromium/cc/paint/image_transfer_cache_entry_unittest.cc
+++ b/chromium/cc/paint/image_transfer_cache_entry_unittest.cc
@@ -22,7 +22,7 @@
#include "third_party/skia/include/core/SkPixmap.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "third_party/skia/include/gpu/GrTypes.h"
#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
@@ -92,7 +92,7 @@ class ImageTransferCacheEntryTest
ASSERT_TRUE(gl_context_->MakeCurrent(surface_.get()));
sk_sp<GrGLInterface> interface(gl::init::CreateGrGLInterface(
*gl_context_->GetVersionInfo(), false /* use_version_es2 */));
- gr_context_ = GrContext::MakeGL(std::move(interface));
+ gr_context_ = GrDirectContext::MakeGL(std::move(interface));
ASSERT_TRUE(gr_context_);
}
@@ -157,13 +157,13 @@ class ImageTransferCacheEntryTest
share_group_.reset();
}
- GrContext* gr_context() const { return gr_context_.get(); }
+ GrDirectContext* gr_context() const { return gr_context_.get(); }
private:
// Uploads a texture corresponding to a single plane in a YUV image. All the
// samples in the plane are set to |color|. The texture is not owned by Skia:
// when Skia doesn't need it anymore, MarkTextureAsReleased() will be called.
- sk_sp<SkImage> CreateSolidPlane(GrContext* gr_context,
+ sk_sp<SkImage> CreateSolidPlane(GrDirectContext* gr_context,
int width,
int height,
GrGLenum texture_format,
@@ -204,7 +204,7 @@ class ImageTransferCacheEntryTest
scoped_refptr<gl::GLSurface> surface_;
scoped_refptr<gl::GLShareGroup> share_group_;
scoped_refptr<gl::GLContext> gl_context_;
- sk_sp<GrContext> gr_context_;
+ sk_sp<GrDirectContext> gr_context_;
gl::DisableNullDrawGLBindings enable_pixel_output_;
};
@@ -231,8 +231,8 @@ TEST_P(ImageTransferCacheEntryTest, Deserialize) {
void* planes[3];
planes[0] = reinterpret_cast<void*>(planes_data.get());
- planes[1] = ((char*)planes[0]) + y_bytes;
- planes[2] = ((char*)planes[1]) + uv_bytes;
+ planes[1] = reinterpret_cast<char*>(planes[0]) + y_bytes;
+ planes[2] = reinterpret_cast<char*>(planes[1]) + uv_bytes;
auto info = SkImageInfo::Make(image_width, image_height, kGray_8_SkColorType,
kUnknown_SkAlphaType);
@@ -407,7 +407,7 @@ INSTANTIATE_TEST_SUITE_P(All,
TEST(ImageTransferCacheEntryTestNoYUV, CPUImageWithMips) {
GrMockOptions options;
- auto gr_context = GrContext::MakeMock(&options);
+ auto gr_context = GrDirectContext::MakeMock(&options);
SkBitmap bitmap;
bitmap.allocPixels(
@@ -433,7 +433,7 @@ TEST(ImageTransferCacheEntryTestNoYUV, CPUImageWithMips) {
TEST(ImageTransferCacheEntryTestNoYUV, CPUImageAddMipsLater) {
GrMockOptions options;
- auto gr_context = GrContext::MakeMock(&options);
+ auto gr_context = GrDirectContext::MakeMock(&options);
SkBitmap bitmap;
bitmap.allocPixels(
diff --git a/chromium/cc/paint/oop_pixeltest.cc b/chromium/cc/paint/oop_pixeltest.cc
index ffc090ebee1..57548566df0 100644
--- a/chromium/cc/paint/oop_pixeltest.cc
+++ b/chromium/cc/paint/oop_pixeltest.cc
@@ -9,6 +9,7 @@
#include "base/command_line.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
#include "cc/base/completion_event.h"
#include "cc/base/region.h"
#include "cc/layers/recording_source.h"
@@ -132,6 +133,8 @@ class OopPixelTest : public testing::Test,
SkColor background_color = SK_ColorBLACK;
int msaa_sample_count = 0;
bool use_lcd_text = false;
+ PlaybackImageProvider::RasterMode image_provider_raster_mode =
+ PlaybackImageProvider::RasterMode::kSoftware;
gfx::Size resource_size;
gfx::Size content_size;
gfx::Rect full_raster_rect;
@@ -159,9 +162,11 @@ class OopPixelTest : public testing::Test,
viz::TestInProcessContextProvider::ScopedRasterContextLock lock(
raster_context_provider_.get(), url.possibly_invalid_spec().c_str());
- PlaybackImageProvider image_provider(oop_image_cache_.get(),
- options.color_space,
- PlaybackImageProvider::Settings());
+ base::Optional<PlaybackImageProvider::Settings> settings;
+ settings.emplace(PlaybackImageProvider::Settings());
+ settings->raster_mode = options.image_provider_raster_mode;
+ PlaybackImageProvider image_provider(
+ oop_image_cache_.get(), options.color_space, std::move(settings));
int width = options.resource_size.width();
int height = options.resource_size.height();
@@ -173,7 +178,8 @@ class OopPixelTest : public testing::Test,
gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
gpu::Mailbox mailbox = sii->CreateSharedImage(
viz::ResourceFormat::RGBA_8888, gfx::Size(width, height),
- options.color_space, flags);
+ options.color_space, kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
+ flags, gpu::kNullSurfaceHandle);
EXPECT_TRUE(mailbox.Verify());
raster_implementation->WaitSyncTokenCHROMIUM(
sii->GenUnverifiedSyncToken().GetConstData());
@@ -264,13 +270,29 @@ class OopPixelTest : public testing::Test,
uint32_t flags = gpu::SHARED_IMAGE_USAGE_RASTER |
gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
gpu::Mailbox mailbox = sii->CreateSharedImage(
- image_format, options.resource_size, options.color_space, flags);
+ image_format, options.resource_size, options.color_space,
+ kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, flags,
+ gpu::kNullSurfaceHandle);
EXPECT_TRUE(mailbox.Verify());
ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
return mailbox;
}
+ void UploadPixels(gpu::gles2::GLES2Interface* gl,
+ const gpu::Mailbox& mailbox,
+ const gfx::Size& size,
+ GLenum format,
+ GLenum type,
+ const void* data) {
+ GLuint texture = gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name);
+ gl->BindTexture(GL_TEXTURE_2D, texture);
+ gl->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.width(), size.height(),
+ format, type, data);
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+ gl->DeleteTextures(1, &texture);
+ }
+
SkBitmap RasterExpectedBitmap(
scoped_refptr<DisplayItemList> display_item_list,
const gfx::Size& playback_size) {
@@ -864,6 +886,75 @@ TEST_P(OopImagePixelTest, DrawImageWithSetMatrix) {
EXPECT_EQ(actual.getColor(0, 0), SK_ColorMAGENTA);
}
+namespace {
+class TestMailboxBacking : public TextureBacking {
+ public:
+ explicit TestMailboxBacking(gpu::Mailbox mailbox, SkImageInfo info)
+ : mailbox_(mailbox), info_(info) {}
+
+ const SkImageInfo& GetSkImageInfo() override { return info_; }
+ gpu::Mailbox GetMailbox() const override { return mailbox_; }
+ sk_sp<SkImage> GetAcceleratedSkImage() override { return nullptr; }
+ sk_sp<SkImage> GetSkImageViaReadback() override { return nullptr; }
+ bool readPixels(const SkImageInfo& dstInfo,
+ void* dstPixels,
+ size_t dstRowBytes,
+ int srcX,
+ int srcY) override {
+ return false;
+ }
+
+ private:
+ gpu::Mailbox mailbox_;
+ SkImageInfo info_;
+};
+} // namespace
+
+TEST_F(OopPixelTest, DrawMailboxBackedImage) {
+ RasterOptions options(gfx::Size(16, 16));
+ options.image_provider_raster_mode = PlaybackImageProvider::RasterMode::kOop;
+ SkImageInfo backing_info = SkImageInfo::MakeN32Premul(
+ options.resource_size.width(), options.resource_size.height());
+
+ SkBitmap expected_bitmap;
+ expected_bitmap.allocPixels(backing_info);
+
+ SkCanvas canvas(expected_bitmap);
+ canvas.drawColor(SK_ColorMAGENTA);
+ SkPaint green;
+ green.setColor(SK_ColorGREEN);
+ canvas.drawRect(SkRect::MakeXYWH(1, 2, 3, 4), green);
+
+ auto* ri = raster_context_provider_->RasterInterface();
+ auto* sii = raster_context_provider_->SharedImageInterface();
+ gpu::Mailbox src_mailbox = CreateMailboxSharedImage(
+ ri, sii, options, viz::ResourceFormat::RGBA_8888);
+ ri->OrderingBarrierCHROMIUM();
+
+ auto* gl = gles2_context_provider_->ContextGL();
+ UploadPixels(gl, src_mailbox, options.resource_size, GL_RGBA,
+ GL_UNSIGNED_BYTE, expected_bitmap.getPixels());
+ gl->OrderingBarrierCHROMIUM();
+
+ auto src_paint_image =
+ PaintImageBuilder::WithDefault()
+ .set_id(PaintImage::GetNextId())
+ .set_texture_backing(sk_sp<TestMailboxBacking>(new TestMailboxBacking(
+ src_mailbox, backing_info)),
+ PaintImage::GetNextContentId())
+ .TakePaintImage();
+
+ auto display_item_list = base::MakeRefCounted<DisplayItemList>();
+ display_item_list->StartPaint();
+ PaintFlags flags;
+ display_item_list->push<DrawImageOp>(src_paint_image, 0.f, 0.f, &flags);
+ display_item_list->EndPaintOfUnpaired(gfx::Rect(options.resource_size));
+ display_item_list->Finalize();
+
+ auto actual_bitmap = Raster(display_item_list, options);
+ ExpectEquals(actual_bitmap, expected_bitmap);
+}
+
TEST_F(OopPixelTest, Preclear) {
gfx::Rect rect(10, 10);
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
@@ -1025,9 +1116,65 @@ TEST_F(OopPixelTest, ClearingOpaqueCornerPartialRaster) {
ExpectEquals(gpu_result, bitmap, "gpu");
}
+TEST_P(OopClearPixelTest, ClearingOpaqueLeftEdge) {
+ // Verify that a tile that intersects the left edge of content
+ // but not other edges only clears the left pixels.
+ RasterOptions options;
+ options.resource_size = gfx::Size(10, 10);
+ int arbitrary_y = 10;
+ options.full_raster_rect = gfx::Rect(0, arbitrary_y, 3, 10);
+ options.content_size = gfx::Size(options.full_raster_rect.right() + 1000,
+ options.full_raster_rect.bottom() + 1000);
+ if (IsPartialRaster()) {
+ // Ignore the right column of pixels here to force partial raster.
+ // Additionally ignore the top and bottom rows of pixels to make sure
+ // that things are not cleared outside the rect.
+ options.playback_rect = gfx::Rect(options.full_raster_rect.x(),
+ options.full_raster_rect.y() + 1,
+ options.full_raster_rect.width() - 1,
+ options.full_raster_rect.height() - 2);
+ } else {
+ options.playback_rect = options.full_raster_rect;
+ }
+
+ options.background_color = SK_ColorGREEN;
+ options.post_translate = gfx::Vector2dF(0.3f, 0.7f);
+ options.requires_clear = false;
+ options.preclear = true;
+ options.preclear_color = SK_ColorRED;
+
+ // Make a non-empty but noop display list to avoid early outs.
+ auto display_item_list = MakeNoopDisplayItemList();
+
+ auto oop_result = Raster(display_item_list, options);
+ auto gpu_result = RasterExpectedBitmap(display_item_list, options);
+
+ SkBitmap bitmap;
+ bitmap.allocPixelsFlags(
+ SkImageInfo::MakeN32Premul(options.resource_size.width(),
+ options.resource_size.height()),
+ SkBitmap::kZeroPixels_AllocFlag);
+
+ SkCanvas canvas(bitmap);
+ canvas.drawColor(options.preclear_color);
+ SkPaint green;
+ green.setColor(options.background_color);
+ if (IsPartialRaster()) {
+ // Expect a one pixel column border on the first column, ignoring the first
+ // and the last rows.
+ canvas.drawRect(SkRect::MakeXYWH(0, 1, 1, 8), green);
+ } else {
+ // Expect a one pixel column border on the first column.
+ canvas.drawRect(SkRect::MakeXYWH(0, 0, 1, 10), green);
+ }
+
+ ExpectEquals(oop_result, bitmap, "oop");
+ ExpectEquals(gpu_result, bitmap, "gpu");
+}
+
TEST_P(OopClearPixelTest, ClearingOpaqueRightEdge) {
// Verify that a tile that intersects the right edge of content
- // but not the bottom only clears the right pixels.
+ // but not other edges only clears the right pixels.
RasterOptions options;
gfx::Point arbitrary_offset(30, 40);
options.resource_size = gfx::Size(10, 10);
@@ -1036,17 +1183,19 @@ TEST_P(OopClearPixelTest, ClearingOpaqueRightEdge) {
options.full_raster_rect.bottom() + 1000);
if (IsPartialRaster()) {
// Ignore the left column of pixels here to force partial raster.
- // Additionally ignore the bottom row of pixels to make sure
+ // Additionally ignore the top and bottom rows of pixels to make sure
// that things are not cleared outside the rect.
options.playback_rect = gfx::Rect(options.full_raster_rect.x() + 1,
- options.full_raster_rect.y(),
+ options.full_raster_rect.y() + 1,
options.full_raster_rect.width() - 1,
- options.full_raster_rect.height() - 1);
+ options.full_raster_rect.height() - 2);
} else {
options.playback_rect = options.full_raster_rect;
}
options.background_color = SK_ColorGREEN;
+ float arbitrary_scale = 0.25f;
+ options.post_scale = arbitrary_scale;
options.requires_clear = false;
options.preclear = true;
options.preclear_color = SK_ColorRED;
@@ -1068,8 +1217,9 @@ TEST_P(OopClearPixelTest, ClearingOpaqueRightEdge) {
SkPaint green;
green.setColor(options.background_color);
if (IsPartialRaster()) {
- // Expect a two pixel column border from texels 2-4, ignoring the last row.
- canvas.drawRect(SkRect::MakeXYWH(2, 0, 2, 9), green);
+ // Expect a two pixel column border from texels 2-4, ignoring the first and
+ // the last rows.
+ canvas.drawRect(SkRect::MakeXYWH(2, 1, 2, 8), green);
} else {
// Expect a two pixel column border from texels 2-4.
canvas.drawRect(SkRect::MakeXYWH(2, 0, 2, 10), green);
@@ -1079,9 +1229,66 @@ TEST_P(OopClearPixelTest, ClearingOpaqueRightEdge) {
ExpectEquals(gpu_result, bitmap, "gpu");
}
+TEST_P(OopClearPixelTest, ClearingOpaqueTopEdge) {
+ // Verify that a tile that intersects only the top edge of content
+ // but not other edges only clears the top pixels.
+
+ RasterOptions options;
+ options.resource_size = gfx::Size(10, 10);
+ int arbitrary_x = 10;
+ options.full_raster_rect = gfx::Rect(arbitrary_x, 0, 10, 5);
+ options.content_size = gfx::Size(options.full_raster_rect.right() + 1000,
+ options.full_raster_rect.bottom() + 1000);
+ if (IsPartialRaster()) {
+ // Ignore the bottom row of pixels here to force partial raster.
+ // Additionally ignore the left and right columns of pixels to make sure
+ // that things are not cleared outside the rect.
+ options.playback_rect = gfx::Rect(options.full_raster_rect.x() + 1,
+ options.full_raster_rect.y(),
+ options.full_raster_rect.width() - 2,
+ options.full_raster_rect.height() - 1);
+ } else {
+ options.playback_rect = options.full_raster_rect;
+ }
+ options.background_color = SK_ColorGREEN;
+ options.post_translate = gfx::Vector2dF(0.3f, 0.7f);
+ options.requires_clear = false;
+ options.preclear = true;
+ options.preclear_color = SK_ColorRED;
+
+ // Make a non-empty but noop display list to avoid early outs.
+ auto display_item_list = MakeNoopDisplayItemList();
+
+ auto oop_result = Raster(display_item_list, options);
+ auto gpu_result = RasterExpectedBitmap(display_item_list, options);
+
+ SkBitmap bitmap;
+ bitmap.allocPixelsFlags(
+ SkImageInfo::MakeN32Premul(options.resource_size.width(),
+ options.resource_size.height()),
+ SkBitmap::kZeroPixels_AllocFlag);
+
+ SkCanvas canvas(bitmap);
+ canvas.drawColor(options.preclear_color);
+ SkPaint green;
+ green.setColor(options.background_color);
+
+ if (IsPartialRaster()) {
+ // Expect a one pixel border on the top row, ignoring the first and the last
+ // columns.
+ canvas.drawRect(SkRect::MakeXYWH(1, 0, 8, 1), green);
+ } else {
+ // Expect a one pixel border on the top row.
+ canvas.drawRect(SkRect::MakeXYWH(0, 0, 10, 1), green);
+ }
+
+ ExpectEquals(oop_result, bitmap, "oop");
+ ExpectEquals(gpu_result, bitmap, "gpu");
+}
+
TEST_P(OopClearPixelTest, ClearingOpaqueBottomEdge) {
// Verify that a tile that intersects the bottom edge of content
- // but not the right only clears the bottom pixels.
+ // but not other edges only clears the bottom pixels.
RasterOptions options;
gfx::Point arbitrary_offset(10, 20);
@@ -1091,11 +1298,11 @@ TEST_P(OopClearPixelTest, ClearingOpaqueBottomEdge) {
options.full_raster_rect.bottom());
if (IsPartialRaster()) {
// Ignore the top row of pixels here to force partial raster.
- // Additionally ignore the right column of pixels to make sure
+ // Additionally ignore the left and right columns of pixels to make sure
// that things are not cleared outside the rect.
- options.playback_rect = gfx::Rect(options.full_raster_rect.x(),
+ options.playback_rect = gfx::Rect(options.full_raster_rect.x() + 1,
options.full_raster_rect.y() + 1,
- options.full_raster_rect.width() - 1,
+ options.full_raster_rect.width() - 2,
options.full_raster_rect.height() - 1);
} else {
options.playback_rect = options.full_raster_rect;
@@ -1125,9 +1332,9 @@ TEST_P(OopClearPixelTest, ClearingOpaqueBottomEdge) {
green.setColor(options.background_color);
if (IsPartialRaster()) {
- // Expect a two pixel border from texels 4-6 on the row, ignoring the last
- // column.
- canvas.drawRect(SkRect::MakeXYWH(0, 4, 9, 2), green);
+ // Expect a two pixel border from texels 4-6 on the row, ignoring the first
+ // and the last columns.
+ canvas.drawRect(SkRect::MakeXYWH(1, 4, 8, 2), green);
} else {
// Expect a two pixel border from texels 4-6 on the row
canvas.drawRect(SkRect::MakeXYWH(0, 4, 10, 2), green);
@@ -1148,6 +1355,7 @@ TEST_F(OopPixelTest, ClearingOpaqueInternal) {
options.content_size = gfx::Size(1000, 1000);
options.playback_rect = options.full_raster_rect;
options.background_color = SK_ColorGREEN;
+ options.post_translate = gfx::Vector2dF(0.3f, 0.7f);
float arbitrary_scale = 1.2345f;
options.post_scale = arbitrary_scale;
options.requires_clear = false;
@@ -1694,20 +1902,6 @@ TEST_F(OopPixelTest, WritePixels) {
}
namespace {
-void UploadPixels(gpu::gles2::GLES2Interface* gl,
- const gpu::Mailbox& mailbox,
- const gfx::Size& size,
- GLenum format,
- GLenum type,
- const void* data) {
- GLuint texture = gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name);
- gl->BindTexture(GL_TEXTURE_2D, texture);
- gl->TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.width(), size.height(), format,
- type, data);
- gl->BindTexture(GL_TEXTURE_2D, 0);
- gl->DeleteTextures(1, &texture);
-}
-
GrBackendTexture MakeBackendTexture(gpu::gles2::GLES2Interface* gl,
const gpu::Mailbox& mailbox,
gfx::Size size,
@@ -1802,10 +1996,51 @@ TEST_F(OopPixelTest, ConvertYUVToRGB) {
sii->DestroySharedImage(sync_token, v_mailbox);
}
+TEST_F(OopPixelTest, ReadbackImagePixels) {
+ RasterOptions options(gfx::Size(16, 16));
+ SkImageInfo dest_info = SkImageInfo::MakeN32Premul(
+ options.resource_size.width(), options.resource_size.height(),
+ gfx::ColorSpace::CreateSRGB().ToSkColorSpace());
+
+ SkBitmap expected_bitmap;
+ expected_bitmap.allocPixels(dest_info);
+
+ SkCanvas canvas(expected_bitmap);
+ canvas.drawColor(SK_ColorMAGENTA);
+ SkPaint green;
+ green.setColor(SK_ColorGREEN);
+ canvas.drawRect(SkRect::MakeXYWH(1, 2, 3, 4), green);
+
+ auto* ri = raster_context_provider_->RasterInterface();
+ auto* sii = raster_context_provider_->SharedImageInterface();
+ gpu::Mailbox mailbox = CreateMailboxSharedImage(
+ ri, sii, options, viz::ResourceFormat::RGBA_8888);
+ ri->OrderingBarrierCHROMIUM();
+
+ gpu::gles2::GLES2Interface* gl = gles2_context_provider_->ContextGL();
+ UploadPixels(gl, mailbox, options.resource_size, GL_RGBA, GL_UNSIGNED_BYTE,
+ expected_bitmap.getPixels());
+ gl->OrderingBarrierCHROMIUM();
+
+ SkBitmap actual_bitmap;
+ actual_bitmap.allocPixels(dest_info);
+
+ ri->ReadbackImagePixels(mailbox, dest_info, dest_info.minRowBytes(), 0, 0,
+ actual_bitmap.getPixels());
+ EXPECT_EQ(ri->GetError(), static_cast<unsigned>(GL_NO_ERROR));
+ ri->OrderingBarrierCHROMIUM();
+
+ ExpectEquals(actual_bitmap, expected_bitmap);
+
+ gpu::SyncToken sync_token;
+ gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
+ sii->DestroySharedImage(sync_token, mailbox);
+}
+
// A workaround on Android that forces the use of GLES 2.0 instead of 3.0
// prevents the use of the GL_RG textures required for NV12 format. This
// test will be reactiviated on Android once the workaround is removed.
-#ifndef OS_ANDROID
+#if !defined(OS_ANDROID)
TEST_F(OopPixelTest, ConvertNV12ToRGB) {
RasterOptions options(gfx::Size(16, 16));
RasterOptions uv_options(gfx::Size(options.resource_size.width() / 2,
@@ -1880,7 +2115,7 @@ TEST_F(OopPixelTest, ConvertNV12ToRGB) {
sii->DestroySharedImage(sync_token, y_mailbox);
sii->DestroySharedImage(sync_token, uv_mailbox);
}
-#endif // OS_ANDROID
+#endif // !defined(OS_ANDROID)
class OopPathPixelTest : public OopPixelTest,
public ::testing::WithParamInterface<bool> {
diff --git a/chromium/cc/paint/paint_image.cc b/chromium/cc/paint/paint_image.cc
index 529718556d9..b4cb4d21da5 100644
--- a/chromium/cc/paint/paint_image.cc
+++ b/chromium/cc/paint/paint_image.cc
@@ -6,9 +6,11 @@
#include <memory>
#include <sstream>
+#include <utility>
#include "base/atomic_sequence_num.h"
#include "base/hash/hash.h"
+#include "base/logging.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/paint/paint_image_generator.h"
#include "cc/paint/paint_record.h"
@@ -61,8 +63,6 @@ bool PaintImage::operator==(const PaintImage& other) const {
return false;
if (completion_state_ != other.completion_state_)
return false;
- if (subset_rect_ != other.subset_rect_)
- return false;
if (is_multipart_ != other.is_multipart_)
return false;
return true;
@@ -114,37 +114,50 @@ const sk_sp<SkImage>& PaintImage::GetSkImage() const {
return cached_sk_image_;
}
-const sk_sp<SkImage>& PaintImage::GetRasterSkImage() const {
+sk_sp<SkImage> PaintImage::GetSwSkImage() const {
+ if (texture_backing_) {
+ return texture_backing_->GetSkImageViaReadback();
+ } else if (cached_sk_image_ && cached_sk_image_->isTextureBacked()) {
+ return cached_sk_image_->makeNonTextureImage();
+ }
return cached_sk_image_;
}
-gpu::Mailbox PaintImage::GetMailbox() const {
- DCHECK(texture_backing_);
- return texture_backing_->GetMailbox();
+sk_sp<SkImage> PaintImage::GetAcceleratedSkImage() const {
+ DCHECK(!cached_sk_image_ || cached_sk_image_->isTextureBacked());
+ return cached_sk_image_;
}
-PaintImage PaintImage::MakeSubset(const gfx::Rect& subset) const {
- DCHECK(!subset.IsEmpty());
-
- // If the subset is the same as the image bounds, we can return the same
- // image.
- gfx::Rect bounds(width(), height());
- if (bounds == subset)
- return *this;
+bool PaintImage::readPixels(const SkImageInfo& dst_info,
+ void* dst_pixels,
+ size_t dst_row_bytes,
+ int src_x,
+ int src_y) const {
+ if (texture_backing_) {
+ return texture_backing_->readPixels(dst_info, dst_pixels, dst_row_bytes,
+ src_x, src_y);
+ } else if (cached_sk_image_) {
+ return cached_sk_image_->readPixels(dst_info, dst_pixels, dst_row_bytes,
+ src_x, src_y);
+ }
+ return false;
+}
- DCHECK(bounds.Contains(subset))
- << "Subset should not be greater than the image bounds";
- PaintImage result(*this);
- result.subset_rect_ = subset;
- // Store the subset from the original image.
- result.subset_rect_.Offset(subset_rect_.x(), subset_rect_.y());
+SkImageInfo PaintImage::GetSkImageInfo() const {
+ if (paint_image_generator_) {
+ return paint_image_generator_->GetSkImageInfo();
+ } else if (texture_backing_) {
+ return texture_backing_->GetSkImageInfo();
+ } else if (cached_sk_image_) {
+ return cached_sk_image_->imageInfo();
+ } else {
+ return SkImageInfo::MakeUnknown();
+ }
+}
- // Creating the |cached_sk_image_| using the SkImage from the original
- // 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_ =
- GetRasterSkImage()->makeSubset(gfx::RectToSkIRect(subset));
- return result;
+gpu::Mailbox PaintImage::GetMailbox() const {
+ DCHECK(texture_backing_);
+ return texture_backing_->GetMailbox();
}
void PaintImage::CreateSkImage() {
@@ -165,19 +178,11 @@ void PaintImage::CreateSkImage() {
} else if (texture_backing_) {
cached_sk_image_ = texture_backing_->GetAcceleratedSkImage();
}
-
- if (!subset_rect_.IsEmpty() && cached_sk_image_) {
- cached_sk_image_ =
- cached_sk_image_->makeSubset(gfx::RectToSkIRect(subset_rect_));
- }
}
SkISize PaintImage::GetSupportedDecodeSize(
const SkISize& requested_size) const {
- // TODO(vmpstr): In some cases we do not support decoding to any other
- // size than the original. See the comment in CanDecodeFromGenerator()
- // for more detail.
- if (CanDecodeFromGenerator())
+ if (paint_image_generator_)
return paint_image_generator_->GetSupportedDecodeSize(requested_size);
return SkISize::Make(width(), height());
}
@@ -194,10 +199,7 @@ bool PaintImage::Decode(void* memory,
// We only support decode to supported decode size.
DCHECK(info->dimensions() == GetSupportedDecodeSize(info->dimensions()));
- // TODO(vmpstr): In some cases we do not support decoding to any other
- // size than the original. See the comment in CanDecodeFromGenerator()
- // for more detail. For now, fallback to DecodeFromSkImage().
- if (CanDecodeFromGenerator()) {
+ if (paint_image_generator_) {
return DecodeFromGenerator(memory, info, std::move(color_space),
frame_index, client_id);
}
@@ -210,12 +212,14 @@ bool PaintImage::DecodeYuv(
size_t frame_index,
GeneratorClientId client_id,
const SkYUVASizeInfo& yuva_size_info,
+ SkColorType color_type,
SkYUVAIndex plane_indices[SkYUVAIndex::kIndexCount]) const {
DCHECK(plane_indices != nullptr);
- DCHECK(CanDecodeFromGenerator());
+ DCHECK(paint_image_generator_);
const uint32_t lazy_pixel_ref = unique_id();
- return paint_image_generator_->GetYUVA8Planes(
- yuva_size_info, plane_indices, planes, frame_index, lazy_pixel_ref);
+ return paint_image_generator_->GetYUVAPlanes(yuva_size_info, color_type,
+ plane_indices, planes,
+ frame_index, lazy_pixel_ref);
}
bool PaintImage::DecodeFromGenerator(void* memory,
@@ -223,7 +227,7 @@ bool PaintImage::DecodeFromGenerator(void* memory,
sk_sp<SkColorSpace> color_space,
size_t frame_index,
GeneratorClientId client_id) const {
- DCHECK(CanDecodeFromGenerator());
+ DCHECK(paint_image_generator_);
// First convert the info to have the requested color space, since the decoder
// will convert this for us.
*info = info->makeColorSpace(std::move(color_space));
@@ -233,15 +237,6 @@ bool PaintImage::DecodeFromGenerator(void* memory,
lazy_pixel_ref);
}
-// TODO(vmpstr): If we're using a subset_rect_ then the info specifies the
-// requested size relative to the subset. However, the generator isn't aware
-// of this subsetting and would need a size that is relative to the original
-// image size. We could still implement this case, but we need to convert the
-// requested size into the space of the original image.
-bool PaintImage::CanDecodeFromGenerator() const {
- return paint_image_generator_ && subset_rect_.IsEmpty();
-}
-
bool PaintImage::DecodeFromSkImage(void* memory,
SkImageInfo* info,
sk_sp<SkColorSpace> color_space,
@@ -250,7 +245,7 @@ bool PaintImage::DecodeFromSkImage(void* memory,
auto image = GetSkImageForFrame(frame_index, client_id);
DCHECK(image);
if (color_space) {
- image = image->makeColorSpace(color_space);
+ image = image->makeColorSpace(color_space, nullptr);
if (!image)
return false;
}
@@ -270,7 +265,7 @@ bool PaintImage::ShouldAnimate() const {
PaintImage::FrameKey PaintImage::GetKeyForFrame(size_t frame_index) const {
DCHECK_LT(frame_index, FrameCount());
- return FrameKey(GetContentIdForFrame(frame_index), frame_index, subset_rect_);
+ return FrameKey(GetContentIdForFrame(frame_index), frame_index);
}
PaintImage::ContentId PaintImage::GetContentIdForFrame(
@@ -283,19 +278,11 @@ PaintImage::ContentId PaintImage::GetContentIdForFrame(
}
SkColorType PaintImage::GetColorType() const {
- if (paint_image_generator_)
- return paint_image_generator_->GetSkImageInfo().colorType();
- if (GetSkImage())
- return GetSkImage()->colorType();
- return kUnknown_SkColorType;
+ return GetSkImageInfo().colorType();
}
SkAlphaType PaintImage::GetAlphaType() const {
- if (paint_image_generator_)
- return paint_image_generator_->GetSkImageInfo().alphaType();
- if (GetSkImage())
- return GetSkImage()->alphaType();
- return kUnknown_SkAlphaType;
+ return GetSkImageInfo().alphaType();
}
bool PaintImage::IsTextureBacked() const {
@@ -309,27 +296,35 @@ bool PaintImage::IsTextureBacked() const {
int PaintImage::width() const {
return paint_worklet_input_
? static_cast<int>(paint_worklet_input_->GetSize().width())
- : GetSkImage()->width();
+ : GetSkImageInfo().width();
}
int PaintImage::height() const {
return paint_worklet_input_
? static_cast<int>(paint_worklet_input_->GetSize().height())
- : GetSkImage()->height();
+ : GetSkImageInfo().height();
}
-bool PaintImage::isSRGB() const {
+gfx::ContentColorUsage PaintImage::GetContentColorUsage() const {
// Right now, JS paint worklets can only be in sRGB
if (paint_worklet_input_)
- return true;
+ return gfx::ContentColorUsage::kSRGB;
- auto* color_space = GetSkImage()->colorSpace();
- if (!color_space) {
- // Assume the image will be sRGB if we don't know yet.
- return true;
+ const auto* color_space = GetSkImageInfo().colorSpace();
+
+ // Assume the image will be sRGB if we don't know yet.
+ if (!color_space || color_space->isSRGB())
+ return gfx::ContentColorUsage::kSRGB;
+
+ skcms_TransferFunction fn;
+ if (!color_space->isNumericalTransferFn(&fn) &&
+ (skcms_TransferFunction_isPQish(&fn) ||
+ skcms_TransferFunction_isHLGish(&fn))) {
+ return gfx::ContentColorUsage::kHDR;
}
- return color_space->isSRGB();
+ // If it's not HDR and not SRGB, report it as WCG.
+ return gfx::ContentColorUsage::kWideColorGamut;
}
const ImageHeaderMetadata* PaintImage::GetImageHeaderMetadata() const {
@@ -340,10 +335,12 @@ const ImageHeaderMetadata* PaintImage::GetImageHeaderMetadata() const {
bool PaintImage::IsYuv(SkYUVASizeInfo* yuva_size_info,
SkYUVAIndex* plane_indices,
- SkYUVColorSpace* yuv_color_space) const {
+ SkYUVColorSpace* yuv_color_space,
+ uint8_t* bit_depth) const {
SkYUVASizeInfo temp_yuva_size_info;
SkYUVAIndex temp_plane_indices[SkYUVAIndex::kIndexCount];
SkYUVColorSpace temp_yuv_color_space;
+ uint8_t temp_bit_depth;
if (!yuva_size_info) {
yuva_size_info = &temp_yuva_size_info;
}
@@ -353,11 +350,14 @@ bool PaintImage::IsYuv(SkYUVASizeInfo* yuva_size_info,
if (!yuv_color_space) {
yuv_color_space = &temp_yuv_color_space;
}
+ if (!bit_depth) {
+ bit_depth = &temp_bit_depth;
+ }
// ImageDecoder will fill out the value of |yuv_color_space| depending on
// the codec's specification.
- return CanDecodeFromGenerator() &&
- paint_image_generator_->QueryYUVA8(yuva_size_info, plane_indices,
- yuv_color_space);
+ return paint_image_generator_ &&
+ paint_image_generator_->QueryYUVA(yuva_size_info, plane_indices,
+ yuv_color_space, bit_depth);
}
const std::vector<FrameMetadata>& PaintImage::GetFrameMetadata() const {
@@ -385,19 +385,17 @@ sk_sp<SkImage> PaintImage::GetSkImageForFrame(
// perform lazy decoding and can be multi-frame.
if (!paint_image_generator_) {
DCHECK_EQ(index, kDefaultFrameIndex);
- return GetRasterSkImage();
+ return GetSwSkImage();
}
// 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 GetRasterSkImage();
+ return GetSwSkImage();
sk_sp<SkImage> image =
SkImage::MakeFromGenerator(std::make_unique<SkiaPaintImageGenerator>(
paint_image_generator_, index, client_id));
- if (!subset_rect_.IsEmpty())
- image = image->makeSubset(gfx::RectToSkIRect(subset_rect_));
return image;
}
@@ -409,35 +407,18 @@ std::string PaintImage::ToString() const {
<< " id_: " << id_
<< " animation_type_: " << static_cast<int>(animation_type_)
<< " completion_state_: " << static_cast<int>(completion_state_)
- << " subset_rect_: " << subset_rect_.ToString()
<< " is_multipart_: " << is_multipart_ << " is YUV: " << IsYuv();
return str.str();
}
-PaintImage::FrameKey::FrameKey(ContentId content_id,
- size_t frame_index,
- gfx::Rect subset_rect)
- : content_id_(content_id),
- frame_index_(frame_index),
- subset_rect_(subset_rect) {
- size_t original_hash = base::HashInts(static_cast<uint64_t>(content_id_),
- static_cast<uint64_t>(frame_index_));
- if (subset_rect_.IsEmpty()) {
- hash_ = original_hash;
- } else {
- size_t subset_hash =
- base::HashInts(static_cast<uint64_t>(
- base::HashInts(subset_rect_.x(), subset_rect_.y())),
- static_cast<uint64_t>(base::HashInts(
- subset_rect_.width(), subset_rect_.height())));
- hash_ = base::HashInts(original_hash, subset_hash);
- }
+PaintImage::FrameKey::FrameKey(ContentId content_id, size_t frame_index)
+ : content_id_(content_id), frame_index_(frame_index) {
+ hash_ = base::HashInts(static_cast<uint64_t>(content_id_),
+ static_cast<uint64_t>(frame_index_));
}
bool PaintImage::FrameKey::operator==(const FrameKey& other) const {
- return content_id_ == other.content_id_ &&
- frame_index_ == other.frame_index_ &&
- subset_rect_ == other.subset_rect_;
+ return content_id_ == other.content_id_ && frame_index_ == other.frame_index_;
}
bool PaintImage::FrameKey::operator!=(const FrameKey& other) const {
@@ -447,8 +428,7 @@ bool PaintImage::FrameKey::operator!=(const FrameKey& other) const {
std::string PaintImage::FrameKey::ToString() const {
std::ostringstream str;
str << "content_id: " << content_id_ << ","
- << "frame_index: " << frame_index_ << ","
- << "subset_rect: " << subset_rect_.ToString();
+ << "frame_index: " << frame_index_;
return str.str();
}
diff --git a/chromium/cc/paint/paint_image.h b/chromium/cc/paint/paint_image.h
index 1d7503a2d04..3791368d91a 100644
--- a/chromium/cc/paint/paint_image.h
+++ b/chromium/cc/paint/paint_image.h
@@ -5,6 +5,7 @@
#ifndef CC_PAINT_PAINT_IMAGE_H_
#define CC_PAINT_PAINT_IMAGE_H_
+#include <string>
#include <vector>
#include "base/gtest_prod_util.h"
@@ -17,6 +18,7 @@
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkYUVAIndex.h"
#include "third_party/skia/include/core/SkYUVASizeInfo.h"
+#include "ui/gfx/display_color_spaces.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
@@ -120,7 +122,7 @@ class CC_PAINT_EXPORT PaintImage {
class CC_PAINT_EXPORT FrameKey {
public:
- FrameKey(ContentId content_id, size_t frame_index, gfx::Rect subset_rect);
+ FrameKey(ContentId content_id, size_t frame_index);
bool operator==(const FrameKey& other) const;
bool operator!=(const FrameKey& other) const;
@@ -132,8 +134,6 @@ class CC_PAINT_EXPORT PaintImage {
private:
ContentId content_id_;
size_t frame_index_;
- // TODO(khushalsagar): Remove this when callers take care of subsetting.
- gfx::Rect subset_rect_;
size_t hash_;
};
@@ -215,26 +215,36 @@ class CC_PAINT_EXPORT PaintImage {
// code that assume YUV420 without alpha because it is currently the only
// subsampling supported for direct YUV rendering.
// - The dimensions of YUV planes are tracked in |yuva_size_info|.
- // This struct is initialized by QueryYUVA8 in calls to
+ // This struct is initialized by QueryYUVA in calls to
// PaintImage::IsYuv(), including within this method.
// - The |frame_index| parameter will be passed along to
// ImageDecoder::DecodeToYUV but for multi-frame YUV support, ImageDecoder
// needs a separate YUV frame buffer cache.
// - The mapping of source planes to channels is tracked by |plane_indices|.
- // This struct is initialized by QueryYUVA8 in calls to
+ // This struct is initialized by QueryYUVA in calls to
// PaintImage::IsYuv(), including within this method.
bool DecodeYuv(void* planes[SkYUVASizeInfo::kMaxCount],
size_t frame_index,
GeneratorClientId client_id,
const SkYUVASizeInfo& yuva_size_info,
+ SkColorType yuva_color_type,
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;
+ // texture backed, this API will always do a readback from GPU to CPU memory,
+ // so avoid using it unless actual pixels are needed. For other cases, prefer
+ // using PaintImage APIs directly or use GetSkImageInfo() for metadata about
+ // the SkImage.
+ sk_sp<SkImage> GetSwSkImage() const;
+
+ // Reads this image's pixels into caller-owned |dst_pixels|
+ bool readPixels(const SkImageInfo& dst_info,
+ void* dst_pixels,
+ size_t dst_row_bytes,
+ int src_x,
+ int src_y) const;
+
+ SkImageInfo GetSkImageInfo() const;
Id stable_id() const { return id_; }
const sk_sp<SkImage>& GetSkImage() const;
@@ -255,20 +265,20 @@ class CC_PAINT_EXPORT PaintImage {
return paint_worklet_input_ ? 0 : GetSkImage()->uniqueID();
}
explicit operator bool() const {
- return paint_worklet_input_ || !!GetSkImage();
+ return paint_worklet_input_ || !!GetSkImage() || texture_backing_;
}
bool IsLazyGenerated() const {
- return paint_worklet_input_ ? false : GetSkImage()->isLazyGenerated();
+ return paint_record_ || paint_image_generator_;
}
bool IsPaintWorklet() const { return !!paint_worklet_input_; }
bool IsTextureBacked() const;
int width() const;
int height() const;
SkColorSpace* color_space() const {
- return paint_worklet_input_ ? nullptr : GetSkImage()->colorSpace();
+ return paint_worklet_input_ ? nullptr : GetSkImageInfo().colorSpace();
}
- bool isSRGB() const;
- const gfx::Rect subset_rect() const { return subset_rect_; }
+
+ gfx::ContentColorUsage GetContentColorUsage() const;
// Returns whether this image will be decoded and rendered from YUV data
// and fills out plane size info, plane index info, and the matrix for
@@ -276,7 +286,8 @@ class CC_PAINT_EXPORT PaintImage {
// |plane_indices|, and |yuv_color_space| if any are provided.
bool IsYuv(SkYUVASizeInfo* yuva_size_info = nullptr,
SkYUVAIndex* plane_indices = nullptr,
- SkYUVColorSpace* yuv_color_space = nullptr) const;
+ SkYUVColorSpace* yuv_color_space = nullptr,
+ uint8_t* bit_depth = nullptr) const;
// Get metadata associated with this image.
SkColorType GetColorType() const;
@@ -306,7 +317,7 @@ class CC_PAINT_EXPORT PaintImage {
return paint_worklet_input_;
}
- bool IsOpaque() const { return GetSkImage() && GetSkImage()->isOpaque(); }
+ bool IsOpaque() const { return GetSkImageInfo().isOpaque(); }
std::string ToString() const;
@@ -319,7 +330,9 @@ class CC_PAINT_EXPORT PaintImage {
friend class ScopedRasterFlags;
friend class PaintOpReader;
- bool CanDecodeFromGenerator() const;
+ friend class PlaybackImageProvider;
+ friend class DrawImageRectOp;
+ friend class DrawImageOp;
bool DecodeFromGenerator(void* memory,
SkImageInfo* info,
@@ -332,7 +345,9 @@ class CC_PAINT_EXPORT PaintImage {
size_t frame_index,
GeneratorClientId client_id) const;
void CreateSkImage();
- PaintImage MakeSubset(const gfx::Rect& subset) const;
+
+ // Only supported in non-OOPR contexts by friend callers.
+ sk_sp<SkImage> GetAcceleratedSkImage() const;
sk_sp<SkImage> sk_image_;
sk_sp<PaintRecord> paint_record_;
@@ -348,10 +363,6 @@ class CC_PAINT_EXPORT PaintImage {
CompletionState completion_state_ = CompletionState::DONE;
int repetition_count_ = kAnimationNone;
- // If non-empty, holds the subset of this image relative to the original image
- // at the origin.
- gfx::Rect subset_rect_;
-
// Whether the data fetched for this image is a part of a multpart response.
bool is_multipart_ = false;
diff --git a/chromium/cc/paint/paint_image_builder.h b/chromium/cc/paint/paint_image_builder.h
index 8820b078f5e..008d0fa5dc0 100644
--- a/chromium/cc/paint/paint_image_builder.h
+++ b/chromium/cc/paint/paint_image_builder.h
@@ -5,6 +5,8 @@
#ifndef CC_PAINT_PAINT_IMAGE_BUILDER_H_
#define CC_PAINT_PAINT_IMAGE_BUILDER_H_
+#include <utility>
+
#include "cc/paint/paint_export.h"
#include "cc/paint/paint_image.h"
#include "cc/paint/paint_image_generator.h"
@@ -95,12 +97,6 @@ class CC_PAINT_EXPORT PaintImageBuilder {
return std::move(*this);
}
- // Makes the PaintImage represent a subset of the original image. The
- // subset must be non-empty and lie within the image bounds.
- PaintImageBuilder&& make_subset(const gfx::Rect& subset) {
- paint_image_ = paint_image_.MakeSubset(subset);
- return std::move(*this);
- }
PaintImageBuilder&& set_decoding_mode(
PaintImage::DecodingMode decoding_mode) {
paint_image_.decoding_mode_ = decoding_mode;
diff --git a/chromium/cc/paint/paint_image_generator.h b/chromium/cc/paint/paint_image_generator.h
index 6481c7a5d7b..7e46f2d2511 100644
--- a/chromium/cc/paint/paint_image_generator.h
+++ b/chromium/cc/paint/paint_image_generator.h
@@ -47,21 +47,23 @@ class CC_PAINT_EXPORT PaintImageGenerator : public SkRefCnt {
// Returns true if the generator supports YUV decoding, providing the output
// information in |info| and |color_space|.
- virtual bool QueryYUVA8(SkYUVASizeInfo* info,
- SkYUVAIndex indices[SkYUVAIndex::kIndexCount],
- SkYUVColorSpace* color_space) const = 0;
+ virtual bool QueryYUVA(SkYUVASizeInfo* info,
+ SkYUVAIndex indices[SkYUVAIndex::kIndexCount],
+ SkYUVColorSpace* color_space,
+ uint8_t* bit_depth) const = 0;
// Decodes to YUV into the provided |planes| for each of the Y, U, and V
// planes, and returns true on success. The method should only be used if
- // QueryYUVA8 returns true.
+ // QueryYUVA returns true.
// |info| and |indices| need to exactly match the values returned by the
// query, except the info.fWidthBytes may be larger than the recommendation
// (but not smaller).
//
// TODO(khushalsagar): |lazy_pixel_ref| is only present for
// DecodingImageGenerator tracing needs. Remove it.
- virtual bool GetYUVA8Planes(
+ virtual bool GetYUVAPlanes(
const SkYUVASizeInfo& info,
+ SkColorType color_type,
const SkYUVAIndex indices[SkYUVAIndex::kIndexCount],
void* planes[3],
size_t frame_index,
diff --git a/chromium/cc/paint/paint_image_unittest.cc b/chromium/cc/paint/paint_image_unittest.cc
index 3837d80ef2a..ac82cbd7469 100644
--- a/chromium/cc/paint/paint_image_unittest.cc
+++ b/chromium/cc/paint/paint_image_unittest.cc
@@ -4,6 +4,8 @@
#include "cc/paint/paint_image.h"
+#include <utility>
+
#include "base/test/gtest_util.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/test/fake_paint_image_generator.h"
@@ -13,36 +15,6 @@
namespace cc {
-TEST(PaintImageTest, Subsetting) {
- PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100));
- EXPECT_EQ(image.width(), 100);
- EXPECT_EQ(image.height(), 100);
-
- PaintImage subset_rect_1 = PaintImageBuilder::WithCopy(image)
- .make_subset(gfx::Rect(25, 25, 50, 50))
- .TakePaintImage();
- EXPECT_EQ(subset_rect_1.width(), 50);
- EXPECT_EQ(subset_rect_1.height(), 50);
- EXPECT_EQ(subset_rect_1.subset_rect_, gfx::Rect(25, 25, 50, 50));
-
- PaintImage subset_rect_2 = PaintImageBuilder::WithCopy(subset_rect_1)
- .make_subset(gfx::Rect(25, 25, 25, 25))
- .TakePaintImage();
- EXPECT_EQ(subset_rect_2.width(), 25);
- EXPECT_EQ(subset_rect_2.height(), 25);
- EXPECT_EQ(subset_rect_2.subset_rect_, gfx::Rect(50, 50, 25, 25));
-
- EXPECT_EQ(image, PaintImageBuilder::WithCopy(image)
- .make_subset(gfx::Rect(100, 100))
- .TakePaintImage());
- EXPECT_DCHECK_DEATH(PaintImageBuilder::WithCopy(subset_rect_2)
- .make_subset(gfx::Rect(10, 10, 25, 25))
- .TakePaintImage());
- EXPECT_DCHECK_DEATH(PaintImageBuilder::WithCopy(image)
- .make_subset(gfx::Rect())
- .TakePaintImage());
-}
-
TEST(PaintImageTest, DecodesCorrectFrames) {
std::vector<FrameMetadata> frames = {
FrameMetadata(true, base::TimeDelta::FromMilliseconds(2)),
@@ -55,6 +27,9 @@ TEST(PaintImageTest, DecodesCorrectFrames) {
.set_paint_image_generator(generator)
.TakePaintImage();
+ // When there's no decoded SkImage the color usage defaults to SRGB.
+ EXPECT_EQ(image.GetContentColorUsage(), gfx::ContentColorUsage::kSRGB);
+
// The recorded index is 0u but ask for 1u frame.
SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
std::vector<size_t> memory(info.computeMinByteSize());
@@ -64,17 +39,6 @@ TEST(PaintImageTest, DecodesCorrectFrames) {
EXPECT_EQ(generator->frames_decoded().count(1u), 1u);
generator->reset_frames_decoded();
- // Subsetted.
- PaintImage subset_image = PaintImageBuilder::WithCopy(image)
- .make_subset(gfx::Rect(0, 0, 5, 5))
- .TakePaintImage();
- SkImageInfo subset_info = info.makeWH(5, 5);
- subset_image.Decode(memory.data(), &subset_info, nullptr, 1u,
- PaintImage::kDefaultGeneratorClientId);
- ASSERT_EQ(generator->frames_decoded().size(), 1u);
- EXPECT_EQ(generator->frames_decoded().count(1u), 1u);
- generator->reset_frames_decoded();
-
// Not N32 color type.
info.makeColorType(kRGB_565_SkColorType);
memory = std::vector<size_t>(info.computeMinByteSize());
@@ -99,12 +63,6 @@ TEST(PaintImageTest, SupportedDecodeSize) {
.TakePaintImage();
EXPECT_EQ(image.GetSupportedDecodeSize(supported_sizes[0]),
supported_sizes[0]);
-
- PaintImage subset = PaintImageBuilder::WithCopy(image)
- .make_subset(gfx::Rect(8, 8))
- .TakePaintImage();
- EXPECT_EQ(subset.GetSupportedDecodeSize(supported_sizes[0]),
- SkISize::Make(8, 8));
}
TEST(PaintImageTest, GetSkImageForFrameNotGeneratorBacked) {
@@ -153,7 +111,7 @@ TEST(PaintImageTest, DecodeToYuv420NoAlpha) {
SkYUVAIndex plane_indices[SkYUVAIndex::kIndexCount];
image.DecodeYuv(planes, 1u /* frame_index */,
PaintImage::kDefaultGeneratorClientId, yuva_size_info,
- plane_indices);
+ kGray_8_SkColorType /* color_type */, plane_indices);
ASSERT_EQ(yuv_generator->frames_decoded().size(), 1u);
EXPECT_EQ(yuv_generator->frames_decoded().count(1u), 1u);
yuv_generator->reset_frames_decoded();
@@ -170,6 +128,61 @@ TEST(PaintImageTest, BuildPaintWorkletImage) {
EXPECT_TRUE(paint_image.paint_worklet_input());
EXPECT_EQ(paint_image.width(), size.width());
EXPECT_EQ(paint_image.height(), size.height());
+ EXPECT_EQ(paint_image.GetContentColorUsage(), gfx::ContentColorUsage::kSRGB);
+}
+
+TEST(PaintImageTest, SrgbImage) {
+ auto generator = sk_make_sp<FakePaintImageGenerator>(
+ SkImageInfo::Make(10, 10, kRGBA_F16_SkColorType, kUnknown_SkAlphaType,
+ gfx::ColorSpace::CreateSRGB().ToSkColorSpace()));
+ PaintImage image = PaintImageBuilder::WithDefault()
+ .set_id(PaintImage::GetNextId())
+ .set_paint_image_generator(generator)
+ .set_is_high_bit_depth(true)
+ .TakePaintImage();
+ EXPECT_TRUE(image.is_high_bit_depth());
+ EXPECT_EQ(image.GetContentColorUsage(), gfx::ContentColorUsage::kSRGB);
+}
+
+TEST(PaintImageTest, HbdImage) {
+ auto generator = sk_make_sp<FakePaintImageGenerator>(SkImageInfo::Make(
+ 10, 10, kRGBA_F16_SkColorType, kUnknown_SkAlphaType,
+ gfx::ColorSpace::CreateDisplayP3D65().ToSkColorSpace()));
+ PaintImage image = PaintImageBuilder::WithDefault()
+ .set_id(PaintImage::GetNextId())
+ .set_paint_image_generator(generator)
+ .set_is_high_bit_depth(true)
+ .TakePaintImage();
+ EXPECT_TRUE(image.is_high_bit_depth());
+ EXPECT_EQ(image.GetContentColorUsage(),
+ gfx::ContentColorUsage::kWideColorGamut);
+}
+
+TEST(PaintImageTest, PqHdrImage) {
+ auto generator = sk_make_sp<FakePaintImageGenerator>(
+ SkImageInfo::Make(10, 10, kRGBA_F16_SkColorType, kUnknown_SkAlphaType,
+ gfx::ColorSpace::CreateHDR10().ToSkColorSpace()));
+ PaintImage image = PaintImageBuilder::WithDefault()
+ .set_id(PaintImage::GetNextId())
+ .set_paint_image_generator(generator)
+ .set_is_high_bit_depth(true)
+ .TakePaintImage();
+ EXPECT_TRUE(image.is_high_bit_depth());
+ EXPECT_EQ(image.GetContentColorUsage(), gfx::ContentColorUsage::kHDR);
+}
+
+TEST(PaintImageTest, HlgHdrImage) {
+ auto generator = sk_make_sp<FakePaintImageGenerator>(
+ SkImageInfo::Make(10, 10, kRGBA_F16_SkColorType, kUnknown_SkAlphaType,
+ gfx::ColorSpace::CreateHLG().ToSkColorSpace()));
+ PaintImage image = PaintImageBuilder::WithDefault()
+ .set_id(PaintImage::GetNextId())
+ .set_paint_image_generator(generator)
+ .set_is_high_bit_depth(true)
+ .TakePaintImage();
+
+ EXPECT_TRUE(image.is_high_bit_depth());
+ EXPECT_EQ(image.GetContentColorUsage(), gfx::ContentColorUsage::kHDR);
}
} // namespace cc
diff --git a/chromium/cc/paint/paint_op_buffer.cc b/chromium/cc/paint/paint_op_buffer.cc
index 04963e4d4b4..ff76c597aae 100644
--- a/chromium/cc/paint/paint_op_buffer.cc
+++ b/chromium/cc/paint/paint_op_buffer.cc
@@ -306,14 +306,6 @@ std::ostream& operator<<(std::ostream& os, PaintOpType type) {
return os << PaintOpTypeToString(type);
}
-template <typename T>
-size_t SimpleSerialize(const PaintOp* op, void* memory, size_t size) {
- if (sizeof(T) > size)
- return 0;
- memcpy(memory, op, sizeof(T));
- return sizeof(T);
-}
-
PlaybackParams::PlaybackParams(ImageProvider* image_provider)
: PlaybackParams(image_provider, SkMatrix::I()) {}
@@ -365,12 +357,14 @@ PaintOp::DeserializeOptions::DeserializeOptions(
ServicePaintCache* paint_cache,
SkStrikeClient* strike_client,
std::vector<uint8_t>* scratch_buffer,
- bool is_privileged)
+ bool is_privileged,
+ SharedImageProvider* shared_image_provider)
: transfer_cache(transfer_cache),
paint_cache(paint_cache),
strike_client(strike_client),
scratch_buffer(scratch_buffer),
- is_privileged(is_privileged) {
+ is_privileged(is_privileged),
+ shared_image_provider(shared_image_provider) {
DCHECK(scratch_buffer);
}
@@ -398,39 +392,59 @@ size_t ClipPathOp::Serialize(const PaintOp* base_op,
return helper.size();
}
-size_t ClipRectOp::Serialize(const PaintOp* op,
+size_t ClipRectOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
const SerializeOptions& options) {
- return SimpleSerialize<ClipRectOp>(op, memory, size);
+ auto* op = static_cast<const ClipRectOp*>(base_op);
+ PaintOpWriter helper(memory, size, options);
+ helper.Write(op->rect);
+ helper.Write(op->op);
+ helper.Write(op->antialias);
+ return helper.size();
}
-size_t ClipRRectOp::Serialize(const PaintOp* op,
+size_t ClipRRectOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
const SerializeOptions& options) {
- return SimpleSerialize<ClipRRectOp>(op, memory, size);
+ auto* op = static_cast<const ClipRRectOp*>(base_op);
+ PaintOpWriter helper(memory, size, options);
+ helper.Write(op->rrect);
+ helper.Write(op->op);
+ helper.Write(op->antialias);
+ return helper.size();
}
-size_t ConcatOp::Serialize(const PaintOp* op,
+size_t ConcatOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
const SerializeOptions& options) {
- return SimpleSerialize<ConcatOp>(op, memory, size);
+ auto* op = static_cast<const ConcatOp*>(base_op);
+ PaintOpWriter helper(memory, size, options);
+ helper.Write(op->matrix);
+ return helper.size();
}
-size_t CustomDataOp::Serialize(const PaintOp* op,
+size_t CustomDataOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
const SerializeOptions& options) {
- return SimpleSerialize<CustomDataOp>(op, memory, size);
+ auto* op = static_cast<const CustomDataOp*>(base_op);
+ PaintOpWriter helper(memory, size, options);
+ helper.Write(op->id);
+ return helper.size();
}
-size_t DrawColorOp::Serialize(const PaintOp* op,
+size_t DrawColorOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
const SerializeOptions& options) {
- return SimpleSerialize<DrawColorOp>(op, memory, size);
+ auto* op = static_cast<const DrawColorOp*>(base_op);
+ PaintOpWriter helper(memory, size, options);
+ helper.Write(op->color);
+ helper.Write(op->mode);
+ return helper.size();
}
size_t DrawDRRectOp::Serialize(const PaintOp* base_op,
@@ -636,32 +650,38 @@ size_t DrawTextBlobOp::Serialize(const PaintOp* base_op,
return helper.size();
}
-size_t NoopOp::Serialize(const PaintOp* op,
+size_t NoopOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
const SerializeOptions& options) {
- return SimpleSerialize<NoopOp>(op, memory, size);
+ PaintOpWriter helper(memory, size, options);
+ return helper.size();
}
-size_t RestoreOp::Serialize(const PaintOp* op,
+size_t RestoreOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
const SerializeOptions& options) {
- return SimpleSerialize<RestoreOp>(op, memory, size);
+ PaintOpWriter helper(memory, size, options);
+ return helper.size();
}
-size_t RotateOp::Serialize(const PaintOp* op,
+size_t RotateOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
const SerializeOptions& options) {
- return SimpleSerialize<RotateOp>(op, memory, size);
+ auto* op = static_cast<const RotateOp*>(base_op);
+ PaintOpWriter helper(memory, size, options);
+ helper.Write(op->degrees);
+ return helper.size();
}
-size_t SaveOp::Serialize(const PaintOp* op,
+size_t SaveOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
const SerializeOptions& options) {
- return SimpleSerialize<SaveOp>(op, memory, size);
+ PaintOpWriter helper(memory, size, options);
+ return helper.size();
}
size_t SaveLayerOp::Serialize(const PaintOp* base_op,
@@ -678,44 +698,64 @@ size_t SaveLayerOp::Serialize(const PaintOp* base_op,
return helper.size();
}
-size_t SaveLayerAlphaOp::Serialize(const PaintOp* op,
+size_t SaveLayerAlphaOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
const SerializeOptions& options) {
- return SimpleSerialize<SaveLayerAlphaOp>(op, memory, size);
+ auto* op = static_cast<const SaveLayerAlphaOp*>(base_op);
+ PaintOpWriter helper(memory, size, options);
+ helper.Write(op->bounds);
+ helper.Write(op->alpha);
+ return helper.size();
}
-size_t ScaleOp::Serialize(const PaintOp* op,
+size_t ScaleOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
const SerializeOptions& options) {
- return SimpleSerialize<ScaleOp>(op, memory, size);
+ auto* op = static_cast<const ScaleOp*>(base_op);
+ PaintOpWriter helper(memory, size, options);
+ helper.Write(op->sx);
+ helper.Write(op->sy);
+ return helper.size();
}
-size_t SetMatrixOp::Serialize(const PaintOp* op,
+size_t SetMatrixOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
const SerializeOptions& options) {
- if (options.original_ctm.isIdentity())
- return SimpleSerialize<SetMatrixOp>(op, memory, size);
+ auto* op = static_cast<const SetMatrixOp*>(base_op);
+ PaintOpWriter helper(memory, size, options);
- SetMatrixOp transformed(*static_cast<const SetMatrixOp*>(op));
- transformed.matrix.postConcat(options.original_ctm);
- return SimpleSerialize<SetMatrixOp>(&transformed, memory, size);
+ if (options.original_ctm.isIdentity()) {
+ helper.Write(op->matrix);
+ } else {
+ SkMatrix transformed = op->matrix;
+ transformed.postConcat(options.original_ctm);
+ helper.Write(transformed);
+ }
+ return helper.size();
}
-size_t SetNodeIdOp::Serialize(const PaintOp* op,
+size_t SetNodeIdOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
const SerializeOptions& options) {
- return SimpleSerialize<SetNodeIdOp>(op, memory, size);
+ auto* op = static_cast<const SetNodeIdOp*>(base_op);
+ PaintOpWriter helper(memory, size, options);
+ helper.Write(op->node_id);
+ return helper.size();
}
-size_t TranslateOp::Serialize(const PaintOp* op,
+size_t TranslateOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
const SerializeOptions& options) {
- return SimpleSerialize<TranslateOp>(op, memory, size);
+ auto* op = static_cast<const TranslateOp*>(base_op);
+ PaintOpWriter helper(memory, size, options);
+ helper.Write(op->dx);
+ helper.Write(op->dy);
+ return helper.size();
}
template <typename T>
@@ -724,24 +764,6 @@ void UpdateTypeAndSkip(T* op) {
op->skip = PaintOpBuffer::ComputeOpSkip(sizeof(T));
}
-template <typename T>
-T* SimpleDeserialize(const volatile void* input,
- size_t input_size,
- void* output,
- size_t output_size) {
- if (input_size < sizeof(T))
- return nullptr;
- memcpy(output, const_cast<void*>(input), sizeof(T));
-
- T* op = reinterpret_cast<T*>(output);
- if (!op->IsValid())
- return nullptr;
- // Type and skip were already read once, so could have been changed.
- // Don't trust them and clobber them with something valid.
- UpdateTypeAndSkip(op);
- return op;
-}
-
PaintOp* AnnotateOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
@@ -790,7 +812,19 @@ PaintOp* ClipRectOp::Deserialize(const volatile void* input,
size_t output_size,
const DeserializeOptions& options) {
DCHECK_GE(output_size, sizeof(ClipRectOp));
- return SimpleDeserialize<ClipRectOp>(input, input_size, output, output_size);
+ ClipRectOp* op = new (output) ClipRectOp;
+
+ PaintOpReader helper(input, input_size, options);
+ helper.Read(&op->rect);
+ helper.Read(&op->op);
+ helper.Read(&op->antialias);
+ if (!helper.valid() || !op->IsValid()) {
+ op->~ClipRectOp();
+ return nullptr;
+ }
+
+ UpdateTypeAndSkip(op);
+ return op;
}
PaintOp* ClipRRectOp::Deserialize(const volatile void* input,
@@ -799,7 +833,19 @@ PaintOp* ClipRRectOp::Deserialize(const volatile void* input,
size_t output_size,
const DeserializeOptions& options) {
DCHECK_GE(output_size, sizeof(ClipRRectOp));
- return SimpleDeserialize<ClipRRectOp>(input, input_size, output, output_size);
+ ClipRRectOp* op = new (output) ClipRRectOp;
+
+ PaintOpReader helper(input, input_size, options);
+ helper.Read(&op->rrect);
+ helper.Read(&op->op);
+ helper.Read(&op->antialias);
+ if (!helper.valid() || !op->IsValid()) {
+ op->~ClipRRectOp();
+ return nullptr;
+ }
+
+ UpdateTypeAndSkip(op);
+ return op;
}
PaintOp* ConcatOp::Deserialize(const volatile void* input,
@@ -808,10 +854,17 @@ PaintOp* ConcatOp::Deserialize(const volatile void* input,
size_t output_size,
const DeserializeOptions& options) {
DCHECK_GE(output_size, sizeof(ConcatOp));
- auto* op =
- SimpleDeserialize<ConcatOp>(input, input_size, output, output_size);
- if (op)
- PaintOpReader::FixupMatrixPostSerialization(&op->matrix);
+ ConcatOp* op = new (output) ConcatOp;
+
+ PaintOpReader helper(input, input_size, options);
+ helper.Read(&op->matrix);
+ if (!helper.valid() || !op->IsValid()) {
+ op->~ConcatOp();
+ return nullptr;
+ }
+
+ UpdateTypeAndSkip(op);
+ PaintOpReader::FixupMatrixPostSerialization(&op->matrix);
return op;
}
@@ -821,8 +874,17 @@ PaintOp* CustomDataOp::Deserialize(const volatile void* input,
size_t output_size,
const DeserializeOptions& options) {
DCHECK_GE(output_size, sizeof(CustomDataOp));
- return SimpleDeserialize<CustomDataOp>(input, input_size, output,
- output_size);
+ CustomDataOp* op = new (output) CustomDataOp;
+
+ PaintOpReader helper(input, input_size, options);
+ helper.Read(&op->id);
+ if (!helper.valid() || !op->IsValid()) {
+ op->~CustomDataOp();
+ return nullptr;
+ }
+
+ UpdateTypeAndSkip(op);
+ return op;
}
PaintOp* DrawColorOp::Deserialize(const volatile void* input,
@@ -831,7 +893,18 @@ PaintOp* DrawColorOp::Deserialize(const volatile void* input,
size_t output_size,
const DeserializeOptions& options) {
DCHECK_GE(output_size, sizeof(DrawColorOp));
- return SimpleDeserialize<DrawColorOp>(input, input_size, output, output_size);
+ DrawColorOp* op = new (output) DrawColorOp;
+
+ PaintOpReader helper(input, input_size, options);
+ helper.Read(&op->color);
+ helper.Read(&op->mode);
+ if (!helper.valid() || !op->IsValid()) {
+ op->~DrawColorOp();
+ return nullptr;
+ }
+
+ UpdateTypeAndSkip(op);
+ return op;
}
PaintOp* DrawDRRectOp::Deserialize(const volatile void* input,
@@ -1094,7 +1167,16 @@ PaintOp* NoopOp::Deserialize(const volatile void* input,
size_t output_size,
const DeserializeOptions& options) {
DCHECK_GE(output_size, sizeof(NoopOp));
- return SimpleDeserialize<NoopOp>(input, input_size, output, output_size);
+ NoopOp* op = new (output) NoopOp;
+
+ PaintOpReader helper(input, input_size, options);
+ if (!helper.valid() || !op->IsValid()) {
+ op->~NoopOp();
+ return nullptr;
+ }
+
+ UpdateTypeAndSkip(op);
+ return op;
}
PaintOp* RestoreOp::Deserialize(const volatile void* input,
@@ -1103,7 +1185,16 @@ PaintOp* RestoreOp::Deserialize(const volatile void* input,
size_t output_size,
const DeserializeOptions& options) {
DCHECK_GE(output_size, sizeof(RestoreOp));
- return SimpleDeserialize<RestoreOp>(input, input_size, output, output_size);
+ RestoreOp* op = new (output) RestoreOp;
+
+ PaintOpReader helper(input, input_size, options);
+ if (!helper.valid() || !op->IsValid()) {
+ op->~RestoreOp();
+ return nullptr;
+ }
+
+ UpdateTypeAndSkip(op);
+ return op;
}
PaintOp* RotateOp::Deserialize(const volatile void* input,
@@ -1112,7 +1203,17 @@ PaintOp* RotateOp::Deserialize(const volatile void* input,
size_t output_size,
const DeserializeOptions& options) {
DCHECK_GE(output_size, sizeof(RotateOp));
- return SimpleDeserialize<RotateOp>(input, input_size, output, output_size);
+ RotateOp* op = new (output) RotateOp;
+
+ PaintOpReader helper(input, input_size, options);
+ helper.Read(&op->degrees);
+ if (!helper.valid() || !op->IsValid()) {
+ op->~RotateOp();
+ return nullptr;
+ }
+
+ UpdateTypeAndSkip(op);
+ return op;
}
PaintOp* SaveOp::Deserialize(const volatile void* input,
@@ -1121,7 +1222,16 @@ PaintOp* SaveOp::Deserialize(const volatile void* input,
size_t output_size,
const DeserializeOptions& options) {
DCHECK_GE(output_size, sizeof(SaveOp));
- return SimpleDeserialize<SaveOp>(input, input_size, output, output_size);
+ SaveOp* op = new (output) SaveOp;
+
+ PaintOpReader helper(input, input_size, options);
+ if (!helper.valid() || !op->IsValid()) {
+ op->~SaveOp();
+ return nullptr;
+ }
+
+ UpdateTypeAndSkip(op);
+ return op;
}
PaintOp* SaveLayerOp::Deserialize(const volatile void* input,
@@ -1149,8 +1259,18 @@ PaintOp* SaveLayerAlphaOp::Deserialize(const volatile void* input,
size_t output_size,
const DeserializeOptions& options) {
DCHECK_GE(output_size, sizeof(SaveLayerAlphaOp));
- return SimpleDeserialize<SaveLayerAlphaOp>(input, input_size, output,
- output_size);
+ SaveLayerAlphaOp* op = new (output) SaveLayerAlphaOp;
+
+ PaintOpReader helper(input, input_size, options);
+ helper.Read(&op->bounds);
+ helper.Read(&op->alpha);
+ if (!helper.valid() || !op->IsValid()) {
+ op->~SaveLayerAlphaOp();
+ return nullptr;
+ }
+
+ UpdateTypeAndSkip(op);
+ return op;
}
PaintOp* ScaleOp::Deserialize(const volatile void* input,
@@ -1159,8 +1279,18 @@ PaintOp* ScaleOp::Deserialize(const volatile void* input,
size_t output_size,
const DeserializeOptions& options) {
DCHECK_GE(output_size, sizeof(ScaleOp));
+ ScaleOp* op = new (output) ScaleOp;
- return SimpleDeserialize<ScaleOp>(input, input_size, output, output_size);
+ PaintOpReader helper(input, input_size, options);
+ helper.Read(&op->sx);
+ helper.Read(&op->sy);
+ if (!helper.valid() || !op->IsValid()) {
+ op->~ScaleOp();
+ return nullptr;
+ }
+
+ UpdateTypeAndSkip(op);
+ return op;
}
PaintOp* SetMatrixOp::Deserialize(const volatile void* input,
@@ -1169,10 +1299,17 @@ PaintOp* SetMatrixOp::Deserialize(const volatile void* input,
size_t output_size,
const DeserializeOptions& options) {
DCHECK_GE(output_size, sizeof(SetMatrixOp));
- auto* op =
- SimpleDeserialize<SetMatrixOp>(input, input_size, output, output_size);
- if (op)
- PaintOpReader::FixupMatrixPostSerialization(&op->matrix);
+ SetMatrixOp* op = new (output) SetMatrixOp;
+
+ PaintOpReader helper(input, input_size, options);
+ helper.Read(&op->matrix);
+ if (!helper.valid() || !op->IsValid()) {
+ op->~SetMatrixOp();
+ return nullptr;
+ }
+
+ UpdateTypeAndSkip(op);
+ PaintOpReader::FixupMatrixPostSerialization(&op->matrix);
return op;
}
@@ -1182,7 +1319,17 @@ PaintOp* SetNodeIdOp::Deserialize(const volatile void* input,
size_t output_size,
const DeserializeOptions& options) {
DCHECK_GE(output_size, sizeof(SetNodeIdOp));
- return SimpleDeserialize<SetNodeIdOp>(input, input_size, output, output_size);
+ SetNodeIdOp* op = new (output) SetNodeIdOp;
+
+ PaintOpReader helper(input, input_size, options);
+ helper.Read(&op->node_id);
+ if (!helper.valid() || !op->IsValid()) {
+ op->~SetNodeIdOp();
+ return nullptr;
+ }
+
+ UpdateTypeAndSkip(op);
+ return op;
}
PaintOp* TranslateOp::Deserialize(const volatile void* input,
@@ -1191,7 +1338,18 @@ PaintOp* TranslateOp::Deserialize(const volatile void* input,
size_t output_size,
const DeserializeOptions& options) {
DCHECK_GE(output_size, sizeof(TranslateOp));
- return SimpleDeserialize<TranslateOp>(input, input_size, output, output_size);
+ TranslateOp* op = new (output) TranslateOp;
+
+ PaintOpReader helper(input, input_size, options);
+ helper.Read(&op->dx);
+ helper.Read(&op->dy);
+ if (!helper.valid() || !op->IsValid()) {
+ op->~TranslateOp();
+ return nullptr;
+ }
+
+ UpdateTypeAndSkip(op);
+ return op;
}
void AnnotateOp::Raster(const AnnotateOp* op,
@@ -1272,8 +1430,10 @@ void DrawImageOp::RasterWithFlags(const DrawImageOp* op,
canvas->scale(1.f / op->scale_adjustment.width(),
1.f / op->scale_adjustment.height());
}
- canvas->drawImage(op->image.GetRasterSkImage().get(), op->left, op->top,
- &paint);
+ auto sk_image = op->image.IsTextureBacked()
+ ? op->image.GetAcceleratedSkImage()
+ : op->image.GetSwSkImage();
+ canvas->drawImage(sk_image.get(), op->left, op->top, &paint);
return;
}
@@ -1343,8 +1503,11 @@ void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op,
if (!params.image_provider) {
SkRect adjusted_src = AdjustSrcRectForScale(op->src, op->scale_adjustment);
flags->DrawToSk(canvas, [op, adjusted_src](SkCanvas* c, const SkPaint& p) {
- c->drawImageRect(op->image.GetRasterSkImage().get(), adjusted_src,
- op->dst, &p, op->constraint);
+ auto sk_image = op->image.IsTextureBacked()
+ ? op->image.GetAcceleratedSkImage()
+ : op->image.GetSwSkImage();
+ c->drawImageRect(sk_image.get(), adjusted_src, op->dst, &p,
+ op->constraint);
});
return;
}
diff --git a/chromium/cc/paint/paint_op_buffer.h b/chromium/cc/paint/paint_op_buffer.h
index 24459b52dd3..57b1d8e4440 100644
--- a/chromium/cc/paint/paint_op_buffer.h
+++ b/chromium/cc/paint/paint_op_buffer.h
@@ -49,6 +49,7 @@ class CC_PAINT_EXPORT ThreadsafeMatrix : public SkMatrix {
explicit ThreadsafeMatrix(const SkMatrix& matrix) : SkMatrix(matrix) {
(void)getType();
}
+ ThreadsafeMatrix() { (void)getType(); }
};
class CC_PAINT_EXPORT ThreadsafePath : public SkPath {
@@ -59,6 +60,13 @@ class CC_PAINT_EXPORT ThreadsafePath : public SkPath {
ThreadsafePath() { updateBoundsCache(); }
};
+class CC_PAINT_EXPORT SharedImageProvider {
+ public:
+ virtual ~SharedImageProvider() = default;
+ virtual sk_sp<SkImage> OpenSharedImageForRead(
+ const gpu::Mailbox& mailbox) = 0;
+};
+
// See PaintOp::Serialize/Deserialize for comments. Derived Serialize types
// don't write the 4 byte type/skip header because they don't know how much
// data they will need to write. PaintOp::Serialize itself must update it.
@@ -179,6 +187,13 @@ class CC_PAINT_EXPORT PaintOp {
// The flags to use when serializing this op. This can be used to override
// the flags serialized with the op. Valid only for PaintOpWithFlags.
const PaintFlags* flags_to_serialize = nullptr;
+
+ // TODO(crbug.com/1096123): Cleanup after study completion.
+ //
+ // If true, perform serializaion in a way that avoids serializing transient
+ // members, such as IDs, so that a stable digest can be calculated. This
+ // means that serialized output can't be deserialized correctly.
+ bool for_identifiability_study = false;
};
struct CC_PAINT_EXPORT DeserializeOptions {
@@ -186,7 +201,8 @@ class CC_PAINT_EXPORT PaintOp {
ServicePaintCache* paint_cache,
SkStrikeClient* strike_client,
std::vector<uint8_t>* scratch_buffer,
- bool is_privileged);
+ bool is_privileged,
+ SharedImageProvider* shared_image_provider);
TransferCacheDeserializeHelper* transfer_cache = nullptr;
ServicePaintCache* paint_cache = nullptr;
SkStrikeClient* strike_client = nullptr;
@@ -197,6 +213,7 @@ class CC_PAINT_EXPORT PaintOp {
// True if the deserialization is happening on a privileged gpu channel.
// e.g. in the case of UI.
bool is_privileged = false;
+ SharedImageProvider* shared_image_provider = nullptr;
};
// Indicates how PaintImages are serialized.
@@ -204,7 +221,8 @@ class CC_PAINT_EXPORT PaintOp {
kNoImage,
kImageData,
kTransferCacheEntry,
- kLastType = kTransferCacheEntry
+ kMailbox,
+ kLastType = kMailbox
};
// Subclasses should provide a static Serialize() method called from here.
@@ -408,6 +426,9 @@ class CC_PAINT_EXPORT ClipRectOp final : public PaintOp {
SkRect rect;
SkClipOp op;
bool antialias;
+
+ private:
+ ClipRectOp() : PaintOp(kType) {}
};
class CC_PAINT_EXPORT ClipRRectOp final : public PaintOp {
@@ -426,6 +447,9 @@ class CC_PAINT_EXPORT ClipRRectOp final : public PaintOp {
SkRRect rrect;
SkClipOp op;
bool antialias;
+
+ private:
+ ClipRRectOp() : PaintOp(kType) {}
};
class CC_PAINT_EXPORT ConcatOp final : public PaintOp {
@@ -440,6 +464,9 @@ class CC_PAINT_EXPORT ConcatOp final : public PaintOp {
HAS_SERIALIZATION_FUNCTIONS();
ThreadsafeMatrix matrix;
+
+ private:
+ ConcatOp() : PaintOp(kType) {}
};
class CC_PAINT_EXPORT CustomDataOp final : public PaintOp {
@@ -455,6 +482,9 @@ class CC_PAINT_EXPORT CustomDataOp final : public PaintOp {
// Stores user defined id as a placeholder op.
uint32_t id;
+
+ private:
+ CustomDataOp() : PaintOp(kType) {}
};
class CC_PAINT_EXPORT DrawColorOp final : public PaintOp {
@@ -472,6 +502,9 @@ class CC_PAINT_EXPORT DrawColorOp final : public PaintOp {
SkColor color;
SkBlendMode mode;
+
+ private:
+ DrawColorOp() : PaintOp(kType) {}
};
class CC_PAINT_EXPORT DrawDRRectOp final : public PaintOpWithFlags {
@@ -819,6 +852,9 @@ class CC_PAINT_EXPORT RotateOp final : public PaintOp {
HAS_SERIALIZATION_FUNCTIONS();
SkScalar degrees;
+
+ private:
+ RotateOp() : PaintOp(kType) {}
};
class CC_PAINT_EXPORT SaveOp final : public PaintOp {
@@ -873,6 +909,9 @@ class CC_PAINT_EXPORT SaveLayerAlphaOp final : public PaintOp {
SkRect bounds;
uint8_t alpha;
+
+ private:
+ SaveLayerAlphaOp() : PaintOp(kType) {}
};
class CC_PAINT_EXPORT ScaleOp final : public PaintOp {
@@ -912,6 +951,9 @@ class CC_PAINT_EXPORT SetMatrixOp final : public PaintOp {
HAS_SERIALIZATION_FUNCTIONS();
ThreadsafeMatrix matrix;
+
+ private:
+ SetMatrixOp() : PaintOp(kType) {}
};
class CC_PAINT_EXPORT SetNodeIdOp final : public PaintOp {
@@ -926,6 +968,9 @@ class CC_PAINT_EXPORT SetNodeIdOp final : public PaintOp {
HAS_SERIALIZATION_FUNCTIONS();
int node_id;
+
+ private:
+ SetNodeIdOp() : PaintOp(kType) {}
};
class CC_PAINT_EXPORT TranslateOp final : public PaintOp {
@@ -941,6 +986,9 @@ class CC_PAINT_EXPORT TranslateOp final : public PaintOp {
SkScalar dx;
SkScalar dy;
+
+ private:
+ TranslateOp() : PaintOp(kType) {}
};
#undef HAS_SERIALIZATION_FUNCTIONS
diff --git a/chromium/cc/paint/paint_op_buffer_fuzzer.cc b/chromium/cc/paint/paint_op_buffer_fuzzer.cc
index 3f28f0e8d29..b712bb4b515 100644
--- a/chromium/cc/paint/paint_op_buffer_fuzzer.cc
+++ b/chromium/cc/paint/paint_op_buffer_fuzzer.cc
@@ -84,7 +84,7 @@ void Raster(scoped_refptr<viz::TestContextProvider> context_provider,
std::vector<uint8_t> scratch_buffer;
cc::PaintOp::DeserializeOptions deserialize_options(
&transfer_cache_helper, paint_cache, strike_client, &scratch_buffer,
- true /* is_privileged */);
+ true /* is_privileged */, nullptr /* shared_image_provider */);
// Need 4 bytes to be able to read the type/skip.
while (size >= 4) {
@@ -132,7 +132,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FontSupport font_support;
scoped_refptr<gpu::ServiceFontManager> font_manager(
- new gpu::ServiceFontManager(&font_support));
+ new gpu::ServiceFontManager(&font_support,
+ false /* disable_oopr_debug_crash_dump */));
cc::ServicePaintCache paint_cache;
std::vector<SkDiscardableHandleId> locked_handles;
if (bytes_for_fonts > 0u) {
diff --git a/chromium/cc/paint/paint_op_buffer_serializer.cc b/chromium/cc/paint/paint_op_buffer_serializer.cc
index 3f59290175c..d17aa684b22 100644
--- a/chromium/cc/paint/paint_op_buffer_serializer.cc
+++ b/chromium/cc/paint/paint_op_buffer_serializer.cc
@@ -11,6 +11,7 @@
#include "base/bind.h"
#include "base/trace_event/trace_event.h"
+#include "cc/paint/clear_for_opaque_raster.h"
#include "cc/paint/scoped_raster_flags.h"
#include "ui/gfx/skia_util.h"
@@ -148,63 +149,26 @@ void PaintOpBufferSerializer::ClearForOpaqueRaster(
const Preamble& preamble,
const PaintOp::SerializeOptions& options,
const PlaybackParams& params) {
- // 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
- // are not painted. Because this raster source is required to be opaque,
- // we may need to do extra clearing outside of the clip. This needs to
- // be done for both full and partial raster.
-
- // The last texel of this content is not guaranteed to be fully opaque, so
- // inset by one to generate the fully opaque coverage rect. This rect is
- // in device space.
- SkIRect coverage_device_rect = SkIRect::MakeWH(
- preamble.content_size.width() - preamble.full_raster_rect.x() - 1,
- preamble.content_size.height() - preamble.full_raster_rect.y() - 1);
-
- // If not fully covered, we need to clear one texel inside the coverage
- // rect (because of blending during raster) and one texel outside the canvas
- // bitmap rect (because of bilinear filtering during draw). See comments
- // in RasterSource.
- SkIRect device_column = SkIRect::MakeXYWH(coverage_device_rect.right(), 0, 2,
- coverage_device_rect.bottom());
- // row includes the corner, column excludes it.
- SkIRect device_row = SkIRect::MakeXYWH(0, coverage_device_rect.bottom(),
- coverage_device_rect.right() + 2, 2);
-
- bool right_edge =
- preamble.content_size.width() == preamble.playback_rect.right();
- bool bottom_edge =
- preamble.content_size.height() == preamble.playback_rect.bottom();
-
- // If the playback rect is touching either edge of the content rect
- // extend it by one pixel to include the extra texel outside the canvas
- // bitmap rect that was added to device column and row above.
- SkIRect playback_device_rect = SkIRect::MakeXYWH(
- preamble.playback_rect.x() - preamble.full_raster_rect.x(),
- preamble.playback_rect.y() - preamble.full_raster_rect.y(),
- preamble.playback_rect.width() + (right_edge ? 1 : 0),
- preamble.playback_rect.height() + (bottom_edge ? 1 : 0));
-
- // Intersect the device column and row with the playback rect and only
- // clear inside of that rect if needed.
- if (device_column.intersect(playback_device_rect)) {
- Save(options, params);
- ClipRectOp clip_op(SkRect::Make(device_column), SkClipOp::kIntersect,
- false);
- SerializeOp(&clip_op, options, params);
- DrawColorOp clear_op(preamble.background_color, SkBlendMode::kSrc);
- SerializeOp(&clear_op, options, params);
- RestoreToCount(1, options, params);
- }
- if (device_row.intersect(playback_device_rect)) {
- Save(options, params);
- ClipRectOp clip_op(SkRect::Make(device_row), SkClipOp::kIntersect, false);
- SerializeOp(&clip_op, options, params);
- DrawColorOp clear_op(preamble.background_color, SkBlendMode::kSrc);
- SerializeOp(&clear_op, options, params);
- RestoreToCount(1, options, params);
+ gfx::Rect outer_rect;
+ gfx::Rect inner_rect;
+ if (!CalculateClearForOpaqueRasterRects(
+ preamble.post_translation, preamble.post_scale, preamble.content_size,
+ preamble.full_raster_rect, preamble.playback_rect, outer_rect,
+ inner_rect))
+ return;
+
+ Save(options, params);
+ ClipRectOp outer_clip_op(gfx::RectToSkRect(outer_rect), SkClipOp::kIntersect,
+ false);
+ SerializeOp(&outer_clip_op, options, params);
+ if (!inner_rect.IsEmpty()) {
+ ClipRectOp inner_clip_op(gfx::RectToSkRect(inner_rect),
+ SkClipOp::kDifference, false);
+ SerializeOp(&inner_clip_op, options, params);
}
+ DrawColorOp clear_op(preamble.background_color, SkBlendMode::kSrc);
+ SerializeOp(&clear_op, options, params);
+ RestoreToCount(1, options, params);
}
void PaintOpBufferSerializer::SerializePreamble(
@@ -218,7 +182,7 @@ void PaintOpBufferSerializer::SerializePreamble(
// 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 && preamble.post_translation.IsZero()) {
+ if (!preamble.requires_clear) {
ClearForOpaqueRaster(preamble, options, params);
} else if (!is_partial_raster) {
// If rastering the entire tile, clear to transparent pre-clip. This is so
diff --git a/chromium/cc/paint/paint_op_buffer_unittest.cc b/chromium/cc/paint/paint_op_buffer_unittest.cc
index fe73fe73b6b..975fe89f528 100644
--- a/chromium/cc/paint/paint_op_buffer_unittest.cc
+++ b/chromium/cc/paint/paint_op_buffer_unittest.cc
@@ -8,6 +8,7 @@
#include "base/memory/scoped_refptr.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
#include "cc/paint/decoded_draw_image.h"
#include "cc/paint/display_item_list.h"
#include "cc/paint/image_provider.h"
@@ -45,7 +46,7 @@ namespace {
// unit test suite as generally deserialized ops are smaller.
static constexpr size_t kBufferBytesPerOp = 1000 + sizeof(LargestPaintOp);
-#ifndef OS_ANDROID
+#if !defined(OS_ANDROID)
// A skottie animation with solid green color for the first 2.5 seconds and then
// a solid blue color for the next 2.5 seconds.
constexpr char kSkottieData[] =
@@ -77,7 +78,7 @@ constexpr char kSkottieData[] =
" }"
" ]"
"}";
-#endif // OS_ANDROID
+#endif // !defined(OS_ANDROID)
template <typename T>
void ValidateOps(PaintOpBuffer* buffer) {
@@ -3373,7 +3374,7 @@ TEST(PaintOpBufferTest, PaintRecordShaderSerialization) {
EXPECT_TRUE(!!rect_op->flags.getShader()->GetSkShader());
}
-#ifndef OS_ANDROID
+#if !defined(OS_ANDROID)
TEST(PaintOpBufferTest, DrawSkottieOpSerialization) {
std::unique_ptr<char, base::AlignedFreeDeleter> memory(
static_cast<char*>(base::AlignedAlloc(PaintOpBuffer::kInitialBufferSize,
@@ -3470,7 +3471,7 @@ TEST(PaintOpBufferTest, DrawSkottieOpSerializationFailure) {
memory.get(), serializer.written(), d_options);
ASSERT_FALSE(deserialized_buffer);
}
-#endif // OS_ANDROID
+#endif // !defined(OS_ANDROID
TEST(PaintOpBufferTest, CustomData) {
// Basic tests: size, move, comparison.
@@ -3679,7 +3680,7 @@ TEST(PaintOpBufferTest, RecordShadersCached) {
std::vector<uint8_t> scratch_buffer;
PaintOp::DeserializeOptions deserialize_options(
transfer_cache, options_provider.service_paint_cache(),
- options_provider.strike_client(), &scratch_buffer, true);
+ options_provider.strike_client(), &scratch_buffer, true, nullptr);
// Several deserialization test cases:
// (0) deserialize once, verify cached is the same as deserialized version
@@ -3767,7 +3768,7 @@ TEST(PaintOpBufferTest, RecordShadersCachedSize) {
std::vector<uint8_t> scratch_buffer;
PaintOp::DeserializeOptions deserialize_options(
transfer_cache, options_provider.service_paint_cache(),
- options_provider.strike_client(), &scratch_buffer, true);
+ options_provider.strike_client(), &scratch_buffer, true, nullptr);
auto record = PaintOpBuffer::MakeFromMemory(
memory.get(), serializer.written(), deserialize_options);
auto* shader_entry =
diff --git a/chromium/cc/paint/paint_op_reader.cc b/chromium/cc/paint/paint_op_reader.cc
index 20a06327cb2..df1e7e6818d 100644
--- a/chromium/cc/paint/paint_op_reader.cc
+++ b/chromium/cc/paint/paint_op_reader.cc
@@ -5,7 +5,10 @@
#include "cc/paint/paint_op_reader.h"
#include <stddef.h>
+
#include <algorithm>
+#include <memory>
+#include <utility>
#include "base/bits.h"
#include "base/compiler_specific.h"
@@ -26,7 +29,7 @@
#include "third_party/skia/include/core/SkTextBlob.h"
#include "third_party/skia/src/core/SkRemoteGlyphCache.h"
-#ifndef OS_ANDROID
+#if !defined(OS_ANDROID)
#include "cc/paint/skottie_transfer_cache_entry.h"
#endif
@@ -332,6 +335,7 @@ void PaintOpReader::Read(PaintImage* image) {
}
return;
case PaintOp::SerializedImageType::kTransferCacheEntry:
+ case PaintOp::SerializedImageType::kMailbox:
SetInvalid();
return;
}
@@ -340,6 +344,34 @@ void PaintOpReader::Read(PaintImage* image) {
return;
}
+ if (serialized_type == PaintOp::SerializedImageType::kMailbox) {
+ if (!options_.shared_image_provider) {
+ SetInvalid();
+ return;
+ }
+
+ gpu::Mailbox mailbox;
+ Read(&mailbox);
+ if (mailbox.IsZero()) {
+ SetInvalid();
+ return;
+ }
+
+ sk_sp<SkImage> sk_image =
+ options_.shared_image_provider->OpenSharedImageForRead(mailbox);
+ if (!sk_image) {
+ SetInvalid();
+ return;
+ }
+
+ *image = PaintImageBuilder::WithDefault()
+ .set_id(PaintImage::GetNextId())
+ .set_texture_image(std::move(sk_image),
+ PaintImage::kNonLazyStableId)
+ .TakePaintImage();
+ return;
+ }
+
if (serialized_type != PaintOp::SerializedImageType::kTransferCacheEntry) {
SetInvalid();
return;
@@ -632,9 +664,13 @@ void PaintOpReader::Read(SkYUVColorSpace* yuv_color_space) {
*yuv_color_space = static_cast<SkYUVColorSpace>(raw_yuv_color_space);
}
+void PaintOpReader::Read(gpu::Mailbox* mailbox) {
+ ReadData(sizeof(gpu::Mailbox::Name), (*mailbox).name);
+}
+
// Android does not use skottie. Remove below section to keep binary size to a
// minimum.
-#ifndef OS_ANDROID
+#if !defined(OS_ANDROID)
void PaintOpReader::Read(scoped_refptr<SkottieWrapper>* skottie) {
if (!options_.is_privileged) {
valid_ = false;
@@ -665,19 +701,10 @@ void PaintOpReader::Read(scoped_refptr<SkottieWrapper>* skottie) {
memory_ += bytes_to_skip;
remaining_bytes_ -= bytes_to_skip;
}
-#endif // OS_ANDROID
+#endif // !defined(OS_ANDROID)
void PaintOpReader::AlignMemory(size_t alignment) {
- // Due to the math below, alignment must be a power of two.
- DCHECK_GT(alignment, 0u);
- DCHECK_EQ(alignment & (alignment - 1), 0u);
-
- uintptr_t memory = reinterpret_cast<uintptr_t>(memory_);
- // The following is equivalent to:
- // padding = (alignment - memory % alignment) % alignment;
- // because alignment is a power of two. This doesn't use modulo operator
- // however, since it can be slow.
- size_t padding = ((memory + alignment - 1) & ~(alignment - 1)) - memory;
+ size_t padding = base::bits::Align(memory_, alignment) - memory_;
if (padding > remaining_bytes_)
SetInvalid();
diff --git a/chromium/cc/paint/paint_op_reader.h b/chromium/cc/paint/paint_op_reader.h
index 03d9a241c0f..c9ab0842ccc 100644
--- a/chromium/cc/paint/paint_op_reader.h
+++ b/chromium/cc/paint/paint_op_reader.h
@@ -8,11 +8,16 @@
#include <vector>
#include "base/memory/scoped_refptr.h"
+#include "build/build_config.h"
#include "cc/paint/paint_export.h"
#include "cc/paint/paint_filter.h"
#include "cc/paint/paint_op_writer.h"
#include "cc/paint/transfer_cache_deserialize_helper.h"
+namespace gpu {
+struct Mailbox;
+}
+
namespace cc {
class PaintShader;
@@ -70,8 +75,9 @@ class CC_PAINT_EXPORT PaintOpReader {
void Read(SkImageInfo* info);
void Read(sk_sp<SkColorSpace>* color_space);
void Read(SkYUVColorSpace* yuv_color_space);
+ void Read(gpu::Mailbox* mailbox);
-#ifndef OS_ANDROID
+#if !defined(OS_ANDROID)
void Read(scoped_refptr<SkottieWrapper>* skottie);
#endif
@@ -99,6 +105,15 @@ class CC_PAINT_EXPORT PaintOpReader {
}
*quality = static_cast<SkFilterQuality>(value);
}
+ void Read(SkBlendMode* blend_mode) {
+ uint8_t value = 0u;
+ Read(&value);
+ if (value > static_cast<uint8_t>(SkBlendMode::kLastMode)) {
+ SetInvalid();
+ return;
+ }
+ *blend_mode = static_cast<SkBlendMode>(value);
+ }
void Read(bool* data) {
uint8_t value = 0u;
Read(&value);
diff --git a/chromium/cc/paint/paint_op_writer.cc b/chromium/cc/paint/paint_op_writer.cc
index bb59e6feaff..ccd4abae467 100644
--- a/chromium/cc/paint/paint_op_writer.cc
+++ b/chromium/cc/paint/paint_op_writer.cc
@@ -4,6 +4,8 @@
#include "cc/paint/paint_op_writer.h"
+#include <memory>
+
#include "base/bits.h"
#include "cc/paint/draw_image.h"
#include "cc/paint/image_provider.h"
@@ -13,23 +15,20 @@
#include "cc/paint/paint_op_buffer_serializer.h"
#include "cc/paint/paint_shader.h"
#include "cc/paint/transfer_cache_serialize_helper.h"
+#include "gpu/command_buffer/common/mailbox.h"
#include "third_party/skia/include/core/SkSerialProcs.h"
#include "third_party/skia/include/core/SkTextBlob.h"
#include "third_party/skia/src/core/SkRemoteGlyphCache.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/skia_util.h"
-#ifndef OS_ANDROID
+#if !defined(OS_ANDROID)
#include "cc/paint/skottie_transfer_cache_entry.h"
#endif
namespace cc {
namespace {
-const size_t kSkiaAlignment = 4u;
-
-size_t RoundDownToAlignment(size_t bytes, size_t alignment) {
- return base::bits::AlignDown(bytes, alignment);
-}
+constexpr size_t kSkiaAlignment = 4u;
SkIRect MakeSrcRect(const PaintImage& image) {
if (!image)
@@ -124,7 +123,7 @@ void PaintOpWriter::WriteFlattenable(const SkFlattenable* val) {
return;
size_t bytes_written = val->serialize(
- memory_, RoundDownToAlignment(remaining_bytes_, kSkiaAlignment));
+ memory_, base::bits::AlignDown(remaining_bytes_, kSkiaAlignment));
if (bytes_written == 0u) {
valid_ = false;
return;
@@ -175,7 +174,8 @@ void PaintOpWriter::Write(const SkRRect& rect) {
void PaintOpWriter::Write(const SkPath& path) {
auto id = path.getGenerationID();
- Write(id);
+ if (!options_.for_identifiability_study)
+ Write(id);
if (options_.paint_cache->Get(PaintCacheDataType::kPath, id)) {
Write(static_cast<uint32_t>(PaintCacheEntryState::kCached));
@@ -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().GetRasterSkImage()->asLegacyBitmap(&bm)) {
+ if (!draw_image.paint_image().GetSwSkImage()->asLegacyBitmap(&bm)) {
Write(static_cast<uint8_t>(PaintOp::SerializedImageType::kNoImage));
return;
}
@@ -267,16 +267,14 @@ void PaintOpWriter::Write(const DrawImage& draw_image,
DCHECK(decoded_draw_image.src_rect_offset().isEmpty())
<< "We shouldn't ask for image subsets";
- base::Optional<uint32_t> id = decoded_draw_image.transfer_cache_entry_id();
*scale_adjustment = decoded_draw_image.scale_adjustment();
- // In the case of a decode failure, id may not be set. Send an invalid ID.
- WriteImage(id.value_or(kInvalidImageTransferCacheEntryId),
- decoded_draw_image.transfer_cache_entry_needs_mips());
+
+ WriteImage(decoded_draw_image);
}
// Android does not use skottie. Remove below section to keep binary size to a
// minimum.
-#ifndef OS_ANDROID
+#if !defined(OS_ANDROID)
void PaintOpWriter::Write(scoped_refptr<SkottieWrapper> skottie) {
uint32_t id = skottie->id();
Write(id);
@@ -301,7 +299,19 @@ void PaintOpWriter::Write(scoped_refptr<SkottieWrapper> skottie) {
memory_ += bytes_written;
remaining_bytes_ -= bytes_written;
}
-#endif // OS_ANDROID
+#endif // !defined(OS_ANDROID)
+
+void PaintOpWriter::WriteImage(const DecodedDrawImage& decoded_draw_image) {
+ if (!decoded_draw_image.mailbox().IsZero()) {
+ WriteImage(decoded_draw_image.mailbox());
+ return;
+ }
+
+ base::Optional<uint32_t> id = decoded_draw_image.transfer_cache_entry_id();
+ // In the case of a decode failure, id may not be set. Send an invalid ID.
+ WriteImage(id.value_or(kInvalidImageTransferCacheEntryId),
+ decoded_draw_image.transfer_cache_entry_needs_mips());
+}
void PaintOpWriter::WriteImage(uint32_t transfer_cache_entry_id,
bool needs_mips) {
@@ -316,6 +326,20 @@ void PaintOpWriter::WriteImage(uint32_t transfer_cache_entry_id,
Write(needs_mips);
}
+void PaintOpWriter::WriteImage(const gpu::Mailbox& mailbox) {
+ DCHECK(!mailbox.IsZero());
+
+ Write(static_cast<uint8_t>(PaintOp::SerializedImageType::kMailbox));
+
+ EnsureBytes(sizeof(mailbox.name));
+ if (!valid_)
+ return;
+
+ memcpy(memory_, mailbox.name, sizeof(mailbox.name));
+ memory_ += sizeof(mailbox.name);
+ remaining_bytes_ -= sizeof(mailbox.name);
+}
+
void PaintOpWriter::Write(const sk_sp<SkData>& data) {
if (data.get() && data->size()) {
WriteSize(data->size());
@@ -371,7 +395,7 @@ void PaintOpWriter::Write(const sk_sp<SkTextBlob>& blob) {
procs.fTypefaceCtx = options_.strike_server;
size_t bytes_written = blob->serialize(
- procs, memory_, RoundDownToAlignment(remaining_bytes_, kSkiaAlignment));
+ procs, memory_, base::bits::AlignDown(remaining_bytes_, kSkiaAlignment));
if (bytes_written == 0u) {
valid_ = false;
return;
@@ -389,7 +413,8 @@ sk_sp<PaintShader> PaintOpWriter::TransformShaderIfNecessary(
SkFilterQuality quality,
uint32_t* paint_image_transfer_cache_entry_id,
gfx::SizeF* paint_record_post_scale,
- bool* paint_image_needs_mips) {
+ bool* paint_image_needs_mips,
+ gpu::Mailbox* mailbox_out) {
DCHECK(!enable_security_constraints_);
const auto type = original->shader_type();
@@ -399,7 +424,8 @@ sk_sp<PaintShader> PaintOpWriter::TransformShaderIfNecessary(
if (!original->paint_image().IsPaintWorklet()) {
return original->CreateDecodedImage(ctm, quality, options_.image_provider,
paint_image_transfer_cache_entry_id,
- &quality, paint_image_needs_mips);
+ &quality, paint_image_needs_mips,
+ mailbox_out);
}
sk_sp<PaintShader> record_shader =
original->CreatePaintWorkletRecord(options_.image_provider);
@@ -428,11 +454,12 @@ void PaintOpWriter::Write(const PaintShader* shader, SkFilterQuality quality) {
uint32_t paint_image_transfer_cache_id = kInvalidImageTransferCacheEntryId;
gfx::SizeF paint_record_post_scale(1.f, 1.f);
bool paint_image_needs_mips = false;
+ gpu::Mailbox mailbox;
if (!enable_security_constraints_ && shader) {
transformed_shader = TransformShaderIfNecessary(
shader, quality, &paint_image_transfer_cache_id,
- &paint_record_post_scale, &paint_image_needs_mips);
+ &paint_record_post_scale, &paint_image_needs_mips, &mailbox);
shader = transformed_shader.get();
}
@@ -476,13 +503,17 @@ void PaintOpWriter::Write(const PaintShader* shader, SkFilterQuality quality) {
DCHECK_EQ(scale_adjustment.width(), 1.f);
DCHECK_EQ(scale_adjustment.height(), 1.f);
} else {
- WriteImage(paint_image_transfer_cache_id, paint_image_needs_mips);
+ if (!mailbox.IsZero())
+ WriteImage(mailbox);
+ else
+ WriteImage(paint_image_transfer_cache_id, paint_image_needs_mips);
}
if (shader->record_) {
Write(true);
DCHECK_NE(shader->id_, PaintShader::kInvalidRecordShaderId);
- Write(shader->id_);
+ if (!options_.for_identifiability_study)
+ Write(shader->id_);
const gfx::Rect playback_rect(
gfx::ToEnclosingRect(gfx::SkRectToRectF(shader->tile())));
diff --git a/chromium/cc/paint/paint_op_writer.h b/chromium/cc/paint/paint_op_writer.h
index cddf8752d2c..d00eeea1d36 100644
--- a/chromium/cc/paint/paint_op_writer.h
+++ b/chromium/cc/paint/paint_op_writer.h
@@ -7,6 +7,7 @@
#include <unordered_set>
+#include "build/build_config.h"
#include "cc/paint/paint_canvas.h"
#include "cc/paint/paint_export.h"
#include "cc/paint/paint_filter.h"
@@ -17,6 +18,10 @@ struct SkRect;
struct SkIRect;
class SkRRect;
+namespace gpu {
+struct Mailbox;
+}
+
namespace cc {
class DrawImage;
@@ -64,6 +69,7 @@ class CC_PAINT_EXPORT PaintOpWriter {
void Write(const sk_sp<SkTextBlob>& blob);
void Write(SkColorType color_type);
void Write(SkYUVColorSpace yuv_color_space);
+ void Write(const gpu::Mailbox& mailbox);
void Write(SkClipOp op) { Write(static_cast<uint8_t>(op)); }
void Write(PaintCanvas::AnnotationType type) {
@@ -75,6 +81,9 @@ class CC_PAINT_EXPORT PaintOpWriter {
void Write(SkFilterQuality filter_quality) {
Write(static_cast<uint8_t>(filter_quality));
}
+ void Write(SkBlendMode blend_mode) {
+ Write(static_cast<uint8_t>(blend_mode));
+ }
void Write(bool data) { Write(static_cast<uint8_t>(data)); }
// Aligns the memory to the given alignment.
@@ -103,7 +112,7 @@ class CC_PAINT_EXPORT PaintOpWriter {
// image.
void Write(const DrawImage& draw_image, SkSize* scale_adjustment);
-#ifndef OS_ANDROID
+#if !defined(OS_ANDROID)
// Serializes the given |skottie| vector graphic.
void Write(scoped_refptr<SkottieWrapper> skottie);
#endif
@@ -144,7 +153,9 @@ class CC_PAINT_EXPORT PaintOpWriter {
const gfx::SizeF& post_scale,
const SkMatrix& post_matrix_for_analysis);
void Write(const SkRegion& region);
+ void WriteImage(const DecodedDrawImage& decoded_draw_image);
void WriteImage(uint32_t transfer_cache_entry_id, bool needs_mips);
+ void WriteImage(const gpu::Mailbox& mailbox);
void EnsureBytes(size_t required_bytes);
sk_sp<PaintShader> TransformShaderIfNecessary(
@@ -152,7 +163,8 @@ class CC_PAINT_EXPORT PaintOpWriter {
SkFilterQuality quality,
uint32_t* paint_image_transfer_cache_entry_id,
gfx::SizeF* paint_record_post_scale,
- bool* paint_image_needs_mips);
+ bool* paint_image_needs_mips,
+ gpu::Mailbox* mailbox_out);
char* memory_ = nullptr;
size_t size_ = 0u;
diff --git a/chromium/cc/paint/paint_shader.cc b/chromium/cc/paint/paint_shader.cc
index 7b55359a9ab..43adbeb4baa 100644
--- a/chromium/cc/paint/paint_shader.cc
+++ b/chromium/cc/paint/paint_shader.cc
@@ -360,7 +360,8 @@ sk_sp<PaintShader> PaintShader::CreateDecodedImage(
ImageProvider* image_provider,
uint32_t* transfer_cache_entry_id,
SkFilterQuality* raster_quality,
- bool* needs_mips) const {
+ bool* needs_mips,
+ gpu::Mailbox* mailbox) const {
DCHECK_EQ(shader_type_, Type::kImage);
if (!image_)
return nullptr;
@@ -387,6 +388,9 @@ sk_sp<PaintShader> PaintShader::CreateDecodedImage(
if (decoded_image.transfer_cache_entry_id()) {
decoded_paint_image = image_;
*transfer_cache_entry_id = *decoded_image.transfer_cache_entry_id();
+ } else if (!decoded_image.mailbox().IsZero()) {
+ decoded_paint_image = image_;
+ *mailbox = decoded_image.mailbox();
} else {
DCHECK(decoded_image.image());
diff --git a/chromium/cc/paint/paint_shader.h b/chromium/cc/paint/paint_shader.h
index 27b65546aa9..7b6940cea87 100644
--- a/chromium/cc/paint/paint_shader.h
+++ b/chromium/cc/paint/paint_shader.h
@@ -19,6 +19,10 @@
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/size_f.h"
+namespace gpu {
+struct Mailbox;
+}
+
namespace cc {
class ImageProvider;
class PaintOpBuffer;
@@ -203,7 +207,8 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt {
ImageProvider* image_provider,
uint32_t* transfer_cache_entry_id,
SkFilterQuality* raster_quality,
- bool* needs_mips) const;
+ bool* needs_mips,
+ gpu::Mailbox* mailbox) const;
// Creates a paint record shader for worklet-backed images.
sk_sp<PaintShader> CreatePaintWorkletRecord(
diff --git a/chromium/cc/paint/raw_memory_transfer_cache_entry.cc b/chromium/cc/paint/raw_memory_transfer_cache_entry.cc
index 8f8e746f01a..c6e4f150185 100644
--- a/chromium/cc/paint/raw_memory_transfer_cache_entry.cc
+++ b/chromium/cc/paint/raw_memory_transfer_cache_entry.cc
@@ -5,6 +5,7 @@
#include "cc/paint/raw_memory_transfer_cache_entry.h"
#include <string.h>
+#include <utility>
namespace cc {
@@ -47,7 +48,7 @@ size_t ServiceRawMemoryTransferCacheEntry::CachedSize() const {
}
bool ServiceRawMemoryTransferCacheEntry::Deserialize(
- GrContext* context,
+ GrDirectContext* context,
base::span<const uint8_t> data) {
data_ = std::vector<uint8_t>(data.begin(), data.end());
return true;
diff --git a/chromium/cc/paint/raw_memory_transfer_cache_entry.h b/chromium/cc/paint/raw_memory_transfer_cache_entry.h
index 2e9aafd490a..52e54dc00af 100644
--- a/chromium/cc/paint/raw_memory_transfer_cache_entry.h
+++ b/chromium/cc/paint/raw_memory_transfer_cache_entry.h
@@ -5,11 +5,10 @@
#ifndef CC_PAINT_RAW_MEMORY_TRANSFER_CACHE_ENTRY_H_
#define CC_PAINT_RAW_MEMORY_TRANSFER_CACHE_ENTRY_H_
-#include "cc/paint/transfer_cache_entry.h"
-
#include <vector>
#include "base/atomic_sequence_num.h"
+#include "cc/paint/transfer_cache_entry.h"
namespace cc {
@@ -37,7 +36,8 @@ class CC_PAINT_EXPORT ServiceRawMemoryTransferCacheEntry
ServiceRawMemoryTransferCacheEntry();
~ServiceRawMemoryTransferCacheEntry() final;
size_t CachedSize() const final;
- bool Deserialize(GrContext* context, base::span<const uint8_t> data) final;
+ bool Deserialize(GrDirectContext* context,
+ base::span<const uint8_t> data) final;
const std::vector<uint8_t>& data() { return data_; }
private:
diff --git a/chromium/cc/paint/scoped_raster_flags.cc b/chromium/cc/paint/scoped_raster_flags.cc
index 54fd30f57ae..1e5b87e6ad3 100644
--- a/chromium/cc/paint/scoped_raster_flags.cc
+++ b/chromium/cc/paint/scoped_raster_flags.cc
@@ -66,12 +66,14 @@ void ScopedRasterFlags::DecodeImageShader(const SkMatrix& ctm) {
uint32_t transfer_cache_entry_id = kInvalidImageTransferCacheEntryId;
SkFilterQuality raster_quality = flags()->getFilterQuality();
bool transfer_cache_entry_needs_mips = false;
+ gpu::Mailbox mailbox;
auto decoded_shader = flags()->getShader()->CreateDecodedImage(
ctm, flags()->getFilterQuality(), &*decode_stashing_image_provider_,
&transfer_cache_entry_id, &raster_quality,
- &transfer_cache_entry_needs_mips);
+ &transfer_cache_entry_needs_mips, &mailbox);
DCHECK_EQ(transfer_cache_entry_id, kInvalidImageTransferCacheEntryId);
DCHECK_EQ(transfer_cache_entry_needs_mips, false);
+ DCHECK(mailbox.IsZero());
if (!decoded_shader) {
decode_failed_ = true;
diff --git a/chromium/cc/paint/shader_transfer_cache_entry.cc b/chromium/cc/paint/shader_transfer_cache_entry.cc
index fda25bd40ef..7a9085a3ff6 100644
--- a/chromium/cc/paint/shader_transfer_cache_entry.cc
+++ b/chromium/cc/paint/shader_transfer_cache_entry.cc
@@ -4,6 +4,8 @@
#include "cc/paint/shader_transfer_cache_entry.h"
+#include <utility>
+
#include "base/notreached.h"
namespace cc {
@@ -21,7 +23,7 @@ size_t ServiceShaderTransferCacheEntry::CachedSize() const {
}
bool ServiceShaderTransferCacheEntry::Deserialize(
- GrContext* context,
+ GrDirectContext* context,
base::span<const uint8_t> data) {
// These entries must be created directly via CreateLocalEntry.
NOTREACHED();
diff --git a/chromium/cc/paint/shader_transfer_cache_entry.h b/chromium/cc/paint/shader_transfer_cache_entry.h
index 736009e4f8a..9fef12c8cc1 100644
--- a/chromium/cc/paint/shader_transfer_cache_entry.h
+++ b/chromium/cc/paint/shader_transfer_cache_entry.h
@@ -29,7 +29,8 @@ class CC_PAINT_EXPORT ServiceShaderTransferCacheEntry
size_t size);
~ServiceShaderTransferCacheEntry() final;
size_t CachedSize() const final;
- bool Deserialize(GrContext* context, base::span<const uint8_t> data) final;
+ bool Deserialize(GrDirectContext* context,
+ base::span<const uint8_t> data) final;
sk_sp<PaintShader> shader() const { return shader_; }
diff --git a/chromium/cc/paint/skia_paint_image_generator.cc b/chromium/cc/paint/skia_paint_image_generator.cc
index 3eb864f12cf..c92416f919f 100644
--- a/chromium/cc/paint/skia_paint_image_generator.cc
+++ b/chromium/cc/paint/skia_paint_image_generator.cc
@@ -4,6 +4,8 @@
#include "cc/paint/skia_paint_image_generator.h"
+#include <utility>
+
#include "cc/paint/paint_image_generator.h"
namespace cc {
@@ -35,15 +37,20 @@ bool SkiaPaintImageGenerator::onQueryYUVA8(
SkYUVASizeInfo* size_info,
SkYUVAIndex indices[SkYUVAIndex::kIndexCount],
SkYUVColorSpace* color_space) const {
- return paint_image_generator_->QueryYUVA8(size_info, indices, color_space);
+ // Only 8-bit YUV is supported by the SkImageGenerator.
+ uint8_t bit_depth = 8;
+ const bool result = paint_image_generator_->QueryYUVA(
+ size_info, indices, color_space, &bit_depth);
+ return result && bit_depth == 8;
}
bool SkiaPaintImageGenerator::onGetYUVA8Planes(
const SkYUVASizeInfo& size_info,
const SkYUVAIndex indices[SkYUVAIndex::kIndexCount],
void* planes[4]) {
- return paint_image_generator_->GetYUVA8Planes(size_info, indices, planes,
- frame_index_, uniqueID());
+ return paint_image_generator_->GetYUVAPlanes(size_info, kGray_8_SkColorType,
+ indices, planes, frame_index_,
+ uniqueID());
}
} // namespace cc
diff --git a/chromium/cc/paint/skottie_transfer_cache_entry.cc b/chromium/cc/paint/skottie_transfer_cache_entry.cc
index 7b1f1bdb8b5..edac56893dd 100644
--- a/chromium/cc/paint/skottie_transfer_cache_entry.cc
+++ b/chromium/cc/paint/skottie_transfer_cache_entry.cc
@@ -4,6 +4,8 @@
#include "cc/paint/skottie_transfer_cache_entry.h"
+#include <utility>
+
#include "cc/paint/skottie_wrapper.h"
namespace cc {
@@ -37,7 +39,7 @@ size_t ServiceSkottieTransferCacheEntry::CachedSize() const {
}
bool ServiceSkottieTransferCacheEntry::Deserialize(
- GrContext* context,
+ GrDirectContext* context,
base::span<const uint8_t> data) {
skottie_ = SkottieWrapper::CreateNonSerializable(data);
cached_size_ = data.size();
diff --git a/chromium/cc/paint/skottie_transfer_cache_entry.h b/chromium/cc/paint/skottie_transfer_cache_entry.h
index c81de74ade2..671c9db50a2 100644
--- a/chromium/cc/paint/skottie_transfer_cache_entry.h
+++ b/chromium/cc/paint/skottie_transfer_cache_entry.h
@@ -40,7 +40,8 @@ class CC_PAINT_EXPORT ServiceSkottieTransferCacheEntry
// ServiceTransferCacheEntry implementation:
size_t CachedSize() const final;
- bool Deserialize(GrContext* context, base::span<const uint8_t> data) final;
+ bool Deserialize(GrDirectContext* context,
+ base::span<const uint8_t> data) final;
const scoped_refptr<SkottieWrapper>& skottie() const { return skottie_; }
diff --git a/chromium/cc/paint/solid_color_analyzer.cc b/chromium/cc/paint/solid_color_analyzer.cc
index cd990c12228..4e3ca84770f 100644
--- a/chromium/cc/paint/solid_color_analyzer.cc
+++ b/chromium/cc/paint/solid_color_analyzer.cc
@@ -81,12 +81,12 @@ bool IsSolidColorPaint(const PaintFlags& flags) {
!flags.getLooper() && !flags.getMaskFilter() && !flags.getColorFilter() &&
!flags.getImageFilter() && flags.getStyle() == PaintFlags::kFill_Style;
-#if defined(OS_MACOSX)
+#if defined(OS_MAC)
// Additionally, on Mac, we require that the color is opaque due to
// https://crbug.com/922899.
// TODO(andrescj): remove this condition once that bug is fixed.
is_solid_color = (is_solid_color && SkColorGetA(flags.getColor()) == 255);
-#endif // OS_MACOSX
+#endif // OS_MAC
return is_solid_color;
}
@@ -166,12 +166,12 @@ void CheckIfSolidColor(const SkCanvas& canvas,
bool solid_color_candidate =
does_cover_canvas && IsSolidColorBlendMode(blendmode);
-#if defined(OS_MACOSX)
+#if defined(OS_MAC)
// Additionally, on Mac, we require that the color is opaque due to
// https://crbug.com/922899.
// TODO(andrescj): remove this condition once that bug is fixed.
solid_color_candidate = (solid_color_candidate && alpha == 255);
-#endif // OS_MACOSX
+#endif // OS_MAC
if (solid_color_candidate) {
CalculateSolidColor(color /* src_color */, blendmode,
diff --git a/chromium/cc/paint/solid_color_analyzer_unittest.cc b/chromium/cc/paint/solid_color_analyzer_unittest.cc
index 3c95df2b2d7..8f1ec62a3bf 100644
--- a/chromium/cc/paint/solid_color_analyzer_unittest.cc
+++ b/chromium/cc/paint/solid_color_analyzer_unittest.cc
@@ -97,13 +97,13 @@ TEST_F(SolidColorAnalyzerTest, ClearTranslucent) {
Initialize();
SkColor color = SkColorSetARGB(128, 11, 22, 33);
canvas()->clear(color);
-#if defined(OS_MACOSX)
- // TODO(andrescj): remove the special treatment of OS_MACOSX once
+#if defined(OS_MAC)
+ // TODO(andrescj): remove the special treatment of OS_MAC once
// https://crbug.com/922899 is fixed.
EXPECT_FALSE(IsSolidColor());
#else
EXPECT_EQ(color, GetColor());
-#endif // OS_MACOSX
+#endif // OS_MAC
}
TEST_F(SolidColorAnalyzerTest, DrawColor) {
@@ -295,13 +295,13 @@ TEST_F(SolidColorAnalyzerTest, DrawRectTranslucent) {
flags.setColor(color);
SkRect rect = SkRect::MakeWH(100, 100);
canvas()->drawRect(rect, flags);
-#if defined(OS_MACOSX)
- // TODO(andrescj): remove the special treatment of OS_MACOSX once
+#if defined(OS_MAC)
+ // TODO(andrescj): remove the special treatment of OS_MAC once
// https://crbug.com/922899 is fixed.
EXPECT_FALSE(IsSolidColor());
#else
EXPECT_EQ(color, GetColor());
-#endif // OS_MACOSX
+#endif // OS_MAC
}
TEST_F(SolidColorAnalyzerTest, DrawRectTranslucentOverNonSolid) {
@@ -343,14 +343,14 @@ TEST_F(SolidColorAnalyzerTest, DrawRectSolidWithSrcOverBlending) {
flags.setColor(color);
rect = SkRect::MakeWH(100, 100);
canvas()->drawRect(rect, flags);
-#if defined(OS_MACOSX)
- // TODO(andrescj): remove the special treatment of OS_MACOSX once
+#if defined(OS_MAC)
+ // TODO(andrescj): remove the special treatment of OS_MAC once
// https://crbug.com/922899 is fixed.
EXPECT_FALSE(IsSolidColor());
#else
EXPECT_EQ(SkColorSetARGB(159, 15, 25, 35),
GetColor(2 /* max_ops_to_analyze */));
-#endif // OS_MACOSX
+#endif // OS_MAC
}
TEST_F(SolidColorAnalyzerTest, SaveLayer) {
diff --git a/chromium/cc/paint/texture_backing.h b/chromium/cc/paint/texture_backing.h
index 51055d27b29..9be172e352c 100644
--- a/chromium/cc/paint/texture_backing.h
+++ b/chromium/cc/paint/texture_backing.h
@@ -31,9 +31,20 @@ class CC_PAINT_EXPORT TextureBacking : public SkRefCnt {
// Returns the shared image mailbox backing for this texture.
virtual gpu::Mailbox GetMailbox() const = 0;
- // Returns a texture backed SkImage wrapping the mailbox. Only supported if
- // the context used to create this image has a valid GrContext.
+ // Returns a texture backed SkImage. Only supported if the context used to
+ // create this image has a valid GrContext.
virtual sk_sp<SkImage> GetAcceleratedSkImage() = 0;
+
+ // Gets SkImage via a readback from GPU memory. Use this when actual SkImage
+ // pixel data is required in software.
+ virtual sk_sp<SkImage> GetSkImageViaReadback() = 0;
+
+ // Read texture's pixels into caller owned |dstPixels|.
+ virtual bool readPixels(const SkImageInfo& dst_info,
+ void* dst_pixels,
+ size_t dst_row_bytes,
+ int src_x,
+ int src_y) = 0;
};
} // namespace cc
diff --git a/chromium/cc/paint/transfer_cache_entry.cc b/chromium/cc/paint/transfer_cache_entry.cc
index e192ed1cce1..5a6dda6942d 100644
--- a/chromium/cc/paint/transfer_cache_entry.cc
+++ b/chromium/cc/paint/transfer_cache_entry.cc
@@ -7,11 +7,12 @@
#include <memory>
#include "base/notreached.h"
+#include "build/build_config.h"
#include "cc/paint/image_transfer_cache_entry.h"
#include "cc/paint/raw_memory_transfer_cache_entry.h"
#include "cc/paint/shader_transfer_cache_entry.h"
-#ifndef OS_ANDROID
+#if !defined(OS_ANDROID)
#include "cc/paint/skottie_transfer_cache_entry.h"
#endif
@@ -29,7 +30,7 @@ std::unique_ptr<ServiceTransferCacheEntry> ServiceTransferCacheEntry::Create(
// CreateLocalEntry and is never serialized/deserialized.
return nullptr;
case TransferCacheEntryType::kSkottie:
-#ifndef OS_ANDROID
+#if !defined(OS_ANDROID)
return std::make_unique<ServiceSkottieTransferCacheEntry>();
#else
return nullptr;
diff --git a/chromium/cc/paint/transfer_cache_entry.h b/chromium/cc/paint/transfer_cache_entry.h
index 62a0179d081..cd209df7315 100644
--- a/chromium/cc/paint/transfer_cache_entry.h
+++ b/chromium/cc/paint/transfer_cache_entry.h
@@ -10,7 +10,7 @@
#include "base/containers/span.h"
#include "cc/paint/paint_export.h"
-class GrContext;
+class GrDirectContext;
namespace cc {
@@ -85,7 +85,7 @@ class CC_PAINT_EXPORT ServiceTransferCacheEntry {
// Deserialize the cache entry from the given span of memory with the given
// context.
- virtual bool Deserialize(GrContext* context,
+ virtual bool Deserialize(GrDirectContext* context,
base::span<const uint8_t> data) = 0;
};
diff --git a/chromium/cc/raster/bitmap_raster_buffer_provider.cc b/chromium/cc/raster/bitmap_raster_buffer_provider.cc
index a2bbd75c2c9..eba189d900f 100644
--- a/chromium/cc/raster/bitmap_raster_buffer_provider.cc
+++ b/chromium/cc/raster/bitmap_raster_buffer_provider.cc
@@ -80,6 +80,8 @@ class BitmapRasterBufferImpl : public RasterBuffer {
/*gpu_compositing=*/false, playback_settings);
}
+ bool SupportsBackgroundThreadPriority() const override { return true; }
+
private:
const gfx::Size resource_size_;
const gfx::ColorSpace color_space_;
diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.cc b/chromium/cc/raster/gpu_raster_buffer_provider.cc
index 4dbb25b4d81..5d33a527a06 100644
--- a/chromium/cc/raster/gpu_raster_buffer_provider.cc
+++ b/chromium/cc/raster/gpu_raster_buffer_provider.cc
@@ -7,7 +7,9 @@
#include <stdint.h>
#include <algorithm>
+#include <memory>
#include <utility>
+#include <vector>
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
@@ -34,7 +36,7 @@
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "third_party/skia/include/core/SkSurface.h"
-#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "ui/gfx/geometry/axis_transform2d.h"
#include "url/gurl.h"
@@ -138,8 +140,9 @@ static void RasterizeSourceOOP(
gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
if (texture_is_overlay_candidate)
flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
- *mailbox = sii->CreateSharedImage(resource_format, resource_size,
- color_space, flags);
+ *mailbox = sii->CreateSharedImage(
+ resource_format, resource_size, color_space, kTopLeft_GrSurfaceOrigin,
+ kPremul_SkAlphaType, flags, gpu::kNullSurfaceHandle);
ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
} else {
ri->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
@@ -192,8 +195,9 @@ static void RasterizeSource(
gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT;
if (texture_is_overlay_candidate)
flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
- *mailbox = sii->CreateSharedImage(resource_format, resource_size,
- color_space, flags);
+ *mailbox = sii->CreateSharedImage(
+ resource_format, resource_size, color_space, kTopLeft_GrSurfaceOrigin,
+ kPremul_SkAlphaType, flags, gpu::kNullSurfaceHandle);
ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
} else {
// Wait on the SyncToken that was created on the compositor thread after
@@ -352,6 +356,11 @@ void GpuRasterBufferProvider::RasterBufferImpl::Playback(
depends_on_hardware_accelerated_webp_candidates_);
}
+bool GpuRasterBufferProvider::RasterBufferImpl::
+ SupportsBackgroundThreadPriority() const {
+ return true;
+}
+
GpuRasterBufferProvider::GpuRasterBufferProvider(
viz::ContextProvider* compositor_context_provider,
viz::RasterContextProvider* worker_context_provider,
@@ -369,7 +378,7 @@ GpuRasterBufferProvider::GpuRasterBufferProvider(
unpremultiply_and_dither_low_bit_depth_tiles_(
unpremultiply_and_dither_low_bit_depth_tiles),
enable_oop_rasterization_(enable_oop_rasterization),
- random_generator_((uint32_t)base::RandUint64()),
+ random_generator_(static_cast<uint32_t>(base::RandUint64())),
bernoulli_distribution_(raster_metric_probability) {
DCHECK(compositor_context_provider);
DCHECK(worker_context_provider);
diff --git a/chromium/cc/raster/gpu_raster_buffer_provider.h b/chromium/cc/raster/gpu_raster_buffer_provider.h
index 4a442a3b6a1..780f3304f5c 100644
--- a/chromium/cc/raster/gpu_raster_buffer_provider.h
+++ b/chromium/cc/raster/gpu_raster_buffer_provider.h
@@ -6,7 +6,9 @@
#define CC_RASTER_GPU_RASTER_BUFFER_PROVIDER_H_
#include <stdint.h>
+#include <memory>
#include <random>
+#include <vector>
#include "base/time/time.h"
#include "cc/raster/raster_buffer_provider.h"
@@ -109,6 +111,7 @@ class CC_EXPORT GpuRasterBufferProvider : public RasterBufferProvider {
const gfx::AxisTransform2d& transform,
const RasterSource::PlaybackSettings& playback_settings,
const GURL& url) override;
+ bool SupportsBackgroundThreadPriority() const override;
private:
// These fields may only be used on the compositor thread.
diff --git a/chromium/cc/raster/lcd_text_disallowed_reason.cc b/chromium/cc/raster/lcd_text_disallowed_reason.cc
index 2bfffcc8b62..0c112055ef9 100644
--- a/chromium/cc/raster/lcd_text_disallowed_reason.cc
+++ b/chromium/cc/raster/lcd_text_disallowed_reason.cc
@@ -27,6 +27,10 @@ const char* LCDTextDisallowedReasonToString(LCDTextDisallowedReason reason) {
return "non-integral-y-offset";
case LCDTextDisallowedReason::kWillChangeTransform:
return "will-change-transform";
+ case LCDTextDisallowedReason::kPixelOrColorEffect:
+ return "pixel-or-color-effect";
+ case LCDTextDisallowedReason::kTransformAnimation:
+ return "transform-animation";
}
NOTREACHED();
return "";
diff --git a/chromium/cc/raster/lcd_text_disallowed_reason.h b/chromium/cc/raster/lcd_text_disallowed_reason.h
index 024b974d4ce..bc163875cca 100644
--- a/chromium/cc/raster/lcd_text_disallowed_reason.h
+++ b/chromium/cc/raster/lcd_text_disallowed_reason.h
@@ -24,7 +24,9 @@ enum class LCDTextDisallowedReason : uint8_t {
kNonIntegralXOffset = 5,
kNonIntegralYOffset = 6,
kWillChangeTransform = 7,
- kMaxValue = kWillChangeTransform,
+ kPixelOrColorEffect = 8,
+ kTransformAnimation = 9,
+ kMaxValue = kTransformAnimation,
};
constexpr size_t kLCDTextDisallowedReasonCount =
static_cast<size_t>(LCDTextDisallowedReason::kMaxValue) + 1;
diff --git a/chromium/cc/raster/one_copy_raster_buffer_provider.cc b/chromium/cc/raster/one_copy_raster_buffer_provider.cc
index f27efe462ad..fe15e4d31fb 100644
--- a/chromium/cc/raster/one_copy_raster_buffer_provider.cc
+++ b/chromium/cc/raster/one_copy_raster_buffer_provider.cc
@@ -11,6 +11,7 @@
#include <utility>
#include "base/debug/alias.h"
+#include "base/feature_list.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/process_memory_dump.h"
@@ -39,6 +40,14 @@ namespace {
// default batch size for copy operations.
const int kMaxBytesPerCopyOperation = 1024 * 1024 * 4;
+// When enabled, OneCopyRasterBufferProvider::RasterBufferImpl::Playback() runs
+// at normal thread priority.
+// TODO(https://crbug.com/1072756): Enable by default and remove the feature
+// once experiments confirm that this prevents priority inversions.
+const base::Feature kOneCopyRasterBufferPlaybackNormalThreadPriority{
+ "OneCopyRasterBufferPlaybackNormalThreadPriority",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
} // namespace
// Subclass for InUsePoolResource that holds ownership of a one-copy backing
@@ -122,6 +131,16 @@ void OneCopyRasterBufferProvider::RasterBufferImpl::Playback(
color_space_, playback_settings, previous_content_id_, new_content_id);
}
+bool OneCopyRasterBufferProvider::RasterBufferImpl::
+ SupportsBackgroundThreadPriority() const {
+ // Playback() should not run at background thread priority because it acquires
+ // the GpuChannelHost lock, which is acquired at normal thread priority by
+ // other code. Acquiring it at background thread priority can cause a priority
+ // inversion. https://crbug.com/1072756
+ return !base::FeatureList::IsEnabled(
+ kOneCopyRasterBufferPlaybackNormalThreadPriority);
+}
+
OneCopyRasterBufferProvider::OneCopyRasterBufferProvider(
scoped_refptr<base::SequencedTaskRunner> task_runner,
viz::ContextProvider* compositor_context_provider,
@@ -371,16 +390,17 @@ gpu::SyncToken OneCopyRasterBufferProvider::CopyOnWorkerThread(
gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_RASTER;
if (mailbox_texture_is_overlay_candidate)
usage |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
- *mailbox = sii->CreateSharedImage(resource_format, resource_size,
- color_space, usage);
+ *mailbox = sii->CreateSharedImage(
+ resource_format, resource_size, color_space, kTopLeft_GrSurfaceOrigin,
+ kPremul_SkAlphaType, usage, gpu::kNullSurfaceHandle);
}
// Create staging shared image.
if (staging_buffer->mailbox.IsZero()) {
const uint32_t usage = gpu::SHARED_IMAGE_USAGE_RASTER;
- staging_buffer->mailbox =
- sii->CreateSharedImage(staging_buffer->gpu_memory_buffer.get(),
- gpu_memory_buffer_manager_, color_space, usage);
+ staging_buffer->mailbox = sii->CreateSharedImage(
+ staging_buffer->gpu_memory_buffer.get(), gpu_memory_buffer_manager_,
+ color_space, kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage);
} else {
sii->UpdateSharedImage(staging_buffer->sync_token, staging_buffer->mailbox);
}
diff --git a/chromium/cc/raster/one_copy_raster_buffer_provider.h b/chromium/cc/raster/one_copy_raster_buffer_provider.h
index 0d2a7d0dbe7..c71b9c56c7a 100644
--- a/chromium/cc/raster/one_copy_raster_buffer_provider.h
+++ b/chromium/cc/raster/one_copy_raster_buffer_provider.h
@@ -105,6 +105,7 @@ class CC_EXPORT OneCopyRasterBufferProvider : public RasterBufferProvider {
const gfx::AxisTransform2d& transform,
const RasterSource::PlaybackSettings& playback_settings,
const GURL& url) override;
+ bool SupportsBackgroundThreadPriority() const override;
private:
// These fields may only be used on the compositor thread.
diff --git a/chromium/cc/raster/playback_image_provider.cc b/chromium/cc/raster/playback_image_provider.cc
index 103382658e9..f3fae3b6ca5 100644
--- a/chromium/cc/raster/playback_image_provider.cc
+++ b/chromium/cc/raster/playback_image_provider.cc
@@ -58,12 +58,17 @@ ImageProvider::ScopedResult PlaybackImageProvider::GetRasterContent(
DrawImage adjusted_image(draw_image, 1.f, frame_index, target_color_space_);
if (!cache_->UseCacheForDrawImage(adjusted_image)) {
- if (settings_->use_oop_raster) {
+ if (settings_->raster_mode == RasterMode::kOop) {
return ScopedResult(DecodedDrawImage(paint_image.GetMailbox(),
draw_image.filter_quality()));
+ } else if (settings_->raster_mode == RasterMode::kGpu) {
+ return ScopedResult(DecodedDrawImage(
+ paint_image.GetAcceleratedSkImage(), SkSize::Make(0, 0),
+ SkSize::Make(1.f, 1.f), draw_image.filter_quality(),
+ true /* is_budgeted */));
} else {
return ScopedResult(
- DecodedDrawImage(paint_image.GetRasterSkImage(), SkSize::Make(0, 0),
+ DecodedDrawImage(paint_image.GetSwSkImage(), SkSize::Make(0, 0),
SkSize::Make(1.f, 1.f), draw_image.filter_quality(),
true /* is_budgeted */));
}
diff --git a/chromium/cc/raster/playback_image_provider.h b/chromium/cc/raster/playback_image_provider.h
index 1f0f1084fd6..e0cc128cd5b 100644
--- a/chromium/cc/raster/playback_image_provider.h
+++ b/chromium/cc/raster/playback_image_provider.h
@@ -18,6 +18,7 @@ class ImageDecodeCache;
// decoded images for raster from the ImageDecodeCache.
class CC_EXPORT PlaybackImageProvider : public ImageProvider {
public:
+ enum class RasterMode { kSoftware, kGpu, kOop };
struct CC_EXPORT Settings {
Settings();
Settings(const Settings&) = delete;
@@ -34,9 +35,8 @@ class CC_EXPORT PlaybackImageProvider : public ImageProvider {
// 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;
+ // Indicates the raster backend that will be consuming the decoded images.
+ RasterMode raster_mode = RasterMode::kSoftware;
};
// If no settings are provided, all images are skipped during rasterization.
diff --git a/chromium/cc/raster/raster_buffer.h b/chromium/cc/raster/raster_buffer.h
index f49a53f2848..5bf719775cf 100644
--- a/chromium/cc/raster/raster_buffer.h
+++ b/chromium/cc/raster/raster_buffer.h
@@ -31,6 +31,12 @@ class CC_EXPORT RasterBuffer {
const gfx::AxisTransform2d& transform,
const RasterSource::PlaybackSettings& playback_settings,
const GURL& url) = 0;
+
+ // Returns true if Playback() can be invoked at background thread priority. To
+ // avoid priority inversions, this should return false if Playback() acquires
+ // resources that are also acquired at normal thread priority.
+ // https://crbug.com/1072756.
+ virtual bool SupportsBackgroundThreadPriority() const = 0;
};
} // namespace cc
diff --git a/chromium/cc/raster/raster_buffer_provider.cc b/chromium/cc/raster/raster_buffer_provider.cc
index 52fdc74ac05..7d777a907d8 100644
--- a/chromium/cc/raster/raster_buffer_provider.cc
+++ b/chromium/cc/raster/raster_buffer_provider.cc
@@ -28,6 +28,7 @@ bool IsSupportedPlaybackToMemoryFormat(viz::ResourceFormat format) {
case viz::RGBA_4444:
case viz::RGBA_8888:
case viz::BGRA_8888:
+ case viz::RGBA_F16:
return true;
case viz::ALPHA_8:
case viz::LUMINANCE_8:
@@ -35,7 +36,6 @@ bool IsSupportedPlaybackToMemoryFormat(viz::ResourceFormat format) {
case viz::ETC1:
case viz::RED_8:
case viz::LUMINANCE_F16:
- case viz::RGBA_F16:
case viz::R16_EXT:
case viz::BGR_565:
case viz::RG_88:
diff --git a/chromium/cc/raster/raster_buffer_provider.h b/chromium/cc/raster/raster_buffer_provider.h
index 7b4937373ff..71632ec2fc2 100644
--- a/chromium/cc/raster/raster_buffer_provider.h
+++ b/chromium/cc/raster/raster_buffer_provider.h
@@ -6,6 +6,8 @@
#define CC_RASTER_RASTER_BUFFER_PROVIDER_H_
#include <stddef.h>
+#include <memory>
+#include <vector>
#include "cc/raster/raster_buffer.h"
#include "cc/raster/raster_source.h"
diff --git a/chromium/cc/raster/raster_buffer_provider_perftest.cc b/chromium/cc/raster/raster_buffer_provider_perftest.cc
index 1164aeb0a39..8290aa08f19 100644
--- a/chromium/cc/raster/raster_buffer_provider_perftest.cc
+++ b/chromium/cc/raster/raster_buffer_provider_perftest.cc
@@ -31,7 +31,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_result_reporter.h"
#include "third_party/khronos/GLES2/gl2.h"
-#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/GrDirectContext.h"
namespace cc {
namespace {
@@ -112,7 +112,7 @@ class PerfContextProvider
return raster_context_.get();
}
gpu::ContextSupport* ContextSupport() override { return &support_; }
- class GrContext* GrContext() override {
+ class GrDirectContext* GrContext() override {
if (!test_context_provider_) {
test_context_provider_ = viz::TestContextProvider::Create();
}
@@ -160,9 +160,10 @@ static const int kTimeCheckInterval = 10;
class PerfTileTask : public TileTask {
public:
- PerfTileTask() : TileTask(true) {}
- explicit PerfTileTask(TileTask::Vector* dependencies)
- : TileTask(true, dependencies) {}
+ explicit PerfTileTask(TileTask::Vector* dependencies = nullptr)
+ : TileTask(TileTask::SupportsConcurrentExecution::kYes,
+ TileTask::SupportsBackgroundThreadPriority::kYes,
+ dependencies) {}
void Reset() {
did_complete_ = false;
diff --git a/chromium/cc/raster/raster_buffer_provider_unittest.cc b/chromium/cc/raster/raster_buffer_provider_unittest.cc
index bee4146a7cd..95369129b4f 100644
--- a/chromium/cc/raster/raster_buffer_provider_unittest.cc
+++ b/chromium/cc/raster/raster_buffer_provider_unittest.cc
@@ -10,6 +10,7 @@
#include <algorithm>
#include <limits>
#include <memory>
+#include <string>
#include <type_traits>
#include <utility>
#include <vector>
@@ -80,7 +81,9 @@ class TestRasterTaskImpl : public TileTask {
unsigned id,
std::unique_ptr<RasterBuffer> raster_buffer,
TileTask::Vector* dependencies)
- : TileTask(true, dependencies),
+ : TileTask(TileTask::SupportsConcurrentExecution::kYes,
+ TileTask::SupportsBackgroundThreadPriority::kYes,
+ dependencies),
completion_handler_(completion_handler),
id_(id),
raster_buffer_(std::move(raster_buffer)),
diff --git a/chromium/cc/raster/raster_source.cc b/chromium/cc/raster/raster_source.cc
index 73ad6fc23ae..dd9e8e2db76 100644
--- a/chromium/cc/raster/raster_source.cc
+++ b/chromium/cc/raster/raster_source.cc
@@ -12,9 +12,11 @@
#include "cc/base/math_util.h"
#include "cc/base/region.h"
#include "cc/debug/debug_colors.h"
+#include "cc/paint/clear_for_opaque_raster.h"
#include "cc/paint/display_item_list.h"
#include "cc/paint/image_provider.h"
#include "cc/paint/skia_paint_canvas.h"
+#include "cc/tiles/picture_layer_tiling.h"
#include "components/viz/common/traced_value.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/gfx/geometry/axis_transform2d.h"
@@ -39,54 +41,28 @@ RasterSource::~RasterSource() = default;
void RasterSource::ClearForOpaqueRaster(
SkCanvas* raster_canvas,
+ const gfx::AxisTransform2d& raster_transform,
const gfx::Size& content_size,
const gfx::Rect& canvas_bitmap_rect,
const gfx::Rect& canvas_playback_rect) const {
- // The last texel of this content is not guaranteed to be fully opaque, so
- // inset by one to generate the fully opaque coverage rect. This rect is
- // in device space.
- SkIRect coverage_device_rect =
- SkIRect::MakeWH(content_size.width() - canvas_bitmap_rect.x() - 1,
- content_size.height() - canvas_bitmap_rect.y() - 1);
-
- // If not fully covered, we need to clear one texel inside the coverage
- // rect (because of blending during raster) and one texel outside the canvas
- // bitmap rect (because of bilinear filtering during draw). See comments
- // in RasterSource.
- SkIRect device_column = SkIRect::MakeXYWH(coverage_device_rect.right(), 0, 2,
- coverage_device_rect.bottom());
- // row includes the corner, column excludes it.
- SkIRect device_row = SkIRect::MakeXYWH(0, coverage_device_rect.bottom(),
- coverage_device_rect.right() + 2, 2);
-
- bool right_edge = content_size.width() == canvas_playback_rect.right();
- bool bottom_edge = content_size.height() == canvas_playback_rect.bottom();
-
- // If the playback rect is touching either edge of the content rect
- // extend it by one pixel to include the extra texel outside the canvas
- // bitmap rect that was added to device column and row above.
- SkIRect playback_device_rect =
- SkIRect::MakeXYWH(canvas_playback_rect.x() - canvas_bitmap_rect.x(),
- canvas_playback_rect.y() - canvas_bitmap_rect.y(),
- canvas_playback_rect.width() + (right_edge ? 1 : 0),
- canvas_playback_rect.height() + (bottom_edge ? 1 : 0));
-
- // Intersect the device column and row with the playback rect and only
- // clear inside of that rect if needed.
- if (device_column.intersect(playback_device_rect)) {
- raster_canvas->save();
- raster_canvas->clipRect(SkRect::Make(device_column), SkClipOp::kIntersect,
- false);
- raster_canvas->drawColor(background_color_, SkBlendMode::kSrc);
- raster_canvas->restore();
- }
- if (device_row.intersect(playback_device_rect)) {
- raster_canvas->save();
- raster_canvas->clipRect(SkRect::Make(device_row), SkClipOp::kIntersect,
- false);
- raster_canvas->drawColor(background_color_, SkBlendMode::kSrc);
- raster_canvas->restore();
+ gfx::Rect outer_rect;
+ gfx::Rect inner_rect;
+ float scale = raster_transform.scale() / recording_scale_factor_;
+ if (!CalculateClearForOpaqueRasterRects(
+ raster_transform.translation(), gfx::SizeF(scale, scale),
+ content_size, canvas_bitmap_rect, canvas_playback_rect, outer_rect,
+ inner_rect))
+ return;
+
+ raster_canvas->save();
+ raster_canvas->clipRect(gfx::RectToSkRect(outer_rect), SkClipOp::kIntersect,
+ false);
+ if (!inner_rect.IsEmpty()) {
+ raster_canvas->clipRect(gfx::RectToSkRect(inner_rect),
+ SkClipOp::kDifference, false);
}
+ raster_canvas->drawColor(background_color_, SkBlendMode::kSrc);
+ raster_canvas->restore();
}
void RasterSource::PlaybackToCanvas(
@@ -106,15 +82,16 @@ void RasterSource::PlaybackToCanvas(
// NOTE: The following code should be kept consistent with
// PaintOpBufferSerializer::SerializePreamble().
bool is_partial_raster = canvas_bitmap_rect != canvas_playback_rect;
- if (!requires_clear_ && raster_transform.translation().IsZero()) {
+ if (!requires_clear_) {
// 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
- // are not painted. Because this raster source is required to be opaque,
- // we may need to do extra clearing outside of the clip. This needs to
- // be done for both full and partial raster.
- ClearForOpaqueRaster(raster_canvas, content_size, canvas_bitmap_rect,
- canvas_playback_rect);
+ // pixels inside the opaque region are painted. However, due to raster
+ // scaling or translation it's possible that the edges of the painted
+ // content might include texels that are not painted opaquely. Because this
+ // raster source is required to be opaque, we may need to do extra clearing
+ // outside of the clip. This needs to be done for both full and partial
+ // raster.
+ ClearForOpaqueRaster(raster_canvas, raster_transform, content_size,
+ canvas_bitmap_rect, canvas_playback_rect);
} else if (!is_partial_raster) {
// For non-opaque raster sources that are rastering the full tile,
// just clear the entire canvas (even if stretches past the canvas
@@ -179,9 +156,18 @@ RasterSource::TakeDecodingModeMap() {
return display_list_->TakeDecodingModeMap();
}
-bool RasterSource::CoversRect(const gfx::Rect& layer_rect) const {
+bool RasterSource::CoversRect(const gfx::Rect& layer_rect,
+ const PictureLayerTilingClient& client) const {
if (size_.IsEmpty())
return false;
+
+ // Directly composited images by definition have a single DrawImageRectOp that
+ // covers the entire layer, so return true for these raster sources.
+ // TODO(crbug.com/1117174): This will miss cases when the raster source
+ // partially covers the layer rect.
+ if (client.IsDirectlyCompositedImage())
+ return true;
+
gfx::Rect bounded_rect = layer_rect;
bounded_rect.Intersect(gfx::Rect(size_));
return recorded_viewport_.Contains(bounded_rect);
diff --git a/chromium/cc/raster/raster_source.h b/chromium/cc/raster/raster_source.h
index 8f7f8679691..3aa3386437a 100644
--- a/chromium/cc/raster/raster_source.h
+++ b/chromium/cc/raster/raster_source.h
@@ -26,6 +26,7 @@ namespace cc {
class DisplayItemList;
class DrawImage;
class ImageProvider;
+class PictureLayerTilingClient;
class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> {
public:
@@ -47,14 +48,12 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> {
RasterSource(const RasterSource&) = delete;
RasterSource& operator=(const RasterSource&) = delete;
- // Helper function to apply a few common operations before passing the canvas
- // to the shorter version. This is useful for rastering into tiles.
- // canvas is expected to be backed by a tile, with a default state.
- // raster_transform will be applied to the display list, rastering the list
- // into the "content space".
- // canvas_bitmap_rect defines the extent of the tile in the content space,
+ // This is useful for rastering into tiles. |canvas| is expected to be backed
+ // by a tile, with a default state. |raster_transform| will be applied to the
+ // display list, rastering the list into the "content space".
+ // |canvas_bitmap_rect| defines the extent of the tile in the content space,
// 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,
+ // |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
@@ -90,7 +89,8 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> {
// Return true iff this raster source can raster the given rect in layer
// space.
- bool CoversRect(const gfx::Rect& layer_rect) const;
+ bool CoversRect(const gfx::Rect& layer_rect,
+ const PictureLayerTilingClient& client) const;
// Returns true if this raster source has anything to rasterize.
bool HasRecordings() const;
@@ -127,6 +127,7 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> {
virtual ~RasterSource();
void ClearForOpaqueRaster(SkCanvas* raster_canvas,
+ const gfx::AxisTransform2d& raster_transform,
const gfx::Size& content_size,
const gfx::Rect& canvas_bitmap_rect,
const gfx::Rect& canvas_playback_rect) const;
diff --git a/chromium/cc/raster/raster_source_unittest.cc b/chromium/cc/raster/raster_source_unittest.cc
index 41ee8c0b387..9c0ab03a143 100644
--- a/chromium/cc/raster/raster_source_unittest.cc
+++ b/chromium/cc/raster/raster_source_unittest.cc
@@ -9,6 +9,7 @@
#include <memory>
#include "base/memory/scoped_refptr.h"
+#include "base/strings/stringprintf.h"
#include "cc/raster/playback_image_provider.h"
#include "cc/test/fake_recording_source.h"
#include "cc/test/skia_common.h"
@@ -30,6 +31,15 @@ using ::testing::Sequence;
namespace cc {
namespace {
+struct Color {
+ SkColor color;
+ bool operator==(const Color& other) const { return color == other.color; }
+};
+std::ostream& operator<<(std::ostream& os, const Color& color) {
+ return os << base::StringPrintf("#%08x", color.color);
+}
+#define EXPECT_COLOR_EQ(a, b) EXPECT_EQ(Color{a}, Color{b})
+
TEST(RasterSourceTest, AnalyzeIsSolidUnscaled) {
gfx::Size layer_bounds(400, 400);
@@ -58,7 +68,7 @@ TEST(RasterSourceTest, AnalyzeIsSolidUnscaled) {
gfx::Rect rect(x, y, 100, 100);
is_solid_color = raster->PerformSolidColorAnalysis(rect, &color);
EXPECT_TRUE(is_solid_color) << rect.ToString();
- EXPECT_EQ(solid_color, color) << rect.ToString();
+ EXPECT_COLOR_EQ(solid_color, color) << rect.ToString();
}
}
@@ -77,26 +87,26 @@ TEST(RasterSourceTest, AnalyzeIsSolidUnscaled) {
is_solid_color =
raster->PerformSolidColorAnalysis(gfx::Rect(100, 0, 100, 100), &color);
EXPECT_TRUE(is_solid_color);
- EXPECT_EQ(solid_color, color);
+ EXPECT_COLOR_EQ(solid_color, color);
// Boundaries should be clipped.
color = SK_ColorTRANSPARENT;
is_solid_color =
raster->PerformSolidColorAnalysis(gfx::Rect(350, 0, 100, 100), &color);
EXPECT_TRUE(is_solid_color);
- EXPECT_EQ(solid_color, color);
+ EXPECT_COLOR_EQ(solid_color, color);
color = SK_ColorTRANSPARENT;
is_solid_color =
raster->PerformSolidColorAnalysis(gfx::Rect(0, 350, 100, 100), &color);
EXPECT_TRUE(is_solid_color);
- EXPECT_EQ(solid_color, color);
+ EXPECT_COLOR_EQ(solid_color, color);
color = SK_ColorTRANSPARENT;
is_solid_color =
raster->PerformSolidColorAnalysis(gfx::Rect(350, 350, 100, 100), &color);
EXPECT_TRUE(is_solid_color);
- EXPECT_EQ(solid_color, color);
+ EXPECT_COLOR_EQ(solid_color, color);
}
TEST(RasterSourceTest, AnalyzeIsSolidScaled) {
@@ -132,7 +142,7 @@ TEST(RasterSourceTest, AnalyzeIsSolidScaled) {
is_solid_color = raster->PerformSolidColorAnalysis(rect, &color);
EXPECT_TRUE(is_solid_color)
<< rect.ToString() << " recording_scale: " << recording_scale;
- EXPECT_EQ(solid_color, color)
+ EXPECT_COLOR_EQ(solid_color, color)
<< rect.ToString() << " recording_scale: " << recording_scale;
}
}
@@ -159,26 +169,30 @@ TEST(RasterSourceTest, AnalyzeIsSolidScaled) {
is_solid_color =
raster->PerformSolidColorAnalysis(gfx::Rect(51, 0, 100, 100), &color);
EXPECT_TRUE(is_solid_color) << " recording_scale: " << recording_scale;
- EXPECT_EQ(solid_color, color) << " recording_scale: " << recording_scale;
+ EXPECT_COLOR_EQ(solid_color, color)
+ << " recording_scale: " << recording_scale;
// Boundaries should be clipped.
color = SK_ColorTRANSPARENT;
is_solid_color =
raster->PerformSolidColorAnalysis(gfx::Rect(350, 0, 100, 100), &color);
EXPECT_TRUE(is_solid_color) << " recording_scale: " << recording_scale;
- EXPECT_EQ(solid_color, color) << " recording_scale: " << recording_scale;
+ EXPECT_COLOR_EQ(solid_color, color)
+ << " recording_scale: " << recording_scale;
color = SK_ColorTRANSPARENT;
is_solid_color =
raster->PerformSolidColorAnalysis(gfx::Rect(0, 350, 100, 100), &color);
EXPECT_TRUE(is_solid_color) << " recording_scale: " << recording_scale;
- EXPECT_EQ(solid_color, color) << " recording_scale: " << recording_scale;
+ EXPECT_COLOR_EQ(solid_color, color)
+ << " recording_scale: " << recording_scale;
color = SK_ColorTRANSPARENT;
is_solid_color = raster->PerformSolidColorAnalysis(
gfx::Rect(350, 350, 100, 100), &color);
EXPECT_TRUE(is_solid_color) << " recording_scale: " << recording_scale;
- EXPECT_EQ(solid_color, color) << " recording_scale: " << recording_scale;
+ EXPECT_COLOR_EQ(solid_color, color)
+ << " recording_scale: " << recording_scale;
}
}
@@ -294,14 +308,14 @@ TEST(RasterSourceTest, RasterFullContents) {
gfx::AxisTransform2d(contents_scale, gfx::Vector2dF()),
RasterSource::PlaybackSettings());
- SkColor* pixels = reinterpret_cast<SkColor*>(bitmap.getPixels());
- int num_pixels = bitmap.width() * bitmap.height();
bool all_white = true;
- for (int i = 0; i < num_pixels; ++i) {
- EXPECT_EQ(SkColorGetA(pixels[i]), 255u);
- all_white &= (SkColorGetR(pixels[i]) == 255);
- all_white &= (SkColorGetG(pixels[i]) == 255);
- all_white &= (SkColorGetB(pixels[i]) == 255);
+ for (int i = 0; i < bitmap.width(); i++) {
+ for (int j = 0; j < bitmap.height(); j++) {
+ SkColor pixel = bitmap.getColor(i, j);
+ EXPECT_EQ(SkColorGetA(pixel), 255u)
+ << offset_x + i << "," << offset_y + j;
+ all_white &= (pixel == SK_ColorWHITE);
+ }
}
// If the canvas doesn't extend past the edge of the content,
@@ -312,6 +326,66 @@ TEST(RasterSourceTest, RasterFullContents) {
}
}
+TEST(RasterSourceTest, RasterFullContentsWithRasterTranslation) {
+ gfx::Size layer_bounds(3, 5);
+ float raster_divisions = 2.f;
+
+ std::unique_ptr<FakeRecordingSource> recording_source =
+ FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
+ recording_source->SetBackgroundColor(SK_ColorBLACK);
+
+ // Because the caller sets content opaque, it also promises that it
+ // has at least filled in layer_bounds opaquely.
+ PaintFlags white_flags;
+ white_flags.setColor(SK_ColorWHITE);
+ white_flags.setAntiAlias(true);
+ recording_source->add_draw_rect_with_flags(gfx::Rect(layer_bounds),
+ white_flags);
+ recording_source->Rerecord();
+
+ scoped_refptr<RasterSource> raster = recording_source->CreateRasterSource();
+
+ gfx::Size content_bounds = layer_bounds;
+
+ // Simulate drawing into different tiles at different offsets.
+ int step_x = std::ceil(content_bounds.width() / raster_divisions);
+ int step_y = std::ceil(content_bounds.height() / raster_divisions);
+ for (int offset_x = 0; offset_x < content_bounds.width();
+ offset_x += step_x) {
+ for (int offset_y = 0; offset_y < content_bounds.height();
+ offset_y += step_y) {
+ gfx::Rect content_rect(offset_x, offset_y, step_x, step_y);
+ content_rect.Intersect(gfx::Rect(content_bounds));
+
+ gfx::Rect canvas_rect = content_rect;
+
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(canvas_rect.width(), canvas_rect.height());
+ SkCanvas canvas(bitmap);
+ canvas.clear(SK_ColorTRANSPARENT);
+
+ raster->PlaybackToCanvas(
+ &canvas, content_bounds, canvas_rect, canvas_rect,
+ gfx::AxisTransform2d(1.0f, gfx::Vector2dF(0.3f, 0.7f)),
+ RasterSource::PlaybackSettings());
+
+ for (int i = 0; i < bitmap.width(); i++) {
+ for (int j = 0; j < bitmap.height(); j++) {
+ SkColor pixel = bitmap.getColor(i, j);
+ int content_x = offset_x + i;
+ int content_y = offset_y + j;
+ EXPECT_EQ(255u, SkColorGetA(pixel)) << content_x << "," << content_y;
+ // Pixels on the top and left edges of the content are blended with
+ // the background.
+ bool expect_white = content_x && content_y;
+ EXPECT_EQ(expect_white, pixel == SK_ColorWHITE)
+ << content_x << "," << content_y;
+ }
+ }
+ }
+ }
+}
+
TEST(RasterSourceTest, RasterPartialContents) {
gfx::Size layer_bounds(3, 5);
float contents_scale = 1.5f;
@@ -345,18 +419,9 @@ TEST(RasterSourceTest, RasterPartialContents) {
gfx::AxisTransform2d(contents_scale, gfx::Vector2dF()),
RasterSource::PlaybackSettings());
- {
- SkColor* pixels = reinterpret_cast<SkColor*>(bitmap.getPixels());
- for (int i = 0; i < bitmap.width(); ++i) {
- for (int j = 0; j < bitmap.height(); ++j) {
- SCOPED_TRACE(i);
- SCOPED_TRACE(j);
- EXPECT_EQ(255u, SkColorGetA(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(255u, SkColorGetR(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(255u, SkColorGetG(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(255u, SkColorGetB(pixels[i + j * bitmap.width()]));
- }
- }
+ for (int i = 0; i < bitmap.width(); ++i) {
+ for (int j = 0; j < bitmap.height(); ++j)
+ EXPECT_COLOR_EQ(SK_ColorWHITE, bitmap.getColor(i, j)) << i << "," << j;
}
// Re-record everything as black.
@@ -377,26 +442,107 @@ TEST(RasterSourceTest, RasterPartialContents) {
gfx::AxisTransform2d(contents_scale, gfx::Vector2dF()),
RasterSource::PlaybackSettings());
- SkColor* pixels = reinterpret_cast<SkColor*>(bitmap.getPixels());
int num_black = 0;
int num_white = 0;
for (int i = 0; i < bitmap.width(); ++i) {
for (int j = 0; j < bitmap.height(); ++j) {
- SCOPED_TRACE(j);
- SCOPED_TRACE(i);
+ SkColor pixel = bitmap.getColor(i, j);
bool expect_black = playback_rect.Contains(i, j);
if (expect_black) {
- EXPECT_EQ(255u, SkColorGetA(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(0u, SkColorGetR(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(0u, SkColorGetG(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(0u, SkColorGetB(pixels[i + j * bitmap.width()]));
+ EXPECT_COLOR_EQ(SK_ColorBLACK, pixel) << i << "," << j;
++num_black;
} else {
- EXPECT_EQ(255u, SkColorGetA(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(255u, SkColorGetR(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(255u, SkColorGetG(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(255u, SkColorGetB(pixels[i + j * bitmap.width()]));
+ EXPECT_COLOR_EQ(SK_ColorWHITE, pixel) << i << "," << j;
+ ++num_white;
+ }
+ }
+ }
+ EXPECT_GT(num_black, 0);
+ EXPECT_GT(num_white, 0);
+}
+
+TEST(RasterSourceTest, RasterPartialContentsWithRasterTranslation) {
+ gfx::Size layer_bounds(3, 5);
+
+ std::unique_ptr<FakeRecordingSource> recording_source =
+ FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
+ recording_source->SetBackgroundColor(SK_ColorGREEN);
+
+ // First record everything as white.
+ PaintFlags white_flags;
+ white_flags.setAntiAlias(true);
+ white_flags.setColor(SK_ColorWHITE);
+ recording_source->add_draw_rect_with_flags(gfx::Rect(layer_bounds),
+ white_flags);
+ recording_source->Rerecord();
+
+ scoped_refptr<RasterSource> raster = recording_source->CreateRasterSource();
+
+ gfx::Size content_bounds = layer_bounds;
+
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(content_bounds.width(), content_bounds.height());
+ SkCanvas canvas(bitmap);
+ canvas.clear(SK_ColorTRANSPARENT);
+
+ // Playback the full rect which should make everything white.
+ gfx::Rect raster_full_rect(content_bounds);
+ gfx::Rect playback_rect(content_bounds);
+ raster->PlaybackToCanvas(
+ &canvas, content_bounds, raster_full_rect, playback_rect,
+ gfx::AxisTransform2d(1.0f, gfx::Vector2dF(0.3f, 0.7f)),
+ RasterSource::PlaybackSettings());
+
+ for (int i = 0; i < bitmap.width(); ++i) {
+ for (int j = 0; j < bitmap.height(); ++j) {
+ SkColor pixel = bitmap.getColor(i, j);
+ EXPECT_EQ(255u, SkColorGetA(pixel)) << i << "," << j;
+ // Pixels on the top and left edges of the content are blended with
+ // the background.
+ bool expect_white = i && j;
+ EXPECT_EQ(expect_white, pixel == SK_ColorWHITE)
+ << Color{pixel} << " " << i << "," << j;
+ }
+ }
+
+ // Re-record everything as black.
+ PaintFlags black_flags;
+ black_flags.setColor(SK_ColorBLACK);
+ black_flags.setAntiAlias(true);
+ recording_source->add_draw_rect_with_flags(gfx::Rect(layer_bounds),
+ black_flags);
+ recording_source->Rerecord();
+
+ // Make a new RasterSource from the new recording.
+ raster = recording_source->CreateRasterSource();
+
+ // We're going to playback from "everything is black" into a smaller area,
+ // that touches the edge pixels of the recording.
+ playback_rect.Inset(1, 2, 0, 1);
+ raster->PlaybackToCanvas(
+ &canvas, content_bounds, raster_full_rect, playback_rect,
+ gfx::AxisTransform2d(1.0f, gfx::Vector2dF(0.3f, 0.7f)),
+ RasterSource::PlaybackSettings());
+
+ int num_black = 0;
+ int num_white = 0;
+ for (int i = 0; i < bitmap.width(); ++i) {
+ for (int j = 0; j < bitmap.height(); ++j) {
+ SkColor pixel = bitmap.getColor(i, j);
+ EXPECT_EQ(255u, SkColorGetA(pixel)) << i << "," << j;
+ bool expect_other = !i || !j;
+ bool expect_black = playback_rect.Contains(i, j);
+ bool expect_white = !expect_other && !expect_black;
+ if (expect_black) {
+ EXPECT_COLOR_EQ(SK_ColorBLACK, pixel) << i << "," << j;
+ ++num_black;
+ } else if (expect_white) {
+ EXPECT_COLOR_EQ(SK_ColorWHITE, pixel) << i << "," << j;
++num_white;
+ } else {
+ ASSERT_TRUE(expect_other);
+ EXPECT_TRUE(pixel != SK_ColorBLACK && pixel != SK_ColorWHITE)
+ << Color{pixel};
}
}
}
@@ -441,18 +587,10 @@ TEST(RasterSourceTest, RasterPartialClear) {
gfx::AxisTransform2d(contents_scale, gfx::Vector2dF()),
RasterSource::PlaybackSettings());
- {
- SkColor* pixels = reinterpret_cast<SkColor*>(bitmap.getPixels());
- for (int i = 0; i < bitmap.width(); ++i) {
- for (int j = 0; j < bitmap.height(); ++j) {
- SCOPED_TRACE(i);
- SCOPED_TRACE(j);
- EXPECT_EQ(alpha_dark, SkColorGetA(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(alpha_dark, SkColorGetR(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(alpha_dark, SkColorGetG(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(alpha_dark, SkColorGetB(pixels[i + j * bitmap.width()]));
- }
- }
+ SkColor pixel_dark = SkColorSetARGB(alpha_dark, 255, 255, 255);
+ for (int i = 0; i < bitmap.width(); ++i) {
+ for (int j = 0; j < bitmap.height(); ++j)
+ EXPECT_COLOR_EQ(pixel_dark, bitmap.getColor(i, j)) << i << "," << j;
}
std::unique_ptr<FakeRecordingSource> recording_source_light =
@@ -481,16 +619,10 @@ TEST(RasterSourceTest, RasterPartialClear) {
RasterSource::PlaybackSettings());
// Test that the whole playback_rect was cleared and repainted with new alpha.
- SkColor* pixels = reinterpret_cast<SkColor*>(bitmap.getPixels());
+ SkColor pixel_light = SkColorSetARGB(alpha_light, 255, 255, 255);
for (int i = 0; i < playback_rect.width(); ++i) {
- for (int j = 0; j < playback_rect.height(); ++j) {
- SCOPED_TRACE(j);
- SCOPED_TRACE(i);
- EXPECT_EQ(alpha_light, SkColorGetA(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(alpha_light, SkColorGetR(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(alpha_light, SkColorGetG(pixels[i + j * bitmap.width()]));
- EXPECT_EQ(alpha_light, SkColorGetB(pixels[i + j * bitmap.width()]));
- }
+ for (int j = 0; j < playback_rect.height(); ++j)
+ EXPECT_COLOR_EQ(pixel_light, bitmap.getColor(i, j)) << i << "," << j;
}
}
@@ -520,10 +652,9 @@ TEST(RasterSourceTest, RasterContentsTransparent) {
gfx::AxisTransform2d(contents_scale, gfx::Vector2dF()),
RasterSource::PlaybackSettings());
- SkColor* pixels = reinterpret_cast<SkColor*>(bitmap.getPixels());
- int num_pixels = bitmap.width() * bitmap.height();
- for (int i = 0; i < num_pixels; ++i) {
- EXPECT_EQ(SkColorGetA(pixels[i]), 0u);
+ for (int i = 0; i < bitmap.width(); i++) {
+ for (int j = 0; j < bitmap.height(); j++)
+ EXPECT_EQ(0u, SkColorGetA(bitmap.getColor(i, j))) << i << "," << j;
}
}
diff --git a/chromium/cc/raster/scoped_gpu_raster.cc b/chromium/cc/raster/scoped_gpu_raster.cc
index 3ba2be38286..2d8a5f94101 100644
--- a/chromium/cc/raster/scoped_gpu_raster.cc
+++ b/chromium/cc/raster/scoped_gpu_raster.cc
@@ -10,7 +10,7 @@
#include "gpu/command_buffer/client/gles2_interface.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
-#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/GrDirectContext.h"
using gpu::gles2::GLES2Interface;
@@ -35,7 +35,7 @@ void ScopedGpuRaster::BeginGpuRaster() {
#if defined(OS_ANDROID)
// TODO(crbug.com/832810): The following reset should not be necessary.
- GrContext* gr_context = context_provider_->GrContext();
+ GrDirectContext* gr_context = context_provider_->GrContext();
gr_context->resetContext();
#endif
}
diff --git a/chromium/cc/raster/task_category.h b/chromium/cc/raster/task_category.h
index 8b1addf23d8..894a5ec425a 100644
--- a/chromium/cc/raster/task_category.h
+++ b/chromium/cc/raster/task_category.h
@@ -13,9 +13,22 @@ namespace cc {
// We don't use an enum class here, as we want to keep TaskGraph::Node::category
// generic, allowing other consumers to provide their own of values.
enum TaskCategory : uint16_t {
+ // Cannot run at the same time as another non-concurrent task.
TASK_CATEGORY_NONCONCURRENT_FOREGROUND,
+ // Can run concurrently with other tasks.
TASK_CATEGORY_FOREGROUND,
+ // Can only start running when there are no foreground tasks to run. May run
+ // at background thread priority, which means that it may take time to
+ // complete once it starts running.
TASK_CATEGORY_BACKGROUND,
+ // Can only start running when there are no foreground tasks to run. Cannot
+ // run at background thread priority, which means that it takes a normal time
+ // to complete once it starts running. This is useful for a task that acquires
+ // resources that must not be held for a long time because they are shared
+ // with foreground work (e.g. a lock under which heavy work is performed).
+ TASK_CATEGORY_BACKGROUND_WITH_NORMAL_THREAD_PRIORITY,
+
+ LAST_TASK_CATEGORY = TASK_CATEGORY_BACKGROUND_WITH_NORMAL_THREAD_PRIORITY
};
} // namespace cc
diff --git a/chromium/cc/raster/tile_task.cc b/chromium/cc/raster/tile_task.cc
index 052a6b06b70..f0c1574663a 100644
--- a/chromium/cc/raster/tile_task.cc
+++ b/chromium/cc/raster/tile_task.cc
@@ -8,14 +8,14 @@
namespace cc {
-TileTask::TileTask(bool supports_concurrent_execution)
+TileTask::TileTask(
+ SupportsConcurrentExecution supports_concurrent_execution,
+ SupportsBackgroundThreadPriority supports_background_thread_priority,
+ TileTask::Vector* dependencies)
: supports_concurrent_execution_(supports_concurrent_execution),
- did_complete_(false) {}
-
-TileTask::TileTask(bool supports_concurrent_execution,
- TileTask::Vector* dependencies)
- : supports_concurrent_execution_(supports_concurrent_execution),
- dependencies_(std::move(*dependencies)),
+ supports_background_thread_priority_(supports_background_thread_priority),
+ dependencies_(dependencies ? std::move(*dependencies)
+ : TileTask::Vector()),
did_complete_(false) {}
TileTask::~TileTask() {
diff --git a/chromium/cc/raster/tile_task.h b/chromium/cc/raster/tile_task.h
index 75547ab48b0..d835dbe26f2 100644
--- a/chromium/cc/raster/tile_task.h
+++ b/chromium/cc/raster/tile_task.h
@@ -19,9 +19,16 @@ class CC_EXPORT TileTask : public Task {
// Indicates whether this TileTask can be run at the same time as other tasks
// in the task graph. If false, this task will be scheduled with
- // TASK_CATEGORY_NONCONCURRENT_FOREGROUND.
+ // TASK_CATEGORY_NONCONCURRENT_FOREGROUND. See comments in task_category.h.
bool supports_concurrent_execution() const {
- return supports_concurrent_execution_;
+ return supports_concurrent_execution_ == SupportsConcurrentExecution::kYes;
+ }
+
+ // Indicates whether this TileTask can run at background thread priority. See
+ // comments in task_category.h.
+ bool supports_background_thread_priority() const {
+ return supports_background_thread_priority_ ==
+ SupportsBackgroundThreadPriority::kYes;
}
// This function should be called from origin thread to process the completion
@@ -32,11 +39,16 @@ class CC_EXPORT TileTask : public Task {
bool HasCompleted() const;
protected:
- explicit TileTask(bool supports_concurrent_execution);
- TileTask(bool supports_concurrent_execution, TileTask::Vector* dependencies);
+ enum class SupportsConcurrentExecution { kYes, kNo };
+ enum class SupportsBackgroundThreadPriority { kYes, kNo };
+
+ TileTask(SupportsConcurrentExecution supports_concurrent_execution,
+ SupportsBackgroundThreadPriority supports_background_thread_priority,
+ TileTask::Vector* dependencies = nullptr);
~TileTask() override;
- const bool supports_concurrent_execution_;
+ const SupportsConcurrentExecution supports_concurrent_execution_;
+ const SupportsBackgroundThreadPriority supports_background_thread_priority_;
TileTask::Vector dependencies_;
bool did_complete_;
};
diff --git a/chromium/cc/raster/zero_copy_raster_buffer_provider.cc b/chromium/cc/raster/zero_copy_raster_buffer_provider.cc
index b364de68c58..21931163fd9 100644
--- a/chromium/cc/raster/zero_copy_raster_buffer_provider.cc
+++ b/chromium/cc/raster/zero_copy_raster_buffer_provider.cc
@@ -96,9 +96,10 @@ class ZeroCopyRasterBufferImpl : public RasterBuffer {
gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_SCANOUT;
// Make a mailbox for export of the GpuMemoryBuffer to the display
// compositor.
- backing_->mailbox = sii->CreateSharedImage(gpu_memory_buffer_.get(),
- gpu_memory_buffer_manager_,
- resource_color_space_, usage);
+ backing_->mailbox = sii->CreateSharedImage(
+ gpu_memory_buffer_.get(), gpu_memory_buffer_manager_,
+ resource_color_space_, kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
+ usage);
} else {
sii->UpdateSharedImage(backing_->returned_sync_token, backing_->mailbox);
}
@@ -146,6 +147,8 @@ class ZeroCopyRasterBufferImpl : public RasterBuffer {
gpu_memory_buffer_->Unmap();
}
+ bool SupportsBackgroundThreadPriority() const override { return true; }
+
private:
// This field may only be used on the compositor thread.
ZeroCopyGpuBacking* backing_;
diff --git a/chromium/cc/scheduler/commit_earlyout_reason.h b/chromium/cc/scheduler/commit_earlyout_reason.h
index 4dc8b98462b..7fe59e45962 100644
--- a/chromium/cc/scheduler/commit_earlyout_reason.h
+++ b/chromium/cc/scheduler/commit_earlyout_reason.h
@@ -11,7 +11,6 @@
namespace cc {
enum class CommitEarlyOutReason {
- ABORTED_LAYER_TREE_FRAME_SINK_LOST,
ABORTED_NOT_VISIBLE,
ABORTED_DEFERRED_MAIN_FRAME_UPDATE,
ABORTED_DEFERRED_COMMIT,
@@ -20,8 +19,6 @@ enum class CommitEarlyOutReason {
inline const char* CommitEarlyOutReasonToString(CommitEarlyOutReason reason) {
switch (reason) {
- case CommitEarlyOutReason::ABORTED_LAYER_TREE_FRAME_SINK_LOST:
- return "CommitEarlyOutReason::ABORTED_LAYER_TREE_FRAME_SINK_LOST";
case CommitEarlyOutReason::ABORTED_NOT_VISIBLE:
return "CommitEarlyOutReason::ABORTED_NOT_VISIBLE";
case CommitEarlyOutReason::ABORTED_DEFERRED_MAIN_FRAME_UPDATE:
diff --git a/chromium/cc/scheduler/scheduler.cc b/chromium/cc/scheduler/scheduler.cc
index 8292ee70801..a030e399643 100644
--- a/chromium/cc/scheduler/scheduler.cc
+++ b/chromium/cc/scheduler/scheduler.cc
@@ -5,6 +5,7 @@
#include "cc/scheduler/scheduler.h"
#include <algorithm>
+#include <utility>
#include <vector>
#include "base/auto_reset.h"
@@ -581,10 +582,12 @@ void Scheduler::FinishImplFrame() {
// ack for any pending begin frame if we are going idle after this. This
// ensures that the acks are sent in order.
if (!state_machine_.did_submit_in_last_frame()) {
+ bool is_waiting_on_main = state_machine_.begin_main_frame_state() !=
+ SchedulerStateMachine::BeginMainFrameState::IDLE;
SendDidNotProduceFrame(begin_impl_frame_tracker_.Current(),
- state_machine_.draw_succeeded_in_last_frame()
- ? FrameSkippedReason::kNoDamage
- : FrameSkippedReason::kWaitingOnMain);
+ is_waiting_on_main
+ ? FrameSkippedReason::kWaitingOnMain
+ : FrameSkippedReason::kNoDamage);
}
begin_impl_frame_tracker_.Finish();
@@ -626,8 +629,7 @@ void Scheduler::BeginImplFrame(const viz::BeginFrameArgs& args,
begin_impl_frame_tracker_.Start(args);
state_machine_.OnBeginImplFrame(args.frame_id, args.animate_only);
devtools_instrumentation::DidBeginFrame(layer_tree_host_id_);
- compositor_timing_history_->WillBeginImplFrame(
- args, state_machine_.NewActiveTreeLikely(), now);
+ compositor_timing_history_->WillBeginImplFrame(args, now);
bool has_damage =
client_->WillBeginImplFrame(begin_impl_frame_tracker_.Current());
diff --git a/chromium/cc/scheduler/scheduler_state_machine.cc b/chromium/cc/scheduler/scheduler_state_machine.cc
index 2dcc5e4bba7..84aa13ae985 100644
--- a/chromium/cc/scheduler/scheduler_state_machine.cc
+++ b/chromium/cc/scheduler/scheduler_state_machine.cc
@@ -872,9 +872,13 @@ void SchedulerStateMachine::WillCommit(bool commit_has_no_updates) {
has_pending_tree_ = true;
pending_tree_needs_first_draw_on_activation_ = true;
pending_tree_is_ready_for_activation_ = false;
- // Wait for the new pending tree to become ready to draw, which may happen
- // before or after activation.
- active_tree_is_ready_to_draw_ = false;
+ if (!active_tree_needs_first_draw_ ||
+ !settings_.wait_for_all_pipeline_stages_before_draw) {
+ // Wait for the new pending tree to become ready to draw, which may happen
+ // before or after activation (unless we're in full-pipeline mode and
+ // need first draw to come through).
+ active_tree_is_ready_to_draw_ = false;
+ }
}
// Update state related to forced draws.
@@ -1437,7 +1441,6 @@ void SchedulerStateMachine::BeginMainFrameAborted(CommitEarlyOutReason reason) {
main_thread_missed_last_deadline_ = false;
switch (reason) {
- case CommitEarlyOutReason::ABORTED_LAYER_TREE_FRAME_SINK_LOST:
case CommitEarlyOutReason::ABORTED_NOT_VISIBLE:
case CommitEarlyOutReason::ABORTED_DEFERRED_MAIN_FRAME_UPDATE:
case CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT:
diff --git a/chromium/cc/scheduler/scheduler_state_machine_unittest.cc b/chromium/cc/scheduler/scheduler_state_machine_unittest.cc
index 13fc6d4669a..f331078d623 100644
--- a/chromium/cc/scheduler/scheduler_state_machine_unittest.cc
+++ b/chromium/cc/scheduler/scheduler_state_machine_unittest.cc
@@ -11,6 +11,7 @@
#include "cc/scheduler/scheduler.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/test/begin_frame_args_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
// Macro to compare two enum values and get nice output.
@@ -21,7 +22,7 @@
// Value of: actual() Actual: "ACTION_DRAW"
// Expected: expected() Which is: "ACTION_NONE"
#define EXPECT_ENUM_EQ(enum_tostring, expected, actual) \
- EXPECT_EQ(enum_tostring(expected), enum_tostring(actual))
+ EXPECT_THAT(enum_tostring(actual), testing::Eq(enum_tostring(expected)))
#define EXPECT_IMPL_FRAME_STATE(expected) \
EXPECT_ENUM_EQ(BeginImplFrameStateToString, expected, \
@@ -2239,8 +2240,7 @@ TEST(SchedulerStateMachineTest,
// Abort the commit, since that is what we expect the main thread to do if the
// LayerTreeFrameSink was lost due to a synchronous call from the main thread
// to release the LayerTreeFrameSink.
- state.BeginMainFrameAborted(
- CommitEarlyOutReason::ABORTED_LAYER_TREE_FRAME_SINK_LOST);
+ state.BeginMainFrameAborted(CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT);
// The scheduler should begin the LayerTreeFrameSink creation now.
EXPECT_ACTION_UPDATE_STATE(
@@ -2901,5 +2901,50 @@ TEST(SchedulerStateMachineTest,
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::ACTIVATE_SYNC_TREE);
}
+TEST(SchedulerStateMachineTest, TestFullPipelineModeDoesntBlockAfterCommit) {
+ SchedulerSettings settings;
+ settings.wait_for_all_pipeline_stages_before_draw = true;
+ StateMachine state(settings);
+ SET_UP_STATE(state);
+
+ const bool needs_first_draw_on_activation = true;
+ state.SetNeedsImplSideInvalidation(needs_first_draw_on_activation);
+ state.SetNeedsBeginMainFrame();
+ state.SetNeedsRedraw(true);
+
+ viz::BeginFrameId frame_id = viz::BeginFrameId(0, 10);
+ state.OnBeginImplFrame(frame_id, kAnimateOnly);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME);
+ state.NotifyReadyToCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::NONE);
+
+ state.NotifyReadyToActivate();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::ACTIVATE_SYNC_TREE);
+ state.NotifyReadyToDraw();
+
+ EXPECT_TRUE(state.active_tree_needs_first_draw());
+ EXPECT_IMPL_FRAME_STATE(
+ SchedulerStateMachine::BeginImplFrameState::INSIDE_BEGIN_FRAME);
+ // Go all the way until ready to draw, but make sure we're not within
+ // the frame deadline, so actual draw doesn't happen...
+ EXPECT_FALSE(state.ShouldDraw());
+
+ // ... then have another commit ...
+ state.SetNeedsBeginMainFrame();
+ frame_id.sequence_number++;
+ state.OnBeginImplFrame(frame_id, kAnimateOnly);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME);
+ state.NotifyReadyToCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::COMMIT);
+ // ... and make sure we're in a state where we can proceed,
+ // rather than draw being blocked by the pending tree.
+ state.OnBeginImplFrameDeadline();
+ EXPECT_TRUE(state.ShouldDraw());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::Action::DRAW_IF_POSSIBLE);
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/scheduler/scheduler_unittest.cc b/chromium/cc/scheduler/scheduler_unittest.cc
index e4be2b3e68b..91e18c80b25 100644
--- a/chromium/cc/scheduler/scheduler_unittest.cc
+++ b/chromium/cc/scheduler/scheduler_unittest.cc
@@ -7,6 +7,7 @@
#include <stddef.h>
#include <string>
+#include <utility>
#include <vector>
#include "base/auto_reset.h"
@@ -3662,7 +3663,7 @@ TEST_F(SchedulerTest, NoLayerTreeFrameSinkCreationWhileCommitPending) {
client_->Reset();
scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks());
scheduler_->BeginMainFrameAborted(
- CommitEarlyOutReason::ABORTED_LAYER_TREE_FRAME_SINK_LOST);
+ CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT);
EXPECT_ACTIONS("ScheduledActionBeginLayerTreeFrameSinkCreation");
}
@@ -3858,11 +3859,40 @@ TEST_F(SchedulerTest, BeginFrameAckForFinishedImplFrame) {
has_damage = false;
EXPECT_EQ(viz::BeginFrameAck(args, has_damage),
client_->last_begin_frame_ack());
+ // The begin-main-frame sent has not been acknowledged yet (either by
+ // doing a commit, or aborting the draw from the main-thread).
EXPECT_EQ(FrameSkippedReason::kWaitingOnMain,
client_->last_frame_skipped_reason());
client_->Reset();
- // Draw succeeds, but 'swap' does not happen (i.e. no frame is submitted).
+ // Start another frame, and end the frame without drawing.
+ args = SendNextBeginFrame();
+ EXPECT_ACTIONS("WillBeginImplFrame");
+ EXPECT_TRUE(client_->IsInsideBeginImplFrame());
+ EXPECT_TRUE(scheduler_->begin_frames_expected());
+ client_->Reset();
+ client_->SetDrawWillHappen(false);
+ client_->SetSwapWillHappenIfDrawHappens(false);
+ task_runner_->RunPendingTasks(); // Run posted deadline.
+ EXPECT_ACTIONS("ScheduledActionDrawIfPossible");
+ // Draw with no damage.
+ has_damage = false;
+ EXPECT_EQ(viz::BeginFrameAck(args, has_damage),
+ client_->last_begin_frame_ack());
+ // The main thread still has not responded to the begin-main-frame.
+ EXPECT_EQ(FrameSkippedReason::kWaitingOnMain,
+ client_->last_frame_skipped_reason());
+ client_->Reset();
+
+ // Allow the commit now. NotifyReadyToCommit should trigger the commit.
+ scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks());
+ scheduler_->NotifyReadyToCommit(nullptr);
+ EXPECT_ACTIONS("ScheduledActionCommit");
+ EXPECT_TRUE(scheduler_->begin_frames_expected());
+ client_->Reset();
+
+ // Start another frame. For this frame, draw succeeds, but 'swap' does not
+ // happen (i.e. no frame is submitted).
args = SendNextBeginFrame();
EXPECT_ACTIONS("WillBeginImplFrame");
EXPECT_TRUE(client_->IsInsideBeginImplFrame());
diff --git a/chromium/cc/tiles/gpu_image_decode_cache.cc b/chromium/cc/tiles/gpu_image_decode_cache.cc
index 1ed44f98cb2..1bb3886bdd4 100644
--- a/chromium/cc/tiles/gpu_image_decode_cache.cc
+++ b/chromium/cc/tiles/gpu_image_decode_cache.cc
@@ -41,7 +41,7 @@
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/core/SkYUVAIndex.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/skia_util.h"
@@ -187,6 +187,7 @@ void SetYuvPixmapsFromSizeInfo(SkPixmap* pixmap_y,
const SkYUVASizeInfo& yuva_size_info,
void* planes[SkYUVASizeInfo::kMaxCount],
const SkImageInfo& info,
+ SkColorType color_type,
void* memory_ptr) {
DCHECK(pixmap_y);
DCHECK(pixmap_u);
@@ -204,7 +205,7 @@ void SetYuvPixmapsFromSizeInfo(SkPixmap* pixmap_y,
const size_t v_width = yuva_size_info.fSizes[SkYUVAIndex::kV_Index].width();
const size_t v_height = yuva_size_info.fSizes[SkYUVAIndex::kV_Index].height();
const SkImageInfo y_decode_info =
- info.makeColorType(kGray_8_SkColorType).makeWH(y_width, y_height);
+ info.makeColorType(color_type).makeWH(y_width, y_height);
const SkImageInfo u_decode_info = y_decode_info.makeWH(u_width, u_height);
const SkImageInfo v_decode_info = y_decode_info.makeWH(v_width, v_height);
yuva_size_info.computePlanes(memory_ptr, planes);
@@ -213,41 +214,6 @@ void SetYuvPixmapsFromSizeInfo(SkPixmap* pixmap_y,
pixmap_v->reset(v_decode_info, planes[SkYUVAIndex::kV_Index], v_width_bytes);
}
-// Helper method to fill in |scaled_u_size| and |scaled_v_size| by computing
-// the mip level for each plane given the final raster dimensions and the
-// unscaled U and V plane sizes. Also takes in |draw_image| to compute the Y
-// plane mip level and DCHECK that the computed mip levels for U and V are
-// reasonable.
-//
-// TODO(crbug.com/915972): Assumes 420 subsampling and DCHECKs the size ratios.
-void ComputeMippedUVPlaneSizes(const gfx::Size& target_raster_size,
- const gfx::Size& unscaled_u_size,
- const gfx::Size& unscaled_v_size,
- const DrawImage& draw_image,
- gfx::Size* scaled_u_size,
- gfx::Size* scaled_v_size) {
- DCHECK(scaled_u_size);
- DCHECK(scaled_v_size);
- DCHECK_EQ(unscaled_u_size, unscaled_v_size);
- DCHECK_EQ((draw_image.paint_image().width() + 1) / 2,
- unscaled_u_size.width());
- const int uv_mip_level =
- MipMapUtil::GetLevelForSize(unscaled_u_size, target_raster_size);
- // Check that the chroma planes do not shrink *more* than the luma.
- // At least for YUV420, they will shrink at most one mip level below luma,
- // which avoids blurriness.
- DCHECK_GE(uv_mip_level, 0);
- if (CalculateUploadScaleMipLevel(draw_image) == 0) {
- // If Y is not scaled, then U and V shouldn't be either.
- DCHECK_EQ(uv_mip_level, 0);
- } else {
- DCHECK_EQ(CalculateUploadScaleMipLevel(draw_image) - 1, uv_mip_level);
- }
-
- *scaled_u_size = MipMapUtil::GetSizeForLevel(unscaled_u_size, uv_mip_level);
- *scaled_v_size = *scaled_u_size;
-}
-
// Estimates the byte size of the decoded data for an image that goes through
// hardware decode acceleration. The actual byte size is only known once the
// image is decoded in the service side because different drivers have different
@@ -302,11 +268,13 @@ size_t EstimateHardwareDecodedDataSize(
//
// The |do_yuv_decode| parameter indicates whether YUV decoding can and should
// be done, which is a combination of the underlying data requesting YUV and the
-// cache mode (i.e. OOP-R or not) supporting it.
+// cache mode (i.e. OOP-R or not) supporting it. The |yuva_color_type| field
+// indicates which SkColorType should be used for each plane.
bool DrawAndScaleImage(const DrawImage& draw_image,
SkPixmap* target_pixmap,
PaintImage::GeneratorClientId client_id,
const bool do_yuv_decode,
+ const SkColorType yuva_color_type = kGray_8_SkColorType,
SkPixmap* pixmap_y = nullptr,
SkPixmap* pixmap_u = nullptr,
SkPixmap* pixmap_v = nullptr) {
@@ -332,6 +300,7 @@ bool DrawAndScaleImage(const DrawImage& draw_image,
SkYUVASizeInfo yuva_size_info;
SkYUVAIndex plane_indices[SkYUVAIndex::kIndexCount];
if (do_yuv_decode) {
+ // If |do_yuv_decode| is true, IsYuv() must be true.
const bool yuva_info_initialized =
paint_image.IsYuv(&yuva_size_info, plane_indices);
DCHECK(yuva_info_initialized);
@@ -348,9 +317,11 @@ bool DrawAndScaleImage(const DrawImage& draw_image,
if (do_yuv_decode) {
void* planes[SkYUVASizeInfo::kMaxCount];
SetYuvPixmapsFromSizeInfo(pixmap_y, pixmap_u, pixmap_v, yuva_size_info,
- planes, info, pixmap.writable_addr());
+ planes, info, yuva_color_type,
+ pixmap.writable_addr());
return paint_image.DecodeYuv(planes, draw_image.frame_index(), client_id,
- yuva_size_info, plane_indices);
+ yuva_size_info, yuva_color_type,
+ plane_indices);
}
return paint_image.Decode(pixmap.writable_addr(), &info, color_space,
draw_image.frame_index(), client_id);
@@ -373,7 +344,8 @@ bool DrawAndScaleImage(const DrawImage& draw_image,
// We temporarily abuse the dimensions of the pixmap to ensure we allocate
// the proper number of bytes, but the actual plane dimensions are stored in
// |yuva_size_info| and accessed within PaintImage::DecodeYuv() and below.
- decode_info = info.makeColorType(kGray_8_SkColorType).makeWH(yuva_bytes, 1);
+
+ decode_info = info.makeColorType(yuva_color_type).makeWH(yuva_bytes, 1);
} else {
SkISize decode_size =
is_nearest_neighbor
@@ -398,12 +370,12 @@ bool DrawAndScaleImage(const DrawImage& draw_image,
yuva_size_info.computePlanes(decode_pixmap.writable_addr(), planes);
}
bool initial_decode_failed =
- do_yuv_decode
- ? !paint_image.DecodeYuv(planes, draw_image.frame_index(), client_id,
- yuva_size_info, plane_indices)
- : !paint_image.Decode(decode_pixmap.writable_addr(), &decode_info,
- color_space, draw_image.frame_index(),
- client_id);
+ do_yuv_decode ? !paint_image.DecodeYuv(planes, draw_image.frame_index(),
+ client_id, yuva_size_info,
+ yuva_color_type, plane_indices)
+ : !paint_image.Decode(decode_pixmap.writable_addr(),
+ &decode_info, color_space,
+ draw_image.frame_index(), client_id);
if (initial_decode_failed)
return false;
@@ -417,26 +389,25 @@ bool DrawAndScaleImage(const DrawImage& draw_image,
SkPixmap unscaled_pixmap_v;
SetYuvPixmapsFromSizeInfo(&unscaled_pixmap_y, &unscaled_pixmap_u,
&unscaled_pixmap_v, yuva_size_info, planes,
- decode_info, decode_pixmap.writable_addr());
-
- const SkImageInfo y_info_scaled = info.makeColorType(kGray_8_SkColorType);
- // The target raster dimensions get passed through:
- // |target_pixmap|.info() -> |pixmap|->info() -> |info| -> |y_info_scaled|
- const gfx::Size target_raster_size(y_info_scaled.width(),
- y_info_scaled.height());
- gfx::Size unscaled_u_size(unscaled_pixmap_u.width(),
- unscaled_pixmap_u.height());
- gfx::Size unscaled_v_size(unscaled_pixmap_v.width(),
- unscaled_pixmap_v.height());
- gfx::Size scaled_u_size;
- gfx::Size scaled_v_size;
- ComputeMippedUVPlaneSizes(target_raster_size, unscaled_u_size,
- unscaled_v_size, draw_image, &scaled_u_size,
- &scaled_v_size);
- const SkImageInfo u_info_scaled =
- y_info_scaled.makeWH(scaled_u_size.width(), scaled_u_size.height());
- const SkImageInfo v_info_scaled =
- y_info_scaled.makeWH(scaled_v_size.width(), scaled_v_size.height());
+ decode_info, yuva_color_type,
+ decode_pixmap.writable_addr());
+
+ const SkImageInfo y_info_scaled = info.makeColorType(yuva_color_type);
+ const auto& yuva_sizes = yuva_size_info.fSizes;
+ DCHECK(yuva_sizes[SkYUVAIndex::kU_Index] ==
+ yuva_sizes[SkYUVAIndex::kV_Index]);
+
+ // Always promote scaled images to 4:4:4 to avoid blurriness. By using the
+ // same dimensions for the UV planes, we can avoid scaling them completely
+ // or at least avoid scaling the width.
+ //
+ // E.g., consider an original (100, 100) image scaled to mips level 1 (50%),
+ // the Y plane size will be (50, 50), but unscaled UV planes are already
+ // (50, 50) for 4:2:0, and (50, 100) for 4:2:2, so leaving them completely
+ // unscaled or only scaling the height for 4:2:2 has superior quality.
+ SkImageInfo u_info_scaled = y_info_scaled;
+ SkImageInfo v_info_scaled = y_info_scaled;
+
const size_t y_plane_bytes = y_info_scaled.computeMinByteSize();
const size_t u_plane_bytes = u_info_scaled.computeMinByteSize();
DCHECK(!SkImageInfo::ByteSizeOverflowed(y_plane_bytes));
@@ -459,7 +430,7 @@ bool DrawAndScaleImage(const DrawImage& draw_image,
// Takes ownership of the backing texture of an SkImage. This allows us to
// delete this texture under Skia (via discardable).
-sk_sp<SkImage> TakeOwnershipOfSkImageBacking(GrContext* context,
+sk_sp<SkImage> TakeOwnershipOfSkImageBacking(GrDirectContext* context,
sk_sp<SkImage> image) {
// If the image is not texture backed, it has no backing, just return it.
if (!image->isTextureBacked()) {
@@ -519,7 +490,8 @@ sk_sp<SkImage> MakeTextureImage(viz::RasterContextProvider* context,
// Step 2: Apply a color-space conversion if necessary.
if (uploaded_image && target_color_space) {
sk_sp<SkImage> pre_converted_image = uploaded_image;
- uploaded_image = uploaded_image->makeColorSpace(target_color_space);
+ uploaded_image = uploaded_image->makeColorSpace(target_color_space,
+ context->GrContext());
if (uploaded_image != pre_converted_image)
DeleteSkImageAndPreventCaching(context, std::move(pre_converted_image));
@@ -594,7 +566,8 @@ class GpuImageDecodeTaskImpl : public TileTask {
const DrawImage& draw_image,
const ImageDecodeCache::TracingInfo& tracing_info,
GpuImageDecodeCache::DecodeTaskType task_type)
- : TileTask(true),
+ : TileTask(TileTask::SupportsConcurrentExecution::kYes,
+ TileTask::SupportsBackgroundThreadPriority::kYes),
cache_(cache),
image_(draw_image),
tracing_info_(tracing_info),
@@ -646,7 +619,8 @@ class ImageUploadTaskImpl : public TileTask {
const DrawImage& draw_image,
scoped_refptr<TileTask> decode_dependency,
const ImageDecodeCache::TracingInfo& tracing_info)
- : TileTask(false),
+ : TileTask(TileTask::SupportsConcurrentExecution::kNo,
+ TileTask::SupportsBackgroundThreadPriority::kYes),
cache_(cache),
image_(draw_image),
tracing_info_(tracing_info) {
@@ -932,7 +906,8 @@ GpuImageDecodeCache::ImageData::ImageData(
bool can_do_hardware_accelerated_decode,
bool do_hardware_accelerated_decode,
bool is_yuv_format,
- SkYUVColorSpace yuv_cs)
+ SkYUVColorSpace yuv_cs,
+ SkColorType yuv_ct)
: paint_image_id(paint_image_id),
mode(mode),
size(size),
@@ -950,6 +925,7 @@ GpuImageDecodeCache::ImageData::ImageData(
if (is_yuv) {
DCHECK_LE(yuv_cs, SkYUVColorSpace::kLastEnum_SkYUVColorSpace);
yuv_color_space = yuv_cs;
+ yuv_color_type = yuv_ct;
}
}
@@ -1038,6 +1014,18 @@ GpuImageDecodeCache::GpuImageDecodeCache(
use_transfer_cache &&
context_->ContextSupport()->IsWebPDecodeAccelerationSupported() &&
base::FeatureList::IsEnabled(features::kVaapiWebPImageDecodeAcceleration);
+
+ {
+ // TODO(crbug.com/1110007): We shouldn't need to lock to get capabilities.
+ base::Optional<viz::RasterContextProvider::ScopedRasterContextLock>
+ context_lock;
+ if (context_->GetLock())
+ context_lock.emplace(context_);
+ const auto& caps = context_->ContextCapabilities();
+ allow_yuv_r16_ext_decoding_ = caps.texture_norm16;
+ allow_yuv_luminance_f16_decoding_ = caps.texture_half_float_linear;
+ }
+
// In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview).
// Don't register a dump provider in these cases.
if (base::ThreadTaskRunnerHandle::IsSet()) {
@@ -1183,7 +1171,7 @@ void GpuImageDecodeCache::UnrefImage(const DrawImage& draw_image) {
bool GpuImageDecodeCache::UseCacheForDrawImage(
const DrawImage& draw_image) const {
- if (draw_image.paint_image().GetSkImage()->isTextureBacked())
+ if (draw_image.paint_image().IsTextureBacked())
return false;
return true;
@@ -1929,7 +1917,7 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image,
NOTREACHED();
} else {
image_data->decode.SetBitmapImage(
- draw_image.paint_image().GetRasterSkImage());
+ draw_image.paint_image().GetSwSkImage());
}
return;
}
@@ -1972,8 +1960,9 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image,
SkPixmap pixmap_u;
SkPixmap pixmap_v;
if (!DrawAndScaleImage(draw_image, &pixmap, generator_client_id_,
- image_data->is_yuv, &pixmap_y, &pixmap_u,
- &pixmap_v)) {
+ image_data->is_yuv,
+ image_data->yuv_color_type.value(), &pixmap_y,
+ &pixmap_u, &pixmap_v)) {
DLOG(ERROR) << "DrawAndScaleImage failed.";
backing_memory->Unlock();
backing_memory.reset();
@@ -2082,6 +2071,12 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image,
color_space = nullptr;
}
+ // Will be nullptr for non-HDR images or when we're using the default level.
+ const bool needs_adjusted_color_space =
+ NeedsColorSpaceAdjustedForUpload(draw_image);
+ if (needs_adjusted_color_space)
+ decoded_target_colorspace = ColorSpaceForImageUpload(draw_image);
+
if (image_data->mode == DecodedDataMode::kTransferCache) {
DCHECK(use_transfer_cache_);
if (image_data->decode.do_hardware_accelerated_decode()) {
@@ -2093,7 +2088,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().GetRasterSkImage()->refEncodedData();
+ draw_image.paint_image().GetSwSkImage()->refEncodedData();
DCHECK(encoded_data);
const uint32_t transfer_cache_id =
ClientImageTransferCacheEntry::GetNextId();
@@ -2143,6 +2138,9 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image,
SkPixmap pixmap;
if (!image_data->decode.image()->peekPixels(&pixmap))
return;
+ if (needs_adjusted_color_space)
+ pixmap.setColorSpace(decoded_target_colorspace);
+
ClientImageTransferCacheEntry image_entry(&pixmap, color_space.get(),
image_data->needs_mips);
InsertTransferCacheEntry(image_entry, image_data);
@@ -2245,6 +2243,12 @@ void GpuImageDecodeCache::UploadImageIfNecessary(const DrawImage& draw_image,
return;
}
+ // TODO(crbug.com/1120719): The RGBX path is broken for HDR images.
+ if (needs_adjusted_color_space) {
+ uploaded_image =
+ uploaded_image->reinterpretColorSpace(decoded_target_colorspace);
+ }
+
// RGBX decoding is below.
// For kGpu, we upload and color convert (if necessary).
if (image_data->mode == DecodedDataMode::kGpu) {
@@ -2339,7 +2343,6 @@ GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image,
// - The caller allows hardware decodes.
// - We are using the transfer cache (OOP-R).
// - The image does not require downscaling for uploading (see TODO below).
- // - The image does not require subsetting.
// - The image is supported according to the profiles advertised by the GPU
// service.
//
@@ -2352,7 +2355,6 @@ GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image,
bool do_hardware_accelerated_decode = false;
if (allow_hardware_decode && mode == DecodedDataMode::kTransferCache &&
upload_scale_mip_level == 0 &&
- draw_image.paint_image().subset_rect().IsEmpty() &&
context_->ContextSupport()->CanDecodeWithHardwareAcceleration(
image_metadata)) {
DCHECK(image_metadata);
@@ -2373,40 +2375,43 @@ GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image,
}
SkYUVASizeInfo target_yuva_size_info;
- // We fill out a default value for |yuv_color_space| but only fill out the
- // base::Optional member in ImageData if it is YUV.
+ // We fill out a default value for |yuv_color_space| and |yuv_color_type| but
+ // only fill out the base::Optional members in ImageData if it is YUV.
SkYUVColorSpace yuv_color_space = SkYUVColorSpace::kIdentity_SkYUVColorSpace;
+ SkColorType yuv_color_type = kGray_8_SkColorType;
+ uint8_t yuv_bit_depth = 8;
const bool is_yuv =
!do_hardware_accelerated_decode &&
draw_image.paint_image().IsYuv(&target_yuva_size_info,
nullptr /* plane_indices */,
- &yuv_color_space) &&
- mode != DecodedDataMode::kCpu && !image_larger_than_max_texture;
+ &yuv_color_space, &yuv_bit_depth) &&
+ mode != DecodedDataMode::kCpu && !image_larger_than_max_texture &&
+ (yuv_bit_depth == 8 || allow_yuv_r16_ext_decoding_ ||
+ allow_yuv_luminance_f16_decoding_);
+
// TODO(crbug.com/910276): Change after alpha support.
if (is_yuv) {
- const gfx::Size target_raster_size(image_info.width(), image_info.height());
- gfx::Size unscaled_u_size(
- target_yuva_size_info.fSizes[SkYUVAIndex::kU_Index].width(),
- target_yuva_size_info.fSizes[SkYUVAIndex::kU_Index].height());
- gfx::Size unscaled_v_size(
- target_yuva_size_info.fSizes[SkYUVAIndex::kV_Index].width(),
- target_yuva_size_info.fSizes[SkYUVAIndex::kV_Index].height());
- gfx::Size scaled_u_size;
- gfx::Size scaled_v_size;
- ComputeMippedUVPlaneSizes(target_raster_size, unscaled_u_size,
- unscaled_v_size, draw_image, &scaled_u_size,
- &scaled_v_size);
-
- size_t y_size_bytes =
- target_yuva_size_info.fWidthBytes[SkYUVAIndex::kY_Index] *
- target_yuva_size_info.fSizes[SkYUVAIndex::kY_Index].height();
- size_t u_size_bytes =
- target_yuva_size_info.fWidthBytes[SkYUVAIndex::kU_Index] *
- target_yuva_size_info.fSizes[SkYUVAIndex::kU_Index].height();
- size_t v_size_bytes =
- target_yuva_size_info.fWidthBytes[SkYUVAIndex::kV_Index] *
- target_yuva_size_info.fSizes[SkYUVAIndex::kV_Index].height();
- data_size = y_size_bytes + u_size_bytes + v_size_bytes;
+ DCHECK_GE(yuv_bit_depth, 8u);
+ DCHECK_LE(yuv_bit_depth, 16);
+ if (yuv_bit_depth == 8)
+ yuv_color_type = kGray_8_SkColorType;
+ else if (allow_yuv_r16_ext_decoding_)
+ yuv_color_type = kA16_unorm_SkColorType;
+ else if (allow_yuv_luminance_f16_decoding_)
+ yuv_color_type = kA16_float_SkColorType;
+
+ if (upload_scale_mip_level > 0) {
+ // Scaled decode. We always promote to 4:4:4 when scaling YUV to avoid
+ // blurriness. See comment in DrawAndScaleImage() for details.
+ const base::CheckedNumeric<size_t> y_plane_size =
+ image_info.makeColorType(yuv_color_type).computeMinByteSize();
+ DCHECK(!SkImageInfo::ByteSizeOverflowed(y_plane_size.ValueOrDie()));
+ data_size = (3 * y_plane_size).ValueOrDie();
+ } else {
+ // Original size decode.
+ data_size = target_yuva_size_info.computeTotalBytes();
+ DCHECK(!SkImageInfo::ByteSizeOverflowed(data_size));
+ }
}
return base::WrapRefCounted(new ImageData(
@@ -2414,7 +2419,7 @@ GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image,
draw_image.target_color_space(),
CalculateDesiredFilterQuality(draw_image), upload_scale_mip_level,
needs_mips, is_bitmap_backed, can_do_hardware_accelerated_decode,
- do_hardware_accelerated_decode, is_yuv, yuv_color_space));
+ do_hardware_accelerated_decode, is_yuv, yuv_color_space, yuv_color_type));
}
void GpuImageDecodeCache::WillAddCacheEntry(const DrawImage& draw_image) {
@@ -2605,7 +2610,19 @@ SkImageInfo GpuImageDecodeCache::CreateImageInfoForDrawImage(
int upload_scale_mip_level) const {
gfx::Size mip_size =
CalculateSizeForMipLevel(draw_image, upload_scale_mip_level);
- return SkImageInfo::Make(mip_size.width(), mip_size.height(), color_type_,
+
+ // Decode HDR images to half float when targeting HDR.
+ //
+ // TODO(crbug.com/1076568): Once we have access to the display's buffer format
+ // via gfx::DisplayColorSpaces, we should also do this for HBD images.
+ auto color_type = color_type_;
+ if (draw_image.paint_image().GetContentColorUsage() ==
+ gfx::ContentColorUsage::kHDR &&
+ draw_image.target_color_space().IsHDR()) {
+ color_type = kRGBA_F16_SkColorType;
+ }
+
+ return SkImageInfo::Make(mip_size.width(), mip_size.height(), color_type,
kPremul_SkAlphaType);
}
@@ -2791,6 +2808,8 @@ sk_sp<SkImage> GpuImageDecodeCache::GetUploadedPlaneForTesting(
base::AutoLock lock(lock_);
ImageData* image_data = GetImageDataForDrawImage(
draw_image, InUseCacheKey::FromDrawImage(draw_image));
+ if (!image_data->is_yuv)
+ return nullptr;
switch (index) {
case SkYUVAIndex::kY_Index:
return image_data->upload.y_image();
@@ -2842,6 +2861,21 @@ sk_sp<SkColorSpace> GpuImageDecodeCache::ColorSpaceForImageDecode(
return sk_ref_sp(image.paint_image().color_space());
}
+bool GpuImageDecodeCache::NeedsColorSpaceAdjustedForUpload(
+ const DrawImage& image) const {
+ return image.sdr_white_level() != gfx::ColorSpace::kDefaultSDRWhiteLevel &&
+ image.paint_image().GetContentColorUsage() ==
+ gfx::ContentColorUsage::kHDR;
+}
+
+sk_sp<SkColorSpace> GpuImageDecodeCache::ColorSpaceForImageUpload(
+ const DrawImage& image) const {
+ DCHECK(NeedsColorSpaceAdjustedForUpload(image));
+ return gfx::ColorSpace(*image.paint_image().color_space())
+ .GetWithSDRWhiteLevel(image.sdr_white_level())
+ .ToSkColorSpace();
+}
+
void GpuImageDecodeCache::CheckContextLockAcquiredIfNecessary() {
if (!context_->GetLock())
return;
@@ -2882,7 +2916,8 @@ sk_sp<SkImage> GpuImageDecodeCache::CreateImageFromYUVATexturesInternal(
SkISize::Make(image_width, image_height), origin_temp,
std::move(decoded_color_space));
if (target_color_space)
- return yuva_image->makeColorSpace(target_color_space);
+ return yuva_image->makeColorSpace(target_color_space,
+ context_->GrContext());
return yuva_image;
}
@@ -2962,15 +2997,17 @@ void GpuImageDecodeCache::UpdateMipsIfNeeded(const DrawImage& draw_image,
draw_image.target_color_space().IsValid()
? draw_image.target_color_space().ToSkColorSpace()
: nullptr;
- sk_sp<SkColorSpace> decoded_color_space =
- ColorSpaceForImageDecode(draw_image, image_data->mode);
+ sk_sp<SkColorSpace> upload_color_space =
+ NeedsColorSpaceAdjustedForUpload(draw_image)
+ ? ColorSpaceForImageUpload(draw_image)
+ : ColorSpaceForImageDecode(draw_image, image_data->mode);
DCHECK(image_data->yuv_color_space.has_value());
sk_sp<SkImage> yuv_image_with_mips_owned =
CreateImageFromYUVATexturesInternal(
image_y_with_mips_owned.get(), image_u_with_mips_owned.get(),
image_v_with_mips_owned.get(), width, height,
image_data->yuv_color_space.value(), color_space,
- decoded_color_space);
+ upload_color_space);
// In case of lost context
if (!yuv_image_with_mips_owned) {
DLOG(WARNING) << "TODO(crbug.com/740737): Context was lost. Early out.";
@@ -3003,6 +3040,15 @@ void GpuImageDecodeCache::UpdateMipsIfNeeded(const DrawImage& draw_image,
// delete, delaying deletion.
sk_sp<SkImage> previous_image = image_data->upload.image();
+#if DCHECK_IS_ON()
+ // For already uploaded images, the correct color space should already have
+ // been set during the upload process.
+ if (NeedsColorSpaceAdjustedForUpload(draw_image)) {
+ DCHECK(SkColorSpace::Equals(previous_image->colorSpace(),
+ ColorSpaceForImageUpload(draw_image).get()));
+ }
+#endif
+
// Generate a new image from the previous, adding mips.
sk_sp<SkImage> image_with_mips = previous_image->makeTextureImage(
context_->GrContext(), GrMipMapped::kYes);
diff --git a/chromium/cc/tiles/gpu_image_decode_cache.h b/chromium/cc/tiles/gpu_image_decode_cache.h
index 7c345dbbbd5..a65ba526027 100644
--- a/chromium/cc/tiles/gpu_image_decode_cache.h
+++ b/chromium/cc/tiles/gpu_image_decode_cache.h
@@ -6,6 +6,7 @@
#define CC_TILES_GPU_IMAGE_DECODE_CACHE_H_
#include <memory>
+#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
@@ -507,7 +508,8 @@ class CC_EXPORT GpuImageDecodeCache
bool can_do_hardware_accelerated_decode,
bool do_hardware_accelerated_decode,
bool is_yuv_format,
- SkYUVColorSpace yuv_cs);
+ SkYUVColorSpace yuv_cs,
+ SkColorType yuv_ct);
bool IsGpuOrTransferCache() const;
bool HasUploadedData() const;
@@ -524,6 +526,7 @@ class CC_EXPORT GpuImageDecodeCache
bool is_yuv;
bool is_budgeted = false;
base::Optional<SkYUVColorSpace> yuv_color_space;
+ base::Optional<SkColorType> yuv_color_type;
// If true, this image is no longer in our |persistent_cache_| and will be
// deleted as soon as its ref count reaches zero.
@@ -672,6 +675,11 @@ class CC_EXPORT GpuImageDecodeCache
sk_sp<SkColorSpace> ColorSpaceForImageDecode(const DrawImage& image,
DecodedDataMode mode) const;
+ // HDR images need the SkColorSpace adjusted during upload to avoid white
+ // level issues on systems with variable SDR white levels (Windows).
+ bool NeedsColorSpaceAdjustedForUpload(const DrawImage& image) const;
+ sk_sp<SkColorSpace> ColorSpaceForImageUpload(const DrawImage& image) const;
+
// Helper function to add a memory dump to |pmd| for a single texture
// identified by |gl_id| with size |bytes| and |locked_size| equal to either
// |bytes| or 0 depending on whether the texture is currently locked.
@@ -712,6 +720,8 @@ class CC_EXPORT GpuImageDecodeCache
const PaintImage::GeneratorClientId generator_client_id_;
bool allow_accelerated_jpeg_decodes_ = false;
bool allow_accelerated_webp_decodes_ = false;
+ bool allow_yuv_r16_ext_decoding_ = false;
+ bool allow_yuv_luminance_f16_decoding_ = false;
// All members below this point must only be accessed while holding |lock_|.
// The exception are const members like |normal_max_cache_bytes_| that can
diff --git a/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc b/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc
index 416cd8233c8..ad32e982078 100644
--- a/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc
+++ b/chromium/cc/tiles/gpu_image_decode_cache_perftest.cc
@@ -15,6 +15,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_result_reporter.h"
#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/gpu/GrDirectContext.h"
namespace cc {
namespace {
diff --git a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc
index 3c9db3a438e..0cf83256910 100644
--- a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc
+++ b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc
@@ -4,7 +4,12 @@
#include "cc/tiles/gpu_image_decode_cache.h"
+#include <algorithm>
+#include <limits>
+#include <map>
#include <memory>
+#include <string>
+#include <tuple>
#include <vector>
#include "base/feature_list.h"
@@ -27,7 +32,7 @@
#include "third_party/skia/include/core/SkImageGenerator.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
-#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/GrDirectContext.h"
using testing::_;
using testing::StrictMock;
@@ -117,7 +122,8 @@ class FakeGPUImageDecodeTestGLES2Interface : public viz::TestGLES2Interface,
: extension_string_(
"GL_EXT_texture_format_BGRA8888 GL_OES_rgb8_rgba8 "
"GL_OES_texture_npot GL_EXT_texture_rg "
- "GL_OES_texture_half_float GL_OES_texture_half_float_linear"),
+ "GL_OES_texture_half_float GL_OES_texture_half_float_linear "
+ "GL_EXT_texture_norm16"),
discardable_manager_(discardable_manager),
transfer_cache_helper_(transfer_cache_helper),
advertise_accelerated_decoding_(advertise_accelerated_decoding) {}
@@ -303,6 +309,17 @@ class GPUImageDecodeTestMockContextProvider : public viz::TestContextProvider {
std::move(support), std::move(gl), std::move(raster));
}
+ void SetContextCapabilitiesOverride(base::Optional<gpu::Capabilities> caps) {
+ capabilities_override_ = caps;
+ }
+
+ const gpu::Capabilities& ContextCapabilities() const override {
+ if (capabilities_override_.has_value())
+ return *capabilities_override_;
+
+ return viz::TestContextProvider::ContextCapabilities();
+ }
+
private:
~GPUImageDecodeTestMockContextProvider() override = default;
GPUImageDecodeTestMockContextProvider(
@@ -314,6 +331,8 @@ class GPUImageDecodeTestMockContextProvider : public viz::TestContextProvider {
std::move(raster),
nullptr /* sii */,
true) {}
+
+ base::Optional<gpu::Capabilities> capabilities_override_;
};
SkMatrix CreateMatrix(const SkSize& scale) {
@@ -395,28 +414,32 @@ class GpuImageDecodeCacheTest
sk_sp<SkColorSpace> color_space = nullptr,
PaintImage::Id id = PaintImage::kInvalidId) {
const bool allocate_encoded_memory = true;
- return CreateDiscardablePaintImage(size, color_space,
- allocate_encoded_memory, id, color_type_,
- do_yuv_decode_);
- }
- PaintImage CreateLargePaintImageForSoftwareFallback() {
- return CreateLargePaintImageForSoftwareFallback(GetLargeImageSize());
+ if (do_yuv_decode_) {
+ return CreateDiscardablePaintImage(
+ size, color_space, allocate_encoded_memory, id, color_type_,
+ yuv_format_, yuv_bytes_per_pixel_);
+ }
+ return CreateDiscardablePaintImage(
+ size, color_space, allocate_encoded_memory, id, color_type_);
}
// Create an image that's too large to upload and will trigger falling back to
// software rendering and decoded data storage.
- PaintImage CreateLargePaintImageForSoftwareFallback(
- const gfx::Size test_image_size) {
- CHECK(test_image_size.width() > max_texture_size_ ||
- test_image_size.height() > max_texture_size_);
+ PaintImage CreateLargePaintImageForSoftwareFallback() {
+ return CreatePaintImageForFallbackToRGB(GetLargeImageSize());
+ }
+
+ PaintImage CreatePaintImageForFallbackToRGB(const gfx::Size test_image_size) {
SkImageInfo info = SkImageInfo::Make(
test_image_size.width(), test_image_size.height(), color_type_,
kPremul_SkAlphaType, SkColorSpace::MakeSRGB());
sk_sp<FakePaintImageGenerator> generator;
if (do_yuv_decode_) {
generator = sk_make_sp<FakePaintImageGenerator>(
- info, GetYUV420SizeInfo(test_image_size));
+ info,
+ GetYUVASizeInfo(test_image_size, yuv_format_, yuv_bytes_per_pixel_),
+ yuv_bytes_per_pixel_ * 8);
generator->SetExpectFallbackToRGB();
} else {
generator = sk_make_sp<FakePaintImageGenerator>(info);
@@ -445,7 +468,8 @@ class GpuImageDecodeCacheTest
gfx::ColorSpace* color_space = nullptr,
SkFilterQuality filter_quality = kMedium_SkFilterQuality,
SkIRect* src_rect = nullptr,
- size_t frame_index = PaintImage::kDefaultFrameIndex) {
+ size_t frame_index = PaintImage::kDefaultFrameIndex,
+ float sdr_white_level = gfx::ColorSpace::kDefaultSDRWhiteLevel) {
SkIRect src_rectangle;
gfx::ColorSpace cs;
if (!src_rect) {
@@ -458,7 +482,7 @@ class GpuImageDecodeCacheTest
color_space = &cs;
}
return DrawImage(paint_image, *src_rect, filter_quality, matrix,
- frame_index, *color_space);
+ frame_index, *color_space, sdr_white_level);
}
GPUImageDecodeTestMockContextProvider* context_provider() {
@@ -466,9 +490,10 @@ class GpuImageDecodeCacheTest
}
size_t GetBytesNeededForSingleImage(gfx::Size image_dimensions) {
- // TODO(crbug.com/915972): Assumes YUV 420.
if (do_yuv_decode_) {
- return GetYUV420SizeInfo(image_dimensions).computeTotalBytes();
+ return GetYUVASizeInfo(image_dimensions, yuv_format_,
+ yuv_bytes_per_pixel_)
+ .computeTotalBytes();
}
const size_t test_image_area_bytes =
base::checked_cast<size_t>(image_dimensions.GetArea());
@@ -548,7 +573,9 @@ class GpuImageDecodeCacheTest
GpuImageDecodeCache* cache,
const DrawImage& draw_image,
const base::Optional<uint32_t> transfer_cache_id,
- const SkISize plane_sizes[SkYUVASizeInfo::kMaxCount]) {
+ const SkISize plane_sizes[SkYUVASizeInfo::kMaxCount],
+ SkColorType expected_type = kGray_8_SkColorType,
+ const SkColorSpace* expected_cs = nullptr) {
for (size_t i = 0; i < SkYUVASizeInfo::kMaxCount; ++i) {
// TODO(crbug.com/910276): Skip alpha plane until supported in cache.
if (i != SkYUVAIndex::kA_Index) {
@@ -565,6 +592,13 @@ class GpuImageDecodeCacheTest
}
ASSERT_TRUE(uploaded_plane);
EXPECT_EQ(plane_sizes[i], uploaded_plane->dimensions());
+ EXPECT_EQ(expected_type, uploaded_plane->colorType());
+ if (expected_cs && use_transfer_cache_) {
+ EXPECT_TRUE(
+ SkColorSpace::Equals(expected_cs, uploaded_plane->colorSpace()));
+ } else if (expected_cs) {
+ // In-process raster sets the ColorSpace on the composite SkImage.
+ }
}
}
}
@@ -578,6 +612,10 @@ class GpuImageDecodeCacheTest
TransferCacheTestHelper transfer_cache_helper_;
scoped_refptr<GPUImageDecodeTestMockContextProvider> context_provider_;
+ // Only used when |do_yuv_decode_| is true.
+ uint8_t yuv_bytes_per_pixel_ = 1;
+ YUVSubsampling yuv_format_ = YUVSubsampling::k420;
+
bool use_transfer_cache_;
SkColorType color_type_;
bool do_yuv_decode_;
@@ -1058,6 +1096,105 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDraw) {
cache->UnrefImage(draw_image);
}
+TEST_P(GpuImageDecodeCacheTest, GetHdrDecodedImageForDrawToHdr) {
+ auto cache = CreateCache();
+ auto color_space = gfx::ColorSpace::CreateHDR10();
+ auto size = GetNormalImageSize();
+ auto info =
+ SkImageInfo::Make(size.width(), size.height(), kRGBA_F16_SkColorType,
+ kPremul_SkAlphaType, color_space.ToSkColorSpace());
+ SkBitmap bitmap;
+ bitmap.allocPixels(info);
+ PaintImage image = PaintImageBuilder::WithDefault()
+ .set_id(PaintImage::kInvalidId)
+ .set_is_high_bit_depth(true)
+ .set_image(SkImage::MakeFromBitmap(bitmap),
+ PaintImage::GetNextContentId())
+ .TakePaintImage();
+
+ constexpr float kCustomWhiteLevel = 200.f;
+ DrawImage draw_image = CreateDrawImageInternal(
+ image, CreateMatrix(SkSize::Make(0.5f, 0.5f)), &color_space,
+ kMedium_SkFilterQuality, nullptr, PaintImage::kDefaultFrameIndex,
+ kCustomWhiteLevel);
+ ImageDecodeCache::TaskResult result =
+ cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
+ EXPECT_EQ(draw_image.target_color_space(), color_space);
+ EXPECT_TRUE(result.need_unref);
+ EXPECT_TRUE(result.task);
+ EXPECT_EQ(result.task->dependencies().size(), 1u);
+ EXPECT_TRUE(result.task->dependencies()[0]);
+
+ TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get());
+ TestTileTaskRunner::ProcessTask(result.task.get());
+
+ // Must hold context lock before calling GetDecodedImageForDraw /
+ // DrawWithImageFinished.
+ viz::ContextProvider::ScopedContextLock context_lock(context_provider());
+ DecodedDrawImage decoded_draw_image =
+ EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image));
+ EXPECT_TRUE(decoded_draw_image.image());
+ EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked());
+ EXPECT_TRUE(decoded_draw_image.is_budgeted());
+ EXPECT_EQ(decoded_draw_image.image()->colorType(), kRGBA_F16_SkColorType);
+
+ auto cs = gfx::ColorSpace(*decoded_draw_image.image()->colorSpace());
+ float sdr_white_level;
+ ASSERT_TRUE(cs.GetPQSDRWhiteLevel(&sdr_white_level));
+ EXPECT_FLOAT_EQ(sdr_white_level, kCustomWhiteLevel);
+
+ EXPECT_FALSE(cache->DiscardableIsLockedForTesting(draw_image));
+
+ cache->DrawWithImageFinished(draw_image, decoded_draw_image);
+ cache->UnrefImage(draw_image);
+}
+
+TEST_P(GpuImageDecodeCacheTest, GetHdrDecodedImageForDrawToSdr) {
+ auto cache = CreateCache();
+ auto image_color_space = gfx::ColorSpace::CreateHDR10();
+ auto size = GetNormalImageSize();
+ auto info = SkImageInfo::Make(size.width(), size.height(),
+ kRGBA_F16_SkColorType, kPremul_SkAlphaType,
+ image_color_space.ToSkColorSpace());
+ SkBitmap bitmap;
+ bitmap.allocPixels(info);
+ PaintImage image = PaintImageBuilder::WithDefault()
+ .set_id(PaintImage::kInvalidId)
+ .set_is_high_bit_depth(true)
+ .set_image(SkImage::MakeFromBitmap(bitmap),
+ PaintImage::GetNextContentId())
+ .TakePaintImage();
+
+ auto raster_color_space = gfx::ColorSpace::CreateSRGB();
+ DrawImage draw_image = CreateDrawImageInternal(
+ image, CreateMatrix(SkSize::Make(0.5f, 0.5f)), &raster_color_space);
+ ImageDecodeCache::TaskResult result =
+ cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
+ EXPECT_EQ(draw_image.target_color_space(), raster_color_space);
+ EXPECT_TRUE(result.need_unref);
+ EXPECT_TRUE(result.task);
+ EXPECT_EQ(result.task->dependencies().size(), 1u);
+ EXPECT_TRUE(result.task->dependencies()[0]);
+
+ TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get());
+ TestTileTaskRunner::ProcessTask(result.task.get());
+
+ // Must hold context lock before calling GetDecodedImageForDraw /
+ // DrawWithImageFinished.
+ viz::ContextProvider::ScopedContextLock context_lock(context_provider());
+ DecodedDrawImage decoded_draw_image =
+ EnsureImageBacked(cache->GetDecodedImageForDraw(draw_image));
+ EXPECT_TRUE(decoded_draw_image.image());
+ EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked());
+ EXPECT_TRUE(decoded_draw_image.is_budgeted());
+ EXPECT_NE(decoded_draw_image.image()->colorType(), kRGBA_F16_SkColorType);
+
+ EXPECT_FALSE(cache->DiscardableIsLockedForTesting(draw_image));
+
+ cache->DrawWithImageFinished(draw_image, decoded_draw_image);
+ cache->UnrefImage(draw_image);
+}
+
TEST_P(GpuImageDecodeCacheTest, GetLargeDecodedImageForDraw) {
auto cache = CreateCache();
PaintImage image = CreateLargePaintImageForSoftwareFallback();
@@ -1249,7 +1386,7 @@ TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawNegative) {
TEST_P(GpuImageDecodeCacheTest, GetLargeScaledDecodedImageForDraw) {
auto cache = CreateCache();
- PaintImage image = CreateLargePaintImageForSoftwareFallback(
+ PaintImage image = CreatePaintImageForFallbackToRGB(
gfx::Size(GetLargeImageSize().width(), GetLargeImageSize().height() * 2));
DrawImage draw_image =
CreateDrawImageInternal(image, CreateMatrix(SkSize::Make(0.5f, 0.5f)),
@@ -1932,7 +2069,10 @@ TEST_P(GpuImageDecodeCacheTest, CacheDecodesExpectedFrames) {
color_type_, kPremul_SkAlphaType);
sk_sp<FakePaintImageGenerator> generator =
do_yuv_decode_ ? sk_make_sp<FakePaintImageGenerator>(
- info, GetYUV420SizeInfo(test_image_size), frames)
+ info,
+ GetYUVASizeInfo(test_image_size, yuv_format_,
+ yuv_bytes_per_pixel_),
+ yuv_bytes_per_pixel_ * 8, frames)
: sk_make_sp<FakePaintImageGenerator>(info, frames);
PaintImage image = PaintImageBuilder::WithDefault()
.set_id(PaintImage::GetNextId())
@@ -2781,49 +2921,265 @@ TEST_P(GpuImageDecodeCacheTest,
// planes.
return;
}
- auto cache = CreateCache();
- SkFilterQuality filter_quality = kMedium_SkFilterQuality;
- SkSize requires_decode_at_original_scale = SkSize::Make(0.8f, 0.8f);
+ auto owned_cache = CreateCache();
+ auto decode_and_check_plane_sizes = [this, cache = owned_cache.get()]() {
+ SkFilterQuality filter_quality = kMedium_SkFilterQuality;
+ SkSize requires_decode_at_original_scale = SkSize::Make(0.8f, 0.8f);
- PaintImage image = CreatePaintImageInternal(GetNormalImageSize());
- DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
- filter_quality,
- CreateMatrix(requires_decode_at_original_scale),
- PaintImage::kDefaultFrameIndex, DefaultColorSpace());
- ImageDecodeCache::TaskResult result =
- cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
- EXPECT_TRUE(result.need_unref);
- EXPECT_TRUE(result.task);
+ PaintImage image = CreatePaintImageInternal(GetNormalImageSize());
+ DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
+ filter_quality,
+ CreateMatrix(requires_decode_at_original_scale),
+ PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+ ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(
+ draw_image, ImageDecodeCache::TracingInfo());
+ EXPECT_TRUE(result.need_unref);
+ EXPECT_TRUE(result.task);
- TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get());
- TestTileTaskRunner::ProcessTask(result.task.get());
+ TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get());
+ TestTileTaskRunner::ProcessTask(result.task.get());
- // Must hold context lock before calling GetDecodedImageForDraw /
- // DrawWithImageFinished.
- viz::ContextProvider::ScopedContextLock context_lock(context_provider());
- // Pull out transfer cache ID from the DecodedDrawImage while it still has
- // it attached.
- DecodedDrawImage serialized_decoded_draw_image =
- cache->GetDecodedImageForDraw(draw_image);
- const base::Optional<uint32_t> transfer_cache_entry_id =
- serialized_decoded_draw_image.transfer_cache_entry_id();
- DecodedDrawImage decoded_draw_image =
- EnsureImageBacked(std::move(serialized_decoded_draw_image));
- EXPECT_TRUE(decoded_draw_image.image());
- EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked());
+ // Must hold context lock before calling GetDecodedImageForDraw /
+ // DrawWithImageFinished.
+ viz::ContextProvider::ScopedContextLock context_lock(context_provider());
+ // Pull out transfer cache ID from the DecodedDrawImage while it still has
+ // it attached.
+ DecodedDrawImage serialized_decoded_draw_image =
+ cache->GetDecodedImageForDraw(draw_image);
+ const base::Optional<uint32_t> transfer_cache_entry_id =
+ serialized_decoded_draw_image.transfer_cache_entry_id();
+ DecodedDrawImage decoded_draw_image =
+ EnsureImageBacked(std::move(serialized_decoded_draw_image));
+ EXPECT_TRUE(decoded_draw_image.image());
+ EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked());
- // Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus, we
- // must separately request mips for each plane and compare to the original
- // uploaded planes.
- CompareAllPlanesToMippedVersions(cache.get(), draw_image,
- transfer_cache_entry_id,
- true /* should_have_mips */);
- SkYUVASizeInfo yuv_size_info = GetYUV420SizeInfo(GetNormalImageSize());
- VerifyUploadedPlaneSizes(cache.get(), draw_image, transfer_cache_entry_id,
- yuv_size_info.fSizes);
+ // Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus, we
+ // must separately request mips for each plane and compare to the original
+ // uploaded planes.
+ CompareAllPlanesToMippedVersions(cache, draw_image, transfer_cache_entry_id,
+ true /* should_have_mips */);
+ SkYUVASizeInfo yuv_size_info = GetYUVASizeInfo(
+ GetNormalImageSize(), yuv_format_, yuv_bytes_per_pixel_);
+ VerifyUploadedPlaneSizes(cache, draw_image, transfer_cache_entry_id,
+ yuv_size_info.fSizes);
- cache->DrawWithImageFinished(draw_image, decoded_draw_image);
- cache->UnrefImage(draw_image);
+ cache->DrawWithImageFinished(draw_image, decoded_draw_image);
+ cache->UnrefImage(draw_image);
+ };
+
+ yuv_format_ = YUVSubsampling::k420;
+ decode_and_check_plane_sizes();
+
+ yuv_format_ = YUVSubsampling::k422;
+ decode_and_check_plane_sizes();
+
+ yuv_format_ = YUVSubsampling::k444;
+ decode_and_check_plane_sizes();
+}
+
+TEST_P(GpuImageDecodeCacheTest, HighBitDepthYUVDecoding) {
+ // This test creates a high bit depth image that will be YUV decoded and drawn
+ // at 80% scale. Because the final size is between mip levels, we expect the
+ // image to be decoded and uploaded at original size (mip level 0 for all
+ // planes) but to have mips attached since kMedium_SkFilterQuality uses
+ // bilinear filtering between mip levels.
+ if (!do_yuv_decode_) {
+ // The YUV case may choose different mip levels between chroma and luma
+ // planes.
+ return;
+ }
+
+ auto decode_and_check_plane_sizes = [this](GpuImageDecodeCache* cache,
+ SkColorType yuv_color_type,
+ gfx::ColorSpace target_cs) {
+ SkFilterQuality filter_quality = kMedium_SkFilterQuality;
+ SkSize requires_decode_at_original_scale = SkSize::Make(0.8f, 0.8f);
+
+ // When we're targeting HDR output, select a reasonable HDR color space for
+ // the decoded content.
+ gfx::ColorSpace decoded_cs;
+ if (target_cs.IsHDR())
+ decoded_cs = gfx::ColorSpace::CreateHDR10();
+
+ // An unknown SkColorType means we expect fallback to RGB.
+ PaintImage image =
+ yuv_color_type == kUnknown_SkColorType
+ ? CreatePaintImageForFallbackToRGB(GetNormalImageSize())
+ : CreatePaintImageInternal(GetNormalImageSize(),
+ decoded_cs.ToSkColorSpace());
+
+ float sdr_white_level = gfx::ColorSpace::kDefaultSDRWhiteLevel;
+ if (target_cs.IsHDR())
+ ASSERT_TRUE(target_cs.GetPQSDRWhiteLevel(&sdr_white_level));
+
+ DrawImage draw_image(
+ image, SkIRect::MakeWH(image.width(), image.height()), filter_quality,
+ CreateMatrix(requires_decode_at_original_scale),
+ PaintImage::kDefaultFrameIndex, target_cs, sdr_white_level);
+ ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(
+ draw_image, ImageDecodeCache::TracingInfo());
+ EXPECT_TRUE(result.need_unref);
+ EXPECT_TRUE(result.task);
+
+ TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get());
+ TestTileTaskRunner::ProcessTask(result.task.get());
+
+ // Must hold context lock before calling GetDecodedImageForDraw /
+ // DrawWithImageFinished.
+ viz::ContextProvider::ScopedContextLock context_lock(context_provider());
+ // Pull out transfer cache ID from the DecodedDrawImage while it still has
+ // it attached.
+ DecodedDrawImage serialized_decoded_draw_image =
+ cache->GetDecodedImageForDraw(draw_image);
+ const base::Optional<uint32_t> transfer_cache_entry_id =
+ serialized_decoded_draw_image.transfer_cache_entry_id();
+ DecodedDrawImage decoded_draw_image =
+ EnsureImageBacked(std::move(serialized_decoded_draw_image));
+ EXPECT_TRUE(decoded_draw_image.image());
+ EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked());
+
+ if (yuv_color_type != kUnknown_SkColorType) {
+ // Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus, we
+ // must separately request mips for each plane and compare to the original
+ // uploaded planes.
+ CompareAllPlanesToMippedVersions(cache, draw_image,
+ transfer_cache_entry_id,
+ true /* should_have_mips */);
+ SkYUVASizeInfo yuv_size_info = GetYUVASizeInfo(
+ GetNormalImageSize(), yuv_format_, yuv_bytes_per_pixel_);
+
+ // Decoded HDR images should have their SDR white level adjusted to match
+ // the display so we avoid scaling them by variable SDR brightness levels.
+ auto expected_cs = decoded_cs.IsHDR()
+ ? decoded_cs.GetWithSDRWhiteLevel(sdr_white_level)
+ : decoded_cs;
+
+ VerifyUploadedPlaneSizes(cache, draw_image, transfer_cache_entry_id,
+ yuv_size_info.fSizes, yuv_color_type,
+ expected_cs.ToSkColorSpace().get());
+
+ if (expected_cs.IsValid()) {
+ EXPECT_TRUE(
+ SkColorSpace::Equals(expected_cs.ToSkColorSpace().get(),
+ decoded_draw_image.image()->colorSpace()));
+ }
+ } else {
+ if (use_transfer_cache_) {
+ EXPECT_FALSE(transfer_cache_helper_
+ .GetEntryAs<ServiceImageTransferCacheEntry>(
+ *transfer_cache_entry_id)
+ ->is_yuv());
+ } else {
+ for (size_t plane = 0; plane < SkYUVASizeInfo::kMaxCount; ++plane)
+ EXPECT_FALSE(cache->GetUploadedPlaneForTesting(draw_image, plane));
+ }
+ }
+
+ cache->DrawWithImageFinished(draw_image, decoded_draw_image);
+ cache->UnrefImage(draw_image);
+ };
+
+ // Setup paint images and associated YUV info structs to be uint16_t based.
+ yuv_bytes_per_pixel_ = 2;
+
+ gpu::Capabilities original_caps;
+ {
+ // TODO(crbug.com/1110007): We shouldn't need to lock to get capabilities.
+ viz::RasterContextProvider::ScopedRasterContextLock auto_lock(
+ context_provider_.get());
+ original_caps = context_provider_->ContextCapabilities();
+ }
+
+ const auto hdr_cs = gfx::ColorSpace::CreateHDR10(/*sdr_white_level=*/200.0f);
+
+ // Ensure that when R16 is supported, it's used and preferred over half-float.
+ {
+ auto r16_caps = original_caps;
+ r16_caps.texture_norm16 = true;
+ r16_caps.texture_half_float_linear = true;
+ context_provider_->SetContextCapabilitiesOverride(r16_caps);
+ auto r16_cache = CreateCache();
+
+ yuv_format_ = YUVSubsampling::k420;
+ decode_and_check_plane_sizes(r16_cache.get(), kA16_unorm_SkColorType,
+ DefaultColorSpace());
+
+ yuv_format_ = YUVSubsampling::k422;
+ decode_and_check_plane_sizes(r16_cache.get(), kA16_unorm_SkColorType,
+ DefaultColorSpace());
+
+ yuv_format_ = YUVSubsampling::k444;
+ decode_and_check_plane_sizes(r16_cache.get(), kA16_unorm_SkColorType,
+ DefaultColorSpace());
+
+ // Verify HDR decoding has white level adjustment.
+ yuv_format_ = YUVSubsampling::k420;
+ decode_and_check_plane_sizes(r16_cache.get(), kA16_unorm_SkColorType,
+ hdr_cs);
+
+ yuv_format_ = YUVSubsampling::k422;
+ decode_and_check_plane_sizes(r16_cache.get(), kA16_unorm_SkColorType,
+ hdr_cs);
+
+ yuv_format_ = YUVSubsampling::k444;
+ decode_and_check_plane_sizes(r16_cache.get(), kA16_unorm_SkColorType,
+ hdr_cs);
+ }
+
+ // Verify that half-float is used when R16 is not available.
+ {
+ auto f16_caps = original_caps;
+ f16_caps.texture_norm16 = false;
+ f16_caps.texture_half_float_linear = true;
+ context_provider_->SetContextCapabilitiesOverride(f16_caps);
+ auto f16_cache = CreateCache();
+
+ yuv_format_ = YUVSubsampling::k420;
+ decode_and_check_plane_sizes(f16_cache.get(), kA16_float_SkColorType,
+ DefaultColorSpace());
+
+ yuv_format_ = YUVSubsampling::k422;
+ decode_and_check_plane_sizes(f16_cache.get(), kA16_float_SkColorType,
+ DefaultColorSpace());
+
+ yuv_format_ = YUVSubsampling::k444;
+ decode_and_check_plane_sizes(f16_cache.get(), kA16_float_SkColorType,
+ DefaultColorSpace());
+
+ // Verify HDR decoding has white level adjustment.
+ yuv_format_ = YUVSubsampling::k420;
+ decode_and_check_plane_sizes(f16_cache.get(), kA16_float_SkColorType,
+ hdr_cs);
+
+ yuv_format_ = YUVSubsampling::k422;
+ decode_and_check_plane_sizes(f16_cache.get(), kA16_float_SkColorType,
+ hdr_cs);
+
+ yuv_format_ = YUVSubsampling::k444;
+ decode_and_check_plane_sizes(f16_cache.get(), kA16_float_SkColorType,
+ hdr_cs);
+ }
+
+ // Verify YUV16 is unsupported when neither R16 or half-float are available.
+ {
+ auto no_yuv16_caps = original_caps;
+ no_yuv16_caps.texture_norm16 = false;
+ no_yuv16_caps.texture_half_float_linear = false;
+ context_provider_->SetContextCapabilitiesOverride(no_yuv16_caps);
+ auto no_yuv16_cache = CreateCache();
+
+ yuv_format_ = YUVSubsampling::k420;
+ decode_and_check_plane_sizes(no_yuv16_cache.get(), kUnknown_SkColorType,
+ DefaultColorSpace());
+
+ yuv_format_ = YUVSubsampling::k422;
+ decode_and_check_plane_sizes(no_yuv16_cache.get(), kUnknown_SkColorType,
+ DefaultColorSpace());
+
+ yuv_format_ = YUVSubsampling::k444;
+ decode_and_check_plane_sizes(no_yuv16_cache.get(), kUnknown_SkColorType,
+ DefaultColorSpace());
+ }
}
TEST_P(GpuImageDecodeCacheTest, ScaledYUVDecodeScaledDrawCorrectlyMipsPlanes) {
@@ -2837,61 +3193,108 @@ TEST_P(GpuImageDecodeCacheTest, ScaledYUVDecodeScaledDrawCorrectlyMipsPlanes) {
// planes.
return;
}
- auto cache = CreateCache();
- SkFilterQuality filter_quality = kMedium_SkFilterQuality;
- SkSize less_than_half_scale = SkSize::Make(0.45f, 0.45f);
+ auto owned_cache = CreateCache();
+ auto decode_and_check_plane_sizes =
+ [this, cache = owned_cache.get()](
+ SkSize scaled_size,
+ const SkISize mipped_plane_sizes[SkYUVASizeInfo::kMaxCount]) {
+ SkFilterQuality filter_quality = kMedium_SkFilterQuality;
+
+ gfx::Size image_size = GetNormalImageSize();
+ PaintImage image = CreatePaintImageInternal(image_size);
+ DrawImage draw_image(
+ image, SkIRect::MakeWH(image.width(), image.height()),
+ filter_quality, CreateMatrix(scaled_size),
+ PaintImage::kDefaultFrameIndex, DefaultColorSpace());
+ ImageDecodeCache::TaskResult result = cache->GetTaskForImageAndRef(
+ draw_image, ImageDecodeCache::TracingInfo());
+ EXPECT_TRUE(result.need_unref);
+ EXPECT_TRUE(result.task);
+
+ TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get());
+ TestTileTaskRunner::ProcessTask(result.task.get());
+
+ // Must hold context lock before calling GetDecodedImageForDraw /
+ // DrawWithImageFinished.
+ viz::ContextProvider::ScopedContextLock context_lock(
+ context_provider());
+ // Pull out transfer cache ID from the DecodedDrawImage while it still
+ // has it attached.
+ DecodedDrawImage serialized_decoded_draw_image =
+ cache->GetDecodedImageForDraw(draw_image);
+ const base::Optional<uint32_t> transfer_cache_entry_id =
+ serialized_decoded_draw_image.transfer_cache_entry_id();
+ DecodedDrawImage decoded_draw_image =
+ EnsureImageBacked(std::move(serialized_decoded_draw_image));
+ EXPECT_TRUE(decoded_draw_image.image());
+ EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked());
+
+ // Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus,
+ // we must separately request mips for each plane and compare to the
+ // original uploaded planes.
+ CompareAllPlanesToMippedVersions(cache, draw_image,
+ transfer_cache_entry_id,
+ true /* should_have_mips */);
+ VerifyUploadedPlaneSizes(cache, draw_image, transfer_cache_entry_id,
+ mipped_plane_sizes);
+
+ cache->DrawWithImageFinished(draw_image, decoded_draw_image);
+ cache->UnrefImage(draw_image);
+ };
gfx::Size image_size = GetNormalImageSize();
- PaintImage image = CreatePaintImageInternal(image_size);
- DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
- filter_quality, CreateMatrix(less_than_half_scale),
- PaintImage::kDefaultFrameIndex, DefaultColorSpace());
- ImageDecodeCache::TaskResult result =
- cache->GetTaskForImageAndRef(draw_image, ImageDecodeCache::TracingInfo());
- EXPECT_TRUE(result.need_unref);
- EXPECT_TRUE(result.task);
-
- TestTileTaskRunner::ProcessTask(result.task->dependencies()[0].get());
- TestTileTaskRunner::ProcessTask(result.task.get());
-
- // Must hold context lock before calling GetDecodedImageForDraw /
- // DrawWithImageFinished.
- viz::ContextProvider::ScopedContextLock context_lock(context_provider());
- // Pull out transfer cache ID from the DecodedDrawImage while it still has
- // it attached.
- DecodedDrawImage serialized_decoded_draw_image =
- cache->GetDecodedImageForDraw(draw_image);
- const base::Optional<uint32_t> transfer_cache_entry_id =
- serialized_decoded_draw_image.transfer_cache_entry_id();
- DecodedDrawImage decoded_draw_image =
- EnsureImageBacked(std::move(serialized_decoded_draw_image));
- EXPECT_TRUE(decoded_draw_image.image());
- EXPECT_TRUE(decoded_draw_image.image()->isTextureBacked());
+ SkISize mipped_plane_sizes[SkYUVASizeInfo::kMaxCount];
- // Skia will flatten a YUV SkImage upon calling makeTextureImage. Thus, we
- // must separately request mips for each plane and compare to the original
- // uploaded planes.
- CompareAllPlanesToMippedVersions(cache.get(), draw_image,
- transfer_cache_entry_id,
- true /* should_have_mips */);
+ SkSize less_than_half_scale = SkSize::Make(0.45f, 0.45f);
// Because we intend to draw this image at 0.45 x 0.45 scale, we will upload
- // the Y plane at mip level 1 (corresponding to half the original size). The
- // chroma planes (U and V) should be uploaded at the same size as the Y plane,
- // corresponding to mip level 0, because the largest dimensions greater than
- // or equal to target dimensions for them is their original size.
- SkISize mipped_plane_sizes[SkYUVASizeInfo::kMaxCount];
+ // the Y plane at mip level 1 (corresponding to 1/2 the original size).
mipped_plane_sizes[SkYUVAIndex::kY_Index] = SkISize::Make(
(image_size.width() + 1) / 2, (image_size.height() + 1) / 2);
mipped_plane_sizes[SkYUVAIndex::kU_Index] =
mipped_plane_sizes[SkYUVAIndex::kY_Index];
mipped_plane_sizes[SkYUVAIndex::kV_Index] =
mipped_plane_sizes[SkYUVAIndex::kY_Index];
- VerifyUploadedPlaneSizes(cache.get(), draw_image, transfer_cache_entry_id,
- mipped_plane_sizes);
- cache->DrawWithImageFinished(draw_image, decoded_draw_image);
- cache->UnrefImage(draw_image);
+ // For 4:2:0, the chroma planes (U and V) should be uploaded at the same size
+ // as the Y plane since they get promoted to 4:4:4 to avoid blurriness from
+ // scaling.
+ yuv_format_ = YUVSubsampling::k420;
+ decode_and_check_plane_sizes(less_than_half_scale, mipped_plane_sizes);
+
+ // For 4:2:2, only the UV height plane should be scaled.
+ yuv_format_ = YUVSubsampling::k422;
+ decode_and_check_plane_sizes(less_than_half_scale, mipped_plane_sizes);
+
+ // For 4:4:4, all planes should be the same size.
+ yuv_format_ = YUVSubsampling::k444;
+ decode_and_check_plane_sizes(less_than_half_scale, mipped_plane_sizes);
+
+ // Now try at 1/4 scale.
+ SkSize one_quarter_scale = SkSize::Make(0.20f, 0.20f);
+
+ // Because we intend to draw this image at 0.20 x 0.20 scale, we will upload
+ // the Y plane at mip level 2 (corresponding to 1/4 the original size).
+ mipped_plane_sizes[SkYUVAIndex::kY_Index] = SkISize::Make(
+ (image_size.width() + 1) / 4, (image_size.height() + 1) / 4);
+ mipped_plane_sizes[SkYUVAIndex::kU_Index] =
+ mipped_plane_sizes[SkYUVAIndex::kY_Index];
+ mipped_plane_sizes[SkYUVAIndex::kV_Index] =
+ mipped_plane_sizes[SkYUVAIndex::kY_Index];
+
+ // For 4:2:0, the chroma planes (U and V) should be uploaded at the same size
+ // as the Y plane since they get promoted to 4:4:4 to avoid blurriness from
+ // scaling.
+ yuv_format_ = YUVSubsampling::k420;
+ decode_and_check_plane_sizes(one_quarter_scale, mipped_plane_sizes);
+
+ // For 4:2:2, only the UV height plane should be scaled.
+ yuv_format_ = YUVSubsampling::k422;
+ decode_and_check_plane_sizes(one_quarter_scale, mipped_plane_sizes);
+
+ // For 4:4:4, all planes should be the same size.
+ yuv_format_ = YUVSubsampling::k444;
+ decode_and_check_plane_sizes(one_quarter_scale, mipped_plane_sizes);
}
TEST_P(GpuImageDecodeCacheTest, GetBorderlineLargeDecodedImageForDraw) {
@@ -2990,7 +3393,10 @@ class GpuImageDecodeCacheWithAcceleratedDecodesTest
sk_sp<FakePaintImageGenerator> generator;
if (do_yuv_decode_) {
generator = sk_make_sp<FakePaintImageGenerator>(
- info, GetYUV420SizeInfo(image_data.image_size));
+ info,
+ GetYUVASizeInfo(image_data.image_size, yuv_format_,
+ yuv_bytes_per_pixel_),
+ yuv_bytes_per_pixel_ * 8);
} else {
generator = sk_make_sp<FakePaintImageGenerator>(info);
}
diff --git a/chromium/cc/tiles/image_controller_unittest.cc b/chromium/cc/tiles/image_controller_unittest.cc
index 06da97ecd4b..c518941f51b 100644
--- a/chromium/cc/tiles/image_controller_unittest.cc
+++ b/chromium/cc/tiles/image_controller_unittest.cc
@@ -163,7 +163,9 @@ class DecodeClient {
// A dummy task that does nothing.
class SimpleTask : public TileTask {
public:
- SimpleTask() : TileTask(true /* supports_concurrent_execution */) {
+ SimpleTask()
+ : TileTask(TileTask::SupportsConcurrentExecution::kYes,
+ TileTask::SupportsBackgroundThreadPriority::kYes) {
EXPECT_TRUE(thread_checker_.CalledOnValidThread());
}
SimpleTask(const SimpleTask&) = delete;
@@ -191,7 +193,9 @@ class SimpleTask : public TileTask {
class BlockingTask : public TileTask {
public:
BlockingTask()
- : TileTask(true /* supports_concurrent_execution */), run_cv_(&lock_) {
+ : TileTask(TileTask::SupportsConcurrentExecution::kYes,
+ TileTask::SupportsBackgroundThreadPriority::kYes),
+ run_cv_(&lock_) {
EXPECT_TRUE(thread_checker_.CalledOnValidThread());
}
BlockingTask(const BlockingTask&) = delete;
diff --git a/chromium/cc/tiles/picture_layer_tiling.cc b/chromium/cc/tiles/picture_layer_tiling.cc
index aefe88d22b8..da5e80c2e2b 100644
--- a/chromium/cc/tiles/picture_layer_tiling.cc
+++ b/chromium/cc/tiles/picture_layer_tiling.cc
@@ -25,7 +25,6 @@
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/rect_f.h"
-#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/size_conversions.h"
namespace cc {
@@ -36,13 +35,15 @@ PictureLayerTiling::PictureLayerTiling(
scoped_refptr<RasterSource> raster_source,
PictureLayerTilingClient* client,
float min_preraster_distance,
- float max_preraster_distance)
+ float max_preraster_distance,
+ bool can_use_lcd_text)
: raster_transform_(raster_transform),
client_(client),
tree_(tree),
raster_source_(raster_source),
min_preraster_distance_(min_preraster_distance),
- max_preraster_distance_(max_preraster_distance) {
+ max_preraster_distance_(max_preraster_distance),
+ can_use_lcd_text_(can_use_lcd_text) {
DCHECK(!raster_source->IsSolidColor());
DCHECK_GE(raster_transform.translation().x(), 0.f);
DCHECK_LT(raster_transform.translation().x(), 1.f);
@@ -73,7 +74,7 @@ Tile* PictureLayerTiling::CreateTile(const Tile::CreateInfo& info) {
TileMapKey key(i, j);
DCHECK(tiles_.find(key) == tiles_.end());
- if (!raster_source_->CoversRect(info.enclosing_layer_rect))
+ if (!raster_source_->CoversRect(info.enclosing_layer_rect, *client_))
return nullptr;
all_tiles_done_ = false;
@@ -302,8 +303,13 @@ Tile::CreateInfo PictureLayerTiling::CreateInfoForTile(int i, int j) const {
tile_rect.set_size(tiling_data_.max_texture_size());
gfx::Rect enclosing_layer_rect =
EnclosingLayerRectFromContentsRect(tile_rect);
- return Tile::CreateInfo(this, i, j, enclosing_layer_rect, tile_rect,
- raster_transform_);
+ return Tile::CreateInfo{this,
+ i,
+ j,
+ enclosing_layer_rect,
+ tile_rect,
+ raster_transform_,
+ can_use_lcd_text_};
}
bool PictureLayerTiling::ShouldCreateTileAt(
@@ -330,9 +336,18 @@ bool PictureLayerTiling::ShouldCreateTileAt(
if (!TilingMatchesTileIndices(active_twin))
return true;
+ // If our settings don't match the active twin, it means that the active
+ // tiles will all be removed when we activate. So we need all the tiles on the
+ // pending tree to be created. See
+ // PictureLayerTilingSet::CopyTilingsAndPropertiesFromPendingTwin.
+ if (can_use_lcd_text() != active_twin->can_use_lcd_text() ||
+ raster_transform() != active_twin->raster_transform())
+ return true;
+
// If the active tree can't create a tile, because of its raster source, then
// the pending tree should create one.
- if (!active_twin->raster_source()->CoversRect(info.enclosing_layer_rect))
+ if (!active_twin->raster_source()->CoversRect(info.enclosing_layer_rect,
+ *active_twin->client()))
return true;
const Region* layer_invalidation = client_->GetPendingInvalidation();
@@ -855,7 +870,7 @@ PrioritizedTile PictureLayerTiling::MakePrioritizedTile(
Tile* tile,
PriorityRectType priority_rect_type) const {
DCHECK(tile);
- DCHECK(raster_source()->CoversRect(tile->enclosing_layer_rect()))
+ DCHECK(raster_source()->CoversRect(tile->enclosing_layer_rect(), *client_))
<< "Recording rect: "
<< EnclosingLayerRectFromContentsRect(tile->content_rect()).ToString();
diff --git a/chromium/cc/tiles/picture_layer_tiling.h b/chromium/cc/tiles/picture_layer_tiling.h
index 253fdea09ac..7f984961057 100644
--- a/chromium/cc/tiles/picture_layer_tiling.h
+++ b/chromium/cc/tiles/picture_layer_tiling.h
@@ -50,6 +50,7 @@ class CC_EXPORT PictureLayerTilingClient {
virtual bool HasValidTilePriorities() const = 0;
virtual bool RequiresHighResToDraw() const = 0;
virtual const PaintWorkletRecordMap& GetPaintWorkletRecords() const = 0;
+ virtual bool IsDirectlyCompositedImage() const = 0;
protected:
virtual ~PictureLayerTilingClient() {}
@@ -94,7 +95,8 @@ class CC_EXPORT PictureLayerTiling {
scoped_refptr<RasterSource> raster_source,
PictureLayerTilingClient* client,
float min_preraster_distance,
- float max_preraster_distance);
+ float max_preraster_distance,
+ bool can_use_lcd_text);
PictureLayerTiling(const PictureLayerTiling&) = delete;
~PictureLayerTiling();
@@ -164,6 +166,8 @@ class CC_EXPORT PictureLayerTiling {
all_tiles_done_ = all_tiles_done;
}
+ bool can_use_lcd_text() const { return can_use_lcd_text_; }
+
WhichTree tree() const { return tree_; }
void VerifyNoTileNeedsRaster() const {
@@ -398,6 +402,7 @@ class CC_EXPORT PictureLayerTiling {
bool has_soon_border_rect_tiles_ = false;
bool has_eventually_rect_tiles_ = false;
bool all_tiles_done_ = true;
+ bool can_use_lcd_text_;
};
} // namespace cc
diff --git a/chromium/cc/tiles/picture_layer_tiling_set.cc b/chromium/cc/tiles/picture_layer_tiling_set.cc
index 5a2bcb740a0..aec6390cc19 100644
--- a/chromium/cc/tiles/picture_layer_tiling_set.cc
+++ b/chromium/cc/tiles/picture_layer_tiling_set.cc
@@ -6,8 +6,11 @@
#include <stddef.h>
+#include <algorithm>
#include <limits>
+#include <memory>
#include <set>
+#include <utility>
#include <vector>
#include "base/memory/ptr_util.h"
@@ -86,16 +89,19 @@ void PictureLayerTilingSet::CopyTilingsAndPropertiesFromPendingTwin(
for (const auto& pending_twin_tiling : pending_twin_set->tilings_) {
gfx::AxisTransform2d raster_transform =
pending_twin_tiling->raster_transform();
+ bool can_use_lcd_text = pending_twin_tiling->can_use_lcd_text();
PictureLayerTiling* this_tiling =
FindTilingWithScaleKey(pending_twin_tiling->contents_scale_key());
- if (this_tiling && this_tiling->raster_transform() != raster_transform) {
+ if (this_tiling && (this_tiling->raster_transform() != raster_transform ||
+ this_tiling->can_use_lcd_text() != can_use_lcd_text)) {
Remove(this_tiling);
this_tiling = nullptr;
}
if (!this_tiling) {
- std::unique_ptr<PictureLayerTiling> new_tiling(new PictureLayerTiling(
- tree_, raster_transform, raster_source_, client_,
- kMaxSoonBorderDistanceInScreenPixels, max_preraster_distance_));
+ std::unique_ptr<PictureLayerTiling> new_tiling(
+ new PictureLayerTiling(tree_, raster_transform, raster_source_,
+ client_, kMaxSoonBorderDistanceInScreenPixels,
+ max_preraster_distance_, can_use_lcd_text));
tilings_.push_back(std::move(new_tiling));
this_tiling = tilings_.back().get();
tiling_sort_required = true;
@@ -268,7 +274,8 @@ void PictureLayerTilingSet::MarkAllTilingsNonIdeal() {
PictureLayerTiling* PictureLayerTilingSet::AddTiling(
const gfx::AxisTransform2d& raster_transform,
- scoped_refptr<RasterSource> raster_source) {
+ scoped_refptr<RasterSource> raster_source,
+ bool can_use_lcd_text) {
if (!raster_source_)
raster_source_ = raster_source;
@@ -281,7 +288,8 @@ PictureLayerTiling* PictureLayerTilingSet::AddTiling(
tilings_.push_back(std::make_unique<PictureLayerTiling>(
tree_, raster_transform, raster_source, client_,
- kMaxSoonBorderDistanceInScreenPixels, max_preraster_distance_));
+ kMaxSoonBorderDistanceInScreenPixels, max_preraster_distance_,
+ can_use_lcd_text));
PictureLayerTiling* appended = tilings_.back().get();
state_since_last_tile_priority_update_.added_tilings = true;
diff --git a/chromium/cc/tiles/picture_layer_tiling_set.h b/chromium/cc/tiles/picture_layer_tiling_set.h
index b5b9c45323d..82d9fe77210 100644
--- a/chromium/cc/tiles/picture_layer_tiling_set.h
+++ b/chromium/cc/tiles/picture_layer_tiling_set.h
@@ -8,6 +8,7 @@
#include <stddef.h>
#include <deque>
+#include <memory>
#include <set>
#include <vector>
@@ -79,7 +80,8 @@ class CC_EXPORT PictureLayerTilingSet {
void Invalidate(const Region& layer_invalidation);
PictureLayerTiling* AddTiling(const gfx::AxisTransform2d& raster_transform,
- scoped_refptr<RasterSource> raster_source);
+ scoped_refptr<RasterSource> raster_source,
+ bool can_use_lcd_text = false);
size_t num_tilings() const { return tilings_.size(); }
int NumHighResTilings() const;
PictureLayerTiling* tiling_at(size_t idx) { return tilings_[idx].get(); }
diff --git a/chromium/cc/tiles/picture_layer_tiling_set_unittest.cc b/chromium/cc/tiles/picture_layer_tiling_set_unittest.cc
index 833fd6813d8..5313a1b9d66 100644
--- a/chromium/cc/tiles/picture_layer_tiling_set_unittest.cc
+++ b/chromium/cc/tiles/picture_layer_tiling_set_unittest.cc
@@ -1106,5 +1106,58 @@ TEST(PictureLayerTilingSetTest, TilingTranslationChanges) {
EXPECT_EQ(1u, active_set->tiling_at(0)->AllTilesForTesting().size());
}
+TEST(PictureLayerTilingSetTest, LcdChanges) {
+ gfx::Size tile_size(64, 64);
+ FakePictureLayerTilingClient pending_client;
+ FakePictureLayerTilingClient active_client;
+ pending_client.SetTileSize(tile_size);
+ active_client.SetTileSize(tile_size);
+ std::unique_ptr<PictureLayerTilingSet> pending_set =
+ PictureLayerTilingSet::Create(PENDING_TREE, &pending_client, 0, 1.f, 0,
+ 0.f);
+ std::unique_ptr<PictureLayerTilingSet> active_set =
+ PictureLayerTilingSet::Create(ACTIVE_TREE, &active_client, 0, 1.f, 0,
+ 0.f);
+ active_client.set_twin_tiling_set(pending_set.get());
+ pending_client.set_twin_tiling_set(active_set.get());
+
+ gfx::Size layer_bounds(100, 100);
+ scoped_refptr<FakeRasterSource> raster_source =
+ FakeRasterSource::CreateFilled(layer_bounds);
+
+ const bool with_lcd_text = true;
+ const bool without_lcd_text = false;
+
+ gfx::AxisTransform2d raster_transform(1.f, gfx::Vector2dF());
+ pending_set->AddTiling(raster_transform, raster_source, with_lcd_text);
+ pending_set->tiling_at(0)->set_resolution(HIGH_RESOLUTION);
+
+ // Set a priority rect so we get tiles.
+ pending_set->UpdateTilePriorities(gfx::Rect(layer_bounds), 1.f, 1.0,
+ Occlusion(), false);
+
+ // Make sure all tiles are generated.
+ EXPECT_EQ(4u, pending_set->tiling_at(0)->AllTilesForTesting().size());
+
+ // Clone from the pending to the active tree.
+ active_set->UpdateTilingsToCurrentRasterSourceForActivation(
+ raster_source.get(), pending_set.get(), Region(), 1.f, 1.f);
+
+ // Verifies active tree cloned the tiling correctly.
+ ASSERT_EQ(1u, active_set->num_tilings());
+ EXPECT_EQ(4u, active_set->tiling_at(0)->AllTilesForTesting().size());
+
+ // Change LCD state on the pending tree
+ pending_set->RemoveAllTilings();
+ pending_set->AddTiling(raster_transform, raster_source, without_lcd_text);
+ pending_set->tiling_at(0)->set_resolution(HIGH_RESOLUTION);
+
+ // Set a priority rect so we get tiles.
+ pending_set->UpdateTilePriorities(gfx::Rect(layer_bounds), 1.f, 1.0,
+ Occlusion(), false);
+ // We should have created all tiles because lcd state changed.
+ EXPECT_EQ(4u, pending_set->tiling_at(0)->AllTilesForTesting().size());
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/tiles/picture_layer_tiling_unittest.cc b/chromium/cc/tiles/picture_layer_tiling_unittest.cc
index 424dc99f8b7..8ac095900df 100644
--- a/chromium/cc/tiles/picture_layer_tiling_unittest.cc
+++ b/chromium/cc/tiles/picture_layer_tiling_unittest.cc
@@ -80,7 +80,8 @@ class TestablePictureLayerTiling : public PictureLayerTiling {
raster_source,
client,
min_preraster_distance,
- max_preraster_distance) {}
+ max_preraster_distance,
+ /*can_use_lcd_text*/ false) {}
};
class PictureLayerTilingIteratorTest : public testing::Test {
diff --git a/chromium/cc/tiles/software_image_decode_cache.cc b/chromium/cc/tiles/software_image_decode_cache.cc
index 412c1b02c3c..f2676e80db0 100644
--- a/chromium/cc/tiles/software_image_decode_cache.cc
+++ b/chromium/cc/tiles/software_image_decode_cache.cc
@@ -61,7 +61,8 @@ class SoftwareImageDecodeTaskImpl : public TileTask {
const PaintImage& paint_image,
SoftwareImageDecodeCache::DecodeTaskType task_type,
const ImageDecodeCache::TracingInfo& tracing_info)
- : TileTask(true),
+ : TileTask(TileTask::SupportsConcurrentExecution::kYes,
+ TileTask::SupportsBackgroundThreadPriority::kYes),
cache_(cache),
image_key_(image_key),
paint_image_(paint_image),
@@ -82,7 +83,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_.GetRasterSkImage().get(),
+ paint_image_.GetSwSkImage().get(),
devtools_instrumentation::ScopedImageDecodeTask::kSoftware,
ImageDecodeCache::ToScopedTaskType(tracing_info_.task_type),
ImageDecodeCache::ToScopedImageType(image_type));
@@ -181,7 +182,9 @@ SoftwareImageDecodeCache::GetTaskForImageAndRefInternal(
const DrawImage& image,
const TracingInfo& tracing_info,
DecodeTaskType task_type) {
- CacheKey key = CacheKey::FromDrawImage(image, color_type_);
+ CacheKey key = CacheKey::FromDrawImage(
+ image, GetColorTypeForPaintImage(image.target_color_space(),
+ image.paint_image()));
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"SoftwareImageDecodeCache::GetTaskForImageAndRefInternal", "key",
key.ToString());
@@ -275,7 +278,9 @@ void SoftwareImageDecodeCache::RemoveBudgetForImage(const CacheKey& key,
}
void SoftwareImageDecodeCache::UnrefImage(const DrawImage& image) {
- const CacheKey& key = CacheKey::FromDrawImage(image, color_type_);
+ const CacheKey& key = CacheKey::FromDrawImage(
+ image, GetColorTypeForPaintImage(image.target_color_space(),
+ image.paint_image()));
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"SoftwareImageDecodeCache::UnrefImage", "key", key.ToString());
@@ -348,7 +353,9 @@ SoftwareImageDecodeCache::DecodeImageIfNecessary(const CacheKey& key,
if (key.type() == CacheKey::kOriginal) {
base::AutoUnlock release(lock_);
local_cache_entry = Utils::DoDecodeImage(
- key, paint_image, color_type_, generator_client_id_,
+ key, paint_image,
+ GetColorTypeForPaintImage(key.target_color_space(), paint_image),
+ generator_client_id_,
base::BindOnce(&SoftwareImageDecodeCache::ClearCache,
base::Unretained(this)));
} else {
@@ -378,7 +385,9 @@ SoftwareImageDecodeCache::DecodeImageIfNecessary(const CacheKey& key,
if (should_decode_to_scale) {
base::AutoUnlock release(lock_);
local_cache_entry = Utils::DoDecodeImage(
- key, paint_image, color_type_, generator_client_id_,
+ key, paint_image,
+ GetColorTypeForPaintImage(key.target_color_space(), paint_image),
+ generator_client_id_,
base::BindOnce(&SoftwareImageDecodeCache::ClearCache,
base::Unretained(this)));
}
@@ -408,8 +417,9 @@ SoftwareImageDecodeCache::DecodeImageIfNecessary(const CacheKey& key,
DrawImage candidate_draw_image(
paint_image, src_rect, kNone_SkFilterQuality, SkMatrix::I(),
key.frame_key().frame_index(), key.target_color_space());
- candidate_key.emplace(
- CacheKey::FromDrawImage(candidate_draw_image, color_type_));
+ candidate_key.emplace(CacheKey::FromDrawImage(
+ candidate_draw_image,
+ GetColorTypeForPaintImage(key.target_color_space(), paint_image)));
}
if (candidate_key) {
@@ -426,7 +436,8 @@ SoftwareImageDecodeCache::DecodeImageIfNecessary(const CacheKey& key,
// already been done to generate the candidate.
local_cache_entry = Utils::GenerateCacheEntryFromCandidate(
key, decoded_draw_image,
- candidate_key->type() == CacheKey::kOriginal, color_type_);
+ candidate_key->type() == CacheKey::kOriginal,
+ GetColorTypeForPaintImage(key.target_color_space(), paint_image));
}
// Unref to balance the GetDecodedImageForDrawInternal() call.
@@ -497,14 +508,14 @@ SoftwareImageDecodeCache::FindCachedCandidate(const CacheKey& key) {
bool SoftwareImageDecodeCache::UseCacheForDrawImage(
const DrawImage& draw_image) const {
- sk_sp<SkImage> sk_image = draw_image.paint_image().GetSkImage();
+ PaintImage paint_image = draw_image.paint_image();
// Software cache doesn't support using texture backed images.
- if (sk_image->isTextureBacked())
+ if (paint_image.IsTextureBacked())
return false;
// Lazy generated images need to have their decode cached.
- if (sk_image->isLazyGenerated())
+ if (paint_image.IsLazyGenerated())
return true;
// Cache images that need to be converted to a non-sRGB color space.
@@ -525,7 +536,9 @@ DecodedDrawImage SoftwareImageDecodeCache::GetDecodedImageForDraw(
base::AutoLock hold(lock_);
return GetDecodedImageForDrawInternal(
- CacheKey::FromDrawImage(draw_image, color_type_),
+ CacheKey::FromDrawImage(
+ draw_image, GetColorTypeForPaintImage(draw_image.target_color_space(),
+ draw_image.paint_image())),
draw_image.paint_image());
}
@@ -565,7 +578,10 @@ void SoftwareImageDecodeCache::DrawWithImageFinished(
DCHECK(UseCacheForDrawImage(image));
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"SoftwareImageDecodeCache::DrawWithImageFinished", "key",
- CacheKey::FromDrawImage(image, color_type_).ToString());
+ CacheKey::FromDrawImage(
+ image, GetColorTypeForPaintImage(image.target_color_space(),
+ image.paint_image()))
+ .ToString());
UnrefImage(image);
}
@@ -678,6 +694,21 @@ size_t SoftwareImageDecodeCache::GetNumCacheEntriesForTesting() {
return decoded_images_.size();
}
+SkColorType SoftwareImageDecodeCache::GetColorTypeForPaintImage(
+ const gfx::ColorSpace& target_color_space,
+ const PaintImage& paint_image) {
+ // Decode HDR images to half float when targeting HDR.
+ //
+ // TODO(crbug.com/1076568): Once we have access to the display's buffer format
+ // via gfx::DisplayColorSpaces, we should also do this for HBD images.
+ if (paint_image.GetContentColorUsage() == gfx::ContentColorUsage::kHDR &&
+ target_color_space.IsHDR()) {
+ return kRGBA_F16_SkColorType;
+ }
+
+ return color_type_;
+}
+
// MemoryBudget ----------------------------------------------------------------
SoftwareImageDecodeCache::MemoryBudget::MemoryBudget(size_t limit_bytes)
: limit_bytes_(limit_bytes), current_usage_bytes_(0u) {}
diff --git a/chromium/cc/tiles/software_image_decode_cache.h b/chromium/cc/tiles/software_image_decode_cache.h
index cc166d0d3e5..84283168042 100644
--- a/chromium/cc/tiles/software_image_decode_cache.h
+++ b/chromium/cc/tiles/software_image_decode_cache.h
@@ -134,6 +134,10 @@ class CC_EXPORT SoftwareImageDecodeCache
void UnrefImage(const CacheKey& key) EXCLUSIVE_LOCKS_REQUIRED(lock_);
+ SkColorType GetColorTypeForPaintImage(
+ const gfx::ColorSpace& target_color_space,
+ const PaintImage& paint_image);
+
base::Lock lock_;
// Decoded images and ref counts (predecode path).
ImageMRUCache decoded_images_ GUARDED_BY(lock_);
diff --git a/chromium/cc/tiles/software_image_decode_cache_unittest.cc b/chromium/cc/tiles/software_image_decode_cache_unittest.cc
index 3e31e7a9a4b..adf90094ec2 100644
--- a/chromium/cc/tiles/software_image_decode_cache_unittest.cc
+++ b/chromium/cc/tiles/software_image_decode_cache_unittest.cc
@@ -1939,5 +1939,62 @@ TEST(SoftwareImageDecodeCacheTest, DecodeToScaleNoneQuality) {
cache.DrawWithImageFinished(draw_image, decoded_image);
}
+TEST(SoftwareImageDecodeCacheTest, HdrDecodeToHdr) {
+ TestSoftwareImageDecodeCache cache;
+
+ auto color_space = gfx::ColorSpace::CreateHDR10();
+ auto size = SkISize::Make(100, 100);
+ auto info =
+ SkImageInfo::Make(size.width(), size.height(), kRGBA_F16_SkColorType,
+ kPremul_SkAlphaType, color_space.ToSkColorSpace());
+ SkBitmap bitmap;
+ bitmap.allocPixels(info);
+ PaintImage image = PaintImageBuilder::WithDefault()
+ .set_id(PaintImage::kInvalidId)
+ .set_is_high_bit_depth(true)
+ .set_image(SkImage::MakeFromBitmap(bitmap),
+ PaintImage::GetNextContentId())
+ .TakePaintImage();
+
+ DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
+ kMedium_SkFilterQuality,
+ CreateMatrix(SkSize::Make(0.5, 0.5), true),
+ PaintImage::kDefaultFrameIndex, color_space);
+
+ DecodedDrawImage decoded_image = cache.GetDecodedImageForDraw(draw_image);
+ EXPECT_EQ(decoded_image.image()->colorType(), kRGBA_F16_SkColorType);
+ cache.DrawWithImageFinished(draw_image, decoded_image);
+}
+
+TEST(SoftwareImageDecodeCacheTest, HdrDecodeToSdr) {
+ TestSoftwareImageDecodeCache cache;
+
+ auto image_color_space = gfx::ColorSpace::CreateHDR10();
+ auto size = SkISize::Make(100, 100);
+ auto info = SkImageInfo::Make(size.width(), size.height(),
+ kRGBA_F16_SkColorType, kPremul_SkAlphaType,
+ image_color_space.ToSkColorSpace());
+ SkBitmap bitmap;
+ bitmap.allocPixels(info);
+ PaintImage image = PaintImageBuilder::WithDefault()
+ .set_id(PaintImage::kInvalidId)
+ .set_is_high_bit_depth(true)
+ .set_image(SkImage::MakeFromBitmap(bitmap),
+ PaintImage::GetNextContentId())
+ .TakePaintImage();
+
+ // Note: We use P3 here since software cache shouldn't be used when conversion
+ // to SRGB is needed.
+ auto raster_color_space = gfx::ColorSpace::CreateDisplayP3D65();
+ DrawImage draw_image(image, SkIRect::MakeWH(image.width(), image.height()),
+ kMedium_SkFilterQuality,
+ CreateMatrix(SkSize::Make(0.5, 0.5), true),
+ PaintImage::kDefaultFrameIndex, raster_color_space);
+
+ DecodedDrawImage decoded_image = cache.GetDecodedImageForDraw(draw_image);
+ EXPECT_NE(decoded_image.image()->colorType(), kRGBA_F16_SkColorType);
+ cache.DrawWithImageFinished(draw_image, decoded_image);
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/tiles/tile.cc b/chromium/cc/tiles/tile.cc
index f0cf47dcfea..31195420491 100644
--- a/chromium/cc/tiles/tile.cc
+++ b/chromium/cc/tiles/tile.cc
@@ -7,6 +7,7 @@
#include <stddef.h>
#include <algorithm>
+#include <utility>
#include "base/numerics/safe_conversions.h"
#include "base/trace_event/trace_event.h"
@@ -22,8 +23,7 @@ Tile::Tile(TileManager* tile_manager,
const CreateInfo& info,
int layer_id,
int source_frame_number,
- int flags,
- bool can_use_lcd_text)
+ int flags)
: tile_manager_(tile_manager),
tiling_(info.tiling),
content_rect_(info.content_rect),
@@ -37,12 +37,10 @@ Tile::Tile(TileManager* tile_manager,
required_for_activation_(false),
required_for_draw_(false),
is_solid_color_analysis_performed_(false),
- can_use_lcd_text_(can_use_lcd_text),
- id_(tile_manager->GetUniqueTileId()),
- invalidated_id_(0),
- scheduled_priority_(0) {
- raster_rects_.push_back(
- std::make_pair(info.content_rect, info.raster_transform));
+ can_use_lcd_text_(info.can_use_lcd_text),
+ raster_task_scheduled_with_checker_images_(false),
+ id_(tile_manager->GetUniqueTileId()) {
+ raster_rects_.emplace_back(info.content_rect, info.raster_transform);
}
Tile::~Tile() {
diff --git a/chromium/cc/tiles/tile.h b/chromium/cc/tiles/tile.h
index 34726608547..6335d05dcd0 100644
--- a/chromium/cc/tiles/tile.h
+++ b/chromium/cc/tiles/tile.h
@@ -8,6 +8,9 @@
#include <stddef.h>
#include <stdint.h>
+#include <utility>
+#include <vector>
+
#include "base/memory/ref_counted.h"
#include "cc/paint/draw_image.h"
#include "cc/raster/tile_task.h"
@@ -23,27 +26,14 @@ class TileManager;
class CC_EXPORT Tile {
public:
- class CC_EXPORT CreateInfo {
- public:
- const PictureLayerTiling* tiling;
- int tiling_i_index;
- int tiling_j_index;
+ struct CreateInfo {
+ const PictureLayerTiling* tiling = nullptr;
+ int tiling_i_index = 0;
+ int tiling_j_index = 0;
gfx::Rect enclosing_layer_rect;
gfx::Rect content_rect;
gfx::AxisTransform2d raster_transform;
-
- CreateInfo(const PictureLayerTiling* tiling,
- int tiling_i_index,
- int tiling_j_index,
- const gfx::Rect& enclosing_layer_rect,
- const gfx::Rect& content_rect,
- const gfx::AxisTransform2d& raster_transform)
- : tiling(tiling),
- tiling_i_index(tiling_i_index),
- tiling_j_index(tiling_j_index),
- enclosing_layer_rect(enclosing_layer_rect),
- content_rect(content_rect),
- raster_transform(raster_transform) {}
+ bool can_use_lcd_text = false;
};
enum TileRasterFlags { USE_PICTURE_ANALYSIS = 1 << 0, IS_OPAQUE = 1 << 1 };
@@ -147,8 +137,7 @@ class CC_EXPORT Tile {
const CreateInfo& info,
int layer_id,
int source_frame_number,
- int flags,
- bool can_use_lcd_text);
+ int flags);
TileManager* const tile_manager_;
const PictureLayerTiling* tiling_;
@@ -163,11 +152,21 @@ class CC_EXPORT Tile {
const int flags_;
const int tiling_i_index_;
const int tiling_j_index_;
+
+ // The |id_| of the Tile that was invalidated and replaced by this tile.
+ Id invalidated_id_ = 0;
+
+ unsigned scheduled_priority_ = 0;
+
bool required_for_activation_ : 1;
bool required_for_draw_ : 1;
bool is_solid_color_analysis_performed_ : 1;
const bool can_use_lcd_text_ : 1;
+ // Set to true if there is a raster task scheduled for this tile that will
+ // rasterize a resource with checker images.
+ bool raster_task_scheduled_with_checker_images_ : 1;
+
Id id_;
// List of Rect-Transform pairs, representing unoccluded parts of the
@@ -178,14 +177,7 @@ class CC_EXPORT Tile {
// The rect bounding the changes in this Tile vs the previous tile it
// replaced.
gfx::Rect invalidated_content_rect_;
- // The |id_| of the Tile that was invalidated and replaced by this tile.
- Id invalidated_id_;
- unsigned scheduled_priority_;
-
- // Set to true if there is a raster task scheduled for this tile that will
- // rasterize a resource with checker images.
- bool raster_task_scheduled_with_checker_images_ = false;
scoped_refptr<TileTask> raster_task_;
};
diff --git a/chromium/cc/tiles/tile_manager.cc b/chromium/cc/tiles/tile_manager.cc
index 14bded9068c..88dac92a4b7 100644
--- a/chromium/cc/tiles/tile_manager.cc
+++ b/chromium/cc/tiles/tile_manager.cc
@@ -86,7 +86,13 @@ class RasterTaskImpl : public TileTask {
bool is_gpu_rasterization,
DispatchingImageProvider image_provider,
GURL url)
- : TileTask(!is_gpu_rasterization, dependencies),
+ : TileTask(
+ is_gpu_rasterization ? TileTask::SupportsConcurrentExecution::kNo
+ : TileTask::SupportsConcurrentExecution::kYes,
+ raster_buffer && raster_buffer->SupportsBackgroundThreadPriority()
+ ? TileTask::SupportsBackgroundThreadPriority::kYes
+ : TileTask::SupportsBackgroundThreadPriority::kNo,
+ dependencies),
tile_manager_(tile_manager),
tile_id_(tile->id()),
resource_(std::move(resource)),
@@ -182,6 +188,9 @@ TaskCategory TaskCategoryForTileTask(TileTask* task,
if (use_foreground_category)
return TASK_CATEGORY_FOREGROUND;
+ if (!task->supports_background_thread_priority())
+ return TASK_CATEGORY_BACKGROUND_WITH_NORMAL_THREAD_PRIORITY;
+
return TASK_CATEGORY_BACKGROUND;
}
@@ -192,10 +201,11 @@ bool IsForegroundCategory(uint16_t category) {
case TASK_CATEGORY_FOREGROUND:
return true;
case TASK_CATEGORY_BACKGROUND:
+ case TASK_CATEGORY_BACKGROUND_WITH_NORMAL_THREAD_PRIORITY:
return false;
}
- DCHECK(false);
+ NOTREACHED();
return false;
}
@@ -295,7 +305,8 @@ class TaskSetFinishedTaskImpl : public TileTask {
explicit TaskSetFinishedTaskImpl(
base::SequencedTaskRunner* task_runner,
base::RepeatingClosure on_task_set_finished_callback)
- : TileTask(true),
+ : TileTask(TileTask::SupportsConcurrentExecution::kYes,
+ TileTask::SupportsBackgroundThreadPriority::kYes),
task_runner_(task_runner),
on_task_set_finished_callback_(
std::move(on_task_set_finished_callback)) {}
@@ -329,7 +340,8 @@ class DidFinishRunningAllTilesTask : public TileTask {
DidFinishRunningAllTilesTask(base::SequencedTaskRunner* task_runner,
RasterBufferProvider* raster_buffer_provider,
CompletionCb completion_cb)
- : TileTask(false /* supports_concurrent_execution */),
+ : TileTask(TileTask::SupportsConcurrentExecution::kNo,
+ TileTask::SupportsBackgroundThreadPriority::kYes),
task_runner_(task_runner),
raster_buffer_provider_(raster_buffer_provider),
completion_cb_(std::move(completion_cb)) {}
@@ -355,13 +367,10 @@ class DidFinishRunningAllTilesTask : public TileTask {
gfx::ContentColorUsage GetContentColorUsageForPrioritizedTile(
const PrioritizedTile& prioritized_tile) {
- // TODO(cblume,ccameron): Add support for HDR.
- bool contains_only_srgb_images = prioritized_tile.raster_source()
- ->GetDisplayItemList()
- ->discardable_image_map()
- .contains_only_srgb_images();
- return contains_only_srgb_images ? gfx::ContentColorUsage::kSRGB
- : gfx::ContentColorUsage::kWideColorGamut;
+ return prioritized_tile.raster_source()
+ ->GetDisplayItemList()
+ ->discardable_image_map()
+ .content_color_usage();
}
} // namespace
@@ -393,6 +402,7 @@ TileManager::TileManager(
scheduled_raster_task_limit_(scheduled_raster_task_limit),
tile_manager_settings_(tile_manager_settings),
use_gpu_rasterization_(false),
+ use_oop_rasterization_(false),
all_tiles_that_need_to_be_rasterized_are_scheduled_(true),
did_check_for_completed_tasks_since_last_schedule_tasks_(true),
did_oom_on_last_assign_(false),
@@ -454,11 +464,13 @@ void TileManager::SetResources(ResourcePool* resource_pool,
ImageDecodeCache* image_decode_cache,
TaskGraphRunner* task_graph_runner,
RasterBufferProvider* raster_buffer_provider,
- bool use_gpu_rasterization) {
+ bool use_gpu_rasterization,
+ bool use_oop_rasterization) {
DCHECK(!tile_task_manager_);
DCHECK(task_graph_runner);
use_gpu_rasterization_ = use_gpu_rasterization;
+ use_oop_rasterization_ = use_oop_rasterization;
resource_pool_ = resource_pool;
image_controller_.SetImageDecodeCache(image_decode_cache);
tile_task_manager_ = TileTaskManagerImpl::Create(task_graph_runner);
@@ -735,6 +747,7 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() {
GetContentColorUsageForPrioritizedTile(prioritized_tile);
const gfx::ColorSpace raster_color_space =
client_->GetRasterColorSpace(content_color_usage);
+ const float sdr_white_level = client_->GetSDRWhiteLevel();
// Tiles in the raster queue should either require raster or decode for
// checker-images. If this tile does not need raster, process it only to
@@ -746,7 +759,7 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() {
DCHECK(prioritized_tile.should_decode_checkered_images_for_tile());
AddCheckeredImagesToDecodeQueue(
- prioritized_tile, raster_color_space,
+ prioritized_tile, raster_color_space, sdr_white_level,
CheckerImageTracker::DecodeType::kRaster,
&work_to_schedule.checker_image_decode_queue);
continue;
@@ -807,7 +820,7 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() {
if (tile->raster_task_scheduled_with_checker_images() &&
prioritized_tile.should_decode_checkered_images_for_tile()) {
AddCheckeredImagesToDecodeQueue(
- prioritized_tile, raster_color_space,
+ prioritized_tile, raster_color_space, sdr_white_level,
CheckerImageTracker::DecodeType::kRaster,
&work_to_schedule.checker_image_decode_queue);
}
@@ -815,7 +828,7 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() {
// Creating the raster task here will acquire resources, but
// this resource usage has already been accounted for above.
auto raster_task = CreateRasterTask(prioritized_tile, raster_color_space,
- &work_to_schedule);
+ sdr_white_level, &work_to_schedule);
if (!raster_task) {
continue;
}
@@ -855,12 +868,13 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() {
GetContentColorUsageForPrioritizedTile(prioritized_tile);
gfx::ColorSpace raster_color_space =
client_->GetRasterColorSpace(content_color_usage);
+ const float sdr_white_level = client_->GetSDRWhiteLevel();
Tile* tile = prioritized_tile.tile();
if (tile->draw_info().is_checker_imaged() ||
tile->raster_task_scheduled_with_checker_images()) {
AddCheckeredImagesToDecodeQueue(
- prioritized_tile, raster_color_space,
+ prioritized_tile, raster_color_space, sdr_white_level,
CheckerImageTracker::DecodeType::kRaster,
&work_to_schedule.checker_image_decode_queue);
}
@@ -911,6 +925,7 @@ void TileManager::FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(
void TileManager::PartitionImagesForCheckering(
const PrioritizedTile& prioritized_tile,
const gfx::ColorSpace& raster_color_space,
+ float sdr_white_level,
std::vector<DrawImage>* sync_decoded_images,
std::vector<PaintImage>* checkered_images,
const gfx::Rect* invalidated_rect,
@@ -933,7 +948,7 @@ void TileManager::PartitionImagesForCheckering(
(*image_to_frame_index)[image.stable_id()] = frame_index;
DrawImage draw_image(*original_draw_image, tile->raster_transform().scale(),
- frame_index, raster_color_space);
+ frame_index, raster_color_space, sdr_white_level);
if (checker_image_tracker_.ShouldCheckerImage(draw_image, tree))
checkered_images->push_back(draw_image.paint_image());
else
@@ -944,6 +959,7 @@ void TileManager::PartitionImagesForCheckering(
void TileManager::AddCheckeredImagesToDecodeQueue(
const PrioritizedTile& prioritized_tile,
const gfx::ColorSpace& raster_color_space,
+ float sdr_white_level,
CheckerImageTracker::DecodeType decode_type,
CheckerImageTracker::ImageDecodeQueue* image_decode_queue) {
Tile* tile = prioritized_tile.tile();
@@ -951,12 +967,11 @@ void TileManager::AddCheckeredImagesToDecodeQueue(
prioritized_tile.raster_source()->GetDiscardableImagesInRect(
tile->enclosing_layer_rect(), &images_in_tile);
WhichTree tree = tile->tiling()->tree();
-
for (const auto* original_draw_image : images_in_tile) {
size_t frame_index = client_->GetFrameIndexForImage(
original_draw_image->paint_image(), tree);
DrawImage draw_image(*original_draw_image, tile->raster_transform().scale(),
- frame_index, raster_color_space);
+ frame_index, raster_color_space, sdr_white_level);
if (checker_image_tracker_.ShouldCheckerImage(draw_image, tree)) {
image_decode_queue->emplace_back(draw_image.paint_image(), decode_type);
}
@@ -1050,12 +1065,13 @@ void TileManager::ScheduleTasks(PrioritizedWorkToSchedule work_to_schedule) {
GetContentColorUsageForPrioritizedTile(prioritized_tile);
gfx::ColorSpace raster_color_space =
client_->GetRasterColorSpace(content_color_usage);
+ float sdr_white_level = client_->GetSDRWhiteLevel();
std::vector<DrawImage> sync_decoded_images;
std::vector<PaintImage> checkered_images;
PartitionImagesForCheckering(prioritized_tile, raster_color_space,
- &sync_decoded_images, &checkered_images,
- nullptr);
+ sdr_white_level, &sync_decoded_images,
+ &checkered_images, nullptr);
// Add the sync decoded images to |new_locked_images| so they can be added
// to the task graph.
@@ -1149,6 +1165,7 @@ void TileManager::ScheduleTasks(PrioritizedWorkToSchedule work_to_schedule) {
scoped_refptr<TileTask> TileManager::CreateRasterTask(
const PrioritizedTile& prioritized_tile,
const gfx::ColorSpace& raster_color_space,
+ float sdr_white_level,
PrioritizedWorkToSchedule* work_to_schedule) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"TileManager::CreateRasterTask");
@@ -1159,6 +1176,17 @@ scoped_refptr<TileTask> TileManager::CreateRasterTask(
const int msaa_sample_count = client_->GetMSAASampleCountForRaster(
prioritized_tile.raster_source()->GetDisplayItemList());
+ // When possible, rasterize HDR content into F16.
+ //
+ // TODO(crbug.com/1076568): Once we have access to the display's buffer format
+ // via gfx::DisplayColorSpaces, we should also do this for HBD images.
+ auto format = DetermineResourceFormat(tile);
+ if (raster_color_space.IsHDR() &&
+ GetContentColorUsageForPrioritizedTile(prioritized_tile) ==
+ gfx::ContentColorUsage::kHDR) {
+ format = viz::ResourceFormat::RGBA_F16;
+ }
+
// Get the resource.
ResourcePool::InUsePoolResource resource;
uint64_t resource_content_id = 0;
@@ -1172,12 +1200,11 @@ scoped_refptr<TileTask> TileManager::CreateRasterTask(
bool partial_tile_decode = false;
if (resource) {
resource_content_id = tile->invalidated_id();
- DCHECK_EQ(DetermineResourceFormat(tile), resource.format());
+ DCHECK_EQ(format, resource.format());
partial_tile_decode = true;
} else {
resource = resource_pool_->AcquireResource(tile->desired_texture_size(),
- DetermineResourceFormat(tile),
- raster_color_space);
+ format, raster_color_space);
DCHECK(resource);
}
@@ -1200,8 +1227,9 @@ scoped_refptr<TileTask> TileManager::CreateRasterTask(
base::flat_map<PaintImage::Id, size_t> image_id_to_current_frame_index;
if (!skip_images) {
PartitionImagesForCheckering(
- prioritized_tile, raster_color_space, &sync_decoded_images,
- &checkered_images, partial_tile_decode ? &invalidated_rect : nullptr,
+ prioritized_tile, raster_color_space, sdr_white_level,
+ &sync_decoded_images, &checkered_images,
+ partial_tile_decode ? &invalidated_rect : nullptr,
&image_id_to_current_frame_index);
}
@@ -1266,6 +1294,11 @@ scoped_refptr<TileTask> TileManager::CreateRasterTask(
settings->images_to_skip = std::move(images_to_skip);
settings->image_to_current_frame_index =
std::move(image_id_to_current_frame_index);
+ if (use_oop_rasterization_) {
+ settings->raster_mode = PlaybackImageProvider::RasterMode::kOop;
+ } else if (use_gpu_rasterization_) {
+ settings->raster_mode = PlaybackImageProvider::RasterMode::kGpu;
+ }
}
PlaybackImageProvider image_provider(image_controller_.cache(),
@@ -1368,13 +1401,12 @@ void TileManager::OnRasterTaskCompleted(
std::unique_ptr<Tile> TileManager::CreateTile(const Tile::CreateInfo& info,
int layer_id,
int source_frame_number,
- int flags,
- bool can_use_lcd_text) {
+ int flags) {
// We need to have a tile task worker pool to do anything meaningful with
// tiles.
DCHECK(tile_task_manager_);
- std::unique_ptr<Tile> tile(new Tile(this, info, layer_id, source_frame_number,
- flags, can_use_lcd_text));
+ std::unique_ptr<Tile> tile(
+ new Tile(this, info, layer_id, source_frame_number, flags));
DCHECK(tiles_.find(tile->id()) == tiles_.end());
tiles_[tile->id()] = tile.get();
diff --git a/chromium/cc/tiles/tile_manager.h b/chromium/cc/tiles/tile_manager.h
index 8998ccb759a..c36a82a36bc 100644
--- a/chromium/cc/tiles/tile_manager.h
+++ b/chromium/cc/tiles/tile_manager.h
@@ -11,6 +11,7 @@
#include <memory>
#include <set>
#include <unordered_map>
+#include <unordered_set>
#include <utility>
#include <vector>
@@ -84,6 +85,12 @@ class CC_EXPORT TileManagerClient {
virtual gfx::ColorSpace GetRasterColorSpace(
gfx::ContentColorUsage content_color_usage) const = 0;
+ // Return the SDR white level for rasterization. Some systems have variable
+ // white levels (e.g., Windows SDR brightness slider). This should return the
+ // level of the monitor on which the rasterized content will be displayed (and
+ // changing the SDR white level of the display will trigger a re-raster).
+ virtual float GetSDRWhiteLevel() const = 0;
+
// Requests that a pending tree be scheduled to invalidate content on the
// pending on active tree. This is currently used when tiles that are
// rasterized with missing images need to be invalidated.
@@ -169,7 +176,8 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient {
ImageDecodeCache* image_decode_cache,
TaskGraphRunner* task_graph_runner,
RasterBufferProvider* raster_buffer_provider,
- bool use_gpu_rasterization);
+ bool use_gpu_rasterization,
+ bool use_oop_rasterization);
// This causes any completed raster work to finalize, so that tiles get up to
// date draw information.
@@ -182,8 +190,7 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient {
std::unique_ptr<Tile> CreateTile(const Tile::CreateInfo& info,
int layer_id,
int source_frame_number,
- int flags,
- bool can_use_lcd_text);
+ int flags);
bool IsReadyToActivate() const;
bool IsReadyToDraw() const;
@@ -364,6 +371,7 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient {
scoped_refptr<TileTask> CreateRasterTask(
const PrioritizedTile& prioritized_tile,
const gfx::ColorSpace& raster_color_space,
+ float sdr_white_level,
PrioritizedWorkToSchedule* work_to_schedule);
std::unique_ptr<EvictionTilePriorityQueue>
@@ -397,6 +405,7 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient {
void PartitionImagesForCheckering(
const PrioritizedTile& prioritized_tile,
const gfx::ColorSpace& raster_color_space,
+ float sdr_white_level,
std::vector<DrawImage>* sync_decoded_images,
std::vector<PaintImage>* checkered_images,
const gfx::Rect* invalidated_rect,
@@ -404,6 +413,7 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient {
void AddCheckeredImagesToDecodeQueue(
const PrioritizedTile& prioritized_tile,
const gfx::ColorSpace& raster_color_space,
+ float sdr_white_level,
CheckerImageTracker::DecodeType decode_type,
CheckerImageTracker::ImageDecodeQueue* image_decode_queue);
@@ -428,6 +438,7 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient {
const TileManagerSettings tile_manager_settings_;
bool use_gpu_rasterization_;
+ bool use_oop_rasterization_;
std::unordered_map<Tile::Id, Tile*> tiles_;
diff --git a/chromium/cc/tiles/tile_manager_unittest.cc b/chromium/cc/tiles/tile_manager_unittest.cc
index 5aa183e3009..48f5c11a031 100644
--- a/chromium/cc/tiles/tile_manager_unittest.cc
+++ b/chromium/cc/tiles/tile_manager_unittest.cc
@@ -86,6 +86,19 @@ class SynchronousSimpleTaskRunner : public base::TestSimpleTaskRunner {
bool run_tasks_synchronously_ = false;
};
+class FakeRasterBuffer : public RasterBuffer {
+ public:
+ void Playback(const RasterSource* raster_source,
+ const gfx::Rect& raster_full_rect,
+ const gfx::Rect& raster_dirty_rect,
+ uint64_t new_content_id,
+ const gfx::AxisTransform2d& transform,
+ const RasterSource::PlaybackSettings& playback_settings,
+ const GURL& url) override {}
+
+ bool SupportsBackgroundThreadPriority() const override { return true; }
+};
+
class TileManagerTilePriorityQueueTest : public TestLayerTreeHostBase {
public:
LayerTreeSettings CreateSettings() override {
@@ -830,7 +843,7 @@ TEST_F(TileManagerTilePriorityQueueTest,
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
scoped_refptr<FakeRasterSource> pending_raster_source =
- FakeRasterSource::CreateFilled(layer_bounds);
+ FakeRasterSource::CreateFilledWithText(layer_bounds);
SetupPendingTree(pending_raster_source);
auto* pending_child_layer = AddLayer<FakePictureLayerImpl>(
@@ -1568,6 +1581,8 @@ class TestSoftwareRasterBufferProvider : public FakeRasterBufferProviderImpl {
gfx::ColorSpace(), kIsGpuCompositing, playback_settings);
}
+ bool SupportsBackgroundThreadPriority() const override { return true; }
+
private:
gfx::Size size_;
void* pixels_;
@@ -2188,17 +2203,6 @@ class InvalidResourceRasterBufferProvider
uint64_t tracing_process_id,
int importance) const override {}
};
-
- class FakeRasterBuffer : public RasterBuffer {
- public:
- void Playback(const RasterSource* raster_source,
- const gfx::Rect& raster_full_rect,
- const gfx::Rect& raster_dirty_rect,
- uint64_t new_content_id,
- const gfx::AxisTransform2d& transform,
- const RasterSource::PlaybackSettings& playback_settings,
- const GURL& url) override {}
- };
};
class InvalidResourceTileManagerTest : public TileManagerTest {
@@ -2273,18 +2277,6 @@ class MockReadyToDrawRasterBufferProviderImpl
resource.set_software_backing(std::make_unique<TestSoftwareBacking>());
return std::make_unique<FakeRasterBuffer>();
}
-
- private:
- class FakeRasterBuffer : public RasterBuffer {
- public:
- void Playback(const RasterSource* raster_source,
- const gfx::Rect& raster_full_rect,
- const gfx::Rect& raster_dirty_rect,
- uint64_t new_content_id,
- const gfx::AxisTransform2d& transform,
- const RasterSource::PlaybackSettings& playback_settings,
- const GURL& url) override {}
- };
};
class TileManagerReadyToDrawTest : public TileManagerTest {
@@ -3241,6 +3233,8 @@ class VerifyImageProviderRasterBuffer : public RasterBuffer {
EXPECT_TRUE(playback_settings.image_provider);
}
+ bool SupportsBackgroundThreadPriority() const override { return true; }
+
private:
bool did_raster_ = false;
};
@@ -3423,6 +3417,107 @@ TEST_F(DecodedImageTrackerTileManagerTest, DecodedImageTrackerDropsLocksOnUse) {
.NumLockedImagesForTesting());
}
+class HdrImageTileManagerTest : public CheckerImagingTileManagerTest {
+ public:
+ void DecodeHdrImage(const gfx::ColorSpace& raster_cs) {
+ auto color_space = gfx::ColorSpace::CreateHDR10();
+ auto size = gfx::Size(250, 250);
+ auto info =
+ SkImageInfo::Make(size.width(), size.height(), kRGBA_F16_SkColorType,
+ kPremul_SkAlphaType, color_space.ToSkColorSpace());
+ SkBitmap bitmap;
+ bitmap.allocPixels(info);
+ PaintImage hdr_image = PaintImageBuilder::WithDefault()
+ .set_id(PaintImage::kInvalidId)
+ .set_is_high_bit_depth(true)
+ .set_image(SkImage::MakeFromBitmap(bitmap),
+ PaintImage::GetNextContentId())
+ .TakePaintImage();
+
+ // Add the image to our decoded_image_tracker.
+ host_impl()->tile_manager()->decoded_image_tracker().QueueImageDecode(
+ hdr_image, raster_cs, base::DoNothing());
+ FlushDecodeTasks();
+
+ // Add images to a fake recording source.
+ constexpr gfx::Size kLayerBounds(1000, 500);
+ auto recording_source =
+ FakeRecordingSource::CreateFilledRecordingSource(kLayerBounds);
+ recording_source->set_fill_with_nonsolid_color(true);
+ recording_source->add_draw_image(hdr_image, gfx::Point(0, 0));
+ recording_source->Rerecord();
+
+ auto raster_source = recording_source->CreateRasterSource();
+
+ constexpr gfx::Size kTileSize(500, 500);
+ Region invalidation((gfx::Rect(kLayerBounds)));
+ SetupPendingTree(raster_source, kTileSize, invalidation);
+
+ constexpr float kCustomWhiteLevel = 200.f;
+ auto display_cs = gfx::DisplayColorSpaces(raster_cs);
+ if (raster_cs.IsHDR())
+ display_cs.SetSDRWhiteLevel(kCustomWhiteLevel);
+
+ pending_layer()->layer_tree_impl()->SetDisplayColorSpaces(display_cs);
+ PictureLayerTilingSet* tiling_set =
+ pending_layer()->picture_layer_tiling_set();
+ PictureLayerTiling* pending_tiling = tiling_set->tiling_at(0);
+ pending_tiling->set_resolution(HIGH_RESOLUTION);
+ pending_tiling->CreateAllTilesForTesting();
+ pending_tiling->SetTilePriorityRectsForTesting(
+ gfx::Rect(kLayerBounds), // Visible rect.
+ gfx::Rect(kLayerBounds), // Skewport rect.
+ gfx::Rect(kLayerBounds), // Soon rect.
+ gfx::Rect(kLayerBounds)); // Eventually rect.
+
+ host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
+ ASSERT_TRUE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
+
+ auto pending_tiles = pending_tiling->AllTilesForTesting();
+ ASSERT_FALSE(pending_tiles.empty());
+
+ if (raster_cs.IsHDR()) {
+ // Only the last tile will have any pending tasks.
+ const auto& pending_tasks =
+ host_impl()->tile_manager()->decode_tasks_for_testing(
+ pending_tiles.back()->id());
+ EXPECT_FALSE(pending_tasks.empty());
+ for (const auto& draw_info : pending_tasks) {
+ EXPECT_EQ(draw_info.target_color_space(), raster_cs);
+ EXPECT_FLOAT_EQ(draw_info.sdr_white_level(), kCustomWhiteLevel);
+ }
+ }
+
+ // Raster all tiles.
+ static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())
+ ->RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+ ASSERT_FALSE(
+ host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
+
+ auto expected_format = raster_cs.IsHDR() ? viz::RGBA_F16 : viz::RGBA_8888;
+ auto all_tiles = host_impl()->tile_manager()->AllTilesForTesting();
+ for (const auto* tile : all_tiles)
+ EXPECT_EQ(expected_format, tile->draw_info().resource_format());
+ }
+};
+
+TEST_F(HdrImageTileManagerTest, DecodeHdrImagesToHdrPq) {
+ DecodeHdrImage(gfx::ColorSpace::CreateHDR10());
+}
+
+TEST_F(HdrImageTileManagerTest, DecodeHdrImagesToHdrHlg) {
+ DecodeHdrImage(gfx::ColorSpace::CreateHLG());
+}
+
+TEST_F(HdrImageTileManagerTest, DecodeHdrImagesToSdrSrgb) {
+ DecodeHdrImage(gfx::ColorSpace::CreateSRGB());
+}
+
+TEST_F(HdrImageTileManagerTest, DecodeHdrImagesToSdrP3) {
+ DecodeHdrImage(gfx::ColorSpace::CreateDisplayP3D65());
+}
+
class TileManagerCheckRasterQueriesTest : public TileManagerTest {
public:
~TileManagerCheckRasterQueriesTest() override {
diff --git a/chromium/cc/trees/browser_controls_params.cc b/chromium/cc/trees/browser_controls_params.cc
index 4c762b335b2..5da3f8dc9b7 100644
--- a/chromium/cc/trees/browser_controls_params.cc
+++ b/chromium/cc/trees/browser_controls_params.cc
@@ -15,7 +15,9 @@ bool BrowserControlsParams::operator==(
animate_browser_controls_height_changes ==
other.animate_browser_controls_height_changes &&
browser_controls_shrink_blink_size ==
- other.browser_controls_shrink_blink_size;
+ other.browser_controls_shrink_blink_size &&
+ only_expand_top_controls_at_page_top ==
+ other.only_expand_top_controls_at_page_top;
}
bool BrowserControlsParams::operator!=(
diff --git a/chromium/cc/trees/browser_controls_params.h b/chromium/cc/trees/browser_controls_params.h
index 7ef5326c9b7..91311103e78 100644
--- a/chromium/cc/trees/browser_controls_params.h
+++ b/chromium/cc/trees/browser_controls_params.h
@@ -35,6 +35,11 @@ struct CC_EXPORT BrowserControlsParams {
// URL-bar (always false on platforms where URL-bar hiding isn't supported).
bool browser_controls_shrink_blink_size = false;
+ // Whether or not the top controls should only expand at the top of the page
+ // contents. If true, collapsed top controls won't begin scrolling into view
+ // until the page is scrolled to the top.
+ bool only_expand_top_controls_at_page_top = false;
+
bool operator==(const BrowserControlsParams& other) const;
bool operator!=(const BrowserControlsParams& other) const;
};
diff --git a/chromium/cc/trees/scroll_and_scale_set.cc b/chromium/cc/trees/compositor_commit_data.cc
index 6049e60b436..939ed2d8347 100644
--- a/chromium/cc/trees/scroll_and_scale_set.cc
+++ b/chromium/cc/trees/compositor_commit_data.cc
@@ -1,14 +1,14 @@
-// Copyright 2011 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "cc/trees/scroll_and_scale_set.h"
+#include "cc/trees/compositor_commit_data.h"
#include "cc/trees/swap_promise.h"
namespace cc {
-ScrollAndScaleSet::ScrollAndScaleSet()
+CompositorCommitData::CompositorCommitData()
: page_scale_delta(1.f),
is_pinch_gesture_active(false),
top_controls_delta(0.f),
@@ -18,11 +18,11 @@ ScrollAndScaleSet::ScrollAndScaleSet()
scroll_gesture_did_end(false),
manipulation_info(kManipulationInfoNone) {}
-ScrollAndScaleSet::~ScrollAndScaleSet() = default;
+CompositorCommitData::~CompositorCommitData() = default;
-ScrollAndScaleSet::ScrollUpdateInfo::ScrollUpdateInfo() = default;
+CompositorCommitData::ScrollUpdateInfo::ScrollUpdateInfo() = default;
-ScrollAndScaleSet::ScrollUpdateInfo::ScrollUpdateInfo(
+CompositorCommitData::ScrollUpdateInfo::ScrollUpdateInfo(
ElementId id,
gfx::ScrollOffset delta,
base::Optional<TargetSnapAreaElementIds> snap_target_ids)
@@ -30,10 +30,11 @@ ScrollAndScaleSet::ScrollUpdateInfo::ScrollUpdateInfo(
scroll_delta(delta),
snap_target_element_ids(snap_target_ids) {}
-ScrollAndScaleSet::ScrollUpdateInfo::ScrollUpdateInfo(
+CompositorCommitData::ScrollUpdateInfo::ScrollUpdateInfo(
const ScrollUpdateInfo& other) = default;
-ScrollAndScaleSet::ScrollUpdateInfo& ScrollAndScaleSet::ScrollUpdateInfo::
-operator=(const ScrollUpdateInfo& other) = default;
+CompositorCommitData::ScrollUpdateInfo&
+CompositorCommitData::ScrollUpdateInfo::operator=(
+ const ScrollUpdateInfo& other) = default;
} // namespace cc
diff --git a/chromium/cc/trees/scroll_and_scale_set.h b/chromium/cc/trees/compositor_commit_data.h
index b7e9d9ed91a..ccee2259983 100644
--- a/chromium/cc/trees/scroll_and_scale_set.h
+++ b/chromium/cc/trees/compositor_commit_data.h
@@ -1,10 +1,11 @@
-// Copyright 2011 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CC_TREES_SCROLL_AND_SCALE_SET_H_
-#define CC_TREES_SCROLL_AND_SCALE_SET_H_
+#ifndef CC_TREES_COMPOSITOR_COMMIT_DATA_H_
+#define CC_TREES_COMPOSITOR_COMMIT_DATA_H_
+#include <memory>
#include <vector>
#include "cc/cc_export.h"
@@ -20,12 +21,12 @@ namespace cc {
class SwapPromise;
-struct CC_EXPORT ScrollAndScaleSet {
- ScrollAndScaleSet();
- ScrollAndScaleSet(const ScrollAndScaleSet&) = delete;
- ~ScrollAndScaleSet();
+struct CC_EXPORT CompositorCommitData {
+ CompositorCommitData();
+ CompositorCommitData(const CompositorCommitData&) = delete;
+ ~CompositorCommitData();
- ScrollAndScaleSet& operator=(const ScrollAndScaleSet&) = delete;
+ CompositorCommitData& operator=(const CompositorCommitData&) = delete;
struct CC_EXPORT ScrollUpdateInfo {
ScrollUpdateInfo();
@@ -108,4 +109,4 @@ struct CC_EXPORT ScrollAndScaleSet {
} // namespace cc
-#endif // CC_TREES_SCROLL_AND_SCALE_SET_H_
+#endif // CC_TREES_COMPOSITOR_COMMIT_DATA_H_
diff --git a/chromium/cc/trees/damage_tracker.cc b/chromium/cc/trees/damage_tracker.cc
index ec33bf09ad7..b6f3dc9822d 100644
--- a/chromium/cc/trees/damage_tracker.cc
+++ b/chromium/cc/trees/damage_tracker.cc
@@ -7,6 +7,7 @@
#include <stddef.h>
#include <algorithm>
+#include <utility>
#include "base/memory/ptr_util.h"
#include "cc/base/math_util.h"
@@ -355,13 +356,14 @@ void DamageTracker::AccumulateDamageFromLayer(LayerImpl* layer) {
LayerRectMapData& data = RectDataForLayer(layer->id(), &layer_is_new);
gfx::Rect old_rect_in_target_space = data.rect_;
- gfx::Rect rect_in_target_space = layer->GetEnclosingRectInTargetSpace();
- data.Update(rect_in_target_space, mailboxId_);
+ gfx::Rect visible_rect_in_target_space =
+ layer->visible_drawable_content_rect();
+ data.Update(visible_rect_in_target_space, mailboxId_);
if (layer_is_new || layer->LayerPropertyChanged()) {
// If a layer is new or has changed, then its entire layer rect affects the
// target surface.
- damage_for_this_update_.Union(rect_in_target_space);
+ damage_for_this_update_.Union(visible_rect_in_target_space);
// The layer's old region is now exposed on the target surface, too.
// Note old_rect_in_target_space is already in target space.
diff --git a/chromium/cc/trees/damage_tracker_unittest.cc b/chromium/cc/trees/damage_tracker_unittest.cc
index db9fcc1fcf6..3e4714170b6 100644
--- a/chromium/cc/trees/damage_tracker_unittest.cc
+++ b/chromium/cc/trees/damage_tracker_unittest.cc
@@ -5,6 +5,8 @@
#include "cc/trees/damage_tracker.h"
#include <stddef.h>
+#include <limits>
+#include <utility>
#include "base/memory/ptr_util.h"
#include "cc/base/math_util.h"
@@ -71,6 +73,13 @@ void ClearDamageForAllSurfaces(LayerImpl* root) {
}
}
+void SetCopyRequest(LayerImpl* root) {
+ auto* root_node = root->layer_tree_impl()->property_trees()->effect_tree.Node(
+ root->effect_tree_index());
+ root_node->has_copy_request = true;
+ root->layer_tree_impl()->property_trees()->effect_tree.set_needs_update(true);
+}
+
class DamageTrackerTest : public LayerTreeImplTestBase, public testing::Test {
public:
LayerImpl* CreateTestTreeWithOneSurface(int number_of_children) {
@@ -270,7 +279,6 @@ class DamageTrackerTest : public LayerTreeImplTestBase, public testing::Test {
// 2. updating all damage trackers in the correct order
// 3. resetting all update_rects and property_changed flags for all layers
// and surfaces.
-
root->layer_tree_impl()->SetDeviceScaleFactor(device_scale_factor);
root->layer_tree_impl()->set_needs_update_draw_properties();
UpdateDrawProperties(root->layer_tree_impl());
@@ -1752,6 +1760,8 @@ TEST_F(DamageTrackerTest, HugeDamageRect) {
for (int i = 0; i < kRange; ++i) {
LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface();
LayerImpl* child = child_layers_[0];
+ // Set copy request to damage the entire layer.
+ SetCopyRequest(root);
gfx::Transform transform;
transform.Translate(-kBigNumber, -kBigNumber);
@@ -1789,6 +1799,9 @@ TEST_F(DamageTrackerTest, DamageRectTooBig) {
LayerImpl* child1 = child_layers_[0];
LayerImpl* child2 = child_layers_[1];
+ // Set copy request to damage the entire layer.
+ SetCopyRequest(root);
+
// Really far left.
child1->SetOffsetToTransformParent(
gfx::Vector2dF(std::numeric_limits<int>::min() + 100, 0));
@@ -1798,9 +1811,7 @@ TEST_F(DamageTrackerTest, DamageRectTooBig) {
child2->SetOffsetToTransformParent(
gfx::Vector2dF(std::numeric_limits<int>::max() - 100, 0));
child2->SetBounds(gfx::Size(1, 1));
-
- float device_scale_factor = 1.f;
- EmulateDrawingOneFrame(root, device_scale_factor);
+ EmulateDrawingOneFrame(root, 1.f);
// The expected damage would be too large to store in a gfx::Rect, so we
// should damage everything (ie, we don't have a valid rect).
@@ -1819,6 +1830,9 @@ TEST_F(DamageTrackerTest, DamageRectTooBigWithFilter) {
LayerImpl* child1 = child_layers_[0];
LayerImpl* child2 = child_layers_[1];
+ // Set copy request to damage the entire layer.
+ SetCopyRequest(root);
+
FilterOperations filters;
filters.Append(FilterOperation::CreateBlurFilter(5.f));
root->SetDrawsContent(true);
@@ -1852,6 +1866,9 @@ TEST_F(DamageTrackerTest, DamageRectTooBigWithFilter) {
TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) {
LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfacesDrawingFullyVisible();
+ // Set copy request to damage the entire layer.
+ SetCopyRequest(root);
+
// Really far left.
grand_child1_->SetOffsetToTransformParent(
gfx::Vector2dF(std::numeric_limits<int>::min() + 500, 0));
@@ -1933,6 +1950,9 @@ TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) {
TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurfaceWithFilter) {
LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces();
+ // Set copy request to damage the entire layer.
+ SetCopyRequest(root);
+
// Set up a moving pixels filter on the child.
FilterOperations filters;
filters.Append(FilterOperation::CreateBlurFilter(5.f));
@@ -2211,5 +2231,124 @@ TEST_F(DamageTrackerTest, CanUseCachedBackdropFilterResultTest) {
->can_use_cached_backdrop_filtered_result());
}
+TEST_F(DamageTrackerTest, DamageRectOnlyVisibleContentsMoveToOutside) {
+ LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface(2);
+ ClearDamageForAllSurfaces(root);
+
+ LayerImpl* child1 = child_layers_[0];
+ LayerImpl* child2 = child_layers_[1];
+ gfx::Rect origin_damage = child1->visible_drawable_content_rect();
+ origin_damage.Union(child2->visible_drawable_content_rect());
+
+ // Really far left.
+ child1->SetOffsetToTransformParent(
+ gfx::Vector2dF(std::numeric_limits<int>::min() + 100, 0));
+ child1->SetBounds(gfx::Size(1, 1));
+
+ // Really far right.
+ child2->SetOffsetToTransformParent(
+ gfx::Vector2dF(std::numeric_limits<int>::max() - 100, 0));
+ child2->SetBounds(gfx::Size(1, 1));
+ EmulateDrawingOneFrame(root, 1.f);
+
+ // Above damages should be excludebe because they're outside of
+ // the root surface.
+ gfx::Rect damage_rect;
+ EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid(
+ &damage_rect));
+ EXPECT_EQ(origin_damage, damage_rect);
+ EXPECT_TRUE(GetRenderSurface(root)->content_rect().Contains(damage_rect));
+ EXPECT_TRUE(GetRenderSurface(root)
+ ->damage_tracker()
+ ->has_damage_from_contributing_content());
+}
+
+TEST_F(DamageTrackerTest, DamageRectOnlyVisibleContentsLargeTwoContents) {
+ LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface(2);
+ ClearDamageForAllSurfaces(root);
+
+ LayerImpl* child1 = child_layers_[0];
+ LayerImpl* child2 = child_layers_[1];
+
+ gfx::Rect expected_damage = child1->visible_drawable_content_rect();
+ expected_damage.Union(child2->visible_drawable_content_rect());
+ expected_damage.set_x(0);
+ expected_damage.set_width(GetRenderSurface(root)->content_rect().width());
+
+ // Really far left.
+ child1->SetOffsetToTransformParent(
+ gfx::Vector2dF(std::numeric_limits<int>::min() + 100, 100));
+ child1->SetBounds(
+ gfx::Size(std::numeric_limits<int>::max(), child1->bounds().height()));
+
+ // Really far right.
+ child2->SetOffsetToTransformParent(gfx::Vector2dF(100, 100));
+ child2->SetBounds(
+ gfx::Size(std::numeric_limits<int>::max(), child2->bounds().height()));
+ EmulateDrawingOneFrame(root, 1.f);
+
+ // Above damages should be excludebe because they're outside of
+ // the root surface.
+ gfx::Rect damage_rect;
+ EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid(
+ &damage_rect));
+ EXPECT_EQ(expected_damage, damage_rect);
+ EXPECT_TRUE(GetRenderSurface(root)->content_rect().Contains(damage_rect));
+ EXPECT_TRUE(GetRenderSurface(root)
+ ->damage_tracker()
+ ->has_damage_from_contributing_content());
+}
+
+TEST_F(DamageTrackerTest,
+ DamageRectOnlyVisibleContentsHugeContentPartiallyVisible) {
+ LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface(1);
+ int content_width = GetRenderSurface(root)->content_rect().width();
+
+ ClearDamageForAllSurfaces(root);
+
+ LayerImpl* child1 = child_layers_[0];
+ int y = child1->offset_to_transform_parent().y();
+ int offset = 100;
+ int expected_width = offset + child1->bounds().width();
+ // Huge content that exceeds on both side.
+ child1->SetOffsetToTransformParent(
+ gfx::Vector2dF(std::numeric_limits<int>::min() + offset, y));
+ child1->SetBounds(
+ gfx::Size(std::numeric_limits<int>::max(), child1->bounds().height()));
+
+ EmulateDrawingOneFrame(root);
+
+ gfx::Rect expected_damage_rect1(0, y, expected_width,
+ child1->bounds().height());
+
+ // Above damages should be excludebe because they're outside of
+ // the root surface.
+ gfx::Rect damage_rect;
+ EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid(
+ &damage_rect));
+ EXPECT_EQ(expected_damage_rect1, damage_rect);
+ EXPECT_TRUE(GetRenderSurface(root)
+ ->damage_tracker()
+ ->has_damage_from_contributing_content());
+
+ ClearDamageForAllSurfaces(root);
+
+ // Now move the huge layer to the right, keeping offset visible.
+ child1->SetOffsetToTransformParent(gfx::Vector2dF(content_width - offset, y));
+ child1->NoteLayerPropertyChanged();
+
+ EmulateDrawingOneFrame(root);
+
+ // The damaged rect should be "letter boxed" region.
+ gfx::Rect expected_damage_rect2(0, y, content_width,
+ child1->bounds().height());
+ EXPECT_TRUE(GetRenderSurface(root)->damage_tracker()->GetDamageRectIfValid(
+ &damage_rect));
+ EXPECT_EQ(expected_damage_rect2, damage_rect);
+ EXPECT_TRUE(GetRenderSurface(root)
+ ->damage_tracker()
+ ->has_damage_from_contributing_content());
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/trees/draw_properties_unittest.cc b/chromium/cc/trees/draw_properties_unittest.cc
index b19a402d142..7e2c465bd57 100644
--- a/chromium/cc/trees/draw_properties_unittest.cc
+++ b/chromium/cc/trees/draw_properties_unittest.cc
@@ -3609,6 +3609,7 @@ TEST_F(TransformInteropTest, BackfaceInvisibleTransform) {
back_facing->SetShouldCheckBackfaceVisibility(true);
back_facing_double_sided->SetShouldCheckBackfaceVisibility(false);
+ back_facing->SetHasWillChangeTransformHint(true);
front_facing->SetShouldCheckBackfaceVisibility(true);
auto& back_facing_transform_node = CreateTransformNode(back_facing);
@@ -3632,6 +3633,7 @@ TEST_F(TransformInteropTest, BackfaceInvisibleTransform) {
front_facing, front_facing->transform_tree_index(),
host_impl()->active_tree()->property_trees()));
+ EXPECT_TRUE(back_facing->raster_even_if_not_drawn());
EXPECT_TRUE(
draw_property_utils::LayerShouldBeSkippedForDrawPropertiesComputation(
back_facing, host_impl()->active_tree()->property_trees()));
@@ -3644,199 +3646,6 @@ TEST_F(TransformInteropTest, BackfaceInvisibleTransform) {
front_facing, host_impl()->active_tree()->property_trees()));
}
-using LCDTextTestParam = std::tuple<bool, bool>;
-class LCDTextTest : public DrawPropertiesTestBase,
- public testing::TestWithParam<LCDTextTestParam> {
- public:
- LCDTextTest() : DrawPropertiesTestBase(LCDTextTestLayerTreeSettings()) {}
-
- protected:
- LayerTreeSettings LCDTextTestLayerTreeSettings() {
- LayerListSettings settings;
-
- can_use_lcd_text_ = std::get<0>(GetParam());
- layers_always_allowed_lcd_text_ = std::get<1>(GetParam());
- settings.can_use_lcd_text = can_use_lcd_text_;
- settings.layers_always_allowed_lcd_text = layers_always_allowed_lcd_text_;
- return settings;
- }
-
- void SetUp() override {
- root_ = root_layer();
- child_ = AddLayer<PictureLayerImpl>();
- grand_child_ = AddLayer<PictureLayerImpl>();
- SetElementIdsForTesting();
-
- root_->SetContentsOpaque(true);
- child_->SetContentsOpaque(true);
- grand_child_->SetContentsOpaque(true);
-
- root_->SetDrawsContent(true);
- child_->SetDrawsContent(true);
- grand_child_->SetDrawsContent(true);
-
- root_->SetBounds(gfx::Size(1, 1));
- child_->SetBounds(gfx::Size(1, 1));
- grand_child_->SetBounds(gfx::Size(1, 1));
-
- CopyProperties(root_, child_);
- CreateTransformNode(child_);
- CreateEffectNode(child_).render_surface_reason = RenderSurfaceReason::kTest;
- CopyProperties(child_, grand_child_);
- }
-
- void CheckCanUseLCDText(LCDTextDisallowedReason expected_disallowed_reason,
- PictureLayerImpl* layer = nullptr) {
- if (layers_always_allowed_lcd_text_)
- expected_disallowed_reason = LCDTextDisallowedReason::kNone;
- else if (!can_use_lcd_text_)
- expected_disallowed_reason = LCDTextDisallowedReason::kSetting;
-
- if (layer) {
- EXPECT_EQ(expected_disallowed_reason,
- layer->ComputeLCDTextDisallowedReasonForTesting());
- } else {
- EXPECT_EQ(expected_disallowed_reason,
- child_->ComputeLCDTextDisallowedReasonForTesting());
- EXPECT_EQ(expected_disallowed_reason,
- grand_child_->ComputeLCDTextDisallowedReasonForTesting());
- }
- }
-
- bool can_use_lcd_text_;
- bool layers_always_allowed_lcd_text_;
-
- LayerImpl* root_ = nullptr;
- PictureLayerImpl* child_ = nullptr;
- PictureLayerImpl* grand_child_ = nullptr;
-};
-
-TEST_P(LCDTextTest, CanUseLCDText) {
- // Case 1: Identity transform.
- UpdateActiveTreeDrawProperties();
- CheckCanUseLCDText(LCDTextDisallowedReason::kNone);
-
- // Case 2: Integral translation.
- gfx::Transform integral_translation;
- integral_translation.Translate(1.0, 2.0);
- SetTransform(child_, integral_translation);
-
- UpdateActiveTreeDrawProperties();
- CheckCanUseLCDText(LCDTextDisallowedReason::kNone);
-
- // Case 3: Non-integral translation.
- gfx::Transform non_integral_translation;
- non_integral_translation.Translate(1.5, 2.5);
- SetTransform(child_, non_integral_translation);
- UpdateActiveTreeDrawProperties();
- CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation);
-
- // Case 4: Rotation.
- gfx::Transform rotation;
- rotation.Rotate(10.0);
- SetTransform(child_, rotation);
- UpdateActiveTreeDrawProperties();
- CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation);
-
- // Case 5: Scale.
- gfx::Transform scale;
- scale.Scale(2.0, 2.0);
- SetTransform(child_, scale);
- UpdateActiveTreeDrawProperties();
- CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation);
-
- // Case 6: Skew.
- gfx::Transform skew;
- skew.Skew(10.0, 0.0);
- SetTransform(child_, skew);
- UpdateActiveTreeDrawProperties();
- CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation);
-
- // Case 7: Translucent: LCD-text is allowed.
- SetTransform(child_, gfx::Transform());
- SetOpacity(child_, 0.5f);
- UpdateActiveTreeDrawProperties();
- CheckCanUseLCDText(LCDTextDisallowedReason::kNone);
-
- // Case 8: Sanity check: restore transform and opacity.
- SetTransform(child_, gfx::Transform());
- SetOpacity(child_, 1.f);
- UpdateActiveTreeDrawProperties();
- CheckCanUseLCDText(LCDTextDisallowedReason::kNone);
-
- // Case 9a: Non-opaque content and opaque background.
- child_->SetContentsOpaque(false);
- child_->SetBackgroundColor(SK_ColorGREEN);
- UpdateActiveTreeDrawProperties();
- CheckCanUseLCDText(LCDTextDisallowedReason::kContentsNotOpaque, child_);
- CheckCanUseLCDText(LCDTextDisallowedReason::kNone, grand_child_);
-
- // Case 9b: Non-opaque content and non-opaque background.
- child_->SetBackgroundColor(SkColorSetARGB(128, 255, 255, 255));
- UpdateActiveTreeDrawProperties();
- CheckCanUseLCDText(LCDTextDisallowedReason::kBackgroundColorNotOpaque,
- child_);
- CheckCanUseLCDText(LCDTextDisallowedReason::kNone, grand_child_);
-
- // Case 10: Sanity check: restore content opaqueness.
- child_->SetContentsOpaque(true);
- UpdateActiveTreeDrawProperties();
- CheckCanUseLCDText(LCDTextDisallowedReason::kNone);
-
- // Case 11: will-change: transform
- child_->SetHasWillChangeTransformHint(true);
- UpdateActiveTreeDrawProperties();
- CheckCanUseLCDText(LCDTextDisallowedReason::kWillChangeTransform, child_);
- // TODO(wangxianzhu): Is this correct?
- 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();
- CheckCanUseLCDText(LCDTextDisallowedReason::kNone);
-
- // Add opacity animation.
- gfx::Transform non_integral_translation;
- non_integral_translation.Translate(1.5, 2.5);
- SetTransform(child_, non_integral_translation);
- AddAnimatedTransformToElementWithAnimation(child_->element_id(), timeline(),
- 10.0, 12, 34);
- UpdateActiveTreeDrawProperties();
- // Text LCD should be adjusted while animation is active.
- CheckCanUseLCDText(LCDTextDisallowedReason::kNonIntegralTranslation);
-}
-
-TEST_P(LCDTextTest, CanUseLCDTextWithAnimationContentsOpaque) {
- // Sanity check: Make sure can_use_lcd_text_ is set on each node.
- UpdateActiveTreeDrawProperties();
- CheckCanUseLCDText(LCDTextDisallowedReason::kNone);
-
- // Mark contents non-opaque within the first animation frame.
- child_->SetContentsOpaque(false);
- child_->SetBackgroundColor(SK_ColorWHITE);
- AddOpacityTransitionToElementWithAnimation(child_->element_id(), timeline(),
- 10.0, 0.9f, 0.1f, false);
- UpdateActiveTreeDrawProperties();
- // LCD text should be disabled for non-opaque layers even during animations.
- CheckCanUseLCDText(LCDTextDisallowedReason::kContentsNotOpaque, child_);
- CheckCanUseLCDText(LCDTextDisallowedReason::kNone, grand_child_);
-}
-
-INSTANTIATE_TEST_SUITE_P(DrawPropertiesTest,
- LCDTextTest,
- testing::Combine(testing::Bool(), testing::Bool()));
-
// Needs layer tree mode: hide_layer_and_subtree.
TEST_F(DrawPropertiesTestWithLayerTree, SubtreeHidden_SingleLayerImpl) {
auto root = Layer::Create();
diff --git a/chromium/cc/trees/draw_property_utils.cc b/chromium/cc/trees/draw_property_utils.cc
index 7c65e513700..3b57867f919 100644
--- a/chromium/cc/trees/draw_property_utils.cc
+++ b/chromium/cc/trees/draw_property_utils.cc
@@ -939,8 +939,12 @@ void ComputeInitialRenderSurfaceList(LayerTreeImpl* layer_tree_impl,
bool skip_layer = !is_root && (skip_draw_properties_computation ||
skip_for_invertibility);
- layer->set_raster_even_if_not_drawn(skip_for_invertibility &&
- !skip_draw_properties_computation);
+ // Raster layers that are animated but currently have a non-invertible
+ // matrix, or layers that have a will-change transform hint and might
+ // animate to not be backface visible soon.
+ layer->set_raster_even_if_not_drawn(
+ (skip_for_invertibility && !skip_draw_properties_computation) ||
+ layer->has_will_change_transform_hint());
if (skip_layer)
continue;
@@ -1154,6 +1158,7 @@ void ComputeDrawPropertiesOfVisibleLayers(const LayerImplList* layer_list,
if (!only_draws_visible_content) {
drawable_bounds = gfx::Rect(layer->bounds());
}
+
gfx::Rect visible_bounds_in_target_space =
MathUtil::MapEnclosingClippedRect(
layer->draw_properties().target_space_transform, drawable_bounds);
@@ -1400,22 +1405,7 @@ void CalculateDrawProperties(
// trying to update property trees whenever these values change, we
// update property trees before using them.
- // We should never be setting a non-unit page scale factor on an oopif
- // subframe ... if we attempt this log it and fail.
- // TODO(wjmaclean): Remove as part of conditions for closing the bug.
- // https://crbug.com/845097
PropertyTrees* property_trees = layer_tree_impl->property_trees();
- if (layer_tree_impl->current_page_scale_factor() !=
- property_trees->transform_tree.page_scale_factor() &&
- !layer_tree_impl->PageScaleTransformNode()) {
- LOG(ERROR) << "Setting PageScale on subframe: new psf = "
- << layer_tree_impl->page_scale_factor() << ", old psf = "
- << property_trees->transform_tree.page_scale_factor()
- << ", in_oopif = "
- << layer_tree_impl->settings().is_layer_tree_for_subframe;
- NOTREACHED();
- }
-
UpdatePageScaleFactor(property_trees,
layer_tree_impl->PageScaleTransformNode(),
layer_tree_impl->current_page_scale_factor());
diff --git a/chromium/cc/trees/effect_node.cc b/chromium/cc/trees/effect_node.cc
index 9bae1e90b74..5109f2c03c5 100644
--- a/chromium/cc/trees/effect_node.cc
+++ b/chromium/cc/trees/effect_node.cc
@@ -35,6 +35,7 @@ EffectNode::EffectNode()
effect_changed(false),
subtree_has_copy_request(false),
is_fast_rounded_corner(false),
+ node_or_ancestor_has_filters(false),
render_surface_reason(RenderSurfaceReason::kNone),
transform_id(0),
clip_id(0),
@@ -59,6 +60,7 @@ bool EffectNode::operator==(const EffectNode& other) const {
backdrop_mask_element_id == other.backdrop_mask_element_id &&
rounded_corner_bounds == other.rounded_corner_bounds &&
is_fast_rounded_corner == other.is_fast_rounded_corner &&
+ node_or_ancestor_has_filters == other.node_or_ancestor_has_filters &&
// The specific reason is just for tracing/testing/debugging, so just
// check whether a render surface is needed.
HasRenderSurface() == other.HasRenderSurface() &&
@@ -153,6 +155,8 @@ void EffectNode::AsValueInto(base::trace_event::TracedValue* value) const {
value->SetString("backdrop_filters", backdrop_filters.ToString());
value->SetDouble("backdrop_filter_quality", backdrop_filter_quality);
value->SetBoolean("is_fast_rounded_corner", is_fast_rounded_corner);
+ value->SetBoolean("node_or_ancestor_has_filters",
+ node_or_ancestor_has_filters);
if (!rounded_corner_bounds.IsEmpty()) {
MathUtil::AddToTracedValue("rounded_corner_bounds", rounded_corner_bounds,
value);
diff --git a/chromium/cc/trees/effect_node.h b/chromium/cc/trees/effect_node.h
index 6663457250c..f4fe2b8ba2a 100644
--- a/chromium/cc/trees/effect_node.h
+++ b/chromium/cc/trees/effect_node.h
@@ -130,6 +130,8 @@ struct CC_EXPORT EffectNode {
// If set, the effect node tries to not trigger a render surface due to it
// having a rounded corner.
bool is_fast_rounded_corner : 1;
+ // If the node or it's parent has the filters, it sets to true.
+ bool node_or_ancestor_has_filters : 1;
// RenderSurfaceReason::kNone if this effect node should not create a render
// surface, or the reason that this effect node should create one.
RenderSurfaceReason render_surface_reason;
diff --git a/chromium/cc/trees/latency_info_swap_promise_monitor.cc b/chromium/cc/trees/latency_info_swap_promise_monitor.cc
index a69055c986d..0cb8e0b2731 100644
--- a/chromium/cc/trees/latency_info_swap_promise_monitor.cc
+++ b/chromium/cc/trees/latency_info_swap_promise_monitor.cc
@@ -5,6 +5,8 @@
#include "cc/trees/latency_info_swap_promise_monitor.h"
#include <stdint.h>
+#include <memory>
+#include <utility>
#include "base/threading/platform_thread.h"
#include "cc/trees/latency_info_swap_promise.h"
@@ -31,9 +33,13 @@ namespace cc {
LatencyInfoSwapPromiseMonitor::LatencyInfoSwapPromiseMonitor(
ui::LatencyInfo* latency,
- SwapPromiseManager* swap_promise_manager,
+ SwapPromiseManager* swap_promise_manager)
+ : SwapPromiseMonitor(swap_promise_manager), latency_(latency) {}
+
+LatencyInfoSwapPromiseMonitor::LatencyInfoSwapPromiseMonitor(
+ ui::LatencyInfo* latency,
LayerTreeHostImpl* host_impl)
- : SwapPromiseMonitor(swap_promise_manager, host_impl), latency_(latency) {}
+ : SwapPromiseMonitor(host_impl), latency_(latency) {}
LatencyInfoSwapPromiseMonitor::~LatencyInfoSwapPromiseMonitor() = default;
diff --git a/chromium/cc/trees/latency_info_swap_promise_monitor.h b/chromium/cc/trees/latency_info_swap_promise_monitor.h
index eb1d8f4a635..a9ffbf0c01c 100644
--- a/chromium/cc/trees/latency_info_swap_promise_monitor.h
+++ b/chromium/cc/trees/latency_info_swap_promise_monitor.h
@@ -20,7 +20,8 @@ namespace cc {
class CC_EXPORT LatencyInfoSwapPromiseMonitor : public SwapPromiseMonitor {
public:
LatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency,
- SwapPromiseManager* swap_promise_manager,
+ SwapPromiseManager* swap_promise_manager);
+ LatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency,
LayerTreeHostImpl* host_impl);
~LatencyInfoSwapPromiseMonitor() override;
diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc
index d5e57eaa4a5..e854fff6847 100644
--- a/chromium/cc/trees/layer_tree_host.cc
+++ b/chromium/cc/trees/layer_tree_host.cc
@@ -46,6 +46,7 @@
#include "cc/paint/paint_worklet_layer_painter.h"
#include "cc/resources/ui_resource_manager.h"
#include "cc/trees/clip_node.h"
+#include "cc/trees/compositor_commit_data.h"
#include "cc/trees/draw_property_utils.h"
#include "cc/trees/effect_node.h"
#include "cc/trees/layer_tree_host_client.h"
@@ -55,7 +56,6 @@
#include "cc/trees/property_tree_builder.h"
#include "cc/trees/proxy_main.h"
#include "cc/trees/render_frame_metadata_observer.h"
-#include "cc/trees/scroll_and_scale_set.h"
#include "cc/trees/scroll_node.h"
#include "cc/trees/single_thread_proxy.h"
#include "cc/trees/swap_promise_manager.h"
@@ -256,7 +256,7 @@ LayerTreeHost::GetScopedEventMetricsMonitor(
}
void LayerTreeHost::ClearEventsMetrics() {
- // Take evens metrics and drop them.
+ // Take events metrics and drop them.
events_metrics_manager_.TakeSavedEventsMetrics();
}
@@ -759,7 +759,7 @@ void LayerTreeHost::RecordGpuRasterizationHistogram(
}
// Record how widely gpu rasterization is enabled.
- // This number takes device/gpu whitelisting/backlisting into account.
+ // This number takes device/gpu allowlist/denylist into account.
// Note that we do not consider the forced gpu rasterization mode, which is
// mostly used for debugging purposes.
UMA_HISTOGRAM_BOOLEAN("Renderer4.GpuRasterizationEnabled",
@@ -852,21 +852,23 @@ bool LayerTreeHost::DoUpdateLayers() {
return did_paint_content;
}
-void LayerTreeHost::ApplyViewportChanges(const ScrollAndScaleSet& info) {
+void LayerTreeHost::ApplyViewportChanges(
+ const CompositorCommitData& commit_data) {
gfx::ScrollOffset inner_viewport_scroll_delta;
- if (info.inner_viewport_scroll.element_id)
- inner_viewport_scroll_delta = info.inner_viewport_scroll.scroll_delta;
+ if (commit_data.inner_viewport_scroll.element_id)
+ inner_viewport_scroll_delta =
+ commit_data.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);
+ // |commit_data.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 |commit_data.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
+ ? commit_data.ongoing_scroll_animation
+ : (commit_data.ongoing_scroll_animation &&
+ commit_data.manipulation_info);
if (scroll_animation_.in_progress && !new_ongoing_scroll) {
scroll_animation_.in_progress = false;
if (!scroll_animation_.end_notification.is_null())
@@ -875,74 +877,113 @@ void LayerTreeHost::ApplyViewportChanges(const ScrollAndScaleSet& info) {
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 &&
- !info.browser_controls_constraint_changed &&
- !info.scroll_gesture_did_end &&
- info.is_pinch_gesture_active == is_pinch_gesture_active_from_impl_) {
+ if (inner_viewport_scroll_delta.IsZero() &&
+ commit_data.page_scale_delta == 1.f &&
+ commit_data.elastic_overscroll_delta.IsZero() &&
+ !commit_data.top_controls_delta && !commit_data.bottom_controls_delta &&
+ !commit_data.browser_controls_constraint_changed &&
+ !commit_data.scroll_gesture_did_end &&
+ commit_data.is_pinch_gesture_active ==
+ is_pinch_gesture_active_from_impl_) {
return;
}
- is_pinch_gesture_active_from_impl_ = info.is_pinch_gesture_active;
+ is_pinch_gesture_active_from_impl_ = commit_data.is_pinch_gesture_active;
- // Preemptively apply the scroll offset and scale delta here before sending
- // it to the client. If the client comes back and sets it to the same
- // value, then the layer can early out without needing a full commit.
if (auto* inner_scroll = property_trees()->scroll_tree.Node(
viewport_property_ids_.inner_scroll)) {
- if (IsUsingLayerLists()) {
- auto& scroll_tree = property_trees()->scroll_tree;
- scroll_tree.NotifyDidScroll(
- inner_scroll->element_id,
- scroll_tree.current_scroll_offset(inner_scroll->element_id) +
- inner_viewport_scroll_delta,
- info.inner_viewport_scroll.snap_target_element_ids);
- } else if (auto* inner_scroll_layer =
- LayerByElementId(inner_scroll->element_id)) {
- inner_scroll_layer->SetScrollOffsetFromImplSide(
- inner_scroll_layer->scroll_offset() + inner_viewport_scroll_delta);
- }
+ UpdateScrollOffsetFromImpl(
+ inner_scroll->element_id, inner_viewport_scroll_delta,
+ commit_data.inner_viewport_scroll.snap_target_element_ids);
}
- ApplyPageScaleDeltaFromImplSide(info.page_scale_delta);
+ ApplyPageScaleDeltaFromImplSide(commit_data.page_scale_delta);
SetElasticOverscrollFromImplSide(elastic_overscroll_ +
- info.elastic_overscroll_delta);
+ commit_data.elastic_overscroll_delta);
// TODO(ccameron): pass the elastic overscroll here so that input events
// may be translated appropriately.
client_->ApplyViewportChanges(
- {inner_viewport_scroll_delta, info.elastic_overscroll_delta,
- info.page_scale_delta, info.is_pinch_gesture_active,
- info.top_controls_delta, info.bottom_controls_delta,
- info.browser_controls_constraint, info.scroll_gesture_did_end});
+ {inner_viewport_scroll_delta, commit_data.elastic_overscroll_delta,
+ commit_data.page_scale_delta, commit_data.is_pinch_gesture_active,
+ commit_data.top_controls_delta, commit_data.bottom_controls_delta,
+ commit_data.browser_controls_constraint,
+ commit_data.scroll_gesture_did_end});
SetNeedsUpdateLayers();
}
void LayerTreeHost::RecordManipulationTypeCounts(
- const ScrollAndScaleSet& scroll_info) {
- client_->RecordManipulationTypeCounts(scroll_info.manipulation_info);
+ const CompositorCommitData& commit_data) {
+ client_->RecordManipulationTypeCounts(commit_data.manipulation_info);
}
void LayerTreeHost::SendOverscrollAndScrollEndEventsFromImplSide(
- const ScrollAndScaleSet& info) {
- if (info.scroll_latched_element_id == ElementId())
+ const CompositorCommitData& commit_data) {
+ if (commit_data.scroll_latched_element_id == ElementId())
return;
- if (!info.overscroll_delta.IsZero()) {
- client_->SendOverscrollEventFromImplSide(info.overscroll_delta,
- info.scroll_latched_element_id);
+ if (!commit_data.overscroll_delta.IsZero()) {
+ client_->SendOverscrollEventFromImplSide(
+ commit_data.overscroll_delta, commit_data.scroll_latched_element_id);
+ }
+ // TODO(bokan): If a scroll ended and a new one began in the same Blink frame
+ // (e.g. during a long running main thread task), this will erroneously
+ // dispatch the scroll end to the latter (still-scrolling) element.
+ // https://crbug.com/1116780.
+ if (commit_data.scroll_gesture_did_end)
+ client_->SendScrollEndEventFromImplSide(
+ commit_data.scroll_latched_element_id);
+}
+
+void LayerTreeHost::UpdateScrollOffsetFromImpl(
+ const ElementId& id,
+ const gfx::ScrollOffset& delta,
+ const base::Optional<TargetSnapAreaElementIds>& snap_target_ids) {
+ if (IsUsingLayerLists()) {
+ auto& scroll_tree = property_trees()->scroll_tree;
+ auto new_offset = scroll_tree.current_scroll_offset(id) + delta;
+ TRACE_EVENT_INSTANT2("cc", "NotifyDidScroll", TRACE_EVENT_SCOPE_THREAD,
+ "cur_y", scroll_tree.current_scroll_offset(id).y(),
+ "delta", delta.y());
+ if (auto* scroll_node = scroll_tree.FindNodeFromElementId(id)) {
+ // This update closely follows
+ // blink::PropertyTreeManager::DirectlyUpdateScrollOffsetTransform.
+
+ scroll_tree.SetScrollOffset(id, new_offset);
+ // |blink::PropertyTreeManager::DirectlySetScrollOffset| (called from
+ // |blink::PropertyTreeManager::DirectlyUpdateScrollOffsetTransform|)
+ // marks the layer as needing to push properties in order to clobber
+ // animations, but that is not needed for an impl-side scroll.
+
+ // Update the offset in the transform node.
+ 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 != new_offset) {
+ transform_node->scroll_offset = new_offset;
+ transform_node->needs_local_transform_update = true;
+ transform_node->transform_changed = true;
+ transform_tree.set_needs_update(true);
+ }
+
+ // The transform tree has been modified which requires a call to
+ // |LayerTreeHost::UpdateLayers| to update the property trees.
+ SetNeedsUpdateLayers();
+ }
+
+ scroll_tree.NotifyDidScroll(id, new_offset, snap_target_ids);
+ } else if (Layer* layer = LayerByElementId(id)) {
+ layer->SetScrollOffsetFromImplSide(layer->scroll_offset() + delta);
+ SetNeedsUpdateLayers();
}
- if (info.scroll_gesture_did_end)
- client_->SendScrollEndEventFromImplSide(info.scroll_latched_element_id);
}
-void LayerTreeHost::ApplyScrollAndScale(ScrollAndScaleSet* info) {
- DCHECK(info);
- TRACE_EVENT0("cc", "LayerTreeHost::ApplyScrollAndScale");
+void LayerTreeHost::ApplyCompositorChanges(CompositorCommitData* commit_data) {
+ DCHECK(commit_data);
+ TRACE_EVENT0("cc", "LayerTreeHost::ApplyCompositorChanges");
using perfetto::protos::pbzero::ChromeLatencyInfo;
using perfetto::protos::pbzero::TrackEvent;
- for (auto& swap_promise : info->swap_promises) {
+ for (auto& swap_promise : commit_data->swap_promises) {
TRACE_EVENT(
"input,benchmark", "LatencyInfo.Flow",
[&swap_promise](perfetto::EventContext ctx) {
@@ -957,37 +998,24 @@ void LayerTreeHost::ApplyScrollAndScale(ScrollAndScaleSet* info) {
if (root_layer_) {
auto& scroll_tree = property_trees()->scroll_tree;
- for (auto& scroll : info->scrolls) {
- if (IsUsingLayerLists()) {
- TRACE_EVENT_INSTANT2(
- "cc", "NotifyDidScroll", TRACE_EVENT_SCOPE_THREAD, "cur_y",
- scroll_tree.current_scroll_offset(scroll.element_id).y(), "delta",
- scroll.scroll_delta.y());
- scroll_tree.NotifyDidScroll(
- scroll.element_id,
- scroll_tree.current_scroll_offset(scroll.element_id) +
- scroll.scroll_delta,
- scroll.snap_target_element_ids);
- } else if (Layer* layer = LayerByElementId(scroll.element_id)) {
- layer->SetScrollOffsetFromImplSide(layer->scroll_offset() +
- scroll.scroll_delta);
- SetNeedsUpdateLayers();
- }
+ for (auto& scroll : commit_data->scrolls) {
+ UpdateScrollOffsetFromImpl(scroll.element_id, scroll.scroll_delta,
+ scroll.snap_target_element_ids);
}
- for (auto& scrollbar : info->scrollbars) {
+ for (auto& scrollbar : commit_data->scrollbars) {
scroll_tree.NotifyDidChangeScrollbarsHidden(scrollbar.element_id,
scrollbar.hidden);
}
}
- SendOverscrollAndScrollEndEventsFromImplSide(*info);
+ SendOverscrollAndScrollEndEventsFromImplSide(*commit_data);
// This needs to happen after scroll deltas have been sent to prevent top
// controls from clamping the layout viewport both on the compositor and
// on the main thread.
- ApplyViewportChanges(*info);
+ ApplyViewportChanges(*commit_data);
- RecordManipulationTypeCounts(*info);
+ RecordManipulationTypeCounts(*commit_data);
}
void LayerTreeHost::ApplyMutatorEvents(std::unique_ptr<MutatorEvents> events) {
@@ -1335,11 +1363,11 @@ void LayerTreeHost::SetRecordingScaleFactor(float recording_scale_factor) {
recording_scale_factor_ = recording_scale_factor;
}
-void LayerTreeHost::SetRasterColorSpace(
- const gfx::ColorSpace& raster_color_space) {
- if (raster_color_space_ == raster_color_space)
+void LayerTreeHost::SetDisplayColorSpaces(
+ const gfx::DisplayColorSpaces& display_color_spaces) {
+ if (display_color_spaces_ == display_color_spaces)
return;
- raster_color_space_ = raster_color_space;
+ display_color_spaces_ = display_color_spaces;
for (auto* layer : *this)
layer->SetNeedsDisplay();
}
@@ -1576,7 +1604,7 @@ void LayerTreeHost::PushLayerTreePropertiesTo(LayerTreeImpl* tree_impl) {
if (tree_impl->IsActiveTree())
tree_impl->elastic_overscroll()->PushPendingToActive();
- tree_impl->SetRasterColorSpace(raster_color_space_);
+ tree_impl->SetDisplayColorSpaces(display_color_spaces_);
tree_impl->SetExternalPageScaleFactor(external_page_scale_factor_);
tree_impl->set_painted_device_scale_factor(painted_device_scale_factor_);
@@ -1625,7 +1653,7 @@ void LayerTreeHost::PushSurfaceRangesTo(LayerTreeImpl* tree_impl) {
void LayerTreeHost::PushLayerTreeHostPropertiesTo(
LayerTreeHostImpl* host_impl) {
- host_impl->set_external_pinch_gesture_active(
+ host_impl->GetInputHandler().set_external_pinch_gesture_active(
is_external_pinch_gesture_active_);
RecordGpuRasterizationHistogram(host_impl);
@@ -1861,4 +1889,9 @@ bool LayerTreeHost::TakeForceSendMetadataRequest() {
return force_send_metadata_request;
}
+void LayerTreeHost::SetEnableFrameRateThrottling(
+ bool enable_frame_rate_throttling) {
+ proxy_->SetEnableFrameRateThrottling(enable_frame_rate_throttling);
+}
+
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host.h b/chromium/cc/trees/layer_tree_host.h
index f365bd9a909..657f82fe182 100644
--- a/chromium/cc/trees/layer_tree_host.h
+++ b/chromium/cc/trees/layer_tree_host.h
@@ -80,7 +80,7 @@ class TaskGraphRunner;
class UIResourceManager;
class UkmRecorderFactory;
struct RenderingStats;
-struct ScrollAndScaleSet;
+struct CompositorCommitData;
// Returned from LayerTreeHost::DeferMainFrameUpdate. Automatically un-defers on
// destruction.
@@ -184,6 +184,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
GetScopedEventMetricsMonitor(std::unique_ptr<EventMetrics> event_metrics);
void ClearEventsMetrics();
+ size_t saved_events_metrics_count_for_testing() const {
+ return events_metrics_manager_.saved_events_metrics_count_for_testing();
+ }
+
// Visibility and LayerTreeFrameSink -------------------------------
// Sets or gets if the LayerTreeHost is visible. When not visible it will:
@@ -392,6 +396,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
return event_listener_properties_[static_cast<size_t>(event_class)];
}
+ // Indicates that its acceptable to throttle the frame rate for this content
+ // to prioritize lower power/CPU use.
+ void SetEnableFrameRateThrottling(bool enable_frame_rate_throttling);
+
void SetViewportRectAndScale(const gfx::Rect& device_viewport_rect,
float device_scale_factor,
const viz::LocalSurfaceIdAllocation&
@@ -461,9 +469,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
return new_local_surface_id_request_;
}
- void SetRasterColorSpace(const gfx::ColorSpace& raster_color_space);
- const gfx::ColorSpace& raster_color_space() const {
- return raster_color_space_;
+ void SetDisplayColorSpaces(
+ const gfx::DisplayColorSpaces& display_color_spaces);
+ const gfx::DisplayColorSpaces& display_color_spaces() const {
+ return display_color_spaces_;
}
bool HasCompositorDrivenScrollAnimationForTesting() const {
@@ -599,7 +608,7 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
const gfx::PresentationFeedback& feedback);
// Called when the compositor completed page scale animation.
void DidCompletePageScaleAnimation();
- void ApplyScrollAndScale(ScrollAndScaleSet* info);
+ void ApplyCompositorChanges(CompositorCommitData* commit_data);
void ApplyMutatorEvents(std::unique_ptr<MutatorEvents> events);
void RecordStartOfFrameMetrics();
void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time,
@@ -760,10 +769,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
// free of slow-paths before toggling the flag.
enum { kNumFramesToConsiderBeforeRemovingSlowPathFlag = 60 };
- void ApplyViewportChanges(const ScrollAndScaleSet& info);
- void RecordManipulationTypeCounts(const ScrollAndScaleSet& scroll_info);
+ void ApplyViewportChanges(const CompositorCommitData& commit_data);
+ void RecordManipulationTypeCounts(const CompositorCommitData& commit_data);
void SendOverscrollAndScrollEndEventsFromImplSide(
- const ScrollAndScaleSet& info);
+ const CompositorCommitData& commit_data);
void ApplyPageScaleDeltaFromImplSide(float page_scale_delta);
void InitializeProxy(std::unique_ptr<Proxy> proxy);
@@ -771,6 +780,13 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
void UpdateDeferMainFrameUpdateInternal();
+ // Preemptively applies the scroll offset and delta before sending it to the
+ // client. This lets the client skip a commit if the value does not change.
+ void UpdateScrollOffsetFromImpl(
+ const ElementId&,
+ const gfx::ScrollOffset& delta,
+ const base::Optional<TargetSnapAreaElementIds>&);
+
const CompositorMode compositor_mode_;
std::unique_ptr<UIResourceManager> ui_resource_manager_;
@@ -837,7 +853,7 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
// Used to track the out-bound state for ApplyViewportChanges.
bool is_pinch_gesture_active_from_impl_ = false;
- gfx::ColorSpace raster_color_space_;
+ gfx::DisplayColorSpaces display_color_spaces_;
bool clear_caches_on_next_commit_ = false;
viz::LocalSurfaceIdAllocation local_surface_id_allocation_from_parent_;
diff --git a/chromium/cc/trees/layer_tree_host_client.h b/chromium/cc/trees/layer_tree_host_client.h
index b4b8c607969..c17aac50b96 100644
--- a/chromium/cc/trees/layer_tree_host_client.h
+++ b/chromium/cc/trees/layer_tree_host_client.h
@@ -62,11 +62,10 @@ struct ApplyViewportChangesArgs {
using ManipulationInfo = uint32_t;
constexpr ManipulationInfo kManipulationInfoNone = 0;
-constexpr ManipulationInfo kManipulationInfoHasScrolledByWheel = 1 << 0;
-constexpr ManipulationInfo kManipulationInfoHasScrolledByTouch = 1 << 1;
-constexpr ManipulationInfo kManipulationInfoHasScrolledByPrecisionTouchPad =
- 1 << 2;
-constexpr ManipulationInfo kManipulationInfoHasPinchZoomed = 1 << 3;
+constexpr ManipulationInfo kManipulationInfoWheel = 1 << 0;
+constexpr ManipulationInfo kManipulationInfoTouch = 1 << 1;
+constexpr ManipulationInfo kManipulationInfoPrecisionTouchPad = 1 << 2;
+constexpr ManipulationInfo kManipulationInfoPinchZoom = 1 << 3;
// A LayerTreeHost is bound to a LayerTreeHostClient. The main rendering
// loop (in ProxyMain or SingleThreadProxy) calls methods on the
diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc
index 0fbcdf35620..bd8212429a8 100644
--- a/chromium/cc/trees/layer_tree_host_impl.cc
+++ b/chromium/cc/trees/layer_tree_host_impl.cc
@@ -10,6 +10,7 @@
#include <algorithm>
#include <limits>
#include <list>
+#include <string>
#include "base/auto_reset.h"
#include "base/bind.h"
@@ -78,6 +79,7 @@
#include "cc/tiles/raster_tile_priority_queue.h"
#include "cc/tiles/software_image_decode_cache.h"
#include "cc/trees/clip_node.h"
+#include "cc/trees/compositor_commit_data.h"
#include "cc/trees/damage_tracker.h"
#include "cc/trees/debug_rect_history.h"
#include "cc/trees/draw_property_utils.h"
@@ -90,7 +92,6 @@
#include "cc/trees/presentation_time_callback_buffer.h"
#include "cc/trees/render_frame_metadata.h"
#include "cc/trees/render_frame_metadata_observer.h"
-#include "cc/trees/scroll_and_scale_set.h"
#include "cc/trees/scroll_node.h"
#include "cc/trees/single_thread_proxy.h"
#include "cc/trees/transform_node.h"
@@ -119,7 +120,7 @@
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_latency_info.pbzero.h"
-#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "ui/events/types/scroll_input_type.h"
#include "ui/gfx/display_color_spaces.h"
#include "ui/gfx/geometry/point_conversions.h"
@@ -130,6 +131,8 @@
#include "ui/gfx/geometry/vector2d_f.h"
#include "ui/gfx/skia_util.h"
+using ScrollThread = cc::InputHandler::ScrollThread;
+
namespace cc {
namespace {
@@ -145,20 +148,6 @@ const float kMobileViewportWidthEpsilon = 0.15f;
// kHitTestAsk after the threshold is reached.
const size_t kAssumeOverlapThreshold = 100;
-FrameSequenceTrackerType GetTrackerTypeForScroll(
- ui::ScrollInputType input_type) {
- switch (input_type) {
- case ui::ScrollInputType::kWheel:
- return FrameSequenceTrackerType::kWheelScroll;
- case ui::ScrollInputType::kTouchscreen:
- return FrameSequenceTrackerType::kTouchScroll;
- case ui::ScrollInputType::kScrollbar:
- return FrameSequenceTrackerType::kScrollbarScroll;
- case ui::ScrollInputType::kAutoscroll:
- return FrameSequenceTrackerType::kMaxType;
- }
-}
-
bool HasFixedPageScale(LayerTreeImpl* active_tree) {
return active_tree->min_page_scale_factor() ==
active_tree->max_page_scale_factor();
@@ -177,20 +166,6 @@ bool IsMobileOptimized(LayerTreeImpl* active_tree) {
return has_fixed_page_scale || has_mobile_viewport;
}
-// This helper returns an adjusted version of |delta| where the scroll delta is
-// cleared in any axis in which user scrolling is disabled (e.g. by
-// |overflow-x: hidden|).
-gfx::Vector2dF UserScrollableDelta(const ScrollNode& node,
- const gfx::Vector2dF& delta) {
- gfx::Vector2dF adjusted_delta = delta;
- if (!node.user_scrollable_horizontal)
- adjusted_delta.set_x(0);
- if (!node.user_scrollable_vertical)
- adjusted_delta.set_y(0);
-
- return adjusted_delta;
-}
-
viz::ResourceFormat TileRasterBufferFormat(
const LayerTreeSettings& settings,
viz::ContextProvider* context_provider,
@@ -227,20 +202,6 @@ void DidVisibilityChange(LayerTreeHostImpl* id, bool visible) {
TRACE_ID_LOCAL(id));
}
-enum ScrollThread { MAIN_THREAD, CC_THREAD };
-
-void RecordCompositorSlowScrollMetric(ui::ScrollInputType type,
- ScrollThread scroll_thread) {
- bool scroll_on_main_thread = (scroll_thread == MAIN_THREAD);
- if (type == ui::ScrollInputType::kWheel) {
- UMA_HISTOGRAM_BOOLEAN("Renderer4.CompositorWheelScrollUpdateThread",
- scroll_on_main_thread);
- } else if (type == ui::ScrollInputType::kTouchscreen) {
- UMA_HISTOGRAM_BOOLEAN("Renderer4.CompositorTouchScrollUpdateThread",
- scroll_on_main_thread);
- }
-}
-
void PopulateMetadataContentColorUsage(
const LayerTreeHostImpl::FrameData* frame,
viz::CompositorFrameMetadata* metadata) {
@@ -315,6 +276,60 @@ void RecordSourceIdConsistency(bool all_valid, bool all_unique) {
DEFINE_SCOPED_UMA_HISTOGRAM_TIMER(PendingTreeRasterDurationHistogramTimer,
"Scheduling.%s.PendingTreeRasterDuration")
+void LayerTreeHostImpl::SetNeedsCommitInputChanges() {
+ client_->SetNeedsCommitOnImplThread();
+}
+
+void LayerTreeHostImpl::DidUpdateScrollAnimationCurve() {
+ // Because we updated the animation target, notify the SwapPromiseMonitor
+ // to tell it that something happened that will cause a swap in the future.
+ // This will happen within the scope of the dispatch of a gesture scroll
+ // update input event. If we don't notify during the handling of the input
+ // event, the LatencyInfo associated with the input event will not be
+ // added as a swap promise and we won't get any swap results.
+ NotifySwapPromiseMonitorsOfSetNeedsRedraw();
+ events_metrics_manager_.SaveActiveEventMetrics();
+}
+
+void LayerTreeHostImpl::AccumulateScrollDeltaForTracing(
+ const gfx::Vector2dF& delta) {
+ scroll_accumulated_this_frame_ += delta;
+}
+
+void LayerTreeHostImpl::DidStartPinchZoom() {
+ client_->RenewTreePriority();
+ frame_trackers_.StartSequence(FrameSequenceTrackerType::kPinchZoom);
+}
+
+void LayerTreeHostImpl::DidEndPinchZoom() {
+ // When a pinch ends, we may be displaying content cached at incorrect scales,
+ // so updating draw properties and drawing will ensure we are using the right
+ // scales that we want when we're not inside a pinch.
+ active_tree_->set_needs_update_draw_properties();
+ SetNeedsRedraw();
+ frame_trackers_.StopSequence(FrameSequenceTrackerType::kPinchZoom);
+}
+
+void LayerTreeHostImpl::DidUpdatePinchZoom() {
+ SetNeedsRedraw();
+ client_->RenewTreePriority();
+}
+
+void LayerTreeHostImpl::DidStartScroll() {
+ client_->RenewTreePriority();
+}
+
+void LayerTreeHostImpl::DidSetRootScrollOffsetForSynchronousInputHandler() {
+ // TODO(bokan): Do these really need to be manually called? (Rather than
+ // damage/redraw being set from scroll offset changes).
+ SetFullViewportDamage();
+ SetNeedsRedraw();
+}
+
+ImplThreadPhase LayerTreeHostImpl::GetCompositorThreadPhase() const {
+ return impl_thread_phase_;
+}
+
LayerTreeHostImpl::FrameData::FrameData() = default;
LayerTreeHostImpl::FrameData::~FrameData() = default;
LayerTreeHostImpl::UIResourceData::UIResourceData() = default;
@@ -358,6 +373,7 @@ LayerTreeHostImpl::LayerTreeHostImpl(
std::make_unique<CompositorFrameReportingController>(
/*should_report_metrics=*/!settings
.single_thread_proxy_scheduler)),
+ input_handler_(this),
settings_(settings),
is_synchronous_single_threaded_(!task_runner_provider->HasImplThread() &&
!settings_.single_thread_proxy_scheduler),
@@ -385,12 +401,13 @@ LayerTreeHostImpl::LayerTreeHostImpl(
this,
settings_.enable_image_animation_resync),
paint_image_generator_client_id_(PaintImage::GetNextGeneratorClientId()),
- scrollbar_controller_(std::make_unique<ScrollbarController>(this)),
frame_trackers_(settings.single_thread_proxy_scheduler,
compositor_frame_reporting_controller_.get()),
- scroll_gesture_did_end_(false),
lcd_text_metrics_reporter_(LCDTextMetricsReporter::CreateIfNeeded(this)),
frame_rate_estimator_(GetTaskRunner()) {
+ // TODO(bokan): Temporary while we decouple input from the layer tree.
+ input_delegate_ = static_cast<InputDelegateForCompositor*>(&input_handler_);
+
DCHECK(mutator_host_);
mutator_host_->SetMutatorHostClient(this);
mutator_events_ = mutator_host_->CreateEvents();
@@ -438,12 +455,7 @@ LayerTreeHostImpl::~LayerTreeHostImpl() {
DCHECK(!image_decode_cache_);
DCHECK(!single_thread_synchronous_task_graph_runner_);
- if (input_handler_client_) {
- input_handler_client_->WillShutdown();
- input_handler_client_ = nullptr;
- }
- if (scroll_elasticity_helper_)
- scroll_elasticity_helper_.reset();
+ input_delegate_->WillShutdown();
// The layer trees must be destroyed before the LayerTreeHost. Also, if they
// are holding onto any resources, destroying them will release them, before
@@ -468,6 +480,10 @@ LayerTreeHostImpl::~LayerTreeHostImpl() {
compositor_frame_reporting_controller_->SetUkmManager(nullptr);
}
+ThreadedInputHandler& LayerTreeHostImpl::GetInputHandler() {
+ return input_handler_;
+}
+
void LayerTreeHostImpl::WillSendBeginMainFrame() {
if (scheduling_client_)
scheduling_client_->DidScheduleBeginMainFrame();
@@ -516,10 +532,7 @@ void LayerTreeHostImpl::BeginCommit() {
void LayerTreeHostImpl::CommitComplete() {
TRACE_EVENT0("cc", "LayerTreeHostImpl::CommitComplete");
- // In high latency mode commit cannot finish within the same frame. We need to
- // flush input here to make sure they got picked up by |PrepareTiles()|.
- if (input_handler_client_ && impl_thread_phase_ == ImplThreadPhase::IDLE)
- input_handler_client_->DeliverInputForHighLatencyMode();
+ input_delegate_->DidCommit();
if (CommitToActiveTree()) {
active_tree_->HandleScrollbarShowRequestsFromMain();
@@ -586,15 +599,6 @@ void LayerTreeHostImpl::UpdateSyncTreeAfterCommitOrImplSideInvalidation() {
// below.
bool update_image_animation_controller = false;
sync_tree()->UpdateDrawProperties(update_image_animation_controller);
- // Because invalidations may be coming from the main thread, it's
- // safe to do an update for lcd text at this point and see if lcd text needs
- // to be disabled on any layers.
- // It'd be ideal if this could be done earlier, but when the raster source
- // is updated from the main thread during push properties, update draw
- // properties has not occurred yet and so it's not clear whether or not the
- // layer can or cannot use lcd text. So, this is the cleanup pass to
- // determine if lcd state needs to switch due to draw properties.
- sync_tree()->UpdateCanUseLCDText();
// Defer invalidating images until UpdateDrawProperties is performed since
// that updates whether an image should be animated based on its visibility
@@ -836,28 +840,20 @@ void LayerTreeHostImpl::AnimateInternal() {
bool did_animate = false;
- if (input_handler_client_) {
- // This animates fling scrolls. But on Android WebView root flings are
- // controlled by the application, so the compositor does not animate them.
- bool ignore_fling =
- settings_.ignore_root_layer_flings && IsCurrentlyScrollingViewport();
- if (!ignore_fling) {
- // This does not set did_animate, because if the InputHandlerClient
- // changes anything it will be through the InputHandler interface which
- // does SetNeedsRedraw.
- input_handler_client_->Animate(monotonic_time);
- }
- }
+ // TODO(bokan): This should return did_animate, see TODO in
+ // ElasticOverscrollController::Animate. crbug.com/551138.
+ input_delegate_->TickAnimations(monotonic_time);
did_animate |= AnimatePageScale(monotonic_time);
did_animate |= AnimateLayers(monotonic_time, /* is_active_tree */ true);
did_animate |= AnimateScrollbars(monotonic_time);
did_animate |= AnimateBrowserControls(monotonic_time);
- // Animating stuff can change the root scroll offset, so inform the
- // synchronous input handler.
- UpdateRootLayerStateForSynchronousInputHandler();
if (did_animate) {
+ // Animating stuff can change the root scroll offset, so inform the
+ // synchronous input handler.
+ input_delegate_->RootLayerStateMayHaveChanged();
+
// If the tree changed, then we want to draw at the end of the current
// frame.
SetNeedsRedraw();
@@ -924,110 +920,35 @@ void LayerTreeHostImpl::StartPageScaleAnimation(
}
void LayerTreeHostImpl::SetNeedsAnimateInput() {
- DCHECK(!IsCurrentlyScrollingViewport() ||
- !settings_.ignore_root_layer_flings);
SetNeedsOneBeginImplFrame();
}
bool LayerTreeHostImpl::IsCurrentlyScrollingViewport() const {
- auto* node = CurrentlyScrollingNode();
- if (!node)
- return false;
- return viewport().ShouldScroll(*node);
+ return input_handler_.IsCurrentlyScrollingViewport();
}
EventListenerProperties LayerTreeHostImpl::GetEventListenerProperties(
EventListenerClass event_class) const {
- return active_tree_->event_listener_properties(event_class);
-}
-
-// Return true if scrollable node for 'ancestor' is the same as 'child' or an
-// ancestor along the scroll tree.
-bool LayerTreeHostImpl::IsScrolledBy(LayerImpl* child, ScrollNode* ancestor) {
- DCHECK(ancestor && ancestor->scrollable);
- if (!child)
- return false;
- DCHECK_EQ(child->layer_tree_impl(), active_tree_.get());
- ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
- for (ScrollNode* scroll_node = scroll_tree.Node(child->scroll_tree_index());
- scroll_node; scroll_node = scroll_tree.parent(scroll_node)) {
- if (scroll_node->id == ancestor->id)
- return true;
- }
- return false;
+ return input_handler_.GetEventListenerProperties(event_class);
}
InputHandler::TouchStartOrMoveEventListenerType
LayerTreeHostImpl::EventListenerTypeForTouchStartOrMoveAt(
const gfx::Point& viewport_point,
TouchAction* out_touch_action) {
- gfx::PointF device_viewport_point = gfx::ScalePoint(
- gfx::PointF(viewport_point), active_tree_->device_scale_factor());
-
- LayerImpl* layer_impl_with_touch_handler =
- active_tree_->FindLayerThatIsHitByPointInTouchHandlerRegion(
- device_viewport_point);
-
- if (layer_impl_with_touch_handler == nullptr) {
- if (out_touch_action)
- *out_touch_action = TouchAction::kAuto;
- return InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER;
- }
-
- if (out_touch_action) {
- gfx::Transform layer_screen_space_transform =
- layer_impl_with_touch_handler->ScreenSpaceTransform();
- gfx::Transform inverse_layer_screen_space(
- gfx::Transform::kSkipInitialization);
- bool can_be_inversed =
- layer_screen_space_transform.GetInverse(&inverse_layer_screen_space);
- // Getting here indicates that |layer_impl_with_touch_handler| is non-null,
- // which means that the |hit| in FindClosestMatchingLayer() is true, which
- // indicates that the inverse is available.
- DCHECK(can_be_inversed);
- bool clipped = false;
- gfx::Point3F planar_point = MathUtil::ProjectPoint3D(
- inverse_layer_screen_space, device_viewport_point, &clipped);
- gfx::PointF hit_test_point_in_layer_space =
- gfx::PointF(planar_point.x(), planar_point.y());
- const auto& region = layer_impl_with_touch_handler->touch_action_region();
- gfx::Point point = gfx::ToRoundedPoint(hit_test_point_in_layer_space);
- *out_touch_action = region.GetAllowedTouchAction(point);
- }
-
- if (!CurrentlyScrollingNode())
- return InputHandler::TouchStartOrMoveEventListenerType::HANDLER;
-
- // Check if the touch start (or move) hits on the current scrolling layer or
- // its descendant. layer_impl_with_touch_handler is the layer hit by the
- // pointer and has an event handler, otherwise it is null. We want to compare
- // the most inner layer we are hitting on which may not have an event listener
- // with the actual scrolling layer.
- LayerImpl* layer_impl =
- active_tree_->FindLayerThatIsHitByPoint(device_viewport_point);
- bool is_ancestor = IsScrolledBy(layer_impl, CurrentlyScrollingNode());
- return is_ancestor ? InputHandler::TouchStartOrMoveEventListenerType::
- HANDLER_ON_SCROLLING_LAYER
- : InputHandler::TouchStartOrMoveEventListenerType::HANDLER;
+ return input_handler_.EventListenerTypeForTouchStartOrMoveAt(
+ viewport_point, out_touch_action);
}
bool LayerTreeHostImpl::HasBlockingWheelEventHandlerAt(
const gfx::Point& viewport_point) const {
- gfx::PointF device_viewport_point = gfx::ScalePoint(
- gfx::PointF(viewport_point), active_tree_->device_scale_factor());
-
- LayerImpl* layer_impl_with_wheel_event_handler =
- active_tree_->FindLayerThatIsHitByPointInWheelEventHandlerRegion(
- device_viewport_point);
-
- return layer_impl_with_wheel_event_handler;
+ return input_handler_.HasBlockingWheelEventHandlerAt(viewport_point);
}
std::unique_ptr<SwapPromiseMonitor>
LayerTreeHostImpl::CreateLatencyInfoSwapPromiseMonitor(
ui::LatencyInfo* latency) {
- return base::WrapUnique(
- new LatencyInfoSwapPromiseMonitor(latency, nullptr, this));
+ return base::WrapUnique(new LatencyInfoSwapPromiseMonitor(latency, this));
}
std::unique_ptr<EventsMetricsManager::ScopedMonitor>
@@ -1037,54 +958,21 @@ LayerTreeHostImpl::GetScopedEventMetricsMonitor(
}
ScrollElasticityHelper* LayerTreeHostImpl::CreateScrollElasticityHelper() {
- DCHECK(!scroll_elasticity_helper_);
- if (settings_.enable_elastic_overscroll) {
- scroll_elasticity_helper_.reset(
- ScrollElasticityHelper::CreateForLayerTreeHostImpl(this));
- }
- return scroll_elasticity_helper_.get();
+ return input_handler_.CreateScrollElasticityHelper();
}
bool LayerTreeHostImpl::GetScrollOffsetForLayer(ElementId element_id,
gfx::ScrollOffset* offset) {
- ScrollTree& scroll_tree = active_tree()->property_trees()->scroll_tree;
- ScrollNode* scroll_node = scroll_tree.FindNodeFromElementId(element_id);
- if (!scroll_node)
- return false;
- *offset = scroll_tree.current_scroll_offset(element_id);
- return true;
+ return input_handler_.GetScrollOffsetForLayer(element_id, offset);
}
bool LayerTreeHostImpl::ScrollLayerTo(ElementId element_id,
const gfx::ScrollOffset& offset) {
- ScrollTree& scroll_tree = active_tree()->property_trees()->scroll_tree;
- ScrollNode* scroll_node = scroll_tree.FindNodeFromElementId(element_id);
- if (!scroll_node)
- return false;
-
- scroll_tree.ScrollBy(
- *scroll_node,
- ScrollOffsetToVector2dF(offset -
- scroll_tree.current_scroll_offset(element_id)),
- active_tree());
- return true;
+ return input_handler_.ScrollLayerTo(element_id, offset);
}
bool LayerTreeHostImpl::ScrollingShouldSwitchtoMainThread() {
- DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification));
- ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
- ScrollNode* scroll_node = scroll_tree.CurrentlyScrollingNode();
-
- if (!scroll_node)
- return true;
-
- for (; scroll_tree.parent(scroll_node);
- scroll_node = scroll_tree.parent(scroll_node)) {
- if (!!scroll_node->main_thread_scrolling_reasons)
- return true;
- }
-
- return false;
+ return input_handler_.ScrollingShouldSwitchtoMainThread();
}
void LayerTreeHostImpl::NotifyInputEvent() {
@@ -1322,7 +1210,7 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) {
for (EffectTreeLayerListIterator it(active_tree());
it.state() != EffectTreeLayerListIterator::State::END; ++it) {
- auto target_render_pass_id = it.target_render_surface()->id();
+ auto target_render_pass_id = it.target_render_surface()->render_pass_id();
viz::RenderPass* target_render_pass =
FindRenderPassById(frame->render_passes, target_render_pass_id);
@@ -1534,6 +1422,11 @@ void LayerTreeHostImpl::SetViewportDamage(const gfx::Rect& damage_rect) {
viewport_damage_rect_.Union(damage_rect);
}
+void LayerTreeHostImpl::SetEnableFrameRateThrottling(
+ bool enable_frame_rate_throttling) {
+ enable_frame_rate_throttling_ = enable_frame_rate_throttling;
+}
+
void LayerTreeHostImpl::UpdateElements(ElementListType changed_list) {
mutator_host()->UpdateRegisteredElementIds(changed_list);
}
@@ -1566,8 +1459,7 @@ DrawResult LayerTreeHostImpl::PrepareToDraw(FrameData* frame) {
TRACE_ID_GLOBAL(CurrentBeginFrameArgs().trace_id),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"step", "GenerateRenderPass");
- if (input_handler_client_)
- input_handler_client_->ReconcileElasticOverscrollAndRootScroll();
+ input_delegate_->WillDraw();
// |client_name| is used for various UMA histograms below.
// GetClientNameForMetrics only returns one non-null value over the lifetime
@@ -1863,31 +1755,54 @@ gfx::ColorSpace LayerTreeHostImpl::GetRasterColorSpace(
gfx::ContentColorUsage content_color_usage) const {
constexpr gfx::ColorSpace srgb = gfx::ColorSpace::CreateSRGB();
- if (settings_.prefer_raster_in_srgb &&
- content_color_usage == gfx::ContentColorUsage::kSRGB)
+ // If we are likely to software composite the resource, we use sRGB because
+ // software compositing is unable to perform color conversion.
+ if (!layer_tree_frame_sink_ || !layer_tree_frame_sink_->context_provider())
return srgb;
- gfx::ColorSpace result;
- // The pending tree will have the most recently updated color space, so
- // prefer that.
- if (pending_tree_) {
- result = pending_tree_->raster_color_space();
- } else if (active_tree_) {
- result = active_tree_->raster_color_space();
+ if (settings_.prefer_raster_in_srgb &&
+ content_color_usage == gfx::ContentColorUsage::kSRGB) {
+ return srgb;
}
- // If we are likely to software composite the resource, we use sRGB because
- // software compositing is unable to perform color conversion. Also always
- // specify a color space if color correct rasterization is requested
+ // The pending tree will has the most recently updated color space, so use it.
+ gfx::DisplayColorSpaces display_cs;
+ if (pending_tree_)
+ display_cs = pending_tree_->display_color_spaces();
+ else if (active_tree_)
+ display_cs = active_tree_->display_color_spaces();
+
+ auto result = display_cs.GetOutputColorSpace(gfx::ContentColorUsage::kHDR,
+ /*needs_alpha=*/false);
+
+ // Always specify a color space if color correct rasterization is requested
// (not specifying a color space indicates that no color conversion is
// required).
- if (!layer_tree_frame_sink_ || !layer_tree_frame_sink_->context_provider() ||
- !result.IsValid()) {
- result = srgb;
- }
+ if (!result.IsValid())
+ return srgb;
+
+ // It's expensive to rasterize in HDR, so we only want to do so when we know
+ // we have HDR content to rasterize.
+ if (result.IsHDR() && content_color_usage != gfx::ContentColorUsage::kHDR)
+ return gfx::ColorSpace::CreateDisplayP3D65();
+
return result;
}
+float LayerTreeHostImpl::GetSDRWhiteLevel() const {
+ // If we are likely to software composite the resource, we use sRGB because
+ // software compositing is unable to perform color conversion.
+ if (!layer_tree_frame_sink_ || !layer_tree_frame_sink_->context_provider())
+ return gfx::ColorSpace::kDefaultSDRWhiteLevel;
+
+ // The pending tree will has the most recently updated color space, so use it.
+ if (pending_tree_)
+ return pending_tree_->display_color_spaces().GetSDRWhiteLevel();
+ if (active_tree_)
+ return active_tree_->display_color_spaces().GetSDRWhiteLevel();
+ return gfx::ColorSpace::kDefaultSDRWhiteLevel;
+}
+
void LayerTreeHostImpl::RequestImplSideInvalidationForCheckerImagedTiles() {
// When using impl-side invalidation for checker-imaging, a pending tree does
// not need to be flushed as an independent update through the pipeline.
@@ -2084,6 +1999,21 @@ void LayerTreeHostImpl::DidPresentCompositorFrame(
client_->NotifyThroughputTrackerResults(
std::move(throughput_tracker_results));
}
+
+ // Send all pending lag events waiting on the frame pointed by |frame_token|.
+ // It is posted as a task because LayerTreeHostImpl::DidPresentCompositorFrame
+ // is in the rendering critical path (it is called by AsyncLayerTreeFrameSink
+ // ::OnBeginFrame).
+ GetTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&LayerTreeHostImpl::LogAverageLagEvents,
+ weak_factory_.GetWeakPtr(), frame_token, details));
+}
+
+void LayerTreeHostImpl::LogAverageLagEvents(
+ uint32_t frame_token,
+ const viz::FrameTimingDetails& details) {
+ lag_tracking_manager_.DidPresentCompositorFrame(frame_token, details);
}
void LayerTreeHostImpl::DidNotNeedBeginFrame() {
@@ -2101,15 +2031,21 @@ void LayerTreeHostImpl::ReclaimResources(
if (resource_pool_) {
if (resource_pool_->memory_usage_bytes()) {
const size_t kMegabyte = 1024 * 1024;
-
// This is a good time to log memory usage. A chunk of work has just
// completed but none of the memory used for that work has likely been
// freed.
- UMA_HISTOGRAM_MEMORY_MB(
- "Renderer4.ResourcePoolMemoryUsage",
+ std::string client_suffix;
+ if (settings_.commit_to_active_tree) {
+ client_suffix = "Browser";
+ } else if (settings_.is_layer_tree_for_subframe) {
+ client_suffix = "OOPIF";
+ } else {
+ client_suffix = "Renderer";
+ }
+ base::UmaHistogramMemoryMB(
+ "Compositing.ResourcePoolMemoryUsage." + client_suffix,
static_cast<int>(resource_pool_->memory_usage_bytes() / kMegabyte));
}
-
resource_pool_->ReduceResourceUsage();
}
@@ -2235,8 +2171,8 @@ viz::CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() {
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());
+ TRACE_EVENT_SCOPE_THREAD, "ink metadata",
+ delegated_ink_metadata->ToString());
metadata.delegated_ink_metadata = std::move(delegated_ink_metadata);
}
@@ -2368,6 +2304,10 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) {
auto compositor_frame = GenerateCompositorFrame(frame);
frame->frame_token = compositor_frame.metadata.frame_token;
+
+ // Collect |latency_info| information for tracking
+ lag_tracking_manager_.CollectScrollEventsFromFrame(
+ frame->frame_token, compositor_frame.metadata.latency_info);
layer_tree_frame_sink_->SubmitCompositorFrame(
std::move(compositor_frame),
/*hit_test_data_changed=*/false, debug_state_.show_hit_test_borders);
@@ -2506,8 +2446,14 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame(
frame->use_default_lower_bound_deadline);
frame_rate_estimator_.WillDraw(CurrentBeginFrameArgs().frame_time);
- metadata.preferred_frame_interval =
- frame_rate_estimator_.GetPreferredInterval();
+
+ if (settings_.force_preferred_interval_for_video ||
+ enable_frame_rate_throttling_) {
+ metadata.preferred_frame_interval = viz::BeginFrameArgs::MaxInterval();
+ } else {
+ metadata.preferred_frame_interval =
+ frame_rate_estimator_.GetPreferredInterval();
+ }
metadata.activation_dependencies = std::move(frame->activation_dependencies);
active_tree()->FinishSwapPromises(&metadata);
@@ -2646,7 +2592,7 @@ void LayerTreeHostImpl::GetGpuRasterizationCapabilities(
bool use_msaa = !caps.msaa_is_slow && !caps.avoid_stencil_buffers;
- if (use_oop_rasterization_) {
+ if (can_use_oop_rasterization_) {
*gpu_rasterization_supported = true;
*supports_disable_msaa = caps.multisample_compatibility;
// For OOP raster, the gpu service side will disable msaa if the
@@ -2662,14 +2608,14 @@ void LayerTreeHostImpl::GetGpuRasterizationCapabilities(
// Do not check GrContext above. It is lazy-created, and we only want to
// create it if it might be used.
- GrContext* gr_context = context_provider->GrContext();
+ GrDirectContext* gr_context = context_provider->GrContext();
*gpu_rasterization_supported = !!gr_context;
if (!*gpu_rasterization_supported)
return;
*supports_disable_msaa = caps.multisample_compatibility;
if (use_msaa) {
- // Skia may blacklist MSAA independently of Chrome. Query Skia for its max
+ // Skia may block MSAA independently of Chrome. Query Skia for its max
// supported sample count. Assume gpu compositing + gpu raster for this, as
// that is what we are hoping to use.
viz::ResourceFormat tile_format = TileRasterBufferFormat(
@@ -2788,6 +2734,7 @@ bool LayerTreeHostImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) {
impl_thread_phase_ = ImplThreadPhase::INSIDE_IMPL_FRAME;
current_begin_frame_tracker_.Start(args);
frame_trackers_.NotifyBeginImplFrame(args);
+ total_frame_counter_.OnBeginFrame(args);
if (is_likely_to_require_a_draw_) {
// Optimistically schedule a draw. This will let us expect the tile manager
@@ -2796,10 +2743,7 @@ bool LayerTreeHostImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) {
SetNeedsRedraw();
}
- if (input_handler_client_) {
- scrollbar_controller_->WillBeginImplFrame();
- input_handler_client_->DeliverInputForBeginFrame(args);
- }
+ input_delegate_->WillBeginImplFrame(args);
Animate();
@@ -3019,6 +2963,11 @@ void LayerTreeHostImpl::DidLoseLayerTreeFrameSink() {
return;
has_valid_layer_tree_frame_sink_ = false;
client_->DidLoseLayerTreeFrameSinkOnImplThread();
+ lag_tracking_manager_.Clear();
+}
+
+bool LayerTreeHostImpl::OnlyExpandTopControlsAtPageTop() const {
+ return active_tree_->only_expand_top_controls_at_page_top();
}
bool LayerTreeHostImpl::HaveRootScrollNode() const {
@@ -3046,21 +2995,13 @@ const ScrollNode* LayerTreeHostImpl::CurrentlyScrollingNode() const {
}
bool LayerTreeHostImpl::IsActivelyPrecisionScrolling() const {
- if (!CurrentlyScrollingNode())
- return false;
- // On Android WebView root flings are controlled by the application,
- // so the compositor does not animate them and can't tell if they
- // are actually animating. So assume there are none.
- if (settings_.ignore_root_layer_flings && IsCurrentlyScrollingViewport())
- return false;
-
- if (!last_scroll_update_state_)
- return false;
+ return input_delegate_->IsActivelyPrecisionScrolling();
+}
- bool did_scroll_content =
- did_scroll_x_for_scroll_gesture_ || did_scroll_y_for_scroll_gesture_;
- return !ShouldAnimateScroll(last_scroll_update_state_.value()) &&
- did_scroll_content;
+bool LayerTreeHostImpl::ScrollAffectsScrollHandler() const {
+ return settings_.enable_synchronized_scrolling &&
+ input_delegate_->IsCurrentlyScrolling() &&
+ active_tree()->have_scroll_event_handlers();
}
void LayerTreeHostImpl::CreatePendingTree() {
@@ -3152,10 +3093,6 @@ void LayerTreeHostImpl::ActivateSyncTree() {
active_tree_->lifecycle().AdvanceTo(LayerTreeLifecycle::kNotSyncing);
- // The previous scrolling node no longer exists in the new tree.
- if (!active_tree_->CurrentlyScrollingNode())
- ClearCurrentlyScrollingNode();
-
// Now that we've synced everything from the pending tree to the active
// tree, rename the pending tree the recycle tree so we can reuse it on the
// next sync.
@@ -3214,9 +3151,8 @@ void LayerTreeHostImpl::ActivateSyncTree() {
pending_page_scale_animation->scale,
pending_page_scale_animation->duration);
}
- // Activation can change the root scroll offset, so inform the synchronous
- // input handler.
- UpdateRootLayerStateForSynchronousInputHandler();
+
+ input_delegate_->DidActivatePendingTree();
// Update the child's LocalSurfaceId.
if (active_tree()->local_surface_id_allocation_from_parent().IsValid()) {
@@ -3278,6 +3214,10 @@ void LayerTreeHostImpl::SetVisible(bool visible) {
if (visible_ == visible)
return;
visible_ = visible;
+ if (visible_)
+ total_frame_counter_.OnShow(base::TimeTicks::Now());
+ else
+ total_frame_counter_.OnHide(base::TimeTicks::Now());
DidVisibilityChange(this, visible_);
UpdateTileManagerMemoryPolicy(ActualManagedMemoryPolicy());
@@ -3372,7 +3312,7 @@ void LayerTreeHostImpl::CreateTileManagerResources() {
if (use_gpu_rasterization_) {
image_decode_cache_ = std::make_unique<GpuImageDecodeCache>(
layer_tree_frame_sink_->worker_context_provider(),
- use_oop_rasterization_,
+ can_use_oop_rasterization_,
viz::ResourceFormatToClosestSkColorType(/*gpu_compositing=*/true,
tile_format),
settings_.decoded_image_working_set_budget_bytes, max_texture_size_,
@@ -3397,7 +3337,7 @@ void LayerTreeHostImpl::CreateTileManagerResources() {
tile_manager_.SetResources(resource_pool_.get(), image_decode_cache_.get(),
task_graph_runner, raster_buffer_provider_.get(),
- use_gpu_rasterization_);
+ use_gpu_rasterization_, use_oop_rasterization());
tile_manager_.SetCheckerImagingForceDisabled(
settings_.only_checker_images_with_gpu_raster && !use_gpu_rasterization_);
UpdateTileManagerMemoryPolicy(ActualManagedMemoryPolicy());
@@ -3428,7 +3368,7 @@ LayerTreeHostImpl::CreateRasterBufferProvider() {
settings_.resource_settings.use_gpu_memory_buffer_resources,
tile_format, settings_.max_gpu_raster_tile_size,
settings_.unpremultiply_and_dither_low_bit_depth_tiles,
- use_oop_rasterization_);
+ can_use_oop_rasterization_);
}
bool use_zero_copy = settings_.use_zero_copy;
@@ -3473,9 +3413,7 @@ void LayerTreeHostImpl::QueueImageDecode(int request_id,
image.GetKeyForFrame(PaintImage::kDefaultFrameIndex).ToString());
// Optimistically specify the current raster color space, since we assume that
// it won't change.
- auto content_color_usage = image.isSRGB()
- ? gfx::ContentColorUsage::kSRGB
- : gfx::ContentColorUsage::kWideColorGamut;
+ auto content_color_usage = image.GetContentColorUsage();
tile_manager_.decoded_image_tracker().QueueImageDecode(
image, GetRasterColorSpace(content_color_usage),
base::BindOnce(&LayerTreeHostImpl::ImageDecodeFinished,
@@ -3658,9 +3596,10 @@ bool LayerTreeHostImpl::InitializeFrameSink(
auto* context = layer_tree_frame_sink_->worker_context_provider();
if (context) {
viz::RasterContextProvider::ScopedRasterContextLock hold(context);
- use_oop_rasterization_ = context->ContextCapabilities().supports_oop_raster;
+ can_use_oop_rasterization_ =
+ context->ContextCapabilities().supports_oop_raster;
} else {
- use_oop_rasterization_ = false;
+ can_use_oop_rasterization_ = false;
}
// Since the new context may support GPU raster or be capable of MSAA, update
@@ -3758,638 +3697,26 @@ float LayerTreeHostImpl::CurrentBottomControlsShownRatio() const {
return active_tree_->CurrentBottomControlsShownRatio();
}
-void LayerTreeHostImpl::BindToClient(InputHandlerClient* client) {
- DCHECK(input_handler_client_ == nullptr);
- input_handler_client_ = client;
-}
-
-gfx::Vector2dF LayerTreeHostImpl::ResolveScrollGranularityToPixels(
- const ScrollNode& scroll_node,
- 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;
- if (scroll_node->main_thread_scrolling_reasons) {
- TRACE_EVENT1("cc", "LayerImpl::TryScroll: Failed ShouldScrollOnMainThread",
- "MainThreadScrollingReason",
- scroll_node->main_thread_scrolling_reasons);
- scroll_status.thread = InputHandler::SCROLL_ON_MAIN_THREAD;
- scroll_status.main_thread_scrolling_reasons =
- scroll_node->main_thread_scrolling_reasons;
- return scroll_status;
- }
-
- gfx::Transform screen_space_transform =
- scroll_tree.ScreenSpaceTransform(scroll_node->id);
- if (!screen_space_transform.IsInvertible()) {
- TRACE_EVENT0("cc", "LayerImpl::TryScroll: Ignored NonInvertibleTransform");
- scroll_status.thread = InputHandler::SCROLL_IGNORED;
- scroll_status.main_thread_scrolling_reasons =
- MainThreadScrollingReason::kNonInvertibleTransform;
- return scroll_status;
- }
-
- if (!scroll_node->scrollable) {
- TRACE_EVENT0("cc", "LayerImpl::tryScroll: Ignored not scrollable");
- scroll_status.thread = InputHandler::SCROLL_IGNORED;
- scroll_status.main_thread_scrolling_reasons =
- MainThreadScrollingReason::kNotScrollable;
- return scroll_status;
- }
-
- // 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.
- if (scroll_node->id != ScrollTree::kRootNodeId &&
- scroll_node->id != ScrollTree::kSecondaryRootNodeId &&
- !scroll_node->scrolls_inner_viewport &&
- !active_tree_->LayerByElementId(scroll_node->element_id)) {
- TRACE_EVENT0("cc",
- "LayerImpl::tryScroll: Failed due to no scrolling layer");
- scroll_status.thread = InputHandler::SCROLL_ON_MAIN_THREAD;
- scroll_status.main_thread_scrolling_reasons =
- MainThreadScrollingReason::kNonFastScrollableRegion;
- return scroll_status;
- }
-
- // The a viewport node should be scrolled even if it has no scroll extent
- // since it'll scroll using the Viewport class which will generate browser
- // controls movement and overscroll delta.
- gfx::ScrollOffset max_scroll_offset =
- scroll_tree.MaxScrollOffset(scroll_node->id);
- if (max_scroll_offset.x() <= 0 && max_scroll_offset.y() <= 0 &&
- !viewport().ShouldScroll(*scroll_node)) {
- TRACE_EVENT0("cc",
- "LayerImpl::tryScroll: Ignored. Technically scrollable,"
- " but has no affordance in either direction.");
- scroll_status.thread = InputHandler::SCROLL_IGNORED;
- scroll_status.main_thread_scrolling_reasons =
- MainThreadScrollingReason::kNotScrollable;
- return scroll_status;
- }
-
- scroll_status.thread = InputHandler::SCROLL_ON_IMPL_THREAD;
- return scroll_status;
-}
-
-static bool IsMainThreadScrolling(const InputHandler::ScrollStatus& status,
- const ScrollNode* scroll_node) {
- if (status.thread == InputHandler::SCROLL_ON_MAIN_THREAD) {
- if (!!scroll_node->main_thread_scrolling_reasons) {
- DCHECK(MainThreadScrollingReason::MainThreadCanSetScrollReasons(
- status.main_thread_scrolling_reasons));
- } else {
- DCHECK(MainThreadScrollingReason::CompositorCanSetScrollReasons(
- status.main_thread_scrolling_reasons));
- }
- return true;
- }
- return false;
-}
-
-base::flat_set<int> LayerTreeHostImpl::NonFastScrollableNodes(
- const gfx::PointF& device_viewport_point) const {
- base::flat_set<int> non_fast_scrollable_nodes;
-
- const auto& non_fast_layers =
- active_tree_->FindLayersHitByPointInNonFastScrollableRegion(
- device_viewport_point);
- for (const auto* layer : non_fast_layers)
- non_fast_scrollable_nodes.insert(layer->scroll_tree_index());
-
- return non_fast_scrollable_nodes;
+gfx::ScrollOffset LayerTreeHostImpl::ViewportScrollOffset() const {
+ return viewport_->TotalScrollOffset();
}
-ScrollNode* LayerTreeHostImpl::FindScrollNodeForCompositedScrolling(
- const gfx::PointF& device_viewport_point,
- 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 =
- MainThreadScrollingReason::kNotScrollingOnMain;
-
- const auto& non_fast_scrollable_nodes =
- NonFastScrollableNodes(device_viewport_point);
-
- // Walk up the hierarchy and look for a scrollable layer.
- ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
- ScrollNode* impl_scroll_node = nullptr;
- if (layer_impl) {
- // If this is a scrollbar layer, we can't directly use the associated
- // scroll_node (because the scroll_node associated with this layer will be
- // the owning scroller's parent). Instead, we first retrieve the scrollable
- // layer corresponding to the scrollbars owner and then use its
- // scroll_tree_index instead.
- int scroll_tree_index = layer_impl->scroll_tree_index();
- if (layer_impl->IsScrollbarLayer()) {
- LayerImpl* owner_scroll_layer = active_tree_->LayerByElementId(
- ToScrollbarLayer(layer_impl)->scroll_element_id());
- scroll_tree_index = owner_scroll_layer->scroll_tree_index();
- }
-
- ScrollNode* scroll_node = scroll_tree.Node(scroll_tree_index);
- for (; scroll_tree.parent(scroll_node);
- scroll_node = scroll_tree.parent(scroll_node)) {
- // The content layer can also block attempts to scroll outside the main
- // thread.
- ScrollStatus status = TryScroll(scroll_tree, scroll_node);
- if (IsMainThreadScrolling(status, scroll_node)) {
- *scroll_on_main_thread = true;
- *main_thread_scrolling_reasons = status.main_thread_scrolling_reasons;
- return scroll_node;
- }
-
- if (non_fast_scrollable_nodes.contains(scroll_node->id)) {
- *scroll_on_main_thread = true;
- *main_thread_scrolling_reasons =
- MainThreadScrollingReason::kNonFastScrollableRegion;
- return scroll_node;
- }
-
- if (status.thread == InputHandler::SCROLL_ON_IMPL_THREAD &&
- !impl_scroll_node) {
- impl_scroll_node = scroll_node;
- }
- }
- }
-
- // TODO(bokan): We shouldn't need this - ordinarily all scrolls should pass
- // through the outer viewport. If we aren't able to find a scroller we should
- // return nullptr here and ignore the scroll. However, it looks like on some
- // pages (reddit.com) we start scrolling from the inner node.
- if (!impl_scroll_node)
- impl_scroll_node = InnerViewportScrollNode();
-
- if (!impl_scroll_node)
- return nullptr;
-
- impl_scroll_node = GetNodeToScroll(impl_scroll_node);
-
- // Ensure that final scroll node scrolls on impl thread (crbug.com/625100)
- ScrollStatus status = TryScroll(scroll_tree, impl_scroll_node);
- if (IsMainThreadScrolling(status, impl_scroll_node)) {
- *scroll_on_main_thread = true;
- *main_thread_scrolling_reasons = status.main_thread_scrolling_reasons;
- } else if (non_fast_scrollable_nodes.contains(impl_scroll_node->id)) {
- *scroll_on_main_thread = true;
- *main_thread_scrolling_reasons =
- MainThreadScrollingReason::kNonFastScrollableRegion;
- }
-
- return impl_scroll_node;
+void LayerTreeHostImpl::BindToClient(InputHandlerClient* client) {
+ input_handler_.BindToClient(client);
}
InputHandler::ScrollStatus LayerTreeHostImpl::RootScrollBegin(
ScrollState* scroll_state,
ui::ScrollInputType type) {
- TRACE_EVENT0("cc", "LayerTreeHostImpl::RootScrollBegin");
- if (!OuterViewportScrollNode()) {
- ScrollStatus scroll_status;
- scroll_status.thread = InputHandler::SCROLL_IGNORED;
- scroll_status.main_thread_scrolling_reasons =
- MainThreadScrollingReason::kNoScrollingLayer;
- return scroll_status;
- }
-
- scroll_state->data()->set_current_native_scrolling_element(
- OuterViewportScrollNode()->element_id);
- 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;
+ return input_handler_.RootScrollBegin(scroll_state, type);
}
InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
ScrollState* scroll_state,
ui::ScrollInputType type) {
- DCHECK(scroll_state);
- DCHECK(scroll_state->delta_x() == 0 && scroll_state->delta_y() == 0);
-
- ScrollStatus scroll_status;
- scroll_status.main_thread_scrolling_reasons =
- MainThreadScrollingReason::kNotScrollingOnMain;
- TRACE_EVENT0("cc", "LayerTreeHostImpl::ScrollBegin");
-
- // If this ScrollBegin is non-animated then ensure we cancel any ongoing
- // animated scrolls.
- // TODO(bokan): This preserves existing behavior when we had diverging
- // paths for animated and non-animated scrolls but we should probably
- // decide when it best makes sense to cancel a scroll animation (maybe
- // ScrollBy is a better place to do it).
- if (scroll_state->delta_granularity() ==
- ui::ScrollGranularity::kScrollByPrecisePixel) {
- mutator_host_->ScrollAnimationAbort();
- scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
- }
-
- if (CurrentlyScrollingNode() && type == latched_scroll_type_) {
- // It's possible we haven't yet cleared the CurrentlyScrollingNode if we
- // received a GSE but we're still animating the last scroll. If that's the
- // case, we'll simply un-defer the GSE and continue latching to the same
- // node.
- DCHECK(deferred_scroll_end_);
- deferred_scroll_end_ = false;
- return scroll_status;
- }
-
- ScrollNode* scrolling_node = nullptr;
- bool scroll_on_main_thread = false;
-
- // TODO(bokan): ClearCurrentlyScrollingNode shouldn't happen in 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;
-
- 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.
- 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 {
- 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;
- }
-
- // 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;
- }
- }
-
- 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;
- }
-
- 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);
- }
- }
-
- // 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.
- scrolling_node = FindNodeToLatch(scroll_state, starting_node, type);
- }
-
- 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;
- } else if (!scrolling_node) {
- 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 {
- // 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;
- }
- return scroll_status;
- }
-
- DCHECK_EQ(scroll_status.main_thread_scrolling_reasons,
- MainThreadScrollingReason::kNotScrollingOnMain);
- DCHECK_EQ(scroll_status.thread, SCROLL_ON_IMPL_THREAD);
-
- active_tree_->SetCurrentlyScrollingNode(scrolling_node);
-
- DidLatchToScroller(*scroll_state, type);
-
- // If the viewport is scrolling and it cannot consume any delta hints, the
- // scroll event will need to get bubbled if the viewport is for a guest or
- // oopif.
- if (viewport().ShouldScroll(*CurrentlyScrollingNode()) &&
- !viewport().CanScroll(*CurrentlyScrollingNode(), *scroll_state)) {
- scroll_status.bubble = true;
- }
-
- return scroll_status;
-}
-
-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) {
- 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.
- {
- LayerImpl* first_scrolling_layer_or_scrollbar =
- active_tree_->FindFirstScrollingLayerOrScrollbarThatIsHitByPoint(
- device_viewport_point);
-
- if (!IsInitialScrollHitTestReliable(layer_impl,
- first_scrolling_layer_or_scrollbar)) {
- TRACE_EVENT_INSTANT0("cc", "Failed Hit Test", TRACE_EVENT_SCOPE_THREAD);
- return 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.
- if (layer_impl->IsScrollbarLayer()) {
- layer_impl = active_tree_->LayerByElementId(
- ToScrollbarLayer(layer_impl)->scroll_element_id());
- DCHECK(layer_impl);
- }
-
- ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
- ScrollNode* scroll_node = scroll_tree.Node(layer_impl->scroll_tree_index());
-
- 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
-// from touch.
-bool LayerTreeHostImpl::IsTouchDraggingScrollbar(
- LayerImpl* first_scrolling_layer_or_scrollbar,
- ui::ScrollInputType type) {
- return first_scrolling_layer_or_scrollbar &&
- first_scrolling_layer_or_scrollbar->IsScrollbarLayer() &&
- type == ui::ScrollInputType::kTouchscreen;
-}
-
-ScrollNode* LayerTreeHostImpl::GetNodeToScroll(ScrollNode* node) const {
- // Blink has a notion of a "root scroller", which is the scroller in a page
- // that is considered to host the main content. Typically this will be the
- // document/LayoutView contents; however, in some situations Blink may choose
- // a sub-scroller (div, iframe) that should scroll with "viewport" behavior.
- // The "root scroller" is the node designated as the outer viewport in CC.
- // See third_party/blink/renderer/core/page/scrolling/README.md for details.
- //
- // "Viewport" scrolling ensures generation of overscroll events, top controls
- // movement, as well as correct multi-viewport panning in pinch-zoom and
- // other scenarios. We use the viewport's outer scroll node to represent the
- // viewport in the scroll chain and apply scroll delta using CC's Viewport
- // class.
- //
- // Scrolling from position: fixed layers will chain directly up to the inner
- // viewport. Whether that should use the outer viewport (and thus the
- // Viewport class) to scroll or not depends on the root scroller scenario
- // because we don't want setting a root scroller to change the scroll chain
- // order. The |prevent_viewport_scrolling_from_inner| bit is used to
- // communicate that context.
- DCHECK(!node->prevent_viewport_scrolling_from_inner ||
- node->scrolls_inner_viewport);
-
- if (node->scrolls_inner_viewport &&
- !node->prevent_viewport_scrolling_from_inner) {
- DCHECK(OuterViewportScrollNode());
- return OuterViewportScrollNode();
- }
-
- return node;
-}
-
-bool LayerTreeHostImpl::IsInitialScrollHitTestReliable(
- LayerImpl* layer_impl,
- LayerImpl* first_scrolling_layer_or_scrollbar) const {
- if (!first_scrolling_layer_or_scrollbar)
- return true;
-
- // Hit tests directly on a composited scrollbar are always reliable.
- if (layer_impl->IsScrollbarLayer()) {
- DCHECK(layer_impl == first_scrolling_layer_or_scrollbar);
- return true;
- }
-
- ScrollNode* closest_scroll_node = nullptr;
- auto& scroll_tree = active_tree_->property_trees()->scroll_tree;
- ScrollNode* scroll_node = scroll_tree.Node(layer_impl->scroll_tree_index());
- for (; scroll_tree.parent(scroll_node);
- scroll_node = scroll_tree.parent(scroll_node)) {
- if (scroll_node->scrollable) {
- closest_scroll_node = GetNodeToScroll(scroll_node);
- break;
- }
- }
- if (!closest_scroll_node)
- return false;
-
- // If |first_scrolling_layer_or_scrollbar| is not a scrollbar, it must be
- // a scrollabe layer with a scroll node. If this scroll node corresponds to
- // first scrollable ancestor along the scroll tree for |layer_impl|, the hit
- // test has not escaped to other areas of the scroll tree and is reliable.
- if (!first_scrolling_layer_or_scrollbar->IsScrollbarLayer()) {
- return closest_scroll_node->id ==
- first_scrolling_layer_or_scrollbar->scroll_tree_index();
- }
-
- return false;
+ return input_handler_.ScrollBegin(scroll_state, type);
}
-gfx::Vector2dF LayerTreeHostImpl::ComputeScrollDelta(
- const ScrollNode& scroll_node,
- const gfx::Vector2dF& delta) {
- ScrollTree& scroll_tree = active_tree()->property_trees()->scroll_tree;
- float scale_factor = active_tree()->page_scale_factor_for_scroll();
-
- gfx::Vector2dF adjusted_scroll(delta);
- adjusted_scroll.Scale(1.f / scale_factor);
- adjusted_scroll = UserScrollableDelta(scroll_node, adjusted_scroll);
-
- gfx::ScrollOffset old_offset =
- scroll_tree.current_scroll_offset(scroll_node.element_id);
- gfx::ScrollOffset new_offset = scroll_tree.ClampScrollOffsetToLimits(
- old_offset + gfx::ScrollOffset(adjusted_scroll), scroll_node);
-
- gfx::ScrollOffset scrolled = new_offset - old_offset;
- return gfx::Vector2dF(scrolled.x(), scrolled.y());
-}
bool LayerTreeHostImpl::AutoScrollAnimationCreate(const ScrollNode& scroll_node,
const gfx::Vector2dF& delta,
@@ -4448,392 +3775,6 @@ bool LayerTreeHostImpl::ScrollAnimationCreateInternal(
return true;
}
-bool LayerTreeHostImpl::CalculateLocalScrollDeltaAndStartPoint(
- const ScrollNode& scroll_node,
- const gfx::PointF& viewport_point,
- const gfx::Vector2dF& viewport_delta,
- const ScrollTree& scroll_tree,
- gfx::Vector2dF* out_local_scroll_delta,
- gfx::PointF* out_local_start_point /*= nullptr*/) {
- // Layers with non-invertible screen space transforms should not have passed
- // the scroll hit test in the first place.
- const gfx::Transform screen_space_transform =
- scroll_tree.ScreenSpaceTransform(scroll_node.id);
- DCHECK(screen_space_transform.IsInvertible());
- gfx::Transform inverse_screen_space_transform(
- gfx::Transform::kSkipInitialization);
- bool did_invert =
- screen_space_transform.GetInverse(&inverse_screen_space_transform);
- // TODO(shawnsingh): With the advent of impl-side scrolling for non-root
- // layers, we may need to explicitly handle uninvertible transforms here.
- DCHECK(did_invert);
-
- float scale_from_viewport_to_screen_space =
- active_tree_->device_scale_factor();
- gfx::PointF screen_space_point =
- gfx::ScalePoint(viewport_point, scale_from_viewport_to_screen_space);
-
- gfx::Vector2dF screen_space_delta = viewport_delta;
- screen_space_delta.Scale(scale_from_viewport_to_screen_space);
-
- // Project the scroll start and end points to local layer space to find the
- // scroll delta in layer coordinates.
- bool start_clipped, end_clipped;
- gfx::PointF screen_space_end_point = screen_space_point + screen_space_delta;
- gfx::PointF local_start_point = MathUtil::ProjectPoint(
- inverse_screen_space_transform, screen_space_point, &start_clipped);
- gfx::PointF local_end_point = MathUtil::ProjectPoint(
- inverse_screen_space_transform, screen_space_end_point, &end_clipped);
- DCHECK(out_local_scroll_delta);
- *out_local_scroll_delta = local_end_point - local_start_point;
-
- if (out_local_start_point)
- *out_local_start_point = local_start_point;
-
- if (start_clipped || end_clipped)
- return false;
-
- return true;
-}
-
-gfx::Vector2dF LayerTreeHostImpl::ScrollNodeWithViewportSpaceDelta(
- const ScrollNode& scroll_node,
- const gfx::PointF& viewport_point,
- const gfx::Vector2dF& viewport_delta,
- ScrollTree* scroll_tree) {
- gfx::PointF local_start_point;
- gfx::Vector2dF local_scroll_delta;
- if (!CalculateLocalScrollDeltaAndStartPoint(
- scroll_node, viewport_point, viewport_delta, *scroll_tree,
- &local_scroll_delta, &local_start_point)) {
- return gfx::Vector2dF();
- }
-
- bool scrolls_outer_viewport = scroll_node.scrolls_outer_viewport;
- TRACE_EVENT2("cc", "ScrollNodeWithViewportSpaceDelta", "delta_y",
- local_scroll_delta.y(), "is_outer", scrolls_outer_viewport);
-
- // Apply the scroll delta.
- gfx::ScrollOffset previous_offset =
- scroll_tree->current_scroll_offset(scroll_node.element_id);
- scroll_tree->ScrollBy(scroll_node, local_scroll_delta, active_tree());
- gfx::ScrollOffset scrolled =
- scroll_tree->current_scroll_offset(scroll_node.element_id) -
- previous_offset;
-
- TRACE_EVENT_INSTANT1("cc", "ConsumedDelta", TRACE_EVENT_SCOPE_THREAD, "y",
- scrolled.y());
-
- // Get the end point in the layer's content space so we can apply its
- // ScreenSpaceTransform.
- gfx::PointF actual_local_end_point =
- local_start_point + gfx::Vector2dF(scrolled.x(), scrolled.y());
-
- // Calculate the applied scroll delta in viewport space coordinates.
- bool end_clipped;
- const gfx::Transform screen_space_transform =
- scroll_tree->ScreenSpaceTransform(scroll_node.id);
- gfx::PointF actual_screen_space_end_point = MathUtil::MapPoint(
- screen_space_transform, actual_local_end_point, &end_clipped);
- DCHECK(!end_clipped);
- if (end_clipped)
- return gfx::Vector2dF();
-
- float scale_from_viewport_to_screen_space =
- active_tree_->device_scale_factor();
- gfx::PointF actual_viewport_end_point = gfx::ScalePoint(
- actual_screen_space_end_point, 1.f / scale_from_viewport_to_screen_space);
- return actual_viewport_end_point - viewport_point;
-}
-
-static gfx::Vector2dF ScrollNodeWithLocalDelta(
- const ScrollNode& scroll_node,
- const gfx::Vector2dF& local_delta,
- float page_scale_factor,
- LayerTreeImpl* layer_tree_impl) {
- bool scrolls_outer_viewport = scroll_node.scrolls_outer_viewport;
- TRACE_EVENT2("cc", "ScrollNodeWithLocalDelta", "delta_y", local_delta.y(),
- "is_outer", scrolls_outer_viewport);
-
- ScrollTree& scroll_tree = layer_tree_impl->property_trees()->scroll_tree;
- gfx::ScrollOffset previous_offset =
- scroll_tree.current_scroll_offset(scroll_node.element_id);
- gfx::Vector2dF delta = local_delta;
- delta.Scale(1.f / page_scale_factor);
- scroll_tree.ScrollBy(scroll_node, delta, layer_tree_impl);
- gfx::ScrollOffset scrolled =
- scroll_tree.current_scroll_offset(scroll_node.element_id) -
- previous_offset;
- gfx::Vector2dF consumed_scroll(scrolled.x(), scrolled.y());
- consumed_scroll.Scale(page_scale_factor);
- TRACE_EVENT_INSTANT1("cc", "ConsumedDelta", TRACE_EVENT_SCOPE_THREAD, "y",
- consumed_scroll.y());
-
- return consumed_scroll;
-}
-
-// TODO(danakj): Make this into two functions, one with delta, one with
-// viewport_point, no bool required.
-gfx::Vector2dF LayerTreeHostImpl::ScrollSingleNode(
- const ScrollNode& scroll_node,
- const gfx::Vector2dF& delta,
- const gfx::Point& viewport_point,
- bool is_direct_manipulation,
- ScrollTree* scroll_tree) {
- gfx::Vector2dF adjusted_delta = UserScrollableDelta(scroll_node, delta);
-
- // Events representing direct manipulation of the screen (such as gesture
- // events) need to be transformed from viewport coordinates to local layer
- // coordinates so that the scrolling contents exactly follow the user's
- // finger. In contrast, events not representing direct manipulation of the
- // screen (such as wheel events) represent a fixed amount of scrolling so we
- // can just apply them directly, but the page scale factor is applied to the
- // scroll delta.
- if (is_direct_manipulation) {
- // For touch-scroll we need to scale the delta here, as the transform tree
- // won't know anything about the external page scale factors used by OOPIFs.
- gfx::Vector2dF scaled_delta(adjusted_delta);
- scaled_delta.Scale(1 / active_tree()->external_page_scale_factor());
- return ScrollNodeWithViewportSpaceDelta(
- scroll_node, gfx::PointF(viewport_point), scaled_delta, scroll_tree);
- }
- float scale_factor = active_tree()->page_scale_factor_for_scroll();
- return ScrollNodeWithLocalDelta(scroll_node, adjusted_delta, scale_factor,
- active_tree());
-}
-
-void LayerTreeHostImpl::ScrollLatchedScroller(ScrollState* scroll_state,
- base::TimeDelta delayed_by) {
- DCHECK(CurrentlyScrollingNode());
- DCHECK(scroll_state);
- DCHECK(latched_scroll_type_.has_value());
-
- ScrollNode& scroll_node = *CurrentlyScrollingNode();
- const gfx::Vector2dF delta(scroll_state->delta_x(), scroll_state->delta_y());
- TRACE_EVENT2("cc", "LayerTreeHostImpl::ScrollLatchedScroller", "delta_x",
- delta.x(), "delta_y", delta.y());
- gfx::Vector2dF applied_delta;
- gfx::Vector2dF delta_applied_to_content;
- // TODO(tdresser): Use a more rational epsilon. See crbug.com/510550 for
- // details.
- const float kEpsilon = 0.1f;
-
- if (ShouldAnimateScroll(*scroll_state)) {
- DCHECK(!scroll_state->is_in_inertial_phase());
-
- if (ElementId id = mutator_host_->ImplOnlyScrollAnimatingElement()) {
- TRACE_EVENT_INSTANT0("cc", "UpdateExistingAnimation",
- TRACE_EVENT_SCOPE_THREAD);
-
- ScrollTree& scroll_tree = active_tree()->property_trees()->scroll_tree;
- ScrollNode* animating_scroll_node = scroll_tree.FindNodeFromElementId(id);
- DCHECK(animating_scroll_node);
-
- // Usually the CurrentlyScrollingNode will be the currently animating
- // one. The one exception is the inner viewport. Scrolling the combined
- // viewport will always set the outer viewport as the currently scrolling
- // node. However, if an animation is created on the inner viewport we
- // must use it when updating the animation curve.
- DCHECK(animating_scroll_node->id == scroll_node.id ||
- animating_scroll_node->scrolls_inner_viewport);
-
- bool animation_updated = ScrollAnimationUpdateTarget(
- *animating_scroll_node, delta, delayed_by);
-
- if (animation_updated) {
- // Because we updated the animation target, consume delta so we notify
- // the SwapPromiseMonitor to tell it that something happened that will
- // cause a swap in the future. This will happen within the scope of
- // the dispatch of a gesture scroll update input event. If we don't
- // notify during the handling of the input event, the LatencyInfo
- // associated with the input event will not be added as a swap promise
- // and we won't get any swap results.
- applied_delta = delta;
- } else {
- TRACE_EVENT_INSTANT0("cc", "Didn't Update Animation",
- TRACE_EVENT_SCOPE_THREAD);
- }
- } else {
- TRACE_EVENT_INSTANT0("cc", "CreateNewAnimation",
- TRACE_EVENT_SCOPE_THREAD);
- if (scroll_node.scrolls_outer_viewport) {
- applied_delta = viewport().ScrollAnimated(delta, delayed_by);
- } else {
- applied_delta = ComputeScrollDelta(scroll_node, delta);
- ScrollAnimationCreate(scroll_node, applied_delta, delayed_by);
- }
- }
-
- // Animated scrolling always applied only to the content (i.e. not to the
- // browser controls).
- delta_applied_to_content = delta;
- } else {
- gfx::Point viewport_point(scroll_state->position_x(),
- scroll_state->position_y());
- if (viewport().ShouldScroll(scroll_node)) {
- // |scrolls_outer_viewport| will only ever be false if the scroll chains
- // up to the viewport without going through the outer viewport scroll
- // node. This is because we normally terminate the scroll chain at the
- // outer viewport node. For example, if we start scrolling from an
- // element that's not a descendant of the root scroller. In these cases we
- // want to scroll *only* the inner viewport -- to allow panning while
- // zoomed -- but still use Viewport::ScrollBy to also move browser
- // controls if needed.
- Viewport::ScrollResult result = viewport().ScrollBy(
- delta, viewport_point, scroll_state->is_direct_manipulation(),
- latched_scroll_type_ != ui::ScrollInputType::kWheel,
- scroll_node.scrolls_outer_viewport);
-
- applied_delta = result.consumed_delta;
- delta_applied_to_content = result.content_scrolled_delta;
- } else {
- applied_delta =
- ScrollSingleNode(scroll_node, delta, viewport_point,
- scroll_state->is_direct_manipulation(),
- &active_tree_->property_trees()->scroll_tree);
- }
- }
-
- // If the layer wasn't able to move, try the next one in the hierarchy.
- bool scrolled = std::abs(applied_delta.x()) > kEpsilon;
- scrolled = scrolled || std::abs(applied_delta.y()) > kEpsilon;
- if (!scrolled) {
- // TODO(bokan): This preserves existing behavior by not allowing tiny
- // scrolls to produce overscroll but is inconsistent in how delta gets
- // chained up. We need to clean this up.
- if (scroll_node.scrolls_outer_viewport)
- scroll_state->ConsumeDelta(applied_delta.x(), applied_delta.y());
- return;
- }
-
- if (!viewport().ShouldScroll(scroll_node)) {
- // If the applied delta is within 45 degrees of the input
- // delta, bail out to make it easier to scroll just one layer
- // in one direction without affecting any of its parents.
- float angle_threshold = 45;
- if (MathUtil::SmallestAngleBetweenVectors(applied_delta, delta) <
- angle_threshold) {
- applied_delta = delta;
- } else {
- // Allow further movement only on an axis perpendicular to the direction
- // in which the layer moved.
- applied_delta = MathUtil::ProjectVector(delta, applied_delta);
- }
- delta_applied_to_content = applied_delta;
- }
-
- scroll_state->set_caused_scroll(
- std::abs(delta_applied_to_content.x()) > kEpsilon,
- std::abs(delta_applied_to_content.y()) > kEpsilon);
- scroll_state->ConsumeDelta(applied_delta.x(), applied_delta.y());
-}
-
-static bool CanPropagate(ScrollNode* scroll_node, float x, float y) {
- return (x == 0 || scroll_node->overscroll_behavior.x ==
- OverscrollBehavior::kOverscrollBehaviorTypeAuto) &&
- (y == 0 || scroll_node->overscroll_behavior.y ==
- OverscrollBehavior::kOverscrollBehaviorTypeAuto);
-}
-
-ScrollNode* LayerTreeHostImpl::FindNodeToLatch(ScrollState* scroll_state,
- ScrollNode* starting_node,
- ui::ScrollInputType type) {
- ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
- ScrollNode* scroll_node = nullptr;
- for (ScrollNode* cur_node = starting_node; cur_node;
- cur_node = scroll_tree.parent(cur_node)) {
- if (viewport().ShouldScroll(*cur_node)) {
- // Don't chain scrolls past a viewport node. Once we reach that, we
- // should scroll using the appropriate viewport node which may not be
- // |cur_node|.
- scroll_node = GetNodeToScroll(cur_node);
- break;
- }
-
- if (!cur_node->scrollable)
- continue;
-
- // For UX reasons, autoscrolling should always latch to the top-most
- // scroller, even if it can't scroll in the initial direction.
- if (type == ui::ScrollInputType::kAutoscroll ||
- CanConsumeDelta(*scroll_state, *cur_node)) {
- scroll_node = cur_node;
- break;
- }
-
- float delta_x = scroll_state->is_beginning() ? scroll_state->delta_x_hint()
- : scroll_state->delta_x();
- float delta_y = scroll_state->is_beginning() ? scroll_state->delta_y_hint()
- : scroll_state->delta_y();
-
- if (!CanPropagate(cur_node, delta_x, delta_y)) {
- // If we reach a node with non-auto overscroll-behavior and we still
- // haven't latched, we must latch to it. Consider a fully scrolled node
- // with non-auto overscroll-behavior: we are not allowed to further
- // chain scroll delta passed to it in the current direction but if we
- // reverse direction we should scroll it so we must be latched to it.
- scroll_node = cur_node;
- scroll_state->set_is_scroll_chain_cut(true);
- break;
- }
- }
-
- return scroll_node;
-}
-
-void LayerTreeHostImpl::DidLatchToScroller(const ScrollState& scroll_state,
- ui::ScrollInputType type) {
- DCHECK(CurrentlyScrollingNode());
- deferred_scroll_end_ = false;
- browser_controls_offset_manager_->ScrollBegin();
- mutator_host_->ScrollAnimationAbort();
-
- scroll_affects_scroll_handler_ = active_tree_->have_scroll_event_handlers();
- scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
- last_latched_scroller_ = CurrentlyScrollingNode()->element_id;
- latched_scroll_type_ = type;
- last_scroll_begin_state_ = scroll_state;
-
- client_->RenewTreePriority();
- RecordCompositorSlowScrollMetric(type, CC_THREAD);
-
- UpdateScrollSourceInfo(scroll_state, type);
-}
-
-bool LayerTreeHostImpl::CanConsumeDelta(const ScrollState& scroll_state,
- const ScrollNode& scroll_node) {
- gfx::Vector2dF delta_to_scroll;
- if (scroll_state.is_beginning()) {
- delta_to_scroll = gfx::Vector2dF(scroll_state.delta_x_hint(),
- scroll_state.delta_y_hint());
- } else {
- delta_to_scroll =
- gfx::Vector2dF(scroll_state.delta_x(), scroll_state.delta_y());
- }
-
- if (delta_to_scroll == gfx::Vector2dF())
- return true;
-
- ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
- if (scroll_state.is_direct_manipulation()) {
- gfx::Vector2dF local_scroll_delta;
- if (!CalculateLocalScrollDeltaAndStartPoint(
- scroll_node,
- gfx::PointF(scroll_state.position_x(), scroll_state.position_y()),
- delta_to_scroll, scroll_tree, &local_scroll_delta)) {
- return false;
- }
- delta_to_scroll = local_scroll_delta;
- } else {
- delta_to_scroll = ResolveScrollGranularityToPixels(
- scroll_node, delta_to_scroll, scroll_state.delta_granularity());
- }
-
- if (ComputeScrollDelta(scroll_node, delta_to_scroll) != gfx::Vector2dF())
- return true;
-
- return false;
-}
void LayerTreeHostImpl::UpdateImageDecodingHints(
base::flat_map<PaintImage::Id, PaintImage::DecodingMode>
@@ -4848,645 +3789,150 @@ void LayerTreeHostImpl::SetRenderFrameObserver(
render_frame_metadata_observer_->BindToCurrentThread();
}
-bool LayerTreeHostImpl::ShouldAnimateScroll(
- const ScrollState& scroll_state) const {
- if (!settings_.enable_smooth_scroll)
- return false;
-
- bool has_precise_scroll_deltas = scroll_state.delta_granularity() ==
- ui::ScrollGranularity::kScrollByPrecisePixel;
-
-#if defined(OS_MACOSX)
- if (has_precise_scroll_deltas)
- return false;
-
- // Mac does not smooth scroll wheel events (crbug.com/574283). We allow tests
- // to force it on.
- return latched_scroll_type_ == ui::ScrollInputType::kScrollbar
- ? true
- : force_smooth_wheel_scrolling_for_testing_;
-#else
- return !has_precise_scroll_deltas;
-#endif
-}
-
InputHandlerScrollResult LayerTreeHostImpl::ScrollUpdate(
ScrollState* scroll_state,
base::TimeDelta delayed_by) {
- DCHECK(scroll_state);
-
- // The current_native_scrolling_element should only be set for ScrollBegin.
- DCHECK(!scroll_state->data()->current_native_scrolling_element());
- TRACE_EVENT2("cc", "LayerTreeHostImpl::ScrollUpdate", "dx",
- scroll_state->delta_x(), "dy", scroll_state->delta_y());
-
- if (!CurrentlyScrollingNode())
- return InputHandlerScrollResult();
-
- last_scroll_update_state_ = *scroll_state;
-
- 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;
- }
-
- scroll_accumulated_this_frame_ +=
- gfx::Vector2dF(scroll_state->delta_x(), scroll_state->delta_y());
-
- // Flash the overlay scrollbar even if the scroll delta is 0.
- if (settings_.scrollbar_flash_after_any_scroll_update) {
- FlashAllScrollbars(false);
- } else {
- ScrollbarAnimationController* animation_controller =
- ScrollbarAnimationControllerForElementId(
- CurrentlyScrollingNode()->element_id);
- if (animation_controller)
- animation_controller->WillUpdateScroll();
- }
-
- float initial_top_controls_offset =
- browser_controls_offset_manager_->ControlsTopOffset();
-
- ScrollLatchedScroller(scroll_state, delayed_by);
-
- bool did_scroll_x = scroll_state->caused_scroll_x();
- bool did_scroll_y = scroll_state->caused_scroll_y();
- did_scroll_x_for_scroll_gesture_ |= did_scroll_x;
- did_scroll_y_for_scroll_gesture_ |= did_scroll_y;
- bool did_scroll_content = did_scroll_x || did_scroll_y;
- if (did_scroll_content) {
- ShowScrollbarsForImplScroll(CurrentlyScrollingNode()->element_id);
- client_->RenewTreePriority();
- if (!ShouldAnimateScroll(*scroll_state)) {
- // SetNeedsRedraw is only called in non-animated cases since an animation
- // won't actually update any scroll offsets until a frame produces a
- // tick. Scheduling a redraw here before ticking means the draw gets
- // aborted due to no damage and the swap promises broken so a LatencyInfo
- // won't be recorded.
- SetNeedsRedraw();
- }
- } else {
- overscroll_delta_for_main_thread_ +=
- gfx::Vector2dF(scroll_state->delta_x(), scroll_state->delta_y());
- }
-
- client_->SetNeedsCommitOnImplThread();
-
- // Scrolling along an axis resets accumulated root overscroll for that axis.
- if (did_scroll_x)
- accumulated_root_overscroll_.set_x(0);
- if (did_scroll_y)
- accumulated_root_overscroll_.set_y(0);
-
- gfx::Vector2dF unused_root_delta;
- if (viewport().ShouldScroll(*CurrentlyScrollingNode())) {
- unused_root_delta =
- gfx::Vector2dF(scroll_state->delta_x(), scroll_state->delta_y());
- }
-
- // When inner viewport is unscrollable, disable overscrolls.
- if (auto* inner_viewport_scroll_node = InnerViewportScrollNode()) {
- unused_root_delta =
- UserScrollableDelta(*inner_viewport_scroll_node, unused_root_delta);
- }
-
- accumulated_root_overscroll_ += unused_root_delta;
-
- bool did_scroll_top_controls =
- initial_top_controls_offset !=
- browser_controls_offset_manager_->ControlsTopOffset();
-
- InputHandlerScrollResult scroll_result;
- scroll_result.did_scroll = did_scroll_content || did_scroll_top_controls;
- scroll_result.did_overscroll_root = !unused_root_delta.IsZero();
- scroll_result.accumulated_root_overscroll = accumulated_root_overscroll_;
- scroll_result.unused_scroll_delta = unused_root_delta;
- scroll_result.overscroll_behavior =
- scroll_state->is_scroll_chain_cut()
- ? OverscrollBehavior(OverscrollBehavior::OverscrollBehaviorType::
- kOverscrollBehaviorTypeNone)
- : active_tree()->overscroll_behavior();
+ return input_handler_.ScrollUpdate(scroll_state, delayed_by);
+}
- if (scroll_result.did_scroll) {
- // Scrolling can change the root scroll offset, so inform the synchronous
- // input handler.
- UpdateRootLayerStateForSynchronousInputHandler();
+void LayerTreeHostImpl::DidScrollContent(bool animated) {
+ client_->RenewTreePriority();
+ if (!animated) {
+ // SetNeedsRedraw is only called in non-animated cases since an animation
+ // won't actually update any scroll offsets until a frame produces a
+ // tick. Scheduling a redraw here before ticking means the draw gets
+ // aborted due to no damage and the swap promises broken so a LatencyInfo
+ // won't be recorded.
+ SetNeedsRedraw();
}
-
- scroll_result.current_visual_offset =
- ScrollOffsetToVector2dF(GetVisualScrollOffset(*CurrentlyScrollingNode()));
- float scale_factor = active_tree()->page_scale_factor_for_scroll();
- scroll_result.current_visual_offset.Scale(scale_factor);
-
- // Run animations which need to respond to updated scroll offset.
- mutator_host_->TickScrollAnimations(
- CurrentBeginFrameArgs().frame_time,
- active_tree_->property_trees()->scroll_tree);
-
- return scroll_result;
}
-void LayerTreeHostImpl::RequestUpdateForSynchronousInputHandler() {
- UpdateRootLayerStateForSynchronousInputHandler();
+float LayerTreeHostImpl::DeviceScaleFactor() const {
+ return active_tree_->device_scale_factor();
}
-static gfx::Vector2dF ContentToPhysical(const gfx::Vector2dF& content_delta,
- float page_scale_factor) {
- gfx::Vector2dF physical_delta = content_delta;
- physical_delta.Scale(page_scale_factor);
- return physical_delta;
+void LayerTreeHostImpl::RequestUpdateForSynchronousInputHandler() {
+ input_handler_.RequestUpdateForSynchronousInputHandler();
}
void LayerTreeHostImpl::SetSynchronousInputHandlerRootScrollOffset(
const gfx::ScrollOffset& root_content_offset) {
- TRACE_EVENT2(
- "cc", "LayerTreeHostImpl::SetSynchronousInputHandlerRootScrollOffset",
- "offset_x", root_content_offset.x(), "offset_y", root_content_offset.y());
-
- gfx::Vector2dF physical_delta = ContentToPhysical(
- root_content_offset.DeltaFrom(viewport().TotalScrollOffset()),
- active_tree()->page_scale_factor_for_scroll());
-
- bool changed = !viewport()
- .ScrollBy(physical_delta,
- /*viewport_point=*/gfx::Point(),
- /*is_direct_manipulation=*/false,
- /*affect_browser_controls=*/false,
- /*scroll_outer_viewport=*/true)
- .consumed_delta.IsZero();
- if (!changed)
- return;
-
- ShowScrollbarsForImplScroll(OuterViewportScrollNode()->element_id);
- client_->SetNeedsCommitOnImplThread();
- // After applying the synchronous input handler's scroll offset, tell it what
- // we ended up with.
- UpdateRootLayerStateForSynchronousInputHandler();
- SetFullViewportDamage();
- SetNeedsRedraw();
-}
-
-bool LayerTreeHostImpl::SnapAtScrollEnd() {
- ScrollNode* scroll_node = CurrentlyScrollingNode();
- if (!scroll_node || !scroll_node->snap_container_data.has_value())
- return false;
-
- SnapContainerData& data = scroll_node->snap_container_data.value();
- gfx::ScrollOffset current_position = GetVisualScrollOffset(*scroll_node);
-
- // You might think that if a scroll never received a scroll update we could
- // just drop the snap. However, if the GSB+GSE arrived while we were mid-snap
- // from a previous gesture, this would leave the scroller at a
- // non-snap-point.
- DCHECK(last_scroll_update_state_ || last_scroll_begin_state_);
- ScrollState& last_scroll_state = last_scroll_update_state_
- ? *last_scroll_update_state_
- : *last_scroll_begin_state_;
-
- bool imprecise_wheel_scrolling =
- latched_scroll_type_ == ui::ScrollInputType::kWheel &&
- last_scroll_state.delta_granularity() !=
- ui::ScrollGranularity::kScrollByPrecisePixel;
- gfx::ScrollOffset last_scroll_delta = last_scroll_state.DeltaOrHint();
-
- std::unique_ptr<SnapSelectionStrategy> strategy;
-
- if (imprecise_wheel_scrolling && !last_scroll_delta.IsZero()) {
- // This was an imprecise wheel scroll so use direction snapping.
- strategy = SnapSelectionStrategy::CreateForDirection(
- current_position, last_scroll_delta, true);
- } else {
- strategy = SnapSelectionStrategy::CreateForEndPosition(
- current_position, did_scroll_x_for_scroll_gesture_,
- did_scroll_y_for_scroll_gesture_);
- }
-
- gfx::ScrollOffset snap_position;
- TargetSnapAreaElementIds snap_target_ids;
- if (!data.FindSnapPosition(*strategy, &snap_position, &snap_target_ids))
- return false;
-
- if (viewport().ShouldScroll(*scroll_node)) {
- // Flash the overlay scrollbar even if the scroll delta is 0.
- if (settings_.scrollbar_flash_after_any_scroll_update) {
- FlashAllScrollbars(false);
- } else {
- ScrollbarAnimationController* animation_controller =
- ScrollbarAnimationControllerForElementId(scroll_node->element_id);
- if (animation_controller)
- animation_controller->WillUpdateScroll();
- }
- }
-
- gfx::Vector2dF delta =
- ScrollOffsetToVector2dF(snap_position - current_position);
- bool did_animate = false;
- if (scroll_node->scrolls_outer_viewport) {
- gfx::Vector2dF scaled_delta(delta);
- scaled_delta.Scale(active_tree()->page_scale_factor_for_scroll());
- gfx::Vector2dF consumed_delta =
- viewport().ScrollAnimated(scaled_delta, base::TimeDelta());
- did_animate = !consumed_delta.IsZero();
- } else {
- did_animate = ScrollAnimationCreate(*scroll_node, delta, base::TimeDelta());
- }
- DCHECK(!IsAnimatingForSnap());
- if (did_animate) {
- // The snap target will be set when the animation is completed.
- scroll_animating_snap_target_ids_ = snap_target_ids;
- } else if (data.SetTargetSnapAreaElementIds(snap_target_ids)) {
- updated_snapped_elements_.insert(scroll_node->element_id);
- client_->SetNeedsCommitOnImplThread();
- }
- return did_animate;
-}
-
-bool LayerTreeHostImpl::IsAnimatingForSnap() const {
- return scroll_animating_snap_target_ids_ != TargetSnapAreaElementIds();
-}
-
-gfx::ScrollOffset LayerTreeHostImpl::GetVisualScrollOffset(
- const ScrollNode& scroll_node) const {
- if (scroll_node.scrolls_outer_viewport)
- return viewport().TotalScrollOffset();
- return active_tree()->property_trees()->scroll_tree.current_scroll_offset(
- scroll_node.element_id);
+ input_handler_.SetSynchronousInputHandlerRootScrollOffset(
+ root_content_offset);
}
bool LayerTreeHostImpl::GetSnapFlingInfoAndSetAnimatingSnapTarget(
const gfx::Vector2dF& natural_displacement_in_viewport,
gfx::Vector2dF* out_initial_position,
gfx::Vector2dF* out_target_position) {
- ScrollNode* scroll_node = CurrentlyScrollingNode();
- if (!scroll_node || !scroll_node->snap_container_data.has_value())
- return false;
- const SnapContainerData& data = scroll_node->snap_container_data.value();
-
- float scale_factor = active_tree()->page_scale_factor_for_scroll();
- gfx::Vector2dF natural_displacement_in_content =
- gfx::ScaleVector2d(natural_displacement_in_viewport, 1.f / scale_factor);
-
- gfx::ScrollOffset current_offset = GetVisualScrollOffset(*scroll_node);
- *out_initial_position = ScrollOffsetToVector2dF(current_offset);
-
- // CC side always uses fractional scroll deltas.
- bool use_fractional_offsets = true;
- gfx::ScrollOffset snap_offset;
- TargetSnapAreaElementIds snap_target_ids;
- std::unique_ptr<SnapSelectionStrategy> strategy =
- SnapSelectionStrategy::CreateForEndAndDirection(
- current_offset, gfx::ScrollOffset(natural_displacement_in_content),
- use_fractional_offsets);
- if (!data.FindSnapPosition(*strategy, &snap_offset, &snap_target_ids))
- return false;
- scroll_animating_snap_target_ids_ = snap_target_ids;
-
- *out_target_position = ScrollOffsetToVector2dF(snap_offset);
- out_target_position->Scale(scale_factor);
- out_initial_position->Scale(scale_factor);
- return true;
+ return input_handler_.GetSnapFlingInfoAndSetAnimatingSnapTarget(
+ natural_displacement_in_viewport, out_initial_position,
+ out_target_position);
}
void LayerTreeHostImpl::ScrollEndForSnapFling(bool did_finish) {
- ScrollNode* scroll_node = CurrentlyScrollingNode();
- // When a snap fling animation reaches its intended target then we update the
- // scrolled node's snap targets. This also ensures blink learns about the new
- // snap targets for this scrolling element.
- if (did_finish && scroll_node &&
- scroll_node->snap_container_data.has_value()) {
- scroll_node->snap_container_data.value().SetTargetSnapAreaElementIds(
- scroll_animating_snap_target_ids_);
- updated_snapped_elements_.insert(scroll_node->element_id);
- client_->SetNeedsCommitOnImplThread();
- }
- scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
- ScrollEnd(false /* should_snap */);
-}
-
-void LayerTreeHostImpl::ClearCurrentlyScrollingNode() {
- TRACE_EVENT0("cc", "LayerTreeHostImpl::ClearCurrentlyScrollingNode");
- active_tree_->ClearCurrentlyScrollingNode();
- scroll_affects_scroll_handler_ = false;
- accumulated_root_overscroll_ = gfx::Vector2dF();
- did_scroll_x_for_scroll_gesture_ = false;
- did_scroll_y_for_scroll_gesture_ = false;
- scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
- latched_scroll_type_.reset();
- last_scroll_update_state_.reset();
- last_scroll_begin_state_.reset();
+ input_handler_.ScrollEndForSnapFling(did_finish);
}
void LayerTreeHostImpl::ScrollEnd(bool should_snap) {
- scrollbar_controller_->ResetState();
- if (!CurrentlyScrollingNode())
- return;
-
- // Note that if we deferred the scroll end then we should not snap. We will
- // snap once we deliver the deferred scroll end.
- if (mutator_host_->ImplOnlyScrollAnimatingElement()) {
- DCHECK(!deferred_scroll_end_);
- deferred_scroll_end_ = true;
- return;
- }
-
- if (should_snap && SnapAtScrollEnd()) {
- deferred_scroll_end_ = true;
- return;
- }
-
- DCHECK(latched_scroll_type_.has_value());
-
- browser_controls_offset_manager_->ScrollEnd();
-
- ClearCurrentlyScrollingNode();
- deferred_scroll_end_ = false;
- scroll_gesture_did_end_ = true;
- client_->SetNeedsCommitOnImplThread();
+ input_handler_.ScrollEnd(should_snap);
}
void LayerTreeHostImpl::RecordScrollBegin(
ui::ScrollInputType input_type,
ScrollBeginThreadState scroll_start_state) {
- auto tracker_type = GetTrackerTypeForScroll(input_type);
- DCHECK_NE(tracker_type, FrameSequenceTrackerType::kMaxType);
-
- // The main-thread is the 'scrolling thread' if:
- // (1) the scroll is driven by the main thread, or
- // (2) the scroll is driven by the compositor, but blocked on the main
- // thread.
- // Otherwise, the compositor-thread is the 'scrolling thread'.
- // TODO(crbug.com/1060712): We should also count 'main thread' as the
- // 'scrolling thread' if the layer being scrolled has scroll-event handlers.
- FrameSequenceMetrics::ThreadType scrolling_thread;
- switch (scroll_start_state) {
- case ScrollBeginThreadState::kScrollingOnCompositor:
- scrolling_thread = FrameSequenceMetrics::ThreadType::kCompositor;
- break;
- case ScrollBeginThreadState::kScrollingOnMain:
- case ScrollBeginThreadState::kScrollingOnCompositorBlockedOnMain:
- scrolling_thread = FrameSequenceMetrics::ThreadType::kMain;
- break;
- }
- frame_trackers_.StartScrollSequence(tracker_type, scrolling_thread);
+ input_handler_.RecordScrollBegin(input_type, scroll_start_state);
}
void LayerTreeHostImpl::RecordScrollEnd(ui::ScrollInputType input_type) {
- frame_trackers_.StopSequence(GetTrackerTypeForScroll(input_type));
+ input_handler_.RecordScrollEnd(input_type);
}
InputHandlerPointerResult LayerTreeHostImpl::MouseDown(
const gfx::PointF& viewport_point,
bool shift_modifier) {
- ScrollbarAnimationController* animation_controller =
- ScrollbarAnimationControllerForElementId(
- scroll_element_id_mouse_currently_over_);
- if (animation_controller) {
- animation_controller->DidMouseDown();
- scroll_element_id_mouse_currently_captured_ =
- scroll_element_id_mouse_currently_over_;
- }
-
- InputHandlerPointerResult result;
- if (settings().compositor_threaded_scrollbar_scrolling) {
- result = scrollbar_controller_->HandlePointerDown(viewport_point,
- shift_modifier);
- }
-
- return result;
+ return input_handler_.MouseDown(viewport_point, shift_modifier);
}
InputHandlerPointerResult LayerTreeHostImpl::MouseUp(
const gfx::PointF& viewport_point) {
- if (scroll_element_id_mouse_currently_captured_) {
- ScrollbarAnimationController* animation_controller =
- ScrollbarAnimationControllerForElementId(
- scroll_element_id_mouse_currently_captured_);
-
- scroll_element_id_mouse_currently_captured_ = ElementId();
-
- if (animation_controller)
- animation_controller->DidMouseUp();
- }
-
- InputHandlerPointerResult result;
- if (settings().compositor_threaded_scrollbar_scrolling)
- result = scrollbar_controller_->HandlePointerUp(viewport_point);
-
- return result;
+ return input_handler_.MouseUp(viewport_point);
}
InputHandlerPointerResult LayerTreeHostImpl::MouseMoveAt(
const gfx::Point& viewport_point) {
- InputHandlerPointerResult result;
- if (settings().compositor_threaded_scrollbar_scrolling) {
- result =
- scrollbar_controller_->HandlePointerMove(gfx::PointF(viewport_point));
- }
-
- // Early out if there are no animation controllers and avoid the hit test.
- // This happens on platforms without animated scrollbars.
- if (scrollbar_animation_controllers_.empty())
- return result;
-
- gfx::PointF device_viewport_point = gfx::ScalePoint(
- gfx::PointF(viewport_point), active_tree_->device_scale_factor());
-
- 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 (!hit_test.hit_test_successful || !scroll_node)
- return result;
-
- // Scrollbars for the viewport are registered with the outer viewport layer.
- if (scroll_node->scrolls_inner_viewport)
- scroll_node = OuterViewportScrollNode();
-
- ElementId scroll_element_id = scroll_node->element_id;
- ScrollbarAnimationController* new_animation_controller =
- ScrollbarAnimationControllerForElementId(scroll_element_id);
- if (scroll_element_id != scroll_element_id_mouse_currently_over_) {
- ScrollbarAnimationController* old_animation_controller =
- ScrollbarAnimationControllerForElementId(
- scroll_element_id_mouse_currently_over_);
- if (old_animation_controller)
- old_animation_controller->DidMouseLeave();
-
- scroll_element_id_mouse_currently_over_ = scroll_element_id;
-
- // Experiment: Enables will flash scrollbar when user move mouse enter a
- // scrollable area.
- if (settings_.scrollbar_flash_when_mouse_enter && new_animation_controller)
- new_animation_controller->DidScrollUpdate();
- }
-
- if (!new_animation_controller)
- return result;
-
- new_animation_controller->DidMouseMove(device_viewport_point);
+ return input_handler_.MouseMoveAt(viewport_point);
+}
- return result;
+bool LayerTreeHostImpl::HasAnimatedScrollbars() const {
+ return !scrollbar_animation_controllers_.empty();
}
void LayerTreeHostImpl::MouseLeave() {
- for (auto& pair : scrollbar_animation_controllers_)
- pair.second->DidMouseLeave();
- scroll_element_id_mouse_currently_over_ = ElementId();
+ input_handler_.MouseLeave();
}
ElementId LayerTreeHostImpl::FindFrameElementIdAtPoint(
const gfx::PointF& viewport_point) {
- gfx::PointF device_viewport_point = gfx::ScalePoint(
- gfx::PointF(viewport_point), active_tree_->device_scale_factor());
- return active_tree_->FindFrameElementIdAtPoint(device_viewport_point);
+ return input_handler_.FindFrameElementIdAtPoint(viewport_point);
}
void LayerTreeHostImpl::PinchGestureBegin() {
- pinch_gesture_active_ = true;
- client_->RenewTreePriority();
- pinch_gesture_end_should_clear_scrolling_node_ = !CurrentlyScrollingNode();
-
- TRACE_EVENT_INSTANT1("cc", "SetCurrentlyScrollingNode PinchGestureBegin",
- TRACE_EVENT_SCOPE_THREAD, "isNull",
- OuterViewportScrollNode() ? false : true);
- active_tree_->SetCurrentlyScrollingNode(OuterViewportScrollNode());
- browser_controls_offset_manager_->PinchBegin();
- frame_trackers_.StartSequence(FrameSequenceTrackerType::kPinchZoom);
+ input_handler_.PinchGestureBegin();
}
void LayerTreeHostImpl::PinchGestureUpdate(float magnify_delta,
const gfx::Point& anchor) {
- TRACE_EVENT0("cc", "LayerTreeHostImpl::PinchGestureUpdate");
- if (!InnerViewportScrollNode())
- return;
- has_pinch_zoomed_ = true;
- viewport().PinchUpdate(magnify_delta, anchor);
- client_->SetNeedsCommitOnImplThread();
- SetNeedsRedraw();
- client_->RenewTreePriority();
- // Pinching can change the root scroll offset, so inform the synchronous input
- // handler.
- UpdateRootLayerStateForSynchronousInputHandler();
+ input_handler_.PinchGestureUpdate(magnify_delta, anchor);
}
void LayerTreeHostImpl::PinchGestureEnd(const gfx::Point& anchor,
bool snap_to_min) {
- pinch_gesture_active_ = false;
- if (pinch_gesture_end_should_clear_scrolling_node_) {
- pinch_gesture_end_should_clear_scrolling_node_ = false;
- ClearCurrentlyScrollingNode();
- }
- viewport().PinchEnd(anchor, snap_to_min);
- browser_controls_offset_manager_->PinchEnd();
- client_->SetNeedsCommitOnImplThread();
- // When a pinch ends, we may be displaying content cached at incorrect scales,
- // so updating draw properties and drawing will ensure we are using the right
- // scales that we want when we're not inside a pinch.
- active_tree_->set_needs_update_draw_properties();
- SetNeedsRedraw();
- frame_trackers_.StopSequence(FrameSequenceTrackerType::kPinchZoom);
-}
-
-void LayerTreeHostImpl::CollectScrollDeltas(ScrollAndScaleSet* scroll_info) {
- if (active_tree_->LayerListIsEmpty())
- return;
-
- ElementId inner_viewport_scroll_element_id =
- InnerViewportScrollNode() ? InnerViewportScrollNode()->element_id
- : ElementId();
-
- active_tree_->property_trees()->scroll_tree.CollectScrollDeltas(
- scroll_info, inner_viewport_scroll_element_id,
- active_tree_->settings().commit_fractional_scroll_deltas,
- updated_snapped_elements_);
- updated_snapped_elements_.clear();
+ input_handler_.PinchGestureEnd(anchor, snap_to_min);
}
-void LayerTreeHostImpl::CollectScrollbarUpdates(
- ScrollAndScaleSet* scroll_info) const {
- scroll_info->scrollbars.reserve(scrollbar_animation_controllers_.size());
+void LayerTreeHostImpl::CollectScrollbarUpdatesForCommit(
+ CompositorCommitData* commit_data) const {
+ commit_data->scrollbars.reserve(scrollbar_animation_controllers_.size());
for (auto& pair : scrollbar_animation_controllers_) {
- scroll_info->scrollbars.push_back(
+ commit_data->scrollbars.push_back(
{pair.first, pair.second->ScrollbarsHidden()});
}
}
-std::unique_ptr<ScrollAndScaleSet> LayerTreeHostImpl::ProcessScrollDeltas() {
- auto scroll_info = std::make_unique<ScrollAndScaleSet>();
+std::unique_ptr<CompositorCommitData>
+LayerTreeHostImpl::ProcessCompositorDeltas() {
+ auto commit_data = std::make_unique<CompositorCommitData>();
+
+ input_delegate_->ProcessCommitDeltas(commit_data.get());
+ CollectScrollbarUpdatesForCommit(commit_data.get());
- CollectScrollDeltas(scroll_info.get());
- CollectScrollbarUpdates(scroll_info.get());
- scroll_info->page_scale_delta =
+ commit_data->page_scale_delta =
active_tree_->page_scale_factor()->PullDeltaForMainThread();
- scroll_info->is_pinch_gesture_active = active_tree_->PinchGestureActive();
+ commit_data->is_pinch_gesture_active = active_tree_->PinchGestureActive();
// We should never process non-unit page_scale_delta for an OOPIF subframe.
// TODO(wjmaclean): Remove this DCHECK as a pre-condition to closing the bug.
// https://crbug.com/845097
DCHECK(!settings().is_layer_tree_for_subframe ||
- scroll_info->page_scale_delta == 1.f);
- scroll_info->top_controls_delta =
+ commit_data->page_scale_delta == 1.f);
+ commit_data->top_controls_delta =
active_tree()->top_controls_shown_ratio()->PullDeltaForMainThread();
- scroll_info->bottom_controls_delta =
+ commit_data->bottom_controls_delta =
active_tree()->bottom_controls_shown_ratio()->PullDeltaForMainThread();
- scroll_info->elastic_overscroll_delta =
+ commit_data->elastic_overscroll_delta =
active_tree_->elastic_overscroll()->PullDeltaForMainThread();
- scroll_info->swap_promises.swap(swap_promises_for_main_thread_scroll_update_);
+ commit_data->swap_promises.swap(swap_promises_for_main_thread_scroll_update_);
- // Record and reset scroll source flags.
- if (has_scrolled_by_wheel_) {
- scroll_info->manipulation_info |= kManipulationInfoHasScrolledByWheel;
- }
- if (has_scrolled_by_touch_) {
- scroll_info->manipulation_info |= kManipulationInfoHasScrolledByTouch;
- }
- if (has_scrolled_by_precisiontouchpad_) {
- scroll_info->manipulation_info |=
- kManipulationInfoHasScrolledByPrecisionTouchPad;
- }
- if (has_pinch_zoomed_) {
- scroll_info->manipulation_info |= kManipulationInfoHasPinchZoomed;
- }
-
- has_scrolled_by_wheel_ = has_scrolled_by_touch_ =
- has_scrolled_by_precisiontouchpad_ = has_pinch_zoomed_ = false;
- scroll_info->scroll_gesture_did_end = scroll_gesture_did_end_;
-
- // Record and reset overscroll delta.
- scroll_info->overscroll_delta = overscroll_delta_for_main_thread_;
- overscroll_delta_for_main_thread_ = gfx::Vector2dF();
-
- scroll_info->ongoing_scroll_animation =
+ commit_data->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.
- scroll_info->scroll_latched_element_id = last_latched_scroller_;
- if (scroll_gesture_did_end_)
- last_latched_scroller_ = ElementId();
-
if (browser_controls_manager()) {
- scroll_info->browser_controls_constraint =
+ commit_data->browser_controls_constraint =
browser_controls_manager()->PullConstraintForMainThread(
- &scroll_info->browser_controls_constraint_changed);
+ &commit_data->browser_controls_constraint_changed);
}
- scroll_gesture_did_end_ = false;
- return scroll_info;
+ return commit_data;
}
void LayerTreeHostImpl::SetFullViewportDamage() {
@@ -5630,8 +4076,10 @@ void LayerTreeHostImpl::RegisterScrollbarAnimationController(
void LayerTreeHostImpl::DidUnregisterScrollbarLayer(
ElementId scroll_element_id,
ScrollbarOrientation orientation) {
- scrollbar_animation_controllers_.erase(scroll_element_id);
- scrollbar_controller_->DidUnregisterScrollbar(scroll_element_id, orientation);
+ if (ScrollbarsFor(scroll_element_id).empty())
+ scrollbar_animation_controllers_.erase(scroll_element_id);
+ if (input_delegate_)
+ input_delegate_->DidUnregisterScrollbar(scroll_element_id, orientation);
}
ScrollbarAnimationController*
@@ -5895,7 +4343,8 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid,
layer_tree_frame_sink_->context_provider();
auto* sii = context_provider->SharedImageInterface();
mailbox = sii->CreateSharedImage(
- format, upload_size, color_space, shared_image_usage,
+ format, upload_size, color_space, kTopLeft_GrSurfaceOrigin,
+ kPremul_SkAlphaType, shared_image_usage,
base::span<const uint8_t>(bitmap.GetPixels(), bitmap.SizeInBytes()));
} else {
DCHECK_EQ(bitmap.GetFormat(), UIResourceBitmap::RGBA8);
@@ -5959,7 +4408,8 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid,
layer_tree_frame_sink_->context_provider();
auto* sii = context_provider->SharedImageInterface();
mailbox = sii->CreateSharedImage(
- format, upload_size, color_space, shared_image_usage,
+ format, upload_size, color_space, kTopLeft_GrSurfaceOrigin,
+ kPremul_SkAlphaType, shared_image_usage,
base::span<const uint8_t>(
reinterpret_cast<const uint8_t*>(pixmap.addr()),
pixmap.computeByteSize()));
@@ -6143,54 +4593,6 @@ void LayerTreeHostImpl::NotifySwapPromiseMonitorsOfSetNeedsRedraw() {
(*it)->OnSetNeedsRedrawOnImpl();
}
-void LayerTreeHostImpl::UpdateRootLayerStateForSynchronousInputHandler() {
- if (!input_handler_client_)
- return;
- input_handler_client_->UpdateRootLayerStateForSynchronousInputHandler(
- active_tree_->TotalScrollOffset(), active_tree_->TotalMaxScrollOffset(),
- active_tree_->ScrollableSize(), active_tree_->current_page_scale_factor(),
- active_tree_->min_page_scale_factor(),
- active_tree_->max_page_scale_factor());
-}
-
-bool LayerTreeHostImpl::ScrollAnimationUpdateTarget(
- const ScrollNode& scroll_node,
- const gfx::Vector2dF& scroll_delta,
- base::TimeDelta delayed_by) {
- // TODO(bokan): Remove |scroll_node| as a parameter and just use the value
- // coming from |mutator_host|.
- DCHECK_EQ(scroll_node.element_id,
- mutator_host_->ImplOnlyScrollAnimatingElement());
-
- float scale_factor = active_tree()->page_scale_factor_for_scroll();
- gfx::Vector2dF adjusted_delta =
- gfx::ScaleVector2d(scroll_delta, 1.f / scale_factor);
- adjusted_delta = UserScrollableDelta(scroll_node, adjusted_delta);
-
- bool animation_updated = mutator_host_->ImplOnlyScrollAnimationUpdateTarget(
- adjusted_delta,
- active_tree_->property_trees()->scroll_tree.MaxScrollOffset(
- scroll_node.id),
- CurrentBeginFrameArgs().frame_time, delayed_by);
- if (animation_updated) {
- // Because we updated the animation target, notify the SwapPromiseMonitor
- // to tell it that something happened that will cause a swap in the future.
- // This will happen within the scope of the dispatch of a gesture scroll
- // update input event. If we don't notify during the handling of the input
- // event, the LatencyInfo associated with the input event will not be
- // added as a swap promise and we won't get any swap results.
- NotifySwapPromiseMonitorsOfSetNeedsRedraw();
- events_metrics_manager_.SaveActiveEventMetrics();
-
- // The animation is no longer targeting a snap position. By clearing the
- // target, this will ensure that we attempt to resnap at the end of this
- // animation.
- scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
- }
-
- return animation_updated;
-}
-
bool LayerTreeHostImpl::IsElementInPropertyTrees(
ElementId element_id,
ElementListType list_type) const {
@@ -6330,32 +4732,7 @@ void LayerTreeHostImpl::AnimationScalesChanged(ElementId element_id,
}
void LayerTreeHostImpl::ScrollOffsetAnimationFinished() {
- TRACE_EVENT0("cc", "LayerTreeHostImpl::ScrollOffsetAnimationFinished");
- // ScrollOffsetAnimationFinished is called in two cases:
- // 1- smooth scrolling animation is over (IsAnimatingForSnap == false).
- // 2- snap scroll animation is over (IsAnimatingForSnap == true).
- //
- // Only for case (1) we should check and run snap scroll animation if needed.
- if (!IsAnimatingForSnap() && SnapAtScrollEnd())
- return;
-
- // The end of a scroll offset animation means that the scrolling node is at
- // the target offset.
- ScrollNode* scroll_node = CurrentlyScrollingNode();
- if (scroll_node && scroll_node->snap_container_data.has_value()) {
- scroll_node->snap_container_data.value().SetTargetSnapAreaElementIds(
- scroll_animating_snap_target_ids_);
- updated_snapped_elements_.insert(scroll_node->element_id);
- client_->SetNeedsCommitOnImplThread();
- }
- scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
-
- // Call scrollEnd with the deferred scroll end state when the scroll animation
- // completes after GSE arrival.
- if (deferred_scroll_end_) {
- ScrollEnd(/*should_snap=*/false);
- return;
- }
+ input_delegate_->ScrollOffsetAnimationFinished();
}
void LayerTreeHostImpl::NotifyAnimationWorkletStateChange(
@@ -6426,19 +4803,6 @@ void LayerTreeHostImpl::SetContextVisibility(bool is_visible) {
}
}
-void LayerTreeHostImpl::UpdateScrollSourceInfo(const ScrollState& scroll_state,
- ui::ScrollInputType type) {
- if (type == ui::ScrollInputType::kWheel &&
- scroll_state.delta_granularity() ==
- ui::ScrollGranularity::kScrollByPrecisePixel) {
- has_scrolled_by_precisiontouchpad_ = true;
- } else if (type == ui::ScrollInputType::kWheel) {
- has_scrolled_by_wheel_ = true;
- } else if (type == ui::ScrollInputType::kTouchscreen) {
- has_scrolled_by_touch_ = true;
- }
-}
-
void LayerTreeHostImpl::ShowScrollbarsForImplScroll(ElementId element_id) {
if (settings_.scrollbar_flash_after_any_scroll_update) {
FlashAllScrollbars(true);
@@ -6472,6 +4836,7 @@ void LayerTreeHostImpl::SetActiveURL(const GURL& url, ukm::SourceId source_id) {
// The source id has already been associated to the URL.
ukm_manager_->SetSourceId(source_id);
}
+ total_frame_counter_.Reset();
}
void LayerTreeHostImpl::AllocateLocalSurfaceId() {
diff --git a/chromium/cc/trees/layer_tree_host_impl.h b/chromium/cc/trees/layer_tree_host_impl.h
index 47dcba6decb..9a2ddbcc917 100644
--- a/chromium/cc/trees/layer_tree_host_impl.h
+++ b/chromium/cc/trees/layer_tree_host_impl.h
@@ -9,6 +9,7 @@
#include <memory>
#include <set>
+#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
@@ -28,11 +29,14 @@
#include "cc/input/input_handler.h"
#include "cc/input/scrollbar_animation_controller.h"
#include "cc/input/scrollbar_controller.h"
+#include "cc/input/threaded_input_handler.h"
#include "cc/layers/layer_collections.h"
+#include "cc/metrics/average_lag_tracking_manager.h"
#include "cc/metrics/dropped_frame_counter.h"
#include "cc/metrics/event_metrics.h"
#include "cc/metrics/events_metrics_manager.h"
#include "cc/metrics/frame_sequence_tracker_collection.h"
+#include "cc/metrics/total_frame_counter.h"
#include "cc/paint/discardable_image_map.h"
#include "cc/paint/paint_worklet_job.h"
#include "cc/resources/ui_resource_client.h"
@@ -84,6 +88,7 @@ class BrowserControlsOffsetManager;
class CompositorFrameReportingController;
class DebugRectHistory;
class EvictionTilePriorityQueue;
+class DroppedFrameCounter;
class ImageAnimationController;
class LCDTextMetricsReporter;
class LayerImpl;
@@ -274,6 +279,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
LayerTreeHostImpl& operator=(const LayerTreeHostImpl&) = delete;
+ ThreadedInputHandler& GetInputHandler();
+
// InputHandler implementation
void BindToClient(InputHandlerClient* client) override;
InputHandler::ScrollStatus ScrollBegin(ScrollState* scroll_state,
@@ -298,7 +305,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
const gfx::Point& viewport_point) override;
void MouseLeave() override;
- // Returns frame_element_id from the layer hit by the given point.
+ // Returns visible_frame_element_id from the layer hit by the given point.
// If the hit test failed, an invalid element ID is returned.
ElementId FindFrameElementIdAtPoint(
const gfx::PointF& viewport_point) override;
@@ -343,9 +350,11 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
float bottom_ratio) override;
float CurrentTopControlsShownRatio() const override;
float CurrentBottomControlsShownRatio() const override;
+ gfx::ScrollOffset ViewportScrollOffset() const override;
void DidChangeBrowserControlsPosition() override;
void DidObserveScrollDelay(base::TimeDelta scroll_delay,
base::TimeTicks scroll_timestamp);
+ bool OnlyExpandTopControlsAtPageTop() const override;
bool HaveRootScrollNode() const override;
void SetNeedsCommit() override;
@@ -384,6 +393,28 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
void DidAnimateScrollOffset();
void SetFullViewportDamage();
void SetViewportDamage(const gfx::Rect& damage_rect);
+ void SetEnableFrameRateThrottling(bool enable_frame_rate_throttling);
+
+ // Interface for ThreadedInputHandler
+ bool HasAnimatedScrollbars() const;
+ void SetNeedsCommitInputChanges();
+ void DidUpdateScrollAnimationCurve();
+ void AccumulateScrollDeltaForTracing(const gfx::Vector2dF& delta);
+ void DidStartPinchZoom();
+ void DidEndPinchZoom();
+ void DidUpdatePinchZoom();
+ void DidStartScroll();
+ void DidSetRootScrollOffsetForSynchronousInputHandler();
+ FrameSequenceTrackerCollection& frame_trackers() { return frame_trackers_; }
+ ImplThreadPhase GetCompositorThreadPhase() const;
+ std::unordered_map<ElementId,
+ std::unique_ptr<ScrollbarAnimationController>,
+ ElementIdHash>&
+ get_scrollbar_animation_controllers() {
+ return scrollbar_animation_controllers_;
+ }
+ void DidScrollContent(bool animated);
+ float DeviceScaleFactor() const;
// Updates registered ElementIds present in |changed_list|. Call this after
// changing the property trees for the |changed_list| trees.
@@ -475,7 +506,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
void ResetTreesForTesting();
void set_force_smooth_wheel_scrolling_for_testing(bool enabled) {
- force_smooth_wheel_scrolling_for_testing_ = enabled;
+ input_handler_.set_force_smooth_wheel_scrolling_for_testing(enabled);
}
size_t SourceAnimationFrameNumberForTesting() const;
@@ -505,6 +536,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
void SetIsLikelyToRequireADraw(bool is_likely_to_require_a_draw) override;
gfx::ColorSpace GetRasterColorSpace(
gfx::ContentColorUsage content_color_usage) const override;
+ float GetSDRWhiteLevel() const override;
void RequestImplSideInvalidationForCheckerImagedTiles() override;
size_t GetFrameIndexForImage(const PaintImage& paint_image,
WhichTree tree) const override;
@@ -565,7 +597,10 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
int* max_msaa_samples,
bool* supports_disable_msaa);
bool use_gpu_rasterization() const { return use_gpu_rasterization_; }
- bool use_oop_rasterization() const { return use_oop_rasterization_; }
+ bool can_use_oop_rasterization() const { return can_use_oop_rasterization_; }
+ bool use_oop_rasterization() const {
+ return use_gpu_rasterization_ && can_use_oop_rasterization_;
+ }
GpuRasterizationStatus gpu_rasterization_status() const {
return gpu_rasterization_status_;
@@ -623,31 +658,16 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
ScrollNode* CurrentlyScrollingNode();
const ScrollNode* CurrentlyScrollingNode() const;
- bool scroll_affects_scroll_handler() const {
- return settings_.enable_synchronized_scrolling &&
- scroll_affects_scroll_handler_;
- }
void QueueSwapPromiseForMainThreadScrollUpdate(
std::unique_ptr<SwapPromise> swap_promise);
- // Returns true if there is an active scroll in progress. "Active" here
- // means that it's been latched (i.e. we have a CurrentlyScrollingNode()) but
- // also that some ScrollUpdates have been received and their delta consumed
- // for scrolling. These can differ significantly e.g. the page allows the
- // touchstart but preventDefaults all the touchmoves. In that case, we latch
- // and have a CurrentlyScrollingNode() but will never receive a ScrollUpdate.
- //
- // "Precision" means it's a non-animated scroll like a touchscreen or
- // high-precision touchpad. The latter distinction is important for things
- // like scheduling decisions which might schedule a wheel and a touch
- // scrolling differently due to user perception.
+ // See comment in equivalent ThreadedInputHandler method for what this means.
bool IsActivelyPrecisionScrolling() const;
+ bool ScrollAffectsScrollHandler() const;
virtual void SetVisible(bool visible);
bool visible() const { return visible_; }
- bool IsAnimatingForSnap() const;
-
void SetNeedsOneBeginImplFrame();
void SetNeedsRedraw();
@@ -655,7 +675,10 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
const gfx::Transform& DrawTransform() const;
- std::unique_ptr<ScrollAndScaleSet> ProcessScrollDeltas();
+ // During commit, processes and returns changes in the compositor since the
+ // last commit.
+ std::unique_ptr<CompositorCommitData> ProcessCompositorDeltas();
+
DroppedFrameCounter* dropped_frame_counter() {
return &dropped_frame_counter_;
}
@@ -676,32 +699,10 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
}
MutatorHost* mutator_host() const { return mutator_host_.get(); }
- ScrollbarController* scrollbar_controller_for_testing() const {
- return scrollbar_controller_.get();
- }
void SetDebugState(const LayerTreeDebugState& new_debug_state);
const LayerTreeDebugState& debug_state() const { return debug_state_; }
- gfx::Vector2dF accumulated_root_overscroll() const {
- return accumulated_root_overscroll_;
- }
-
- bool pinch_gesture_active() const {
- return pinch_gesture_active_ || external_pinch_gesture_active_;
- }
- // Used to set the pinch gesture active state when the pinch gesture is
- // handled on another layer tree. In a page with OOPIFs, only the main
- // frame's layer tree directly handles pinch events. But layer trees for
- // sub-frames need to know when pinch gestures are active so they can
- // throttle the re-rastering. This function allows setting this flag on
- // OOPIF layer trees using information sent (initially) from the main-frame.
- void set_external_pinch_gesture_active(bool external_pinch_gesture_active) {
- external_pinch_gesture_active_ = external_pinch_gesture_active;
- // Only one of the flags should ever be true at any given time.
- DCHECK(!pinch_gesture_active_ || !external_pinch_gesture_active_);
- }
-
void SetTreePriority(TreePriority priority);
TreePriority GetTreePriority() const;
@@ -733,10 +734,6 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
virtual bool IsUIResourceOpaque(UIResourceId uid) const;
- // This method gets the scroll offset for a regular scroller, or the combined
- // visual and layout offsets of the viewport.
- gfx::ScrollOffset GetVisualScrollOffset(const ScrollNode& scroll_node) const;
-
bool GetSnapFlingInfoAndSetAnimatingSnapTarget(
const gfx::Vector2dF& natural_displacement_in_viewport,
gfx::Vector2dF* out_initial_position,
@@ -744,18 +741,6 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
void ScrollEndForSnapFling(bool did_finish) override;
- // Returns the amount of delta that can be applied to scroll_node, taking
- // page scale into account.
- gfx::Vector2dF ComputeScrollDelta(const ScrollNode& scroll_node,
- const gfx::Vector2dF& delta);
-
- // 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();
@@ -793,12 +778,6 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
bool prepare_tiles_needed() const { return tile_priorities_dirty_; }
- gfx::Vector2dF ScrollSingleNode(const ScrollNode& scroll_node,
- const gfx::Vector2dF& delta,
- const gfx::Point& viewport_point,
- bool is_direct_manipulation,
- ScrollTree* scroll_tree);
-
base::SingleThreadTaskRunner* GetTaskRunner() const {
DCHECK(task_runner_provider_);
return task_runner_provider_->HasImplThread()
@@ -806,11 +785,6 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
: task_runner_provider_->MainThreadTaskRunner();
}
- // Return all ScrollNode indices that have an associated layer with a non-fast
- // region that intersects the point.
- base::flat_set<int> NonFastScrollableNodes(
- const gfx::PointF& device_viewport_point) const;
-
// Returns true if a scroll offset animation is created and false if we scroll
// by the desired amount without an animation.
bool ScrollAnimationCreate(const ScrollNode& scroll_node,
@@ -835,9 +809,6 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
void ClearCaches();
- bool CanConsumeDelta(const ScrollState& scroll_state,
- const ScrollNode& scroll_node);
-
void UpdateImageDecodingHints(
base::flat_map<PaintImage::Id, PaintImage::DecodingMode>
decoding_mode_map);
@@ -870,6 +841,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
bool can_use_msaa() const { return can_use_msaa_; }
+ Viewport& viewport() const { return *viewport_.get(); }
+
protected:
LayerTreeHostImpl(
const LayerTreeSettings& settings,
@@ -903,38 +876,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
compositor_frame_reporting_controller_;
private:
- void CollectScrollDeltas(ScrollAndScaleSet* scroll_info);
- void CollectScrollbarUpdates(ScrollAndScaleSet* scroll_info) const;
-
- // 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.
- bool CalculateLocalScrollDeltaAndStartPoint(
- const ScrollNode& scroll_node,
- const gfx::PointF& viewport_point,
- const gfx::Vector2dF& viewport_delta,
- const ScrollTree& scroll_tree,
- gfx::Vector2dF* out_local_scroll_delta,
- gfx::PointF* out_local_start_point = nullptr);
- gfx::Vector2dF ScrollNodeWithViewportSpaceDelta(
- const ScrollNode& scroll_node,
- const gfx::PointF& viewport_point,
- const gfx::Vector2dF& viewport_delta,
- ScrollTree* scroll_tree);
+ void CollectScrollbarUpdatesForCommit(
+ CompositorCommitData* commit_data) const;
bool ScrollAnimationCreateInternal(const ScrollNode& scroll_node,
const gfx::Vector2dF& delta,
base::TimeDelta delayed_by,
@@ -969,55 +912,6 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
bool UpdateGpuRasterizationStatus();
void UpdateTreeResourcesForGpuRasterizationIfNeeded();
- Viewport& viewport() const { return *viewport_.get(); }
-
- bool IsTouchDraggingScrollbar(
- LayerImpl* first_scrolling_layer_or_drawn_scrollbar,
- ui::ScrollInputType type);
-
- // |layer| is returned from a regular hit test, and
- // |first_scrolling_layer_or_drawn_scrollbar| is returned from a hit test
- // performed only on scrollers and scrollbars. Initial scroll hit testing can
- // be unreliable if the latter is not the direct scroll ancestor of the
- // former. In this case, we will fall back to main thread scrolling because
- // the compositor thread doesn't know which layer to scroll. This happens when
- // a layer covers a scroller that doesn't scroll the former, or a scroller is
- // masked by a mask layer for mask image, clip-path, rounded border, etc.
- //
- // Note, position: fixed layers use the inner viewport as their ScrollNode
- // (since they don't scroll with the outer viewport), however, scrolls from
- // the fixed layer still chain to the outer viewport. It's also possible for a
- // node to have the inner viewport as its ancestor without going through the
- // outer viewport; however, it may still scroll using the viewport(). Hence,
- // this method must use the same scroll chaining logic we use in ApplyScroll.
- bool IsInitialScrollHitTestReliable(
- LayerImpl* layer,
- LayerImpl* first_scrolling_layer_or_drawn_scrollbar) const;
-
- // Given a starting node (determined by hit-test), walks up the scroll tree
- // looking for the first node that can consume scroll from the given
- // scroll_state and returns the first such node. If none is found, or if
- // starting_node is nullptr, returns nullptr;
- ScrollNode* FindNodeToLatch(ScrollState* scroll_state,
- ScrollNode* starting_node,
- ui::ScrollInputType type);
-
- // Called during ScrollBegin once a scroller was successfully latched to
- // (i.e. it can and will consume scroll delta on the compositor thread). The
- // latched scroller is now available in CurrentlyScrollingNode().
- // TODO(bokan): There's some debate about the name of this method. We should
- // get consensus on terminology to use and apply it consistently.
- // https://crrev.com/c/1981336/9/cc/trees/layer_tree_host_impl.cc#4520
- void DidLatchToScroller(const ScrollState& scroll_state,
- ui::ScrollInputType type);
-
- // Applies the scroll_state to the currently latched scroller. See comment in
- // InputHandler::ScrollUpdate declaration for the meaning of |delayed_by|.
- void ScrollLatchedScroller(ScrollState* scroll_state,
- base::TimeDelta delayed_by);
-
- bool ShouldAnimateScroll(const ScrollState& scroll_state) const;
-
bool AnimatePageScale(base::TimeTicks monotonic_time);
bool AnimateScrollbars(base::TimeTicks monotonic_time);
bool AnimateBrowserControls(base::TimeTicks monotonic_time);
@@ -1034,38 +928,6 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
// the frame should be drawn.
DrawResult CalculateRenderPasses(FrameData* frame);
- void ClearCurrentlyScrollingNode();
-
- // Performs a hit test to determine the ScrollNode to use when scrolling at
- // |viewport_point|. If no layer is hit, this falls back to the inner
- // viewport scroll node. Returns:
- // - If |hit_test_sucessful| is false, hit testing has failed and the
- // compositor cannot determine the correct scroll node (e.g. see comments in
- // IsInitialScrollHitTestReliable). |scroll_node| is always nullptr in this
- // case.
- // - If |hit_test_successful| is true, returns the ScrollNode to use in
- // |scroll_node|. This can be nullptr if no layer was hit and there are no
- // viewport nodes (e.g. OOPIF, UI compositor).
- struct ScrollHitTestResult {
- ScrollNode* scroll_node;
- bool hit_test_successful;
- };
- ScrollHitTestResult HitTestScrollNode(
- const gfx::PointF& device_viewport_point) const;
-
- // Similar to above but includes complicated logic to determine whether the
- // ScrollNode is able to be scrolled on the compositor or requires main
- // thread scrolling. If main thread scrolling is required
- // |scroll_on_main_thread| is set to true and the reason is given in
- // |main_thread_scrolling_reason| to on of the enum values in
- // main_thread_scrolling_reason.h. Can be removed after scroll unification
- // https://crbug.com/476553.
- ScrollNode* FindScrollNodeForCompositedScrolling(
- const gfx::PointF& device_viewport_point,
- LayerImpl* layer_hit_by_point,
- bool* scroll_on_main_thread,
- uint32_t* main_thread_scrolling_reason) const;
-
void StartScrollbarFadeRecursive(LayerImpl* layer);
void SetManagedMemoryPolicy(const ManagedMemoryPolicy& policy);
@@ -1088,29 +950,10 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
void NotifySwapPromiseMonitorsOfSetNeedsRedraw();
- void UpdateRootLayerStateForSynchronousInputHandler();
-
- bool ScrollAnimationUpdateTarget(const ScrollNode& scroll_node,
- const gfx::Vector2dF& scroll_delta,
- base::TimeDelta delayed_by);
-
- // Creates an animation curve and returns true if we need to update the
- // scroll position to a snap point. Otherwise returns false.
- bool SnapAtScrollEnd();
-
+ private:
void SetContextVisibility(bool is_visible);
void ImageDecodeFinished(int request_id, bool decode_succeeded);
- // This function keeps track of sources of scrolls that are handled in the
- // compositor side. The information gets shared by the main thread as part of
- // the begin_main_frame_state. Finally Use counters are updated in the main
- // thread side to keep track of the frequency of scrolling with different
- // sources per page load. TODO(crbug.com/691886): Use GRC API to plumb the
- // scroll source info for Use Counters.
- void UpdateScrollSourceInfo(const ScrollState& scroll_state,
- ui::ScrollInputType type);
-
- bool IsScrolledBy(LayerImpl* child, ScrollNode* ancestor);
void ShowScrollbarsForImplScroll(ElementId element_id);
// Copy any opacity values already in the active tree to the pending
@@ -1128,6 +971,17 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
void AllocateLocalSurfaceId();
+ // Log the AverageLag events from the frame identified by |frame_token| and
+ // the information in |details|.
+ void LogAverageLagEvents(uint32_t frame_token,
+ const viz::FrameTimingDetails& details);
+
+ // TODO(bokan): Temporary, keep multiple pointers to the input_handler_ while
+ // we decouple this into clean interfaces. Once we're done LTHI should only
+ // talk to InputDelegateForCompositor.
+ InputDelegateForCompositor* input_delegate_;
+ ThreadedInputHandler input_handler_;
+
const LayerTreeSettings settings_;
// This is set to true only if:
@@ -1173,7 +1027,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
bool need_update_gpu_rasterization_status_ = false;
bool use_gpu_rasterization_ = false;
- bool use_oop_rasterization_ = false;
+ bool can_use_oop_rasterization_ = false;
GpuRasterizationStatus gpu_rasterization_status_ =
GpuRasterizationStatus::OFF_DEVICE;
std::unique_ptr<RasterBufferProvider> raster_buffer_provider_;
@@ -1193,35 +1047,14 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
// by the next sync from the main thread.
std::unique_ptr<LayerTreeImpl> recycle_tree_;
- InputHandlerClient* input_handler_client_ = nullptr;
-
- // This is used to tell the scheduler there are active scroll handlers on the
- // page so we should prioritize latency during a scroll to try to keep
- // scroll-linked effects up to data.
- // TODO(bokan): This is quite old and scheduling has become much more
- // sophisticated since so it's not clear how much value it's still providing.
- bool scroll_affects_scroll_handler_ = false;
-
- ElementId scroll_element_id_mouse_currently_over_;
- ElementId scroll_element_id_mouse_currently_captured_;
-
// Tracks, for debugging purposes, the amount of scroll received (not
// necessarily applied) in this compositor frame. This will be reset each
// time a CompositorFrame is generated.
gfx::Vector2dF scroll_accumulated_this_frame_;
- // Tracks the last scroll update/begin state received. Used to infer the most
- // recent scroll type and direction.
- base::Optional<ScrollState> last_scroll_begin_state_;
- base::Optional<ScrollState> last_scroll_update_state_;
-
std::vector<std::unique_ptr<SwapPromise>>
swap_promises_for_main_thread_scroll_update_;
- // An object to implement the ScrollElasticityHelper interface and
- // hold all state related to elasticity. May be NULL if never requested.
- std::unique_ptr<ScrollElasticityHelper> scroll_elasticity_helper_;
-
bool tile_priorities_dirty_ = false;
LayerTreeDebugState debug_state_;
@@ -1230,36 +1063,14 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
TileManager tile_manager_;
- gfx::Vector2dF accumulated_root_overscroll_;
-
- // Unconsumed scroll delta sent to the main thread for firing overscroll DOM
- // events. Resets after each commit.
- gfx::Vector2dF overscroll_delta_for_main_thread_;
-
- // True iff some of the delta has been consumed for the current scroll
- // sequence on the specific axis.
- bool did_scroll_x_for_scroll_gesture_;
- bool did_scroll_y_for_scroll_gesture_;
-
- // This value is used to allow the compositor to throttle re-rastering during
- // pinch gestures, when the page scale factor may be changing frequently. It
- // is set in one of two ways:
- // i) In a layer tree serving the root of the frame/compositor tree, it is
- // directly set during processing of GesturePinch events on the impl thread
- // (only the root layer tree has access to these).
- // ii) In a layer tree serving a sub-frame in the frame/compositor tree, it
- // is set from the main thread during the commit process, using information
- // sent from the root layer tree via IPC messaging.
- bool pinch_gesture_active_ = false;
- bool external_pinch_gesture_active_ = false;
- bool pinch_gesture_end_should_clear_scrolling_node_ = false;
-
std::unique_ptr<BrowserControlsOffsetManager>
browser_controls_offset_manager_;
std::unique_ptr<PageScaleAnimation> page_scale_animation_;
DroppedFrameCounter dropped_frame_counter_;
+ TotalFrameCounter total_frame_counter_;
+
std::unique_ptr<MemoryHistory> memory_history_;
std::unique_ptr<DebugRectHistory> debug_rect_history_;
@@ -1322,29 +1133,11 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
std::unique_ptr<PendingTreeRasterDurationHistogramTimer>
pending_tree_raster_duration_timer_;
- // If a scroll snap is being animated, then the value of this will be the
- // element id(s) of the target(s). Otherwise, the ids will be invalid.
- // At the end of a scroll animation, the target should be set as the scroll
- // node's snap target.
- TargetSnapAreaElementIds scroll_animating_snap_target_ids_;
-
- // A set of elements that scroll-snapped to a new target since the last
- // begin main frame. The snap target ids of these elements will be sent to
- // the main thread in the next begin main frame.
- base::flat_set<ElementId> updated_snapped_elements_;
-
// These completion states to be transfered to the main thread when we
// begin main frame. The pair represents a request id and the completion (ie
// success) state.
std::vector<std::pair<int, bool>> completed_image_decode_requests_;
- // These are used to transfer usage of different types of scrolling to the
- // main thread.
- bool has_scrolled_by_wheel_ = false;
- bool has_scrolled_by_touch_ = false;
- bool has_scrolled_by_precisiontouchpad_ = false;
- bool has_pinch_zoomed_ = false;
-
ImplThreadPhase impl_thread_phase_ = ImplThreadPhase::IDLE;
ImageAnimationController image_animation_controller_;
@@ -1374,37 +1167,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
const PaintImage::GeneratorClientId paint_image_generator_client_id_;
- // Manages composited scrollbar hit testing.
- std::unique_ptr<ScrollbarController> scrollbar_controller_;
-
FrameSequenceTrackerCollection frame_trackers_;
- // Set to true when a scroll gesture being handled on the compositor has
- // ended. i.e. When a GSE has arrived and any ongoing scroll animation has
- // ended.
- bool scroll_gesture_did_end_;
-
- // Set in ScrollBegin and outlives the currently scrolling node so it can be
- // used to send the scrollend and overscroll DOM events from the main thread
- // when scrolling occurs on the compositor thread. This value is cleared at
- // the first commit after a GSE.
- ElementId last_latched_scroller_;
-
- // The source device type that started the scroll gesture. Only set between a
- // ScrollBegin and ScrollEnd.
- base::Optional<ui::ScrollInputType> latched_scroll_type_;
-
- // Scroll animation can finish either before or after GSE arrival.
- // deferred_scroll_end_ is set when the GSE has arrvied before scroll
- // animation completion. ScrollEnd will get called once the animation is
- // over.
- bool deferred_scroll_end_ = false;
-
- // TODO(bokan): Mac doesn't yet have smooth scrolling for wheel; however, to
- // allow consistency in tests we use this bit to override that decision.
- // https://crbug.com/574283.
- bool force_smooth_wheel_scrolling_for_testing_ = false;
-
// PaintWorklet painting is controlled from the LayerTreeHostImpl, dispatched
// to the worklet thread via |paint_worklet_painter_|.
std::unique_ptr<PaintWorkletLayerPainter> paint_worklet_painter_;
@@ -1425,6 +1189,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
// invalidating PaintWorklets as the property values change.
AnimatedPaintWorkletTracker paint_worklet_tracker_;
+ AverageLagTrackingManager lag_tracking_manager_;
+
// Helper for de-jelly logic.
DeJellyState de_jelly_state_;
@@ -1435,6 +1201,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
FrameRateEstimator frame_rate_estimator_;
bool has_observed_first_scroll_delay_ = false;
+ bool enable_frame_rate_throttling_ = false;
+
// Must be the last member to ensure this is destroyed first in the
// destruction order and invalidates all weak pointers.
base::WeakPtrFactory<LayerTreeHostImpl> weak_factory_{this};
diff --git a/chromium/cc/trees/layer_tree_host_impl_unittest.cc b/chromium/cc/trees/layer_tree_host_impl_unittest.cc
index fd82323bf7e..bcad5411f31 100644
--- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc
@@ -64,6 +64,7 @@
#include "cc/test/test_paint_worklet_layer_painter.h"
#include "cc/test/test_task_graph_runner.h"
#include "cc/trees/clip_node.h"
+#include "cc/trees/compositor_commit_data.h"
#include "cc/trees/draw_property_utils.h"
#include "cc/trees/effect_node.h"
#include "cc/trees/latency_info_swap_promise.h"
@@ -71,7 +72,6 @@
#include "cc/trees/mutator_host.h"
#include "cc/trees/render_frame_metadata.h"
#include "cc/trees/render_frame_metadata_observer.h"
-#include "cc/trees/scroll_and_scale_set.h"
#include "cc/trees/scroll_node.h"
#include "cc/trees/single_thread_proxy.h"
#include "cc/trees/transform_node.h"
@@ -113,6 +113,8 @@ using ::testing::AtLeast;
using ::testing::_;
using media::VideoFrame;
+using ScrollThread = cc::InputHandler::ScrollThread;
+
namespace viz {
struct FrameTimingDetails;
}
@@ -135,6 +137,8 @@ struct TestFrameData : public LayerTreeHostImpl::FrameData {
}
};
+} // namespace
+
class LayerTreeHostImplTest : public testing::Test,
public LayerTreeHostImplClient {
public:
@@ -359,28 +363,28 @@ class LayerTreeHostImplTest : public testing::Test,
}
static ::testing::AssertionResult ScrollInfoContains(
- const ScrollAndScaleSet& scroll_info,
+ const CompositorCommitData& commit_data,
ElementId id,
const gfx::ScrollOffset& scroll_delta) {
int times_encountered = 0;
- for (size_t i = 0; i < scroll_info.scrolls.size(); ++i) {
- if (scroll_info.scrolls[i].element_id != id)
+ for (size_t i = 0; i < commit_data.scrolls.size(); ++i) {
+ if (commit_data.scrolls[i].element_id != id)
continue;
- if (scroll_delta != scroll_info.scrolls[i].scroll_delta) {
+ if (scroll_delta != commit_data.scrolls[i].scroll_delta) {
return ::testing::AssertionFailure()
<< "Expected " << scroll_delta.ToString() << ", not "
- << scroll_info.scrolls[i].scroll_delta.ToString();
+ << commit_data.scrolls[i].scroll_delta.ToString();
}
times_encountered++;
}
- if (id == scroll_info.inner_viewport_scroll.element_id) {
- if (scroll_delta != scroll_info.inner_viewport_scroll.scroll_delta) {
+ if (id == commit_data.inner_viewport_scroll.element_id) {
+ if (scroll_delta != commit_data.inner_viewport_scroll.scroll_delta) {
return ::testing::AssertionFailure()
<< "Expected " << scroll_delta.ToString() << ", not "
- << scroll_info.inner_viewport_scroll.scroll_delta.ToString();
+ << commit_data.inner_viewport_scroll.scroll_delta.ToString();
}
times_encountered++;
}
@@ -390,11 +394,12 @@ class LayerTreeHostImplTest : public testing::Test,
return ::testing::AssertionSuccess();
}
- static void ExpectNone(const ScrollAndScaleSet& scroll_info, ElementId id) {
+ static void ExpectNone(const CompositorCommitData& commit_data,
+ ElementId id) {
int times_encountered = 0;
- for (size_t i = 0; i < scroll_info.scrolls.size(); ++i) {
- if (scroll_info.scrolls[i].element_id != id)
+ for (size_t i = 0; i < commit_data.scrolls.size(); ++i) {
+ if (commit_data.scrolls[i].element_id != id)
continue;
times_encountered++;
}
@@ -515,10 +520,10 @@ class LayerTreeHostImplTest : public testing::Test,
.get(),
ui::ScrollInputType::kWheel);
if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
- ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
ASSERT_TRUE(status.needs_main_thread_hit_test);
} else {
- ASSERT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+ ASSERT_EQ(ScrollThread::SCROLL_UNKNOWN, status.thread);
ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_scrolling_reasons);
}
@@ -530,10 +535,10 @@ class LayerTreeHostImplTest : public testing::Test,
.get(),
ui::ScrollInputType::kWheel);
if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
- ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
ASSERT_TRUE(status.needs_main_thread_hit_test);
} else {
- ASSERT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+ ASSERT_EQ(ScrollThread::SCROLL_UNKNOWN, status.thread);
ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_scrolling_reasons);
}
@@ -545,7 +550,7 @@ class LayerTreeHostImplTest : public testing::Test,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
}
LayerImpl* AddScrollableLayer(LayerImpl* container,
@@ -949,26 +954,26 @@ INSTANTIATE_TEST_SUITE_P(All,
TEST_P(ScrollUnifiedLayerTreeHostImplTest, LocalAndExternalPinchState) {
// PinchGestureBegin/End update pinch_gesture_active() properly.
- EXPECT_FALSE(host_impl_->pinch_gesture_active());
+ EXPECT_FALSE(host_impl_->GetInputHandler().pinch_gesture_active());
host_impl_->PinchGestureBegin();
- EXPECT_TRUE(host_impl_->pinch_gesture_active());
+ EXPECT_TRUE(host_impl_->GetInputHandler().pinch_gesture_active());
host_impl_->PinchGestureEnd(gfx::Point(), false /* snap_to_min */);
- EXPECT_FALSE(host_impl_->pinch_gesture_active());
+ EXPECT_FALSE(host_impl_->GetInputHandler().pinch_gesture_active());
// set_external_pinch_gesture_active updates pinch_gesture_active() properly.
- host_impl_->set_external_pinch_gesture_active(true);
- EXPECT_TRUE(host_impl_->pinch_gesture_active());
- host_impl_->set_external_pinch_gesture_active(false);
- EXPECT_FALSE(host_impl_->pinch_gesture_active());
+ host_impl_->GetInputHandler().set_external_pinch_gesture_active(true);
+ EXPECT_TRUE(host_impl_->GetInputHandler().pinch_gesture_active());
+ host_impl_->GetInputHandler().set_external_pinch_gesture_active(false);
+ EXPECT_FALSE(host_impl_->GetInputHandler().pinch_gesture_active());
// Clearing external_pinch_gesture_active doesn't affect
// pinch_gesture_active() if it was set by PinchGestureBegin().
host_impl_->PinchGestureBegin();
- EXPECT_TRUE(host_impl_->pinch_gesture_active());
- host_impl_->set_external_pinch_gesture_active(false);
- EXPECT_TRUE(host_impl_->pinch_gesture_active());
+ EXPECT_TRUE(host_impl_->GetInputHandler().pinch_gesture_active());
+ host_impl_->GetInputHandler().set_external_pinch_gesture_active(false);
+ EXPECT_TRUE(host_impl_->GetInputHandler().pinch_gesture_active());
host_impl_->PinchGestureEnd(gfx::Point(), false /* snap_to_min */);
- EXPECT_FALSE(host_impl_->pinch_gesture_active());
+ EXPECT_FALSE(host_impl_->GetInputHandler().pinch_gesture_active());
}
TEST_P(ScrollUnifiedLayerTreeHostImplTest, NotifyIfCanDrawChanged) {
@@ -1028,9 +1033,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ResourcelessDrawWithEmptyViewport) {
TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDeltaNoLayers) {
host_impl_->active_tree()->SetRootLayerForTesting(nullptr);
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- ASSERT_EQ(scroll_info->scrolls.size(), 0u);
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ ASSERT_EQ(commit_data->scrolls.size(), 0u);
}
TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDeltaTreeButNoChanges) {
@@ -1050,14 +1055,14 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDeltaTreeButNoChanges) {
ExpectClearedScrollDeltasRecursive(root);
- std::unique_ptr<ScrollAndScaleSet> scroll_info;
+ std::unique_ptr<CompositorCommitData> commit_data;
- scroll_info = host_impl_->ProcessScrollDeltas();
- ASSERT_EQ(scroll_info->scrolls.size(), 0u);
+ commit_data = host_impl_->ProcessCompositorDeltas();
+ ASSERT_EQ(commit_data->scrolls.size(), 0u);
ExpectClearedScrollDeltasRecursive(root);
- scroll_info = host_impl_->ProcessScrollDeltas();
- ASSERT_EQ(scroll_info->scrolls.size(), 0u);
+ commit_data = host_impl_->ProcessCompositorDeltas();
+ ASSERT_EQ(commit_data->scrolls.size(), 0u);
ExpectClearedScrollDeltasRecursive(root);
}
@@ -1074,24 +1079,24 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDeltaRepeatedScrolls) {
scroll_offset);
UpdateDrawProperties(host_impl_->active_tree());
- std::unique_ptr<ScrollAndScaleSet> scroll_info;
+ std::unique_ptr<CompositorCommitData> commit_data;
root->ScrollBy(gfx::ScrollOffsetToVector2dF(scroll_delta));
- scroll_info = host_impl_->ProcessScrollDeltas();
- ASSERT_EQ(scroll_info->scrolls.size(), 1u);
+ commit_data = host_impl_->ProcessCompositorDeltas();
+ ASSERT_EQ(commit_data->scrolls.size(), 1u);
EXPECT_TRUE(
- ScrollInfoContains(*scroll_info, root->element_id(), scroll_delta));
+ ScrollInfoContains(*commit_data, root->element_id(), scroll_delta));
gfx::ScrollOffset scroll_delta2(-5, 27);
root->ScrollBy(gfx::ScrollOffsetToVector2dF(scroll_delta2));
- scroll_info = host_impl_->ProcessScrollDeltas();
- ASSERT_EQ(scroll_info->scrolls.size(), 1u);
- EXPECT_TRUE(ScrollInfoContains(*scroll_info, root->element_id(),
+ commit_data = host_impl_->ProcessCompositorDeltas();
+ ASSERT_EQ(commit_data->scrolls.size(), 1u);
+ EXPECT_TRUE(ScrollInfoContains(*commit_data, root->element_id(),
scroll_delta + scroll_delta2));
root->ScrollBy(gfx::Vector2d());
- scroll_info = host_impl_->ProcessScrollDeltas();
- EXPECT_TRUE(ScrollInfoContains(*scroll_info, root->element_id(),
+ commit_data = host_impl_->ProcessCompositorDeltas();
+ EXPECT_TRUE(ScrollInfoContains(*commit_data, root->element_id(),
scroll_delta + scroll_delta2));
}
@@ -1129,7 +1134,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBeforeRootLayerAttached) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_IGNORED, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
status.main_thread_scrolling_reasons);
@@ -1138,7 +1143,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBeforeRootLayerAttached) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_IGNORED, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
status.main_thread_scrolling_reasons);
}
@@ -1173,7 +1178,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollUpdateAndEndNoOpWithoutBegin) {
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_TRUE(host_impl_->CurrentlyScrollingNode());
host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
@@ -1201,7 +1206,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, TargetMainThreadScroller) {
{
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
scroll_state.get(), ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(host_impl_->OuterViewportScrollNode(),
host_impl_->CurrentlyScrollingNode());
host_impl_->ScrollEnd();
@@ -1218,13 +1223,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, TargetMainThreadScroller) {
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
scroll_state.get(), ui::ScrollInputType::kWheel);
if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(
host_impl_->OuterViewportScrollNode()->main_thread_scrolling_reasons,
status.main_thread_scrolling_reasons);
@@ -1242,7 +1247,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
@@ -1277,7 +1282,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
EXPECT_FALSE(host_impl_->IsActivelyPrecisionScrolling());
@@ -1307,7 +1312,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_FALSE(host_impl_->IsActivelyPrecisionScrolling());
host_impl_->ScrollUpdate(
@@ -1353,7 +1358,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutRootLayer) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_IGNORED, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
status.main_thread_scrolling_reasons);
}
@@ -1377,7 +1382,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutRenderer) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
}
@@ -1389,7 +1394,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ReplaceTreeWhileScrolling) {
// We should not crash if the tree is replaced while we are scrolling.
gfx::ScrollOffset scroll_delta(0, 10);
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(),
gfx::ScrollOffsetToVector2dF(scroll_delta),
@@ -1409,9 +1414,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ReplaceTreeWhileScrolling) {
ui::ScrollInputType::kWheel)
.get());
host_impl_->ScrollEnd();
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->element_id(),
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(),
scroll_delta));
}
@@ -1424,7 +1429,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
@@ -1469,7 +1474,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBlocksOnWheelEventHandlers) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
host_impl_->ScrollEnd();
@@ -1505,7 +1510,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBlocksOnTouchEventHandlers) {
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
host_impl_->ScrollEnd();
@@ -1539,13 +1544,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ShouldScrollOnMainThread) {
.get(),
ui::ScrollInputType::kWheel);
if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
status.main_thread_scrolling_reasons);
}
@@ -1556,13 +1561,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ShouldScrollOnMainThread) {
.get(),
ui::ScrollInputType::kTouchscreen);
if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
status.main_thread_scrolling_reasons);
}
@@ -1613,12 +1618,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
.get(),
ui::ScrollInputType::kWheel);
if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::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(ScrollThread::SCROLL_UNKNOWN, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_scrolling_reasons);
}
@@ -1630,7 +1635,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
}
@@ -1725,12 +1730,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, NonFastScrollableRegionBasic) {
.get(),
ui::ScrollInputType::kWheel);
if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
status.main_thread_scrolling_reasons);
}
@@ -1741,12 +1746,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, NonFastScrollableRegionBasic) {
.get(),
ui::ScrollInputType::kTouchscreen);
if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
status.main_thread_scrolling_reasons);
}
@@ -1757,7 +1762,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, NonFastScrollableRegionBasic) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
@@ -1771,7 +1776,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, NonFastScrollableRegionBasic) {
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
@@ -1797,7 +1802,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
EXPECT_FALSE(status.needs_main_thread_hit_test);
@@ -1814,30 +1819,486 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
.get(),
ui::ScrollInputType::kWheel);
if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
status.main_thread_scrolling_reasons);
}
}
+// Tests the following tricky case:
+// - Scrolling Layer A with scrolling children:
+// - Ordinary Layer B with NonFastScrollableRegion
+// - Ordinary Layer C
+//
+// +---------+
+// +---------| |+
+// | Layer A | ||
+// | +-----+-----+ ||
+// | | Layer C | ||
+// | +-----+-----+ ||
+// | | Layer B ||
+// +---------| |+
+// +---------+
+//
+//
+// Both B and C scroll with A but overlap each other and C appears above B. If
+// we try scrolling over C, we need to check if we intersect the NFSR on B
+// because C may not be fully opaque to hit testing (e.g. the layer may be for
+// |pointer-events:none| or be a squashing layer with "holes").
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ LayerOverlapsNonFastScrollableRegionInLayer) {
+ SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
+
+ // The viewport will be layer A in the description above.
+ LayerImpl* outer_scroll = OuterViewportScrollLayer();
+
+ // Layer B is a 50x50 layer filled with a non fast scrollable region. It
+ // occupies the right half of the viewport.
+ auto* layer_b = AddLayer<LayerImpl>(host_impl_->active_tree());
+ layer_b->SetBounds(gfx::Size(50, 100));
+ layer_b->SetDrawsContent(true);
+ layer_b->SetHitTestable(true);
+ CopyProperties(outer_scroll, layer_b);
+ layer_b->SetOffsetToTransformParent(gfx::Vector2dF(50, 0));
+ layer_b->SetNonFastScrollableRegion(gfx::Rect(0, 0, 50, 100));
+
+ // Do a sanity check - scrolling over layer b should cause main thread
+ // scrolling/hit testing because of the non fast scrolling region.
+ {
+ ASSERT_EQ(layer_b, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
+ gfx::PointF(60, 50)));
+ InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+ BeginState(gfx::Point(60, 50), gfx::Vector2d(0, 10),
+ ui::ScrollInputType::kWheel)
+ .get(),
+ ui::ScrollInputType::kWheel);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ ASSERT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ ASSERT_EQ(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
+ ASSERT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
+ host_impl_->ScrollEnd();
+ }
+
+ // Layer C is a 50x50 layer initially centered in the viewport. The right
+ // half overlaps Layer B.
+ auto* layer_c = AddLayer<LayerImpl>(host_impl_->active_tree());
+ layer_c->SetBounds(gfx::Size(50, 50));
+ layer_c->SetDrawsContent(true);
+ layer_c->SetHitTestable(true);
+ CopyProperties(outer_scroll, layer_c);
+ layer_c->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
+
+ // Do a sanity check - scrolling over layer c where it doesn't overlap B
+ // should cause scrolling on the viewport. It doesn't matter whether the
+ // point hits a "hit-test transparent" part of layer C because will cause
+ // scrolling in Layer A in either case since C scrolls with A.
+ {
+ ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
+ gfx::PointF(40, 50)));
+ InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+ BeginState(gfx::Point(40, 50), gfx::Vector2d(0, 10),
+ ui::ScrollInputType::kWheel)
+ .get(),
+ ui::ScrollInputType::kWheel);
+ ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ ASSERT_EQ(host_impl_->CurrentlyScrollingNode(),
+ host_impl_->OuterViewportScrollNode());
+ host_impl_->ScrollEnd();
+ }
+
+ // Now perform the real test - scrolling in the overlapping region should
+ // fallback to the main thread. In this case, we really do need to know
+ // whether the point hits a "hit-test transparent" part of Layer C because if
+ // it does it'll hit the NonFastScrollingRegion but if it doesn't it targets
+ // Layer C which scrolls the viewport.
+ {
+ ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
+ gfx::PointF(60, 50)));
+ InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+ BeginState(gfx::Point(60, 50), gfx::Vector2d(0, 10),
+ ui::ScrollInputType::kWheel)
+ .get(),
+ ui::ScrollInputType::kWheel);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(ScrollThread::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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
+ host_impl_->ScrollEnd();
+ }
+}
+
+// Similar to the above test but this time layer B does not scroll with layer
+// A. This is an edge case where the CSS painting algorithm allows a sibling of
+// an overflow scroller to appear on top of the scroller itself but below some
+// of the scroller's children. e.g. https://output.jsbin.com/tejulip/quiet.
+//
+// <div id="scroller" style="position:relative">
+// <div id="child" style="position:relative; z-index:2"></div>
+// </div>
+// <div id="sibling" style="position:absolute; z-index: 1"></div>
+//
+// So we setup:
+//
+// - Scrolling Layer A with scrolling child:
+// - Ordinary Layer C
+// - Ordinary layer B with a NonFastScrollableRegion
+//
+// +---------+
+// +---------| |+
+// | Layer A | ||
+// | +-----+-----+ ||
+// | | Layer C | ||
+// | +-----+-----+ ||
+// | | Layer B ||
+// +---------| |+
+// +---------+
+//
+//
+// Only C scrolls with A but C appears above B. If we try scrolling over C, we
+// need to check if we intersect the NFSR on B because C may not be fully
+// opaque to hit testing (e.g. the layer may be for |pointer-events:none| or be
+// a squashing layer with "holes").
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ LayerOverlapsNonFastScrollableRegionInNonScrollAncestorLayer) {
+ // TODO(bokan): This fails without scroll unification.
+ // https://crbug.com/1098425.
+ if (!GetParam())
+ return;
+ SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
+ LayerImpl* outer_scroll = OuterViewportScrollLayer();
+
+ LayerImpl* layer_a = AddScrollableLayer(outer_scroll, gfx::Size(100, 100),
+ gfx::Size(200, 200));
+
+ auto* layer_b = AddLayer<LayerImpl>(host_impl_->active_tree());
+ layer_b->SetBounds(gfx::Size(50, 100));
+ layer_b->SetDrawsContent(true);
+ layer_b->SetHitTestable(true);
+ CopyProperties(outer_scroll, layer_b);
+ layer_b->SetOffsetToTransformParent(gfx::Vector2dF(50, 0));
+
+ // Do a sanity check - scrolling over layer b should fallback to the main
+ // thread because the first hit scrolling layer is layer a which is not a
+ // scroll ancestor of b.
+ {
+ ASSERT_EQ(layer_b, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
+ gfx::PointF(75, 50)));
+ InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+ BeginState(gfx::Point(75, 50), gfx::Vector2d(0, 10),
+ ui::ScrollInputType::kWheel)
+ .get(),
+ ui::ScrollInputType::kWheel);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ ASSERT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ ASSERT_EQ(ScrollThread::SCROLL_UNKNOWN, status.thread);
+ ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest,
+ status.main_thread_scrolling_reasons);
+ }
+ host_impl_->ScrollEnd();
+ }
+
+ // Layer C is a 50x50 layer initially centered in the viewport. Layer C
+ // scrolls with Layer A and appears on top of Layer B. The right half of it
+ // overlaps Layer B.
+ auto* layer_c = AddLayer<LayerImpl>(host_impl_->active_tree());
+ layer_c->SetBounds(gfx::Size(50, 50));
+ layer_c->SetDrawsContent(true);
+ layer_c->SetHitTestable(true);
+ CopyProperties(layer_a, layer_c);
+ layer_c->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
+
+ // Do a sanity check - scrolling over layer c where it doesn't overlap B
+ // should cause scrolling of Layer A. It doesn't matter whether the
+ // point hits a "hit-test transparent" part of layer C because will cause
+ // scrolling in Layer A in either case since C scrolls with A.
+ {
+ ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
+ gfx::PointF(40, 50)));
+ InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+ BeginState(gfx::Point(40, 50), gfx::Vector2d(0, 10),
+ ui::ScrollInputType::kWheel)
+ .get(),
+ ui::ScrollInputType::kWheel);
+ ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ ASSERT_EQ(host_impl_->CurrentlyScrollingNode()->id,
+ layer_a->scroll_tree_index());
+ host_impl_->ScrollEnd();
+ }
+
+ // Now perform the real test - scrolling in the overlapping region should
+ // fallback to the main thread. In this case, we really do need to know
+ // whether the point hits a "hit-test transparent" part of Layer C because if
+ // it does it'll hit Layer B which scrolls the outer viewport but if it
+ // doesn't it targets Layer C which scrolls Layer A.
+ {
+ ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
+ gfx::PointF(60, 50)));
+ InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+ BeginState(gfx::Point(60, 50), gfx::Vector2d(0, 10),
+ ui::ScrollInputType::kWheel)
+ .get(),
+ ui::ScrollInputType::kWheel);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(ScrollThread::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(ScrollThread::SCROLL_UNKNOWN, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
+ status.main_thread_scrolling_reasons);
+ }
+ host_impl_->ScrollEnd();
+ }
+}
+
+// - Scrolling Layer A with scrolling child:
+// - Ordinary Layer B with NonFastScrollableRegion
+// - Fixed (scrolls with inner viewport) ordinary Layer C.
+//
+// +---------+---------++
+// | Layer A | ||
+// | +-----+-----+ ||
+// | | Layer C | ||
+// | +-----+-----+ ||
+// | | Layer B ||
+// +---------+---------++
+//
+//
+// B scrolls with A but C, which is fixed, appears above B. If we try scrolling
+// over C, we need to check if we intersect the NFSR on B because C may not be
+// fully opaque to hit testing (e.g. the layer may be for |pointer-events:none|
+// or be a squashing layer with "holes"). This is similar to the cases above
+// but uses a fixed Layer C to exercise the case where we hit the viewport via
+// the inner viewport.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ FixedLayerOverlapsNonFastScrollableRegionInLayer) {
+ SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
+
+ // The viewport will be layer A in the description above.
+ LayerImpl* outer_scroll = OuterViewportScrollLayer();
+ LayerImpl* inner_scroll = InnerViewportScrollLayer();
+
+ // Layer B is a 50x50 layer filled with a non fast scrollable region. It
+ // occupies the right half of the viewport.
+ auto* layer_b = AddLayer<LayerImpl>(host_impl_->active_tree());
+ layer_b->SetBounds(gfx::Size(50, 100));
+ layer_b->SetDrawsContent(true);
+ layer_b->SetHitTestable(true);
+ CopyProperties(outer_scroll, layer_b);
+ layer_b->SetOffsetToTransformParent(gfx::Vector2dF(50, 0));
+ layer_b->SetNonFastScrollableRegion(gfx::Rect(0, 0, 50, 100));
+
+ // Do a sanity check - scrolling over layer b should cause main thread
+ // scrolling/hit testing because of the non fast scrolling region.
+ {
+ ASSERT_EQ(layer_b, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
+ gfx::PointF(60, 50)));
+ InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+ BeginState(gfx::Point(60, 50), gfx::Vector2d(0, 10),
+ ui::ScrollInputType::kWheel)
+ .get(),
+ ui::ScrollInputType::kWheel);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ ASSERT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ ASSERT_EQ(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
+ ASSERT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
+ host_impl_->ScrollEnd();
+ }
+
+ // Layer C is a 50x50 layer initially centered in the viewport. The right
+ // half overlaps Layer B.
+ auto* layer_c = AddLayer<LayerImpl>(host_impl_->active_tree());
+ layer_c->SetBounds(gfx::Size(50, 50));
+ layer_c->SetDrawsContent(true);
+ layer_c->SetHitTestable(true);
+ CopyProperties(inner_scroll, layer_c);
+ layer_c->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
+
+ // Do a sanity check - scrolling over layer c where it doesn't overlap B
+ // should cause scrolling on the viewport. It doesn't matter whether the
+ // point hits a "hit-test transparent" part of layer C because will cause
+ // scrolling in Layer A in either case since C scrolls with A.
+ {
+ ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
+ gfx::PointF(40, 50)));
+ InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+ BeginState(gfx::Point(40, 50), gfx::Vector2d(0, 10),
+ ui::ScrollInputType::kWheel)
+ .get(),
+ ui::ScrollInputType::kWheel);
+ ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ ASSERT_EQ(host_impl_->CurrentlyScrollingNode(),
+ host_impl_->OuterViewportScrollNode());
+ host_impl_->ScrollEnd();
+ }
+
+ // Now perform the real test - scrolling in the overlapping region should
+ // fallback to the main thread. In this case, we really do need to know
+ // whether the point hits a "hit-test transparent" part of Layer C because if
+ // it does it'll hit the NonFastScrollingRegion but if it doesn't it targets
+ // Layer C which scrolls the viewport.
+ {
+ ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
+ gfx::PointF(60, 50)));
+ InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+ BeginState(gfx::Point(60, 50), gfx::Vector2d(0, 10),
+ ui::ScrollInputType::kWheel)
+ .get(),
+ ui::ScrollInputType::kWheel);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(ScrollThread::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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
+ host_impl_->ScrollEnd();
+ }
+}
+
+// - Scrolling Layer A with scrolling child:
+// - Ordinary Layer B
+// - Fixed (scrolls with inner viewport) ordinary Layer C.
+//
+// +---------+---------++
+// | Layer A | ||
+// | +-----+-----+ ||
+// | | Layer C | ||
+// | +-----+-----+ ||
+// | | Layer B ||
+// +---------+---------++
+//
+// This test simply ensures that a scroll over the region where layer C and
+// layer B overlap can be handled on the compositor thread. Both of these
+// layers have the viewport as the first scrolling ancestor but C has the
+// inner viewport while B has the outer viewport as an ancestor. Ensure we
+// treat these as equivalent.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, FixedLayerOverNonFixedLayer) {
+ SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
+
+ // The viewport will be layer A in the description above.
+ LayerImpl* outer_scroll = OuterViewportScrollLayer();
+ LayerImpl* inner_scroll = InnerViewportScrollLayer();
+
+ // Layer B is a 50x50 layer filled with a non fast scrollable region. It
+ // occupies the right half of the viewport.
+ auto* layer_b = AddLayer<LayerImpl>(host_impl_->active_tree());
+ layer_b->SetBounds(gfx::Size(50, 100));
+ layer_b->SetDrawsContent(true);
+ layer_b->SetHitTestable(true);
+ CopyProperties(outer_scroll, layer_b);
+ layer_b->SetOffsetToTransformParent(gfx::Vector2dF(50, 0));
+
+ // Layer C is a 50x50 layer initially centered in the viewport. The right
+ // half overlaps Layer B.
+ auto* layer_c = AddLayer<LayerImpl>(host_impl_->active_tree());
+ layer_c->SetBounds(gfx::Size(50, 50));
+ layer_c->SetDrawsContent(true);
+ layer_c->SetHitTestable(true);
+ CopyProperties(inner_scroll, layer_c);
+ layer_c->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
+
+ // A scroll in the overlapping region should not fallback to the main thread
+ // since we'll scroll the viewport regardless which layer we really should
+ // hit.
+ {
+ ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
+ gfx::PointF(60, 50)));
+ InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+ BeginState(gfx::Point(60, 50), gfx::Vector2d(0, 10),
+ ui::ScrollInputType::kWheel)
+ .get(),
+ ui::ScrollInputType::kWheel);
+ ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ ASSERT_EQ(host_impl_->CurrentlyScrollingNode(),
+ host_impl_->OuterViewportScrollNode());
+ host_impl_->ScrollEnd();
+ }
+
+ // However, if we have a non-default root scroller, the inner and outer
+ // viewports really should be treated as separate scrollers (i.e. if you
+ // "bubble" up to the inner viewport, we shouldn't cause scrolling in the
+ // outer viewport because the outer viewport is not an ancestor of the
+ // scrolling Element so that would be unexpected). So now the scroll node to
+ // use for scrolling depends on whether Layer B or Layer C is actually hit so
+ // the main thread needs to decide.
+ {
+ GetScrollNode(InnerViewportScrollLayer())
+ ->prevent_viewport_scrolling_from_inner = true;
+
+ ASSERT_EQ(layer_c, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
+ gfx::PointF(60, 50)));
+ InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
+ BeginState(gfx::Point(60, 50), gfx::Vector2d(0, 10),
+ ui::ScrollInputType::kWheel)
+ .get(),
+ ui::ScrollInputType::kWheel);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(ScrollThread::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(ScrollThread::SCROLL_UNKNOWN, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
+ status.main_thread_scrolling_reasons);
+ }
+ host_impl_->ScrollEnd();
+ }
+}
+
TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHandlerNotPresent) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
EXPECT_FALSE(host_impl_->active_tree()->have_scroll_event_handlers());
DrawFrame();
- EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
+ EXPECT_FALSE(host_impl_->ScrollAffectsScrollHandler());
host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
+ EXPECT_FALSE(host_impl_->ScrollAffectsScrollHandler());
host_impl_->ScrollEnd();
- EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
+ EXPECT_FALSE(host_impl_->ScrollAffectsScrollHandler());
}
TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHandlerPresent) {
@@ -1845,14 +2306,14 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHandlerPresent) {
host_impl_->active_tree()->set_have_scroll_event_handlers(true);
DrawFrame();
- EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
+ EXPECT_FALSE(host_impl_->ScrollAffectsScrollHandler());
host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_TRUE(host_impl_->scroll_affects_scroll_handler());
+ EXPECT_TRUE(host_impl_->ScrollAffectsScrollHandler());
host_impl_->ScrollEnd();
- EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
+ EXPECT_FALSE(host_impl_->ScrollAffectsScrollHandler());
}
TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollUpdateReturnsCorrectValue) {
@@ -1864,7 +2325,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollUpdateReturnsCorrectValue) {
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
@@ -1962,7 +2423,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnX) {
gfx::Point pointer_position(10, 10);
gfx::Vector2dF x_delta(20, 0);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, x_delta,
ui::ScrollInputType::kWheel)
@@ -1984,7 +2445,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnX) {
host_impl_->ScrollEnd(true);
// Snap target should not be set until the end of the animation.
- EXPECT_TRUE(host_impl_->IsAnimatingForSnap());
+ EXPECT_TRUE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
@@ -1996,7 +2457,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnX) {
BeginImplFrameAndAnimate(
begin_frame_args, start_time + base::TimeDelta::FromMilliseconds(1000));
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 0), CurrentScrollOffset(overflow));
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId()),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
@@ -2007,7 +2468,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnY) {
gfx::Point pointer_position(10, 10);
gfx::Vector2dF y_delta(0, 20);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, y_delta,
ui::ScrollInputType::kWheel)
@@ -2029,7 +2490,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnY) {
host_impl_->ScrollEnd(true);
// Snap target should not be set until the end of the animation.
- EXPECT_TRUE(host_impl_->IsAnimatingForSnap());
+ EXPECT_TRUE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
@@ -2041,7 +2502,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnY) {
BeginImplFrameAndAnimate(
begin_frame_args, start_time + base::TimeDelta::FromMilliseconds(1000));
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 50), CurrentScrollOffset(overflow));
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(), ElementId(10)),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
@@ -2052,7 +2513,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnBoth) {
gfx::Point pointer_position(10, 10);
gfx::Vector2dF delta(20, 20);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, delta,
ui::ScrollInputType::kWheel)
@@ -2073,7 +2534,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnBoth) {
host_impl_->ScrollEnd(true);
// Snap target should not be set until the end of the animation.
- EXPECT_TRUE(host_impl_->IsAnimatingForSnap());
+ EXPECT_TRUE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
@@ -2085,7 +2546,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnBoth) {
BeginImplFrameAndAnimate(
begin_frame_args, start_time + base::TimeDelta::FromMilliseconds(1000));
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50), CurrentScrollOffset(overflow));
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId(10)),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
@@ -2098,7 +2559,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAfterEmptyScroll) {
gfx::Point pointer_position(10, 10);
gfx::Vector2dF y_delta(0, 20);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, y_delta,
ui::ScrollInputType::kWheel)
@@ -2114,7 +2575,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapAfterAnimatedScroll) {
gfx::Point pointer_position(10, 10);
gfx::Vector2dF delta(20, 20);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, delta,
ui::ScrollInputType::kWheel)
@@ -2139,7 +2600,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapAfterAnimatedScroll) {
// Animating for the wheel scroll.
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::TimeDelta::FromMilliseconds(50));
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
gfx::ScrollOffset current_offset = CurrentScrollOffset(overflow);
EXPECT_LT(0, current_offset.x());
EXPECT_GT(20, current_offset.x());
@@ -2153,7 +2614,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapAfterAnimatedScroll) {
begin_frame_args, start_time + base::TimeDelta::FromMilliseconds(1000));
// The snap target should not be set until the end of the animation.
- EXPECT_TRUE(host_impl_->IsAnimatingForSnap());
+ EXPECT_TRUE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
@@ -2161,7 +2622,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapAfterAnimatedScroll) {
BeginImplFrameAndAnimate(
begin_frame_args, start_time + base::TimeDelta::FromMilliseconds(1500));
EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 50), CurrentScrollOffset(overflow));
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId(10)),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
@@ -2171,7 +2632,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAnimationTargetUpdated) {
gfx::Point pointer_position(10, 10);
gfx::Vector2dF y_delta(0, 20);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, y_delta,
ui::ScrollInputType::kWheel)
@@ -2183,7 +2644,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAnimationTargetUpdated) {
host_impl_->ScrollUpdate(
UpdateState(pointer_position, y_delta, ui::ScrollInputType::kWheel)
.get());
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
@@ -2195,7 +2656,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAnimationTargetUpdated) {
// Finish smooth wheel scroll animation which starts a snap animation.
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::TimeDelta::FromMilliseconds(100));
- EXPECT_TRUE(host_impl_->IsAnimatingForSnap());
+ EXPECT_TRUE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
@@ -2208,20 +2669,20 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAnimationTargetUpdated) {
// animation.
host_impl_->ScrollUpdate(
AnimatedUpdateState(gfx::Point(10, 10), gfx::Vector2dF(0, -10)).get());
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
// Finish the smooth scroll animation for wheel.
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::TimeDelta::FromMilliseconds(150));
// At the end of the previous scroll animation, a new animation for the
// snapping should have started.
- EXPECT_TRUE(host_impl_->IsAnimatingForSnap());
+ EXPECT_TRUE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
// Finish the snap animation.
BeginImplFrameAndAnimate(
begin_frame_args, start_time + base::TimeDelta::FromMilliseconds(1000));
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
// At the end of snap animation we should have updated the
// TargetSnapAreaElementIds.
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(), ElementId(10)),
@@ -2234,7 +2695,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAnimationCancelledByScroll) {
gfx::Point pointer_position(10, 10);
gfx::Vector2dF x_delta(20, 0);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, x_delta,
ui::ScrollInputType::kWheel)
@@ -2246,7 +2707,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAnimationCancelledByScroll) {
host_impl_->ScrollUpdate(
UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel)
.get());
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
@@ -2258,7 +2719,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAnimationCancelledByScroll) {
// Animating for the snap.
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::TimeDelta::FromMilliseconds(100));
- EXPECT_TRUE(host_impl_->IsAnimatingForSnap());
+ EXPECT_TRUE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
@@ -2273,10 +2734,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAnimationCancelledByScroll) {
begin_state->data()->delta_granularity =
ui::ScrollGranularity::kScrollByPrecisePixel;
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_->ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel)
.thread);
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
@@ -2298,7 +2759,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
gfx::Point pointer_position(10, 10);
gfx::Vector2dF x_delta(50, 0);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, x_delta,
ui::ScrollInputType::kWheel)
@@ -2311,14 +2772,14 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
host_impl_->ScrollUpdate(
UpdateState(pointer_position, x_delta, ui::ScrollInputType::kWheel)
.get());
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
host_impl_->ScrollEnd(true);
// No animation is created, but the snap target should be updated.
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId()),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
@@ -2327,13 +2788,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
BeginImplFrameAndAnimate(begin_frame_args, start_time);
// We are already at a snap target so we should not animate for snap.
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
// Verify that we are not actually animating by running one frame and ensuring
// scroll offset has not changed.
BeginImplFrameAndAnimate(begin_frame_args,
start_time + base::TimeDelta::FromMilliseconds(100));
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 0), CurrentScrollOffset(overflow));
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId()),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
@@ -2348,7 +2809,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
// Should be (10, 10) in the scroller's coordinate.
gfx::Point pointer_position(2, 2);
gfx::Vector2dF delta(4, 4);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, delta,
ui::ScrollInputType::kWheel)
@@ -2366,7 +2827,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
gfx::Vector2dF initial_offset, target_offset;
EXPECT_TRUE(host_impl_->GetSnapFlingInfoAndSetAnimatingSnapTarget(
gfx::Vector2dF(10, 10), &initial_offset, &target_offset));
- EXPECT_TRUE(host_impl_->IsAnimatingForSnap());
+ EXPECT_TRUE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_VECTOR_EQ(gfx::Vector2dF(4, 4), initial_offset);
EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), target_offset);
// Snap targets shouldn't be set until the fling animation is complete.
@@ -2374,7 +2835,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
host_impl_->ScrollEndForSnapFling(true /* did_finish */);
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId(10)),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
@@ -2388,7 +2849,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
// Should be (10, 10) in the scroller's coordinate.
gfx::Point pointer_position(2, 2);
gfx::Vector2dF delta(4, 4);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, delta,
ui::ScrollInputType::kWheel)
@@ -2406,7 +2867,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
gfx::Vector2dF initial_offset, target_offset;
EXPECT_TRUE(host_impl_->GetSnapFlingInfoAndSetAnimatingSnapTarget(
gfx::Vector2dF(10, 10), &initial_offset, &target_offset));
- EXPECT_TRUE(host_impl_->IsAnimatingForSnap());
+ EXPECT_TRUE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_VECTOR_EQ(gfx::Vector2dF(4, 4), initial_offset);
EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), target_offset);
// Snap targets shouldn't be set until the fling animation is complete.
@@ -2415,7 +2876,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
// The snap targets should not be set if the snap fling did not finish.
host_impl_->ScrollEndForSnapFling(false /* did_finish */);
- EXPECT_FALSE(host_impl_->IsAnimatingForSnap());
+ EXPECT_FALSE(host_impl_->GetInputHandler().animating_for_snap_for_testing());
EXPECT_EQ(TargetSnapAreaElementIds(),
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
@@ -2440,7 +2901,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
gfx::Vector2dF diagonal_delta(-10, -10);
// OverscrollBehaviorTypeAuto shouldn't prevent scroll propagation.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, x_delta,
ui::ScrollInputType::kWheel)
@@ -2465,7 +2926,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
// OverscrollBehaviorContain on x should prevent propagations of scroll
// on x.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, x_delta,
ui::ScrollInputType::kWheel)
@@ -2484,7 +2945,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
// OverscrollBehaviorContain on x shouldn't prevent propagations of
// scroll on y.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, y_delta,
ui::ScrollInputType::kWheel)
@@ -2503,7 +2964,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
// A scroll update with both x & y delta will adhere to the most restrictive
// case.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, diagonal_delta,
ui::ScrollInputType::kWheel)
@@ -2529,7 +2990,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
// OverscrollBehaviorContain on y shouldn't prevent propagations of
// scroll on x.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, x_delta,
ui::ScrollInputType::kWheel)
@@ -2548,7 +3009,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
// OverscrollBehaviorContain on y should prevent propagations of scroll
// on y.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, y_delta,
ui::ScrollInputType::kWheel)
@@ -2567,7 +3028,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
// A scroll update with both x & y delta will adhere to the most restrictive
// case.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, diagonal_delta,
ui::ScrollInputType::kWheel)
@@ -2592,7 +3053,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
DrawFrame();
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(pointer_position, x_delta,
ui::ScrollInputType::kWheel)
@@ -2634,7 +3095,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) {
gfx::Point scroll_position(10, 10);
gfx::Vector2dF scroll_delta(10, 10);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(scroll_position, scroll_delta,
ui::ScrollInputType::kWheel)
@@ -2655,7 +3116,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) {
DrawFrame();
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(scroll_position, scroll_delta,
ui::ScrollInputType::kWheel)
@@ -2675,7 +3136,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) {
GetScrollNode(overflow)->user_scrollable_vertical = false;
DrawFrame();
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(scroll_position, scroll_delta,
ui::ScrollInputType::kWheel)
@@ -2711,7 +3172,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNodeWithoutScrollLayer) {
ui::ScrollInputType::kWheel);
if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::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
@@ -2719,7 +3180,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNodeWithoutScrollLayer) {
// 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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
status.main_thread_scrolling_reasons);
}
@@ -2990,9 +3451,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ImplPinchZoom) {
EXPECT_TRUE(did_request_commit_);
EXPECT_EQ(gfx::Size(50, 50), root_layer()->bounds());
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta);
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta);
EXPECT_EQ(gfx::ScrollOffset(75.0, 75.0), MaxScrollOffset(scroll_layer));
}
@@ -3016,7 +3477,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ImplPinchZoom) {
host_impl_->ScrollEnd();
gfx::Vector2d scroll_delta(0, 10);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kWheel)
@@ -3028,10 +3489,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ImplPinchZoom) {
.get());
host_impl_->ScrollEnd();
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
EXPECT_TRUE(ScrollInfoContains(
- *scroll_info.get(), scroll_layer->element_id(),
+ *commit_data.get(), scroll_layer->element_id(),
gfx::ScrollOffset(0, scroll_delta.y() / page_scale_delta)));
}
}
@@ -3444,7 +3905,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithSwapPromises) {
new LatencyInfoSwapPromise(latency_info));
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen)
@@ -3458,10 +3919,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithSwapPromises) {
std::move(swap_promise));
host_impl_->ScrollEnd();
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(1u, scroll_info->swap_promises.size());
- EXPECT_EQ(latency_info.trace_id(), scroll_info->swap_promises[0]->TraceId());
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(1u, commit_data->swap_promises.size());
+ EXPECT_EQ(latency_info.trace_id(), commit_data->swap_promises[0]->TraceId());
}
// Test that scrolls targeting a layer with a non-null scroll_parent() don't
@@ -3578,9 +4039,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) {
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta);
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta);
}
// Zoom-in clamping
@@ -3599,9 +4060,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) {
host_impl_->PinchGestureEnd(gfx::Point(50, 50), true);
host_impl_->ScrollEnd();
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(scroll_info->page_scale_delta, max_page_scale);
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(commit_data->page_scale_delta, max_page_scale);
}
// Zoom-out clamping
@@ -3627,11 +4088,11 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) {
host_impl_->PinchGestureEnd(gfx::Point(), true);
host_impl_->ScrollEnd();
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(scroll_info->page_scale_delta, min_page_scale);
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(commit_data->page_scale_delta, min_page_scale);
- EXPECT_TRUE(scroll_info->scrolls.empty());
+ EXPECT_TRUE(commit_data->scrolls.empty());
}
// Two-finger panning should not happen based on pinch events only
@@ -3658,10 +4119,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) {
host_impl_->PinchGestureEnd(gfx::Point(20, 20), true);
host_impl_->ScrollEnd();
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta);
- EXPECT_TRUE(scroll_info->scrolls.empty());
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta);
+ EXPECT_TRUE(commit_data->scrolls.empty());
}
// Two-finger panning should work with interleaved scroll events
@@ -3693,10 +4154,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) {
host_impl_->PinchGestureEnd(gfx::Point(20, 20), true);
host_impl_->ScrollEnd();
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta);
- EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->element_id(),
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta);
+ EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(),
gfx::ScrollOffset(-10, -10)));
}
@@ -3731,10 +4192,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) {
host_impl_->PinchGestureEnd(gfx::Point(10, 10), true);
host_impl_->ScrollEnd();
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(scroll_info->page_scale_delta, 2);
- EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->element_id(),
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(commit_data->page_scale_delta, 2);
+ EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(),
gfx::ScrollOffset(10, 10)));
}
}
@@ -3775,10 +4236,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SyncSubpixelScrollDelta) {
host_impl_->PinchGestureEnd(gfx::Point(10, 9), true);
host_impl_->ScrollEnd();
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta);
- EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->element_id(),
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta);
+ EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(),
gfx::ScrollOffset(0, -1)));
// Verify this scroll delta is consistent with the snapped position of the
@@ -3823,9 +4284,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
scroll_layer->element_id());
EXPECT_VECTOR_EQ(active_base, gfx::Vector2dF(0, 20.5));
// Fractional active base should not affect the scroll delta.
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_VECTOR_EQ(scroll_info->inner_viewport_scroll.scroll_delta,
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_VECTOR_EQ(commit_data->inner_viewport_scroll.scroll_delta,
gfx::Vector2dF(0, -1));
}
@@ -3891,9 +4352,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
EXPECT_FALSE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(scroll_info->page_scale_delta, 1);
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(commit_data->page_scale_delta, 1);
}
start_time += base::TimeDelta::FromSeconds(10);
@@ -3946,9 +4407,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
EXPECT_FALSE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta);
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(commit_data->page_scale_delta, page_scale_delta);
}
}
@@ -4020,10 +4481,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PageScaleAnimation) {
EXPECT_FALSE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(scroll_info->page_scale_delta, 2);
- EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->element_id(),
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(commit_data->page_scale_delta, 2);
+ EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(),
gfx::ScrollOffset(-50, -50)));
}
@@ -4072,11 +4533,11 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PageScaleAnimation) {
EXPECT_TRUE(did_request_commit_);
host_impl_->DidFinishImplFrame(begin_frame_args);
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(scroll_info->page_scale_delta, min_page_scale);
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(commit_data->page_scale_delta, min_page_scale);
// Pushed to (0,0) via clamping against contents layer size.
- EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->element_id(),
+ EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(),
gfx::ScrollOffset(-50, -50)));
}
}
@@ -4132,10 +4593,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PageScaleAnimationNoOp) {
EXPECT_TRUE(did_request_commit_);
host_impl_->DidFinishImplFrame(begin_frame_args);
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(scroll_info->page_scale_delta, 1);
- ExpectNone(*scroll_info, scroll_layer->element_id());
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(commit_data->page_scale_delta, 1);
+ ExpectNone(*commit_data, scroll_layer->element_id());
}
}
@@ -4257,10 +4718,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
EXPECT_FALSE(did_request_next_frame_);
host_impl_->DidFinishImplFrame(begin_frame_args);
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_EQ(scroll_info->page_scale_delta, target_scale);
- EXPECT_TRUE(ScrollInfoContains(*scroll_info, scroll_layer->element_id(),
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_EQ(commit_data->page_scale_delta, target_scale);
+ EXPECT_TRUE(ScrollInfoContains(*commit_data, scroll_layer->element_id(),
gfx::ScrollOffset(-50, -50)));
}
@@ -4352,7 +4813,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
// TODO(bokan): Unfortunately, Mac currently doesn't support smooth scrolling
// wheel events. https://crbug.com/574283.
-#if defined(OS_MACOSX)
+#if defined(OS_MAC)
std::vector<ui::ScrollInputType> types = {ui::ScrollInputType::kScrollbar};
#else
std::vector<ui::ScrollInputType> types = {ui::ScrollInputType::kScrollbar,
@@ -4996,7 +5457,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestOnScrollbar) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
host_impl_->ScrollEnd();
}
@@ -5007,7 +5468,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestOnScrollbar) {
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kScrollbarScrolling,
status.main_thread_scrolling_reasons);
}
@@ -5019,7 +5480,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestOnScrollbar) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
host_impl_->ScrollEnd();
@@ -5032,7 +5493,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestOnScrollbar) {
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kScrollbarScrolling,
status.main_thread_scrolling_reasons);
}
@@ -5502,7 +5963,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, CompositorFrameMetadata) {
}
// Scrolling should update metadata immediately.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -5543,7 +6004,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, CompositorFrameMetadata) {
}
// Likewise if set from the main thread.
- host_impl_->ProcessScrollDeltas();
+ host_impl_->ProcessCompositorDeltas();
host_impl_->active_tree()->PushPageScaleFromMainThread(4, 0.5f, 4);
host_impl_->active_tree()->SetPageScaleOnActiveTree(4);
{
@@ -6066,7 +6527,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollRootIgnored) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_IGNORED, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
status.main_thread_scrolling_reasons);
EXPECT_FALSE(did_request_redraw_);
@@ -6199,7 +6660,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
EXPECT_VIEWPORT_GEOMETRIES(1);
EXPECT_EQ(gfx::SizeF(50, 50), active_tree->ScrollableSize());
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 25),
ui::ScrollInputType::kTouchscreen)
@@ -6274,7 +6735,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
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,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 25),
ui::ScrollInputType::kTouchscreen)
@@ -6353,7 +6814,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
EXPECT_VIEWPORT_GEOMETRIES(1.0f);
EXPECT_EQ(gfx::SizeF(200, 1000), active_tree->ScrollableSize());
- ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 25),
ui::ScrollInputType::kTouchscreen)
@@ -6403,7 +6864,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
EXPECT_EQ(gfx::Size(50, 15), scrollbar_layer->bounds());
EXPECT_EQ(gfx::Rect(20, 0, 10, 3), scrollbar_layer->ComputeThumbQuadRect());
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 25),
ui::ScrollInputType::kTouchscreen)
@@ -6458,7 +6919,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
gfx::Vector2dF top_controls_scroll_delta(0, 5.25f);
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), top_controls_scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -6498,7 +6959,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
outer_scroll->SetDrawsContent(true);
host_impl_->active_tree()->PushPageScaleFromMainThread(2, 1, 2);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 50),
ui::ScrollInputType::kTouchscreen)
@@ -6526,7 +6987,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
host_impl_->ScrollEnd();
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, -50),
ui::ScrollInputType::kTouchscreen)
@@ -6587,7 +7048,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) {
gfx::Vector2dF top_controls_scroll_delta(0, 20);
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), top_controls_scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -6610,7 +7071,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) {
// Scroll past the maximum extent. The delta shouldn't be greater than the
// browser controls height.
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), top_controls_scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -6628,7 +7089,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) {
// Scroll in the direction to make the browser controls show.
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), -top_controls_scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -6701,7 +7162,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
// Scroll 25px to hide browser controls
gfx::Vector2dF scroll_delta(0, 25);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -6902,7 +7363,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
// Hide the browser controls by 25px.
gfx::Vector2dF scroll_delta(0, 25);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -6933,7 +7394,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
// Bring the browser controls down by 25px.
scroll_delta = gfx::Vector2dF(0, -25);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -6967,7 +7428,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest, BrowserControlsAspectRatio) {
host_impl_->browser_controls_manager()->ContentTopOffset());
gfx::Vector2dF scroll_delta(0, 25);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -7009,7 +7470,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
// Send a gesture scroll that will scroll the outer viewport, make sure the
// browser controls get scrolled.
gfx::Vector2dF scroll_delta(0, 15);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -7030,7 +7491,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
host_impl_->browser_controls_manager()->ContentTopOffset());
scroll_delta = gfx::Vector2dF(0, 50);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -7053,7 +7514,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
SetScrollOffsetDelta(InnerViewportScrollLayer(), inner_viewport_offset);
scroll_delta = gfx::Vector2dF(0, -65);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -7079,7 +7540,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
layer_size_, layer_size_, layer_size_);
DrawFrame();
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 50),
ui::ScrollInputType::kTouchscreen)
@@ -7098,7 +7559,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
host_impl_->ScrollEnd();
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, -25),
ui::ScrollInputType::kTouchscreen)
@@ -7130,7 +7591,7 @@ TEST_P(LayerTreeHostImplBrowserControlsTest,
// Verify the layer is once-again non-scrollable.
EXPECT_EQ(gfx::ScrollOffset(), MaxScrollOffset(InnerViewportScrollLayer()));
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kTouchscreen)
@@ -7291,7 +7752,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNonCompositedRoot) {
DrawFrame();
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -7320,7 +7781,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollChildCallsCommitAndRedraw) {
DrawFrame();
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -7349,7 +7810,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollMissesChild) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode(),
host_impl_->OuterViewportScrollNode());
@@ -7379,7 +7840,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollMissesBackfacingChild) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode(),
host_impl_->OuterViewportScrollNode());
@@ -7408,7 +7869,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollLayerWithMainThreadReason) {
.get(),
ui::ScrollInputType::kWheel);
if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_FALSE(status.needs_main_thread_hit_test);
EXPECT_EQ(
MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
@@ -7416,7 +7877,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollLayerWithMainThreadReason) {
} 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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
status.main_thread_scrolling_reasons);
}
@@ -7436,7 +7897,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
gfx::ScrollOffset expected_scroll_delta(scroll_delta);
LayerImpl* outer_scroll = OuterViewportScrollLayer();
gfx::ScrollOffset expected_max_scroll = MaxScrollOffset(outer_scroll);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kWheel)
@@ -7452,10 +7913,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
float page_scale = 2;
host_impl_->active_tree()->PushPageScaleFromMainThread(page_scale, 1, 2);
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
LayerImpl* inner_scroll = InnerViewportScrollLayer();
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(), inner_scroll->element_id(),
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), inner_scroll->element_id(),
expected_scroll_delta));
// The scroll range should also have been updated.
@@ -7480,7 +7941,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
gfx::ScrollOffset expected_scroll_delta(scroll_delta);
LayerImpl* outer_scroll = OuterViewportScrollLayer();
gfx::ScrollOffset expected_max_scroll = MaxScrollOffset(outer_scroll);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kWheel)
@@ -7506,10 +7967,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
DrawOneFrame();
// The scroll delta is not scaled because the main thread did not scale.
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
LayerImpl* inner_scroll = InnerViewportScrollLayer();
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(), inner_scroll->element_id(),
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), inner_scroll->element_id(),
expected_scroll_delta));
// The scroll range should also have been updated.
@@ -7581,7 +8042,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
gfx::Vector2d scroll_delta(0, 10);
gfx::ScrollOffset expected_scroll_delta(scroll_delta);
gfx::ScrollOffset expected_max_scroll(MaxScrollOffset(outer_scroll));
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kWheel)
@@ -7598,9 +8059,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
page_scale);
DrawOneFrame();
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(), inner_scroll->element_id(),
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), inner_scroll->element_id(),
expected_scroll_delta));
// The scroll range should not have changed.
@@ -7639,7 +8100,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollChildBeyondLimit) {
DrawFrame();
{
gfx::Vector2d scroll_delta(-8, -7);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kWheel)
@@ -7651,16 +8112,16 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollChildBeyondLimit) {
.get());
host_impl_->ScrollEnd();
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
// The grand child should have scrolled up to its limit.
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(),
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(),
grand_child_layer->element_id(),
gfx::ScrollOffset(0, -5)));
// The child should not have scrolled.
- ExpectNone(*scroll_info.get(), child_layer->element_id());
+ ExpectNone(*commit_data.get(), child_layer->element_id());
}
}
@@ -7697,7 +8158,7 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedLatchToChild) {
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, -100),
ui::ScrollInputType::kWheel)
@@ -7781,7 +8242,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutBubbling) {
DrawFrame();
{
gfx::Vector2d scroll_delta(0, -10);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -7793,20 +8254,20 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutBubbling) {
.get());
host_impl_->ScrollEnd();
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
// The grand child should have scrolled up to its limit.
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(),
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(),
grand_child_layer->element_id(),
gfx::ScrollOffset(0, -2)));
// The child should not have scrolled.
- ExpectNone(*scroll_info.get(), child_layer->element_id());
+ ExpectNone(*commit_data.get(), child_layer->element_id());
// The next time we scroll we should only scroll the parent.
scroll_delta = gfx::Vector2d(0, -3);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -7822,22 +8283,22 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutBubbling) {
child_layer->scroll_tree_index());
host_impl_->ScrollEnd();
- scroll_info = host_impl_->ProcessScrollDeltas();
+ commit_data = host_impl_->ProcessCompositorDeltas();
// The child should have scrolled up to its limit.
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(),
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(),
child_layer->element_id(),
gfx::ScrollOffset(0, -3)));
// The grand child should not have scrolled.
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(),
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(),
grand_child_layer->element_id(),
gfx::ScrollOffset(0, -2)));
// After scrolling the parent, another scroll on the opposite direction
// should still scroll the child.
scroll_delta = gfx::Vector2d(0, 7);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -7853,15 +8314,15 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutBubbling) {
grand_child_layer->scroll_tree_index());
host_impl_->ScrollEnd();
- scroll_info = host_impl_->ProcessScrollDeltas();
+ commit_data = host_impl_->ProcessCompositorDeltas();
// The grand child should have scrolled.
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(),
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(),
grand_child_layer->element_id(),
gfx::ScrollOffset(0, 5)));
// The child should not have scrolled.
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(),
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(),
child_layer->element_id(),
gfx::ScrollOffset(0, -3)));
@@ -7870,7 +8331,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutBubbling) {
host_impl_->active_tree()->SetPageScaleOnActiveTree(2);
scroll_delta = gfx::Vector2d(0, -2);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(1, 1), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -7884,10 +8345,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutBubbling) {
.get());
host_impl_->ScrollEnd();
- scroll_info = host_impl_->ProcessScrollDeltas();
+ commit_data = host_impl_->ProcessCompositorDeltas();
// Should have scrolled by half the amount in layer space (5 - 2/2)
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(),
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(),
grand_child_layer->element_id(),
gfx::ScrollOffset(0, 4)));
}
@@ -7915,7 +8376,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
gfx::ScrollOffset scroll_delta(0, 4);
// Scrolling should be able to happen on the compositor thread here.
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5),
gfx::ScrollOffsetToVector2dF(scroll_delta),
@@ -7929,13 +8390,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
.get());
host_impl_->ScrollEnd();
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
// The outer viewport should have scrolled.
- ASSERT_EQ(scroll_info->scrolls.size(), 1u);
+ ASSERT_EQ(commit_data->scrolls.size(), 1u);
EXPECT_TRUE(ScrollInfoContains(
- *scroll_info.get(), host_impl_->OuterViewportScrollNode()->element_id,
+ *commit_data.get(), host_impl_->OuterViewportScrollNode()->element_id,
scroll_delta));
}
}
@@ -7958,7 +8419,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollEventBubbling) {
{
gfx::ScrollOffset scroll_delta(0, 4);
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5),
gfx::ScrollOffsetToVector2dF(scroll_delta),
@@ -7972,13 +8433,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollEventBubbling) {
.get());
host_impl_->ScrollEnd();
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
// Only the root scroll should have scrolled.
- ASSERT_EQ(scroll_info->scrolls.size(), 1u);
+ ASSERT_EQ(commit_data->scrolls.size(), 1u);
EXPECT_TRUE(ScrollInfoContains(
- *scroll_info.get(), host_impl_->OuterViewportScrollNode()->element_id,
+ *commit_data.get(), host_impl_->OuterViewportScrollNode()->element_id,
scroll_delta));
}
}
@@ -7998,7 +8459,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBeforeRedraw) {
// Scrolling should still work even though we did not draw yet.
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -8021,7 +8482,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
// Scroll to the right in screen coordinates with a gesture.
gfx::Vector2d gesture_scroll_delta(10, 0);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gesture_scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -8034,16 +8495,16 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
host_impl_->ScrollEnd();
// The layer should have scrolled down in its local coordinates.
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
EXPECT_TRUE(
- ScrollInfoContains(*scroll_info.get(), scroll_layer->element_id(),
+ ScrollInfoContains(*commit_data.get(), scroll_layer->element_id(),
gfx::ScrollOffset(0, gesture_scroll_delta.x())));
// Reset and scroll down with the wheel.
SetScrollOffsetDelta(scroll_layer, gfx::Vector2dF());
gfx::ScrollOffset wheel_scroll_delta(0, 10);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(
BeginState(gfx::Point(),
@@ -8060,8 +8521,8 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
host_impl_->ScrollEnd();
// The layer should have scrolled down in its local coordinates.
- scroll_info = host_impl_->ProcessScrollDeltas();
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(), scroll_layer->element_id(),
+ commit_data = host_impl_->ProcessCompositorDeltas();
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), scroll_layer->element_id(),
wheel_scroll_delta));
}
@@ -8100,7 +8561,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) {
// Scroll down in screen coordinates with a gesture.
gfx::Vector2d gesture_scroll_delta(0, 10);
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(1, 1), gesture_scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -8117,21 +8578,21 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) {
gfx::ScrollOffset expected_scroll_delta(
0, std::floor(gesture_scroll_delta.y() *
std::cos(gfx::DegToRad(child_layer_angle))));
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(), child_scroll_id,
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), child_scroll_id,
expected_scroll_delta));
// The root scroll layer should not have scrolled, because the input delta
// was close to the layer's axis of movement.
- EXPECT_EQ(scroll_info->scrolls.size(), 1u);
+ EXPECT_EQ(commit_data->scrolls.size(), 1u);
}
{
// Now reset and scroll the same amount horizontally.
SetScrollOffsetDelta(child, gfx::Vector2dF());
gfx::Vector2d gesture_scroll_delta(10, 0);
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(1, 1), gesture_scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -8148,13 +8609,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) {
gfx::ScrollOffset expected_scroll_delta(
0, std::floor(-gesture_scroll_delta.x() *
std::sin(gfx::DegToRad(child_layer_angle))));
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(), child_scroll_id,
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), child_scroll_id,
expected_scroll_delta));
// The root scroll layer shouldn't have scrolled.
- ExpectNone(*scroll_info.get(), scroll_layer->element_id());
+ ExpectNone(*commit_data.get(), scroll_layer->element_id());
}
}
@@ -8186,7 +8647,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollPerspectiveTransformedLayer) {
UpdateDrawProperties(host_impl_->active_tree());
- std::unique_ptr<ScrollAndScaleSet> scroll_info;
+ std::unique_ptr<CompositorCommitData> commit_data;
gfx::ScrollOffset gesture_scroll_deltas[4];
gesture_scroll_deltas[0] = gfx::ScrollOffset(4, 10);
@@ -8212,7 +8673,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollPerspectiveTransformedLayer) {
for (int i = 0; i < 4; ++i) {
SetScrollOffsetDelta(child, gfx::Vector2dF());
DrawFrame();
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(viewport_point,
gfx::ScrollOffsetToVector2dF(
@@ -8230,13 +8691,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollPerspectiveTransformedLayer) {
gfx::ScrollOffsetToFlooredVector2d(gesture_scroll_deltas[i]);
host_impl_->ScrollEnd();
- scroll_info = host_impl_->ProcessScrollDeltas();
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(), child->element_id(),
+ commit_data = host_impl_->ProcessCompositorDeltas();
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), child->element_id(),
expected_scroll_deltas[i]));
// The root scroll layer should not have scrolled, because the input delta
// was close to the layer's axis of movement.
- EXPECT_EQ(scroll_info->scrolls.size(), 1u);
+ EXPECT_EQ(commit_data->scrolls.size(), 1u);
}
}
@@ -8254,7 +8715,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollScaledLayer) {
// Scroll down in screen coordinates with a gesture.
gfx::Vector2d scroll_delta(0, 10);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -8268,16 +8729,16 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollScaledLayer) {
// The layer should have scrolled down in its local coordinates, but half the
// amount.
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
EXPECT_TRUE(
- ScrollInfoContains(*scroll_info.get(), scroll_layer->element_id(),
+ ScrollInfoContains(*commit_data.get(), scroll_layer->element_id(),
gfx::ScrollOffset(0, scroll_delta.y() / scale)));
// Reset and scroll down with the wheel.
SetScrollOffsetDelta(scroll_layer, gfx::Vector2dF());
gfx::ScrollOffset wheel_scroll_delta(0, 10);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(
BeginState(gfx::Point(),
@@ -8294,8 +8755,8 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollScaledLayer) {
host_impl_->ScrollEnd();
// It should apply the scale factor to the scroll delta for the wheel event.
- scroll_info = host_impl_->ProcessScrollDeltas();
- EXPECT_TRUE(ScrollInfoContains(*scroll_info.get(), scroll_layer->element_id(),
+ commit_data = host_impl_->ProcessCompositorDeltas();
+ EXPECT_TRUE(ScrollInfoContains(*commit_data.get(), scroll_layer->element_id(),
wheel_scroll_delta));
}
@@ -8382,7 +8843,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, RootLayerScrollOffsetDelegation) {
gfx::Vector2dF scroll_delta(0, 10);
gfx::ScrollOffset current_offset(7, 8);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -8498,7 +8959,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ViewportUserScrollable) {
auto begin_state = BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(begin_state.get(), ui::ScrollInputType::kTouchscreen)
.thread);
@@ -8541,7 +9002,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ViewportUserScrollable) {
auto begin_state =
BeginState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel);
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_->ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel)
.thread);
@@ -8760,10 +9221,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
DrawFrame();
- EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
// In-bounds scrolling does not affect overscroll.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -8777,7 +9240,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(), scroll_result.unused_scroll_delta);
- EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
// Overscroll events are reflected immediately.
scroll_result =
@@ -8787,9 +9252,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, 10), scroll_result.unused_scroll_delta);
- EXPECT_EQ(gfx::Vector2dF(0, 10), host_impl_->accumulated_root_overscroll());
- EXPECT_EQ(scroll_result.accumulated_root_overscroll,
- host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(0, 10),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
+ EXPECT_EQ(
+ scroll_result.accumulated_root_overscroll,
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
// In-bounds scrolling resets accumulated overscroll for the scrolled axes.
scroll_result =
@@ -8799,9 +9267,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(), scroll_result.unused_scroll_delta);
- EXPECT_EQ(gfx::Vector2dF(0, 0), host_impl_->accumulated_root_overscroll());
- EXPECT_EQ(scroll_result.accumulated_root_overscroll,
- host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(0, 0),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
+ EXPECT_EQ(
+ scroll_result.accumulated_root_overscroll,
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result =
host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -10),
@@ -8810,9 +9281,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
EXPECT_FALSE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, -10), scroll_result.unused_scroll_delta);
- EXPECT_EQ(gfx::Vector2dF(0, -10), host_impl_->accumulated_root_overscroll());
- EXPECT_EQ(scroll_result.accumulated_root_overscroll,
- host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(0, -10),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
+ EXPECT_EQ(
+ scroll_result.accumulated_root_overscroll,
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result =
host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(10, 0),
@@ -8821,9 +9295,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, 0), scroll_result.unused_scroll_delta);
- EXPECT_EQ(gfx::Vector2dF(0, -10), host_impl_->accumulated_root_overscroll());
- EXPECT_EQ(scroll_result.accumulated_root_overscroll,
- host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(0, -10),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
+ EXPECT_EQ(
+ scroll_result.accumulated_root_overscroll,
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result =
host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(-15, 0),
@@ -8832,9 +9309,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(-5, 0), scroll_result.unused_scroll_delta);
- EXPECT_EQ(gfx::Vector2dF(-5, -10), host_impl_->accumulated_root_overscroll());
- EXPECT_EQ(scroll_result.accumulated_root_overscroll,
- host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(-5, -10),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
+ EXPECT_EQ(
+ scroll_result.accumulated_root_overscroll,
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result =
host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 60),
@@ -8843,9 +9323,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, 10), scroll_result.unused_scroll_delta);
- EXPECT_EQ(gfx::Vector2dF(-5, 10), host_impl_->accumulated_root_overscroll());
- EXPECT_EQ(scroll_result.accumulated_root_overscroll,
- host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(-5, 10),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
+ EXPECT_EQ(
+ scroll_result.accumulated_root_overscroll,
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result =
host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(10, -60),
@@ -8854,9 +9337,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, -10), scroll_result.unused_scroll_delta);
- EXPECT_EQ(gfx::Vector2dF(0, -10), host_impl_->accumulated_root_overscroll());
- EXPECT_EQ(scroll_result.accumulated_root_overscroll,
- host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(0, -10),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
+ EXPECT_EQ(
+ scroll_result.accumulated_root_overscroll,
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
// Overscroll accumulates within the scope of ScrollBegin/ScrollEnd as long
// as no scroll occurs.
@@ -8867,9 +9353,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
EXPECT_FALSE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, -20), scroll_result.unused_scroll_delta);
- EXPECT_EQ(gfx::Vector2dF(0, -30), host_impl_->accumulated_root_overscroll());
- EXPECT_EQ(scroll_result.accumulated_root_overscroll,
- host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(0, -30),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
+ EXPECT_EQ(
+ scroll_result.accumulated_root_overscroll,
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result =
host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -20),
@@ -8878,9 +9367,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
EXPECT_FALSE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, -20), scroll_result.unused_scroll_delta);
- EXPECT_EQ(gfx::Vector2dF(0, -50), host_impl_->accumulated_root_overscroll());
- EXPECT_EQ(scroll_result.accumulated_root_overscroll,
- host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(0, -50),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
+ EXPECT_EQ(
+ scroll_result.accumulated_root_overscroll,
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
// Overscroll resets on valid scroll.
scroll_result =
@@ -8890,9 +9382,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, 0), scroll_result.unused_scroll_delta);
- EXPECT_EQ(gfx::Vector2dF(0, 0), host_impl_->accumulated_root_overscroll());
- EXPECT_EQ(scroll_result.accumulated_root_overscroll,
- host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(0, 0),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
+ EXPECT_EQ(
+ scroll_result.accumulated_root_overscroll,
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
scroll_result =
host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, -20),
@@ -8901,9 +9396,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF(0, -10), scroll_result.unused_scroll_delta);
- EXPECT_EQ(gfx::Vector2dF(0, -10), host_impl_->accumulated_root_overscroll());
- EXPECT_EQ(scroll_result.accumulated_root_overscroll,
- host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(0, -10),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
+ EXPECT_EQ(
+ scroll_result.accumulated_root_overscroll,
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
host_impl_->ScrollEnd();
}
@@ -8937,7 +9435,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
DrawFrame();
{
gfx::Vector2d scroll_delta(0, -10);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -8952,13 +9450,14 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
grand_child_layer->scroll_tree_index());
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
- EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(gfx::Vector2dF(), host_impl_->GetInputHandler()
+ .accumulated_root_overscroll_for_testing());
host_impl_->ScrollEnd();
// The next time we scroll we should only scroll the parent, but overscroll
// should still not reach the root layer.
scroll_delta = gfx::Vector2d(0, -30);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -8967,10 +9466,11 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
.thread);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id,
child_layer->scroll_tree_index());
- EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(gfx::Vector2dF(), host_impl_->GetInputHandler()
+ .accumulated_root_overscroll_for_testing());
host_impl_->ScrollEnd();
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -8985,13 +9485,14 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id,
child_layer->scroll_tree_index());
- EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(gfx::Vector2dF(), host_impl_->GetInputHandler()
+ .accumulated_root_overscroll_for_testing());
host_impl_->ScrollEnd();
// After scrolling the parent, another scroll on the opposite direction
// should scroll the child.
scroll_delta = gfx::Vector2d(0, 70);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -9008,7 +9509,8 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(host_impl_->CurrentlyScrollingNode()->id,
grand_child_layer->scroll_tree_index());
- EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(gfx::Vector2dF(), host_impl_->GetInputHandler()
+ .accumulated_root_overscroll_for_testing());
host_impl_->ScrollEnd();
}
}
@@ -9022,7 +9524,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollChildEventBubbling) {
DrawFrame();
{
gfx::Vector2d scroll_delta(0, 8);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(5, 5), scroll_delta,
ui::ScrollInputType::kWheel)
@@ -9034,19 +9536,24 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollChildEventBubbling) {
.get());
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
- EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(gfx::Vector2dF(), host_impl_->GetInputHandler()
+ .accumulated_root_overscroll_for_testing());
scroll_result = host_impl_->ScrollUpdate(
UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel)
.get());
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
- EXPECT_EQ(gfx::Vector2dF(0, 6), host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(gfx::Vector2dF(0, 6),
+ host_impl_->GetInputHandler()
+ .accumulated_root_overscroll_for_testing());
scroll_result = host_impl_->ScrollUpdate(
UpdateState(gfx::Point(), scroll_delta, ui::ScrollInputType::kWheel)
.get());
EXPECT_FALSE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
- EXPECT_EQ(gfx::Vector2dF(0, 14), host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(gfx::Vector2dF(0, 14),
+ host_impl_->GetInputHandler()
+ .accumulated_root_overscroll_for_testing());
host_impl_->ScrollEnd();
}
}
@@ -9061,10 +9568,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollAlways) {
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
DrawFrame();
- EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
// Even though the layer can't scroll the overscroll still happens.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -9077,7 +9586,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollAlways) {
.get());
EXPECT_FALSE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
- EXPECT_EQ(gfx::Vector2dF(0, 10), host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(0, 10),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
}
TEST_P(ScrollUnifiedLayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) {
@@ -9091,7 +9602,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) {
// of the content. unnecessary glow effect calls shouldn't be
// called while scrolling up without reaching the edge of the content.
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 100),
ui::ScrollInputType::kWheel)
@@ -9105,7 +9616,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) {
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF().ToString(),
- host_impl_->accumulated_root_overscroll().ToString());
+ host_impl_->GetInputHandler()
+ .accumulated_root_overscroll_for_testing()
+ .ToString());
scroll_result = host_impl_->ScrollUpdate(
UpdateState(gfx::Point(), gfx::Vector2dF(0, -2.30f),
ui::ScrollInputType::kWheel)
@@ -9113,12 +9626,14 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) {
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF().ToString(),
- host_impl_->accumulated_root_overscroll().ToString());
+ host_impl_->GetInputHandler()
+ .accumulated_root_overscroll_for_testing()
+ .ToString());
host_impl_->ScrollEnd();
// unusedrootDelta should be subtracted from applied delta so that
// unwanted glow effect calls are not called.
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 20),
ui::ScrollInputType::kTouchscreen)
@@ -9132,7 +9647,8 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) {
EXPECT_TRUE(scroll_result.did_scroll);
EXPECT_TRUE(scroll_result.did_overscroll_root);
EXPECT_VECTOR2DF_EQ(gfx::Vector2dF(0.000000f, 17.699997f),
- host_impl_->accumulated_root_overscroll());
+ host_impl_->GetInputHandler()
+ .accumulated_root_overscroll_for_testing());
scroll_result = host_impl_->ScrollUpdate(
UpdateState(gfx::Point(), gfx::Vector2dF(0.02f, -0.01f),
@@ -9141,11 +9657,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) {
EXPECT_FALSE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_VECTOR2DF_EQ(gfx::Vector2dF(0.000000f, 17.699997f),
- host_impl_->accumulated_root_overscroll());
+ host_impl_->GetInputHandler()
+ .accumulated_root_overscroll_for_testing());
host_impl_->ScrollEnd();
// TestCase to check kEpsilon, which prevents minute values to trigger
// gloweffect without reaching edge.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(
BeginState(gfx::Point(0, 0), gfx::Vector2dF(-0.12f, 0.1f),
@@ -9160,7 +9677,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) {
EXPECT_FALSE(scroll_result.did_scroll);
EXPECT_FALSE(scroll_result.did_overscroll_root);
EXPECT_EQ(gfx::Vector2dF().ToString(),
- host_impl_->accumulated_root_overscroll().ToString());
+ host_impl_->GetInputHandler()
+ .accumulated_root_overscroll_for_testing()
+ .ToString());
host_impl_->ScrollEnd();
}
}
@@ -9257,7 +9776,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollOnMainThread) {
EXPECT_EQ(nullptr, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(0, 60)));
EXPECT_EQ(
- InputHandler::SCROLL_ON_MAIN_THREAD,
+ ScrollThread::SCROLL_ON_MAIN_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(0, 60), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -9269,7 +9788,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollOnMainThread) {
EXPECT_NE(nullptr, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(0, 0)));
EXPECT_EQ(
- InputHandler::SCROLL_ON_MAIN_THREAD,
+ ScrollThread::SCROLL_ON_MAIN_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -9677,7 +10196,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollOnImplThread) {
EXPECT_EQ(nullptr, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(0, 60)));
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(0, 60), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -9691,7 +10210,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollOnImplThread) {
EXPECT_NE(nullptr, host_impl_->active_tree()->FindLayerThatIsHitByPoint(
gfx::PointF(0, 0)));
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -11055,6 +11574,8 @@ class LayerTreeHostImplTestWithRenderer
public ::testing::WithParamInterface<RendererType> {
protected:
RendererType renderer_type() const { return GetParam(); }
+
+ viz::DebugRendererSettings debug_settings_;
};
INSTANTIATE_TEST_SUITE_P(All,
@@ -11073,8 +11594,9 @@ TEST_P(LayerTreeHostImplTestWithRenderer, ShutdownReleasesContext) {
renderer_settings.use_skia_renderer = renderer_type() == RENDERER_SKIA;
auto layer_tree_frame_sink = std::make_unique<TestLayerTreeFrameSink>(
context_provider, viz::TestContextProvider::CreateWorker(), nullptr,
- renderer_settings, base::ThreadTaskRunnerHandle::Get().get(),
- synchronous_composite, disable_display_vsync, refresh_rate);
+ renderer_settings, &debug_settings_,
+ base::ThreadTaskRunnerHandle::Get().get(), synchronous_composite,
+ disable_display_vsync, refresh_rate);
layer_tree_frame_sink->SetClient(&test_client);
CreateHostImpl(DefaultSettings(), std::move(layer_tree_frame_sink));
@@ -11125,7 +11647,7 @@ TEST_P(LayerTreeHostImplTestWithRenderer, ShutdownReleasesContext) {
// 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.
+ // we should return ScrollThread::SCROLL_UNKNOWN.
gfx::Size viewport_size(50, 50);
gfx::Size content_size(100, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -11148,10 +11670,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestIsNotReliable) {
.get(),
ui::ScrollInputType::kWheel);
if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_TRUE(status.needs_main_thread_hit_test);
} else {
- EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_UNKNOWN, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_scrolling_reasons);
}
@@ -11162,7 +11684,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestIsNotReliable) {
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.
+ // should also return ScrollThread::SCROLL_UNKNOWN.
gfx::Size viewport_size(50, 50);
gfx::Size content_size(100, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -11191,10 +11713,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestAncestorMismatch) {
.get(),
ui::ScrollInputType::kWheel);
if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_TRUE(status.needs_main_thread_hit_test);
} else {
- EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_UNKNOWN, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
status.main_thread_scrolling_reasons);
}
@@ -11214,7 +11736,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollInvisibleScroller) {
// We should have scrolled |child_scroll| even though it does not move
// any layer that is a drawn RSLL member.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -11327,14 +11849,10 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, HiddenSelectionBoundsStayHidden) {
class SimpleSwapPromiseMonitor : public SwapPromiseMonitor {
public:
- SimpleSwapPromiseMonitor(LayerTreeHost* layer_tree_host,
- LayerTreeHostImpl* layer_tree_host_impl,
+ SimpleSwapPromiseMonitor(LayerTreeHostImpl* layer_tree_host_impl,
int* set_needs_commit_count,
int* set_needs_redraw_count)
- : SwapPromiseMonitor(
- (layer_tree_host ? layer_tree_host->GetSwapPromiseManager()
- : nullptr),
- layer_tree_host_impl),
+ : SwapPromiseMonitor(layer_tree_host_impl),
set_needs_commit_count_(set_needs_commit_count),
set_needs_redraw_count_(set_needs_redraw_count) {}
@@ -11355,8 +11873,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SimpleSwapPromiseMonitor) {
{
std::unique_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor(
- new SimpleSwapPromiseMonitor(nullptr, host_impl_.get(),
- &set_needs_commit_count,
+ new SimpleSwapPromiseMonitor(host_impl_.get(), &set_needs_commit_count,
&set_needs_redraw_count));
host_impl_->SetNeedsRedraw();
EXPECT_EQ(0, set_needs_commit_count);
@@ -11371,8 +11888,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SimpleSwapPromiseMonitor) {
{
std::unique_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor(
- new SimpleSwapPromiseMonitor(nullptr, host_impl_.get(),
- &set_needs_commit_count,
+ new SimpleSwapPromiseMonitor(host_impl_.get(), &set_needs_commit_count,
&set_needs_redraw_count));
// Redraw with damage.
host_impl_->SetFullViewportDamage();
@@ -11383,8 +11899,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SimpleSwapPromiseMonitor) {
{
std::unique_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor(
- new SimpleSwapPromiseMonitor(nullptr, host_impl_.get(),
- &set_needs_commit_count,
+ new SimpleSwapPromiseMonitor(host_impl_.get(), &set_needs_commit_count,
&set_needs_redraw_count));
// Redraw without damage.
host_impl_->SetNeedsRedraw();
@@ -11397,13 +11912,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SimpleSwapPromiseMonitor) {
{
std::unique_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor(
- new SimpleSwapPromiseMonitor(nullptr, host_impl_.get(),
- &set_needs_commit_count,
+ new SimpleSwapPromiseMonitor(host_impl_.get(), &set_needs_commit_count,
&set_needs_redraw_count));
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
// Scrolling normally should not trigger any forwarding.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kTouchscreen)
@@ -11424,7 +11938,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, SimpleSwapPromiseMonitor) {
// Scrolling with a scroll handler should defer the swap to the main
// thread.
host_impl_->active_tree()->set_have_scroll_event_handlers(true);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kTouchscreen)
@@ -11533,7 +12047,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest,
const float residue = 10;
float offset = top_controls_height_ - residue;
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, offset),
ui::ScrollInputType::kTouchscreen)
@@ -11620,7 +12134,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest,
LayerImpl* viewport_layer = InnerViewportScrollLayer();
const float delta = top_controls_height_;
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, delta),
ui::ScrollInputType::kWheel)
@@ -11668,7 +12182,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest,
const float residue = 35;
float offset = top_controls_height_ - residue;
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, offset),
ui::ScrollInputType::kTouchscreen)
@@ -11759,7 +12273,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest,
const float residue = 15;
float offset = top_controls_height_ - residue;
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, offset),
ui::ScrollInputType::kTouchscreen)
@@ -11837,7 +12351,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest,
float offset = 50;
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, offset),
ui::ScrollInputType::kTouchscreen)
@@ -11890,7 +12404,9 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest,
EXPECT_EQ(gfx::Vector2dF(0, 2 * offset).ToString(),
CurrentScrollOffset(scroll_layer).ToString());
EXPECT_EQ(gfx::Vector2dF(0, overscrollamount).ToString(),
- host_impl_->accumulated_root_overscroll().ToString());
+ host_impl_->GetInputHandler()
+ .accumulated_root_overscroll_for_testing()
+ .ToString());
EXPECT_TRUE(host_impl_
->ScrollUpdate(UpdateState(gfx::Point(),
@@ -12028,7 +12544,7 @@ TEST_F(LayerTreeHostImplVirtualViewportTest,
inner_viewport.height() / 2);
// Make sure the scroll goes to the inner viewport first.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kTouchscreen)
@@ -12078,7 +12594,7 @@ TEST_F(LayerTreeHostImplVirtualViewportTest,
DrawFrame();
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->RootScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kTouchscreen)
@@ -12088,7 +12604,7 @@ TEST_F(LayerTreeHostImplVirtualViewportTest,
EXPECT_EQ(host_impl_->CurrentlyScrollingNode(),
host_impl_->OuterViewportScrollNode());
host_impl_->ScrollEnd();
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kTouchscreen)
@@ -12117,7 +12633,7 @@ TEST_F(LayerTreeHostImplVirtualViewportTest,
// Ensure inner viewport doesn't react to scrolls (test it's unscrollable).
EXPECT_VECTOR_EQ(gfx::Vector2dF(), CurrentScrollOffset(inner_scroll));
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 100),
ui::ScrollInputType::kTouchscreen)
@@ -12132,7 +12648,9 @@ TEST_F(LayerTreeHostImplVirtualViewportTest,
// When inner viewport is unscrollable, a fling gives zero overscroll.
EXPECT_FALSE(scroll_result.did_overscroll_root);
- EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
+ EXPECT_EQ(
+ gfx::Vector2dF(),
+ host_impl_->GetInputHandler().accumulated_root_overscroll_for_testing());
}
class LayerTreeHostImplWithImplicitLimitsTest : public LayerTreeHostImplTest {
@@ -12495,7 +13013,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
EXPECT_EQ(node->surface_contents_scale, gfx::Vector2dF(1, 1));
}
-#if defined(OS_MACOSX)
+#if defined(OS_MAC)
// Ensure Mac wheel scrolling causes instant scrolling. This test can be removed
// once https://crbug.com/574283 is fixed.
TEST_P(ScrollUnifiedLayerTreeHostImplTest, MacWheelIsNonAnimated) {
@@ -12505,7 +13023,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, MacWheelIsNonAnimated) {
LayerImpl* scrolling_layer = OuterViewportScrollLayer();
host_impl_->set_force_smooth_wheel_scrolling_for_testing(false);
- ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
ui::ScrollInputType::kWheel)
@@ -12649,10 +13167,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimated) {
int set_needs_commit_count = 0;
int set_needs_redraw_count = 0;
std::unique_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor(
- new SimpleSwapPromiseMonitor(nullptr, host_impl_.get(),
- &set_needs_commit_count,
+ new SimpleSwapPromiseMonitor(host_impl_.get(), &set_needs_commit_count,
&set_needs_redraw_count));
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
ui::ScrollInputType::kWheel)
@@ -12695,8 +13212,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimated) {
int set_needs_commit_count = 0;
int set_needs_redraw_count = 0;
std::unique_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor(
- new SimpleSwapPromiseMonitor(nullptr, host_impl_.get(),
- &set_needs_commit_count,
+ new SimpleSwapPromiseMonitor(host_impl_.get(), &set_needs_commit_count,
&set_needs_redraw_count));
// Update target.
host_impl_->ScrollUpdate(
@@ -12859,7 +13375,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimatedWhileZoomed) {
// animation update code.
{
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(10, 10), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
@@ -13118,14 +13634,16 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, AutoscrollOnDeletedScrollbar) {
// the logic inside ScrollbarController::DidUnregisterScrollbar)
host_impl_->DidUnregisterScrollbarLayer(scroll_layer->element_id(),
ScrollbarOrientation::HORIZONTAL);
- EXPECT_TRUE(host_impl_->scrollbar_controller_for_testing()
+ EXPECT_TRUE(host_impl_->GetInputHandler()
+ .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()
+ EXPECT_FALSE(host_impl_->GetInputHandler()
+ .scrollbar_controller_for_testing()
->AutoscrollTaskIsScheduled());
// End the scroll.
@@ -13350,6 +13868,55 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
host_impl_ = nullptr;
}
+// Test if the AverageLagTrackingManager's pending frames list is cleared when
+// the LayerTreeFrameSink loses context. It is necessary since the frames won't
+// receive a presentation feedback if the context is lost, and the pending
+// frames will never be removed from the list otherwise.
+TEST_F(LayerTreeHostImplTest,
+ ClearTrackingManagerOnLayerTreeFrameSinkLoseContext) {
+ const gfx::Size content_size(1000, 10000);
+ const gfx::Size viewport_size(500, 500);
+ SetupViewportLayersOuterScrolls(viewport_size, content_size);
+ DrawFrame();
+
+ host_impl_->ScrollBegin(BeginState(gfx::Point(250, 250), gfx::Vector2dF(),
+ ui::ScrollInputType::kTouchscreen)
+ .get(),
+ ui::ScrollInputType::kTouchscreen);
+
+ // Draw 30 frames, each with 1 LatencyInfo object that will be added to the
+ // AverageLagTrackingManager.
+ for (int i = 0; i < 30; i++) {
+ // Makes a scroll update so the next frame is set to be processed
+ // (to force frame->has_no_damage = false).
+ host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
+ ui::ScrollInputType::kTouchscreen)
+ .get());
+
+ // Add a LatencyInfo object that will be accepted by
+ // AverageLagTrackingManager::CollectScrollEventsFromFrame.
+ ui::LatencyInfo latency_info;
+ latency_info.set_source_event_type(ui::SourceEventType::TOUCH);
+ latency_info.AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT);
+ latency_info.AddLatencyNumberWithTimestamp(
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_LAST_EVENT_COMPONENT,
+ base::TimeTicks::Now());
+ std::unique_ptr<SwapPromise> swap_promise(
+ new LatencyInfoSwapPromise(latency_info));
+ host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise));
+
+ DrawFrame();
+ }
+
+ // Make LayerTreeFrameSink lose context. It should clear
+ // |lag_tracking_manager_|.
+ host_impl_->DidLoseLayerTreeFrameSink();
+
+ // Finish the test. |lag_tracking_manager_| will check in its destructor if
+ // there is less than 20 frames in its pending frames list.
+}
+
// Tests that the scheduled autoscroll task aborts if a 2nd mousedown occurs in
// the same frame.
TEST_F(LayerTreeHostImplTest, AutoscrollTaskAbort) {
@@ -13397,11 +13964,12 @@ TEST_F(LayerTreeHostImplTest, AutoscrollTaskAbort) {
auto begin_state = BeginState(gfx::Point(350, 575), gfx::Vector2d(0, 40),
ui::ScrollInputType::kScrollbar);
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(begin_state.get(), ui::ScrollInputType::kScrollbar)
.thread);
- EXPECT_TRUE(host_impl_->scrollbar_controller_for_testing()
+ EXPECT_TRUE(host_impl_->GetInputHandler()
+ .scrollbar_controller_for_testing()
->AutoscrollTaskIsScheduled());
}
@@ -13412,7 +13980,8 @@ TEST_F(LayerTreeHostImplTest, AutoscrollTaskAbort) {
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()
+ EXPECT_FALSE(host_impl_->GetInputHandler()
+ .scrollbar_controller_for_testing()
->AutoscrollTaskIsScheduled());
}
@@ -13560,12 +14129,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, AnimatedScrollYielding) {
{
// Set up an animated scrollbar autoscroll.
- host_impl_->scrollbar_controller_for_testing()->HandlePointerDown(
- gfx::PointF(350, 560), /*jump_key_modifier*/ false);
+ host_impl_->GetInputHandler()
+ .scrollbar_controller_for_testing()
+ ->HandlePointerDown(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(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(begin_state.get(), ui::ScrollInputType::kScrollbar)
.thread);
@@ -13576,7 +14146,8 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, AnimatedScrollYielding) {
host_impl_->ScrollUpdate(update_state.get());
// Autoscroll animations should be active.
- EXPECT_TRUE(host_impl_->scrollbar_controller_for_testing()
+ EXPECT_TRUE(host_impl_->GetInputHandler()
+ .scrollbar_controller_for_testing()
->ScrollbarScrollIsActive());
EXPECT_TRUE(GetImplAnimationHost()->ImplOnlyScrollAnimatingElement());
}
@@ -13596,11 +14167,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, AnimatedScrollYielding) {
// clears ScrollbarController::autoscroll_state_,
// captured_scrollbar_metadata_ etc. That means
// ScrollbarController::ScrollbarLayer should return null.
- EXPECT_FALSE(
- host_impl_->scrollbar_controller_for_testing()->ScrollbarLayer());
+ EXPECT_FALSE(host_impl_->GetInputHandler()
+ .scrollbar_controller_for_testing()
+ ->ScrollbarLayer());
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_->ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel)
.thread);
@@ -13613,7 +14185,9 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, AnimatedScrollYielding) {
EXPECT_TRUE(GetImplAnimationHost()->ImplOnlyScrollAnimatingElement());
// This should not trigger any DCHECKs and will be a no-op.
- host_impl_->scrollbar_controller_for_testing()->WillBeginImplFrame();
+ host_impl_->GetInputHandler()
+ .scrollbar_controller_for_testing()
+ ->WillBeginImplFrame();
}
// Tear down the LayerTreeHostImpl before the InputHandlerClient.
@@ -13786,7 +14360,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -13797,7 +14371,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
host_impl_->ScrollEnd();
// The second ScrollBegin should not get ignored.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -13827,7 +14401,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
host_impl_->UpdateAnimationState(true);
host_impl_->DidFinishImplFrame(begin_frame_args);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
ui::ScrollInputType::kWheel)
@@ -13880,7 +14454,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimatedWithDelay) {
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
// Create animation with a 100ms delay.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 100),
ui::ScrollInputType::kWheel)
@@ -13946,7 +14520,7 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedAborted) {
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
// Perform animated scroll.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
ui::ScrollInputType::kWheel)
@@ -13991,7 +14565,7 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedAborted) {
begin_state->data()->delta_granularity =
ui::ScrollGranularity::kScrollByPrecisePixel;
EXPECT_EQ(
- InputHandler::SCROLL_ON_IMPL_THREAD,
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_->ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel)
.thread);
auto update_state = UpdateState(gfx::Point(0, y), gfx::Vector2d(0, 50),
@@ -14027,7 +14601,7 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimated) {
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 50),
ui::ScrollInputType::kWheel)
@@ -14117,7 +14691,7 @@ TEST_F(LayerTreeHostImplTimelinesTest, ImplPinchZoomScrollAnimated) {
base::TimeTicks() + base::TimeDelta::FromMilliseconds(250);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(10, 20),
ui::ScrollInputType::kWheel)
@@ -14213,7 +14787,7 @@ TEST_F(LayerTreeHostImplTimelinesTest, ImplPinchZoomScrollAnimatedUpdate) {
base::TimeTicks() + base::TimeDelta::FromMilliseconds(50);
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(90, 90),
ui::ScrollInputType::kWheel)
@@ -14277,7 +14851,7 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedNotUserScrollable) {
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(50, 50),
ui::ScrollInputType::kWheel)
@@ -14493,7 +15067,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
host_impl_->ScrollEnd();
gfx::Vector2dF scroll_delta(0, 5);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), scroll_delta,
ui::ScrollInputType::kWheel)
@@ -14546,9 +15120,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, RemoveUnreferencedRenderPass) {
frame.render_passes.push_back(viz::RenderPass::Create());
viz::RenderPass* pass1 = frame.render_passes.back().get();
- pass1->SetNew(1, gfx::Rect(), gfx::Rect(), gfx::Transform());
- pass2->SetNew(2, gfx::Rect(), gfx::Rect(), gfx::Transform());
- pass3->SetNew(3, gfx::Rect(), gfx::Rect(), gfx::Transform());
+ pass1->SetNew(viz::RenderPassId{1}, gfx::Rect(), gfx::Rect(),
+ gfx::Transform());
+ pass2->SetNew(viz::RenderPassId{2}, gfx::Rect(), gfx::Rect(),
+ gfx::Transform());
+ pass3->SetNew(viz::RenderPassId{3}, gfx::Rect(), gfx::Rect(),
+ gfx::Transform());
// Add a quad to each pass so they aren't empty.
auto* color_quad = pass1->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
@@ -14566,10 +15143,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, RemoveUnreferencedRenderPass) {
// But pass2 is not referenced by pass1. So pass2 and pass3 should be culled.
FakeLayerTreeHostImpl::RemoveRenderPasses(&frame);
EXPECT_EQ(1u, frame.render_passes.size());
- EXPECT_EQ(1u, CountRenderPassesWithId(frame.render_passes, 1u));
- EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes, 2u));
- EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes, 3u));
- EXPECT_EQ(1u, frame.render_passes[0]->id);
+ EXPECT_EQ(
+ 1u, CountRenderPassesWithId(frame.render_passes, viz::RenderPassId{1u}));
+ EXPECT_EQ(
+ 0u, CountRenderPassesWithId(frame.render_passes, viz::RenderPassId{2u}));
+ EXPECT_EQ(
+ 0u, CountRenderPassesWithId(frame.render_passes, viz::RenderPassId{3u}));
+ EXPECT_EQ(viz::RenderPassId{1u}, frame.render_passes[0]->id);
}
TEST_P(ScrollUnifiedLayerTreeHostImplTest, RemoveEmptyRenderPass) {
@@ -14581,9 +15161,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, RemoveEmptyRenderPass) {
frame.render_passes.push_back(viz::RenderPass::Create());
viz::RenderPass* pass1 = frame.render_passes.back().get();
- pass1->SetNew(1, gfx::Rect(), gfx::Rect(), gfx::Transform());
- pass2->SetNew(2, gfx::Rect(), gfx::Rect(), gfx::Transform());
- pass3->SetNew(3, gfx::Rect(), gfx::Rect(), gfx::Transform());
+ pass1->SetNew(viz::RenderPassId{1}, gfx::Rect(), gfx::Rect(),
+ gfx::Transform());
+ pass2->SetNew(viz::RenderPassId{2}, gfx::Rect(), gfx::Rect(),
+ gfx::Transform());
+ pass3->SetNew(viz::RenderPassId{3}, gfx::Rect(), gfx::Rect(),
+ gfx::Transform());
// pass1 is not empty, but pass2 and pass3 are.
auto* color_quad = pass1->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
@@ -14603,10 +15186,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, RemoveEmptyRenderPass) {
// should be removed.
FakeLayerTreeHostImpl::RemoveRenderPasses(&frame);
EXPECT_EQ(1u, frame.render_passes.size());
- EXPECT_EQ(1u, CountRenderPassesWithId(frame.render_passes, 1u));
- EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes, 2u));
- EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes, 3u));
- EXPECT_EQ(1u, frame.render_passes[0]->id);
+ EXPECT_EQ(
+ 1u, CountRenderPassesWithId(frame.render_passes, viz::RenderPassId{1u}));
+ EXPECT_EQ(
+ 0u, CountRenderPassesWithId(frame.render_passes, viz::RenderPassId{2u}));
+ EXPECT_EQ(
+ 0u, CountRenderPassesWithId(frame.render_passes, viz::RenderPassId{3u}));
+ EXPECT_EQ(viz::RenderPassId{1u}, frame.render_passes[0]->id);
// The viz::RenderPassDrawQuad should be removed from pass1.
EXPECT_EQ(1u, pass1->quad_list.size());
EXPECT_EQ(viz::DrawQuad::Material::kSolidColor,
@@ -14622,9 +15208,12 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, DoNotRemoveEmptyRootRenderPass) {
frame.render_passes.push_back(viz::RenderPass::Create());
viz::RenderPass* pass1 = frame.render_passes.back().get();
- pass1->SetNew(1, gfx::Rect(), gfx::Rect(), gfx::Transform());
- pass2->SetNew(2, gfx::Rect(), gfx::Rect(), gfx::Transform());
- pass3->SetNew(3, gfx::Rect(), gfx::Rect(), gfx::Transform());
+ pass1->SetNew(viz::RenderPassId{1}, gfx::Rect(), gfx::Rect(),
+ gfx::Transform());
+ pass2->SetNew(viz::RenderPassId{2}, gfx::Rect(), gfx::Rect(),
+ gfx::Transform());
+ pass3->SetNew(viz::RenderPassId{3}, gfx::Rect(), gfx::Rect(),
+ gfx::Transform());
// pass3 is referenced by pass2.
auto* rpdq = pass2->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>();
@@ -14641,10 +15230,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, DoNotRemoveEmptyRootRenderPass) {
// not be removed.
FakeLayerTreeHostImpl::RemoveRenderPasses(&frame);
EXPECT_EQ(1u, frame.render_passes.size());
- EXPECT_EQ(1u, CountRenderPassesWithId(frame.render_passes, 1u));
- EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes, 2u));
- EXPECT_EQ(0u, CountRenderPassesWithId(frame.render_passes, 3u));
- EXPECT_EQ(1u, frame.render_passes[0]->id);
+ EXPECT_EQ(
+ 1u, CountRenderPassesWithId(frame.render_passes, viz::RenderPassId{1u}));
+ EXPECT_EQ(
+ 0u, CountRenderPassesWithId(frame.render_passes, viz::RenderPassId{2u}));
+ EXPECT_EQ(
+ 0u, CountRenderPassesWithId(frame.render_passes, viz::RenderPassId{3u}));
+ EXPECT_EQ(viz::RenderPassId{1u}, frame.render_passes[0]->id);
// The viz::RenderPassDrawQuad should be removed from pass1.
EXPECT_EQ(0u, pass1->quad_list.size());
}
@@ -15310,11 +15902,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, RasterColorSpace) {
host_impl_->GetRasterColorSpace(gfx::ContentColorUsage::kWideColorGamut),
gfx::ColorSpace::CreateSRGB());
// The raster color space should update with tree activation.
- host_impl_->active_tree()->SetRasterColorSpace(
- gfx::ColorSpace::CreateDisplayP3D65());
+ host_impl_->active_tree()->SetDisplayColorSpaces(
+ gfx::DisplayColorSpaces(gfx::ColorSpace::CreateDisplayP3D65()));
EXPECT_EQ(
host_impl_->GetRasterColorSpace(gfx::ContentColorUsage::kWideColorGamut),
gfx::ColorSpace::CreateDisplayP3D65());
+ EXPECT_EQ(gfx::ColorSpace::kDefaultSDRWhiteLevel,
+ host_impl_->GetSDRWhiteLevel());
}
TEST_P(ScrollUnifiedLayerTreeHostImplTest, RasterColorSpaceSoftware) {
@@ -15324,11 +15918,13 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, RasterColorSpaceSoftware) {
EXPECT_EQ(
host_impl_->GetRasterColorSpace(gfx::ContentColorUsage::kWideColorGamut),
gfx::ColorSpace::CreateSRGB());
- host_impl_->active_tree()->SetRasterColorSpace(
- gfx::ColorSpace::CreateDisplayP3D65());
+ host_impl_->active_tree()->SetDisplayColorSpaces(
+ gfx::DisplayColorSpaces(gfx::ColorSpace::CreateDisplayP3D65()));
EXPECT_EQ(
host_impl_->GetRasterColorSpace(gfx::ContentColorUsage::kWideColorGamut),
gfx::ColorSpace::CreateSRGB());
+ EXPECT_EQ(gfx::ColorSpace::kDefaultSDRWhiteLevel,
+ host_impl_->GetSDRWhiteLevel());
}
TEST_P(ScrollUnifiedLayerTreeHostImplTest, RasterColorPrefersSRGB) {
@@ -15337,15 +15933,51 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, RasterColorPrefersSRGB) {
LayerTreeSettings settings = DefaultSettings();
settings.prefer_raster_in_srgb = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
- host_impl_->active_tree()->SetRasterColorSpace(p3);
-
+ host_impl_->active_tree()->SetDisplayColorSpaces(gfx::DisplayColorSpaces(p3));
EXPECT_EQ(host_impl_->GetRasterColorSpace(gfx::ContentColorUsage::kSRGB),
gfx::ColorSpace::CreateSRGB());
settings.prefer_raster_in_srgb = false;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
- host_impl_->active_tree()->SetRasterColorSpace(p3);
+ host_impl_->active_tree()->SetDisplayColorSpaces(gfx::DisplayColorSpaces(p3));
EXPECT_EQ(host_impl_->GetRasterColorSpace(gfx::ContentColorUsage::kSRGB), p3);
+ EXPECT_EQ(gfx::ColorSpace::kDefaultSDRWhiteLevel,
+ host_impl_->GetSDRWhiteLevel());
+}
+
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RasterColorSpaceHDR) {
+ auto hdr = gfx::ColorSpace::CreateHDR10();
+
+ LayerTreeSettings settings = DefaultSettings();
+ CreateHostImpl(settings, CreateLayerTreeFrameSink());
+ host_impl_->active_tree()->SetDisplayColorSpaces(
+ gfx::DisplayColorSpaces(hdr));
+
+ // Non-HDR content should be rasterized in P3.
+ EXPECT_EQ(host_impl_->GetRasterColorSpace(gfx::ContentColorUsage::kSRGB),
+ gfx::ColorSpace::CreateDisplayP3D65());
+ EXPECT_EQ(
+ host_impl_->GetRasterColorSpace(gfx::ContentColorUsage::kWideColorGamut),
+ gfx::ColorSpace::CreateDisplayP3D65());
+
+ EXPECT_EQ(host_impl_->GetRasterColorSpace(gfx::ContentColorUsage::kHDR), hdr);
+ EXPECT_EQ(gfx::ColorSpace::kDefaultSDRWhiteLevel,
+ host_impl_->GetSDRWhiteLevel());
+}
+
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SDRWhiteLevel) {
+ constexpr float kCustomWhiteLevel = 200.f;
+ auto hdr = gfx::ColorSpace::CreateHDR10();
+ auto display_cs = gfx::DisplayColorSpaces(hdr);
+ display_cs.SetSDRWhiteLevel(kCustomWhiteLevel);
+
+ LayerTreeSettings settings = DefaultSettings();
+ CreateHostImpl(settings, CreateLayerTreeFrameSink());
+ host_impl_->active_tree()->SetDisplayColorSpaces(display_cs);
+
+ // Non-HDR content should be rasterized in P3.
+ EXPECT_EQ(host_impl_->GetRasterColorSpace(gfx::ContentColorUsage::kHDR), hdr);
+ EXPECT_EQ(kCustomWhiteLevel, host_impl_->GetSDRWhiteLevel());
}
TEST_P(ScrollUnifiedLayerTreeHostImplTest, UpdatedTilingsForNonDrawingLayers) {
@@ -15567,7 +16199,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, RenderFrameMetadata) {
}
// Scrolling should update metadata immediately.
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -15660,7 +16292,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, RenderFrameMetadata) {
}
// Likewise if set from the main thread.
- host_impl_->ProcessScrollDeltas();
+ host_impl_->ProcessCompositorDeltas();
host_impl_->active_tree()->PushPageScaleFromMainThread(4, 0.5f, 4);
host_impl_->active_tree()->SetPageScaleOnActiveTree(4);
{
@@ -15830,7 +16462,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest,
ScrollTree& scroll_tree =
host_impl_->active_tree()->property_trees()->scroll_tree;
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD,
host_impl_
->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(),
ui::ScrollInputType::kTouchscreen)
@@ -16263,7 +16895,7 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, TouchScrollOnAndroidScrollbar) {
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
@@ -16503,6 +17135,57 @@ TEST_P(ScrollUnifiedLayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) {
host_impl_ = nullptr;
}
+// Verify that page based scrolling resolves to the correct amount of scroll
+// delta.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PageBasedScroll) {
+ const gfx::Size kViewportSize(100, 100);
+ const gfx::Size kContentSize(300, 300);
+ SetupViewportLayersOuterScrolls(kViewportSize, kContentSize);
+ DrawFrame();
+
+ const gfx::Vector2dF kPageDelta(2, 1);
+
+ auto begin_state =
+ BeginState(gfx::Point(), kPageDelta, ui::ScrollInputType::kWheel);
+ begin_state->data()->delta_granularity = ui::ScrollGranularity::kScrollByPage;
+ EXPECT_EQ(
+ ScrollThread::SCROLL_ON_IMPL_THREAD,
+ host_impl_->ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel)
+ .thread);
+
+ auto update_state =
+ UpdateState(gfx::Point(), kPageDelta, ui::ScrollInputType::kWheel);
+ update_state->data()->delta_granularity =
+ ui::ScrollGranularity::kScrollByPage;
+ // We should still be scrolling, because the scrolled layer also exists in the
+ // new tree.
+ host_impl_->ScrollUpdate(update_state.get());
+
+ viz::BeginFrameArgs begin_frame_args =
+ viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
+
+ base::TimeTicks start_time =
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(100);
+ BeginImplFrameAndAnimate(begin_frame_args, start_time);
+ BeginImplFrameAndAnimate(begin_frame_args,
+ start_time + base::TimeDelta::FromMilliseconds(50));
+ BeginImplFrameAndAnimate(
+ begin_frame_args, start_time + base::TimeDelta::FromMilliseconds(2000));
+
+ const gfx::ScrollOffset kExpectedDelta(
+ kPageDelta.x() * kViewportSize.width() * kMinFractionToStepWhenPaging,
+ kPageDelta.y() * kViewportSize.height() * kMinFractionToStepWhenPaging);
+ const gfx::ScrollOffset kCurrentDelta =
+ host_impl_->active_tree()
+ ->property_trees()
+ ->scroll_tree.current_scroll_offset(
+ host_impl_->OuterViewportScrollNode()->element_id);
+
+ EXPECT_EQ(kExpectedDelta.ToString(), kCurrentDelta.ToString());
+
+ host_impl_->ScrollEnd();
+}
+
class UnifiedScrollingTest : public LayerTreeHostImplTest {
public:
using Point = gfx::Point;
@@ -16682,7 +17365,7 @@ TEST_F(UnifiedScrollingTest, UnifiedScrollNonFastScrollableRegion) {
{
ScrollStatus status = ScrollBegin(Vector2d(0, 10));
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_TRUE(status.needs_main_thread_hit_test);
// The scroll hasn't started yet though.
@@ -16695,7 +17378,7 @@ TEST_F(UnifiedScrollingTest, UnifiedScrollNonFastScrollableRegion) {
{
ScrollStatus status = ContinuedScrollBegin(ScrollerElementId());
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_FALSE(status.needs_main_thread_hit_test);
EXPECT_TRUE(CurrentlyScrollingNode());
@@ -16771,7 +17454,7 @@ TEST_F(UnifiedScrollingDeathTest, EmptyMainThreadHitTest) {
"");
#else
status = ContinuedScrollBegin(kInvalidId);
- EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_IGNORED, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
status.main_thread_scrolling_reasons);
#endif
@@ -16789,7 +17472,7 @@ TEST_F(UnifiedScrollingTest, MainThreadHitTestScrollNodeNotFound) {
ScrollStatus status = ScrollBegin(Vector2d(0, 10));
status = ContinuedScrollBegin(kUnknown);
- EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_IGNORED, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
status.main_thread_scrolling_reasons);
}
@@ -16814,7 +17497,7 @@ TEST_F(UnifiedScrollingTest, SquashingLayerCausesMainThreadHitTest) {
// thread hit test.
{
ScrollStatus status = ScrollBegin(Vector2d(0, 10));
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_TRUE(status.needs_main_thread_hit_test);
}
@@ -16822,7 +17505,7 @@ TEST_F(UnifiedScrollingTest, SquashingLayerCausesMainThreadHitTest) {
// normal on the impl thread.
{
ScrollStatus status = ContinuedScrollBegin(ScrollerElementId());
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_FALSE(status.needs_main_thread_hit_test);
EXPECT_TRUE(CurrentlyScrollingNode());
@@ -16839,7 +17522,7 @@ TEST_F(UnifiedScrollingTest, MainThreadScrollingReasonsScrollOnCompositor) {
{
ScrollStatus status = ScrollBegin(Vector2d(0, 10));
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_FALSE(status.needs_main_thread_hit_test);
}
}
@@ -16995,5 +17678,169 @@ TEST_F(UnifiedScrollingTest, CompositedWithSquashedLayerMutatesTransform) {
ScrollEnd();
}
-} // namespace
+TEST_F(LayerTreeHostImplTest, FrameElementIdHitTestSimple) {
+ SetupDefaultRootLayer(gfx::Size(100, 100));
+
+ LayerImpl* frame_layer = AddLayer();
+ frame_layer->SetBounds(gfx::Size(50, 50));
+ frame_layer->SetDrawsContent(true);
+ frame_layer->SetHitTestable(true);
+ CopyProperties(root_layer(), frame_layer);
+ CreateTransformNode(frame_layer).visible_frame_element_id = ElementId(0x10);
+
+ UpdateDrawProperties(host_impl_->active_tree());
+
+ EXPECT_EQ(host_impl_->FindFrameElementIdAtPoint(gfx::PointF(10, 10)),
+ ElementId(0x10));
+}
+
+TEST_F(LayerTreeHostImplTest, FrameElementIdHitTestInheritance) {
+ SetupDefaultRootLayer(gfx::Size(100, 100));
+
+ LayerImpl* frame_layer = AddLayer();
+ frame_layer->SetBounds(gfx::Size(50, 50));
+ frame_layer->SetHitTestable(true);
+ CopyProperties(root_layer(), frame_layer);
+ CreateTransformNode(frame_layer, root_layer()->transform_tree_index())
+ .visible_frame_element_id = ElementId(0x20);
+
+ // Create a child layer with no associated frame, but with the above frame
+ // layer as a parent.
+ LayerImpl* child_layer = AddLayer();
+ child_layer->SetBounds(gfx::Size(50, 50));
+ child_layer->SetHitTestable(true);
+ CopyProperties(root_layer(), child_layer);
+ auto& child_node =
+ CreateTransformNode(child_layer, frame_layer->transform_tree_index());
+ child_node.parent_frame_id = frame_layer->transform_tree_index();
+ child_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
+
+ UpdateDrawProperties(host_impl_->active_tree());
+
+ // Hit tests on the parent should return the parent's frame element ID.
+ EXPECT_EQ(host_impl_->FindFrameElementIdAtPoint(gfx::PointF(15, 15)),
+ ElementId(0x20));
+
+ // Ensure that hit tests on the child (non-frame) layer returns the frame
+ // element id of its parent.
+ EXPECT_EQ(host_impl_->FindFrameElementIdAtPoint(gfx::PointF(60, 60)),
+ ElementId(0x20));
+}
+
+TEST_F(LayerTreeHostImplTest, FrameElementIdHitTestOverlap) {
+ SetupDefaultRootLayer(gfx::Size(100, 100));
+
+ LayerImpl* frame_layer = AddLayer();
+ frame_layer->SetBounds(gfx::Size(50, 50));
+ frame_layer->SetHitTestable(true);
+ CopyProperties(root_layer(), frame_layer);
+ CreateTransformNode(frame_layer).visible_frame_element_id = ElementId(0x10);
+
+ LayerImpl* occluding_frame_layer = AddLayer();
+ occluding_frame_layer->SetBounds(gfx::Size(50, 50));
+ occluding_frame_layer->SetHitTestable(true);
+ CopyProperties(root_layer(), occluding_frame_layer);
+ auto& occluding_frame_node = CreateTransformNode(
+ occluding_frame_layer, frame_layer->transform_tree_index());
+ occluding_frame_node.visible_frame_element_id = ElementId(0x20);
+ occluding_frame_node.parent_frame_id = frame_layer->transform_tree_index();
+ occluding_frame_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
+
+ UpdateDrawProperties(host_impl_->active_tree());
+
+ // Both frame layers should return their own frame element IDs, despite
+ // overlapping.
+ EXPECT_EQ(host_impl_->FindFrameElementIdAtPoint(gfx::PointF(15, 15)),
+ ElementId(0x10));
+ EXPECT_EQ(host_impl_->FindFrameElementIdAtPoint(gfx::PointF(30, 30)),
+ ElementId(0x20));
+}
+
+TEST_F(LayerTreeHostImplTest, FrameElementIdHitTestOverlapSimpleClip) {
+ SetupDefaultRootLayer(gfx::Size(100, 100));
+
+ LayerImpl* frame_layer = AddLayer();
+ frame_layer->SetBounds(gfx::Size(50, 50));
+ frame_layer->SetHitTestable(true);
+ CopyProperties(root_layer(), frame_layer);
+ CreateTransformNode(frame_layer).visible_frame_element_id = ElementId(0x10);
+
+ LayerImpl* clipped_frame_layer = AddLayer();
+ clipped_frame_layer->SetBounds(gfx::Size(50, 50));
+ clipped_frame_layer->SetHitTestable(true);
+ CopyProperties(root_layer(), clipped_frame_layer);
+ CreateTransformNode(clipped_frame_layer).visible_frame_element_id =
+ ElementId(0x20);
+ clipped_frame_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
+
+ // Create a clip excluding the overlapped region.
+ auto& clip_node = CreateClipNode(clipped_frame_layer);
+ clip_node.clip = gfx::RectF(40, 40, 10, 10);
+
+ UpdateDrawProperties(host_impl_->active_tree());
+
+ // Ensure that the overlapping (clipped) layer isn't targeted.
+ EXPECT_EQ(host_impl_->FindFrameElementIdAtPoint(gfx::PointF(30, 30)),
+ ElementId(0x10));
+}
+
+TEST_F(LayerTreeHostImplTest, FrameElementIdHitTestOverlapRoundedCorners) {
+ SetupDefaultRootLayer(gfx::Size(100, 100));
+
+ LayerImpl* frame_layer = AddLayer();
+ frame_layer->SetBounds(gfx::Size(50, 50));
+ frame_layer->SetHitTestable(true);
+ CopyProperties(root_layer(), frame_layer);
+ CreateTransformNode(frame_layer).visible_frame_element_id = ElementId(0x10);
+
+ LayerImpl* rounded_frame_layer = AddLayer();
+ rounded_frame_layer->SetBounds(gfx::Size(50, 50));
+ rounded_frame_layer->SetHitTestable(true);
+ CopyProperties(root_layer(), rounded_frame_layer);
+ CreateTransformNode(rounded_frame_layer, frame_layer->transform_tree_index())
+ .visible_frame_element_id = ElementId(0x20);
+ rounded_frame_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
+
+ // Add rounded corners to the layer, which are unable to be hit tested by the
+ // simple quad-based logic.
+ CreateEffectNode(rounded_frame_layer).rounded_corner_bounds =
+ gfx::RRectF(25, 25, 50, 50, 5);
+
+ UpdateDrawProperties(host_impl_->active_tree());
+
+ // The lookup should bail out in the presence of a complex clip/mask on the
+ // target chain.
+ EXPECT_FALSE(host_impl_->FindFrameElementIdAtPoint(gfx::PointF(30, 30)));
+}
+
+TEST_F(LayerTreeHostImplTest, FrameElementIdHitTestOverlapSibling) {
+ SetupDefaultRootLayer(gfx::Size(100, 100));
+
+ LayerImpl* frame_layer = AddLayer();
+ frame_layer->SetBounds(gfx::Size(50, 50));
+ frame_layer->SetHitTestable(true);
+ CopyProperties(root_layer(), frame_layer);
+ CreateTransformNode(frame_layer, root_layer()->transform_tree_index())
+ .visible_frame_element_id = ElementId(0x20);
+
+ LayerImpl* sibling_frame_layer = AddLayer();
+ sibling_frame_layer->SetBounds(gfx::Size(50, 50));
+ sibling_frame_layer->SetHitTestable(true);
+ CopyProperties(root_layer(), sibling_frame_layer);
+ CreateTransformNode(sibling_frame_layer, root_layer()->transform_tree_index())
+ .visible_frame_element_id = ElementId(0x30);
+ sibling_frame_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
+
+ UpdateDrawProperties(host_impl_->active_tree());
+
+ EXPECT_EQ(host_impl_->FindFrameElementIdAtPoint(gfx::PointF(15, 15)),
+ ElementId(0x20));
+ EXPECT_EQ(host_impl_->FindFrameElementIdAtPoint(gfx::PointF(60, 60)),
+ ElementId(0x30));
+
+ // If we have a layer occluded by a layer from another document, attributions
+ // should be discarded outside of the simple frame -> subframe case.
+ EXPECT_FALSE(host_impl_->FindFrameElementIdAtPoint(gfx::PointF(30, 30)));
+}
+
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host_perftest.cc b/chromium/cc/trees/layer_tree_host_perftest.cc
index 7879c909efd..b19a9c8ad5d 100644
--- a/chromium/cc/trees/layer_tree_host_perftest.cc
+++ b/chromium/cc/trees/layer_tree_host_perftest.cc
@@ -60,8 +60,9 @@ class LayerTreeHostPerfTest : public LayerTreeTest {
!layer_tree_host()->GetSettings().single_thread_proxy_scheduler;
return std::make_unique<TestLayerTreeFrameSink>(
compositor_context_provider, std::move(worker_context_provider),
- gpu_memory_buffer_manager(), renderer_settings, ImplThreadTaskRunner(),
- synchronous_composite, disable_display_vsync, refresh_rate);
+ gpu_memory_buffer_manager(), renderer_settings, &debug_settings_,
+ ImplThreadTaskRunner(), synchronous_composite, disable_display_vsync,
+ refresh_rate);
}
void BeginTest() override {
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc
index 0a6809e778a..390a9f84ebc 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc
@@ -213,7 +213,7 @@ class LayerTreeHostBlendingPixelTest
const int kRootHeight = kRootWidth * kCSSTestColorsCount;
// Force shaders only applies to gl renderer.
- if (renderer_type_ != RENDERER_GL && flags & kForceShaders)
+ if (renderer_type_ != TestRendererType::kGL && flags & kForceShaders)
return;
SCOPED_TRACE(TestTypeToString());
@@ -232,8 +232,8 @@ class LayerTreeHostBlendingPixelTest
force_antialiasing_ = (flags & kUseAntialiasing);
force_blending_with_shaders_ = (flags & kForceShaders);
- if ((renderer_type_ == RENDERER_GL && force_antialiasing_) ||
- renderer_type_ == RENDERER_SKIA_VK) {
+ if ((renderer_type_ == TestRendererType::kGL && force_antialiasing_) ||
+ renderer_type_ == TestRendererType::kSkiaVk) {
// Blending results might differ with one pixel.
float percentage_pixels_error = 35.f;
float percentage_pixels_small_error = 0.f;
@@ -260,11 +260,11 @@ class LayerTreeHostBlendingPixelTest
};
std::vector<PixelResourceTestCase> const kTestCases = {
- {LayerTreeTest::RENDERER_SOFTWARE, SOFTWARE},
- {LayerTreeTest::RENDERER_GL, ZERO_COPY},
- {LayerTreeTest::RENDERER_SKIA_GL, GPU},
+ {TestRendererType::kSoftware, TestRasterType::kBitmap},
+ {TestRendererType::kGL, TestRasterType::kZeroCopy},
+ {TestRendererType::kSkiaGL, TestRasterType::kGpu},
#if defined(ENABLE_CC_VULKAN_TESTS)
- {LayerTreeTest::RENDERER_SKIA_VK, GPU},
+ {TestRendererType::kSkiaVk, TestRasterType::kOop},
#endif // defined(ENABLE_CC_VULKAN_TESTS)
};
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc
index 4042e97367c..1b071580b9b 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -20,25 +20,25 @@ namespace {
class LayerTreeHostFiltersPixelTest
: public LayerTreePixelTest,
- public ::testing::WithParamInterface<LayerTreeTest::RendererType> {
+ public ::testing::WithParamInterface<TestRendererType> {
protected:
LayerTreeHostFiltersPixelTest() : LayerTreePixelTest(renderer_type()) {}
- RendererType renderer_type() const { return GetParam(); }
+ TestRendererType renderer_type() const { return GetParam(); }
- // Text string for graphics backend of the RendererType. Suitable for
+ // Text string for graphics backend of the TestRendererType. Suitable for
// generating separate base line file paths.
const char* GetRendererSuffix() {
switch (renderer_type_) {
- case RENDERER_GL:
+ case TestRendererType::kGL:
return "gl";
- case RENDERER_SKIA_GL:
+ case TestRendererType::kSkiaGL:
return "skia_gl";
- case RENDERER_SKIA_VK:
+ case TestRendererType::kSkiaVk:
return "skia_vk";
- case RENDERER_SKIA_DAWN:
+ case TestRendererType::kSkiaDawn:
return "skia_dawn";
- case RENDERER_SOFTWARE:
+ case TestRendererType::kSoftware:
return "sw";
}
}
@@ -72,14 +72,14 @@ class LayerTreeHostFiltersPixelTest
}
};
-LayerTreeTest::RendererType const kRendererTypes[] = {
- LayerTreeTest::RENDERER_GL, LayerTreeTest::RENDERER_SKIA_GL,
- LayerTreeTest::RENDERER_SOFTWARE,
+TestRendererType const kRendererTypes[] = {
+ TestRendererType::kGL, TestRendererType::kSkiaGL,
+ TestRendererType::kSoftware,
#if defined(ENABLE_CC_VULKAN_TESTS)
- LayerTreeTest::RENDERER_SKIA_VK,
+ TestRendererType::kSkiaVk,
#endif // defined(ENABLE_CC_VULKAN_TESTS)
#if defined(ENABLE_CC_DAWN_TESTS)
- LayerTreeTest::RENDERER_SKIA_DAWN,
+ TestRendererType::kSkiaDawn,
#endif // defined(ENABLE_CC_DAWN_TESTS)
};
@@ -89,14 +89,14 @@ INSTANTIATE_TEST_SUITE_P(All,
using LayerTreeHostFiltersPixelTestGPU = LayerTreeHostFiltersPixelTest;
-LayerTreeTest::RendererType const kRendererTypesGpu[] = {
- LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL,
+TestRendererType const kRendererTypesGpu[] = {
+ TestRendererType::kGL,
+ TestRendererType::kSkiaGL,
#if defined(ENABLE_CC_VULKAN_TESTS)
- LayerTreeTest::RENDERER_SKIA_VK,
+ TestRendererType::kSkiaVk,
#endif // defined(ENABLE_CC_VULKAN_TESTS)
#if defined(ENABLE_CC_DAWN_TESTS)
- LayerTreeTest::RENDERER_SKIA_DAWN,
+ TestRendererType::kSkiaDawn,
#endif // defined(ENABLE_CC_DAWN_TESTS)
};
@@ -382,11 +382,11 @@ class LayerTreeHostBlurFiltersPixelTestGPULayerList
// TODO(sgilhuly): Enable these tests for Skia Dawn, and switch over to using
// kRendererTypesGpu.
-LayerTreeTest::RendererType const kRendererTypesGpuNonDawn[] = {
- LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL,
+TestRendererType const kRendererTypesGpuNonDawn[] = {
+ TestRendererType::kGL,
+ TestRendererType::kSkiaGL,
#if defined(ENABLE_CC_VULKAN_TESTS)
- LayerTreeTest::RENDERER_SKIA_VK,
+ TestRendererType::kSkiaVk,
#endif // defined(ENABLE_CC_VULKAN_TESTS)
};
@@ -755,12 +755,12 @@ TEST_P(LayerTreeHostFiltersPixelTest, ImageRenderSurfaceScaled) {
// kRendererTypes.
using LayerTreeHostFiltersPixelTestNonDawn = LayerTreeHostFiltersPixelTest;
-LayerTreeTest::RendererType const kRendererTypesNonDawn[] = {
- LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL,
- LayerTreeTest::RENDERER_SOFTWARE,
+TestRendererType const kRendererTypesNonDawn[] = {
+ TestRendererType::kGL,
+ TestRendererType::kSkiaGL,
+ TestRendererType::kSoftware,
#if defined(ENABLE_CC_VULKAN_TESTS)
- LayerTreeTest::RENDERER_SKIA_VK,
+ TestRendererType::kSkiaVk,
#endif // defined(ENABLE_CC_VULKAN_TESTS)
};
@@ -987,7 +987,7 @@ TEST_P(LayerTreeHostFiltersPixelTest, TranslatedFilter) {
parent->AddChild(child);
clip->AddChild(parent);
- if (use_software_renderer() || renderer_type_ == RENDERER_SKIA_DAWN)
+ if (use_software_renderer() || renderer_type_ == TestRendererType::kSkiaDawn)
pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true);
RunPixelTest(clip, base::FilePath(
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc
index 702d4e5dbb7..0bd49e5d284 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc
@@ -29,15 +29,16 @@ namespace {
// TODO(penghuang): Fix vulkan with one copy or zero copy
// https://crbug.com/979703
std::vector<PixelResourceTestCase> const kTestCases = {
- {LayerTreeTest::RENDERER_SOFTWARE, SOFTWARE},
- {LayerTreeTest::RENDERER_GL, GPU},
- {LayerTreeTest::RENDERER_GL, ONE_COPY},
- {LayerTreeTest::RENDERER_GL, ZERO_COPY},
- {LayerTreeTest::RENDERER_SKIA_GL, GPU},
- {LayerTreeTest::RENDERER_SKIA_GL, ONE_COPY},
- {LayerTreeTest::RENDERER_SKIA_GL, ZERO_COPY},
+ {TestRendererType::kSoftware, TestRasterType::kBitmap},
+ {TestRendererType::kGL, TestRasterType::kGpu},
+ {TestRendererType::kGL, TestRasterType::kOneCopy},
+ {TestRendererType::kGL, TestRasterType::kZeroCopy},
+ {TestRendererType::kSkiaGL, TestRasterType::kGpu},
+ {TestRendererType::kSkiaGL, TestRasterType::kOneCopy},
+ {TestRendererType::kSkiaGL, TestRasterType::kZeroCopy},
#if defined(ENABLE_CC_VULKAN_TESTS)
- {LayerTreeTest::RENDERER_SKIA_VK, GPU},
+ {TestRendererType::kSkiaVk, TestRasterType::kOop},
+ {TestRendererType::kSkiaVk, TestRasterType::kZeroCopy},
#endif // defined(ENABLE_CC_VULKAN_TESTS)
};
@@ -277,11 +278,14 @@ TEST_P(LayerTreeHostMaskPixelTest_MaskWithEffectNoContentToMask, Test) {
class LayerTreeHostMaskPixelTest_ScaledMaskWithEffect
: public LayerTreeHostMaskPixelTestWithLayerList {
protected:
- // Scale the mask with a non-integral transform. This will trigger the
- // AA path in the renderer.
+ // Scale the mask with a non-integral transform. This will trigger raster
+ // translation and may or may not trigger the AA path in the renderer.
void SetupTree() override {
LayerTreeHostMaskPixelTestWithLayerList::SetupTree();
+ // Use this offset to ensure the same rounding direction in different code
+ // paths for non-AA drawing (25.1 * 1.5 = 37.65).
+ mask_layer_->SetOffsetToTransformParent(gfx::Vector2dF(25.1, 25.1));
auto& transform = CreateTransformNode(mask_layer_.get());
transform.local.Scale(1.5, 1.5);
}
@@ -575,14 +579,14 @@ INSTANTIATE_TEST_SUITE_P(
TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTestWithLayerList, Test) {
base::FilePath image_name =
- (raster_type() == GPU)
+ (raster_type() == TestRasterType::kGpu)
? base::FilePath(FILE_PATH_LITERAL("mask_of_backdrop_filter_gpu.png"))
: base::FilePath(FILE_PATH_LITERAL("mask_of_backdrop_filter.png"));
- if (use_skia_vulkan() && raster_type() == GPU) {
- // Vulkan with GPU raster has 4 pixels errors (the circle mask shape is
- // slight different).
- float percentage_pixels_large_error = 0.04f; // 4px / (100*100)
+ if (use_skia_vulkan() && raster_type() == TestRasterType::kOop) {
+ // Vulkan with OOP raster has 3 pixels errors (the circle mask shape is
+ // slightly different).
+ float percentage_pixels_large_error = 0.031f; // 3px / (100*100)
float percentage_pixels_small_error = 0.0f;
float average_error_allowed_in_bad_pixels = 182.f;
int large_error_allowed = 182;
@@ -634,14 +638,14 @@ TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTestWithLayerTree, Test) {
blur->SetMaskLayer(mask);
base::FilePath image_name =
- (raster_type() == GPU)
+ (raster_type() == TestRasterType::kGpu)
? base::FilePath(FILE_PATH_LITERAL("mask_of_backdrop_filter_gpu.png"))
: base::FilePath(FILE_PATH_LITERAL("mask_of_backdrop_filter.png"));
- if (use_skia_vulkan() && raster_type() == GPU) {
- // Vulkan with GPU raster has 4 pixels errors (the circle mask shape is
- // slight different).
- float percentage_pixels_large_error = 0.04f; // 4px / (100*100)
+ if (use_skia_vulkan() && raster_type() == TestRasterType::kOop) {
+ // Vulkan with OOP raster has 3 pixels errors (the circle mask shape is
+ // slightly different).
+ float percentage_pixels_large_error = 0.031f; // 3px / (100*100)
float percentage_pixels_small_error = 0.0f;
float average_error_allowed_in_bad_pixels = 182.f;
int large_error_allowed = 182;
@@ -867,18 +871,20 @@ class LayerTreeHostMaskAsBlendingPixelTest
};
MaskTestConfig const kTestConfigs[] = {
- MaskTestConfig{{LayerTreeTest::RENDERER_SOFTWARE, SOFTWARE}, 0},
- MaskTestConfig{{LayerTreeTest::RENDERER_GL, ZERO_COPY}, 0},
- MaskTestConfig{{LayerTreeTest::RENDERER_GL, ZERO_COPY}, kUseAntialiasing},
- MaskTestConfig{{LayerTreeTest::RENDERER_GL, ZERO_COPY}, kForceShaders},
- MaskTestConfig{{LayerTreeTest::RENDERER_GL, ZERO_COPY},
+ MaskTestConfig{{TestRendererType::kSoftware, TestRasterType::kBitmap}, 0},
+ MaskTestConfig{{TestRendererType::kGL, TestRasterType::kZeroCopy}, 0},
+ MaskTestConfig{{TestRendererType::kGL, TestRasterType::kZeroCopy},
+ kUseAntialiasing},
+ MaskTestConfig{{TestRendererType::kGL, TestRasterType::kZeroCopy},
+ kForceShaders},
+ MaskTestConfig{{TestRendererType::kGL, TestRasterType::kZeroCopy},
kUseAntialiasing | kForceShaders},
- MaskTestConfig{{LayerTreeTest::RENDERER_SKIA_GL, ZERO_COPY}, 0},
- MaskTestConfig{{LayerTreeTest::RENDERER_SKIA_GL, ZERO_COPY},
+ MaskTestConfig{{TestRendererType::kSkiaGL, TestRasterType::kZeroCopy}, 0},
+ MaskTestConfig{{TestRendererType::kSkiaGL, TestRasterType::kZeroCopy},
kUseAntialiasing},
#if defined(ENABLE_CC_VULKAN_TESTS)
- MaskTestConfig{{LayerTreeTest::RENDERER_SKIA_VK, ZERO_COPY}, 0},
- MaskTestConfig{{LayerTreeTest::RENDERER_SKIA_VK, ZERO_COPY},
+ MaskTestConfig{{TestRendererType::kSkiaVk, TestRasterType::kZeroCopy}, 0},
+ MaskTestConfig{{TestRendererType::kSkiaVk, TestRasterType::kZeroCopy},
kUseAntialiasing},
#endif // defined(ENABLE_CC_VULKAN_TESTS)
};
@@ -1021,7 +1027,7 @@ TEST_P(LayerTreeHostMaskAsBlendingPixelTest, RotatedClippedCircle) {
mask_isolation->AddChild(mask_layer);
base::FilePath image_name =
- (raster_type() == SOFTWARE)
+ (raster_type() == TestRasterType::kBitmap)
? base::FilePath(
FILE_PATH_LITERAL("mask_as_blending_rotated_circle.png"))
: base::FilePath(
@@ -1067,7 +1073,7 @@ TEST_P(LayerTreeHostMaskAsBlendingPixelTest, RotatedClippedCircleUnderflow) {
mask_isolation->AddChild(mask_layer);
base::FilePath image_name =
- (raster_type() == SOFTWARE)
+ (raster_type() == TestRasterType::kBitmap)
? base::FilePath(FILE_PATH_LITERAL(
"mask_as_blending_rotated_circle_underflow.png"))
: base::FilePath(FILE_PATH_LITERAL(
@@ -1135,7 +1141,7 @@ INSTANTIATE_TEST_SUITE_P(PixelResourceTest,
TEST_P(LayerTreeHostMasksForBackdropFiltersAndBlendPixelTest, Test) {
base::FilePath result_path(
FILE_PATH_LITERAL("mask_of_backdrop_filter_and_blend_.png"));
- if (raster_type() != GPU) {
+ if (!use_accelerated_raster()) {
result_path = result_path.InsertBeforeExtensionASCII("sw");
} else {
result_path = result_path.InsertBeforeExtensionASCII(GetRendererSuffix());
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_mirror.cc b/chromium/cc/trees/layer_tree_host_pixeltest_mirror.cc
index 82a9f66663b..15ab1efea06 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_mirror.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_mirror.cc
@@ -17,20 +17,22 @@ namespace {
class LayerTreeHostMirrorPixelTest
: public LayerTreePixelTest,
- public ::testing::WithParamInterface<LayerTreeTest::RendererType> {
+ public ::testing::WithParamInterface<TestRendererType> {
protected:
LayerTreeHostMirrorPixelTest() : LayerTreePixelTest(renderer_type()) {}
- RendererType renderer_type() const { return GetParam(); }
+ TestRendererType renderer_type() const { return GetParam(); }
};
-const LayerTreeTest::RendererType kRendererTypes[] = {
- LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL,
- LayerTreeTest::RENDERER_SOFTWARE,
+const TestRendererType kRendererTypes[] = {
+ TestRendererType::kGL, TestRendererType::kSkiaGL,
+ TestRendererType::kSoftware,
#if defined(ENABLE_CC_VULKAN_TESTS)
- LayerTreeTest::RENDERER_SKIA_VK,
+ TestRendererType::kSkiaVk,
#endif // defined(ENABLE_CC_VULKAN_TESTS)
+#if defined(ENABLE_CC_DAWN_TESTS)
+ TestRendererType::kSkiaDawn,
+#endif // defined(ENABLE_CC_DAWN_TESTS)
};
INSTANTIATE_TEST_SUITE_P(All,
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc b/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc
index 6d7e9365f4b..e049e13e778 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc
@@ -21,16 +21,16 @@
namespace cc {
namespace {
-// Can't templatize a class on its own members, so ReadbackType and
+// Can't templatize a class on its own members, so TestReadBackType and
// ReadbackTestConfig are declared here, before LayerTreeHostReadbackPixelTest.
-enum ReadbackType {
- READBACK_TEXTURE,
- READBACK_BITMAP,
+enum class TestReadBackType {
+ kTexture,
+ kBitmap,
};
struct ReadbackTestConfig {
- LayerTreeTest::RendererType renderer_type;
- ReadbackType readback_type;
+ TestRendererType renderer_type;
+ TestReadBackType readback_type;
};
class LayerTreeHostReadbackPixelTest
@@ -41,21 +41,21 @@ class LayerTreeHostReadbackPixelTest
: LayerTreePixelTest(renderer_type()),
insert_copy_request_after_frame_count_(0) {}
- RendererType renderer_type() const { return GetParam().renderer_type; }
+ TestRendererType renderer_type() const { return GetParam().renderer_type; }
- ReadbackType readback_type() const { return GetParam().readback_type; }
+ TestReadBackType readback_type() const { return GetParam().readback_type; }
std::unique_ptr<viz::CopyOutputRequest> CreateCopyOutputRequest() override {
std::unique_ptr<viz::CopyOutputRequest> request;
- if (readback_type() == READBACK_BITMAP) {
+ if (readback_type() == TestReadBackType::kBitmap) {
request = std::make_unique<viz::CopyOutputRequest>(
viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
base::BindOnce(
&LayerTreeHostReadbackPixelTest::ReadbackResultAsBitmap,
base::Unretained(this)));
} else {
- DCHECK_NE(renderer_type_, RENDERER_SOFTWARE);
+ DCHECK_NE(renderer_type_, TestRendererType::kSoftware);
request = std::make_unique<viz::CopyOutputRequest>(
viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
base::BindOnce(
@@ -417,18 +417,17 @@ TEST_P(LayerTreeHostReadbackPixelTest, MultipleReadbacksOnLayer) {
// TODO(crbug.com/971257): Enable these tests for Skia Vulkan using texture
// readback.
ReadbackTestConfig const kTestConfigs[] = {
- ReadbackTestConfig{LayerTreeTest::RENDERER_SOFTWARE, READBACK_BITMAP},
- ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_TEXTURE},
- ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_BITMAP},
- // TODO(crbug.com/1046788): The skia readback path doesn't support
- // RGBA_TEXTURE readback requests yet. Don't run these tests on platforms
- // that have UseSkiaForGLReadback enabled by default.
- //
- // ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_TEXTURE},
- ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_BITMAP},
+ ReadbackTestConfig{TestRendererType::kSoftware, TestReadBackType::kBitmap},
+ ReadbackTestConfig{TestRendererType::kGL, TestReadBackType::kTexture},
+ ReadbackTestConfig{TestRendererType::kGL, TestReadBackType::kBitmap},
+ ReadbackTestConfig{TestRendererType::kSkiaGL, TestReadBackType::kTexture},
+ ReadbackTestConfig{TestRendererType::kSkiaGL, TestReadBackType::kBitmap},
#if defined(ENABLE_CC_VULKAN_TESTS)
- ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_VK, READBACK_BITMAP},
+ ReadbackTestConfig{TestRendererType::kSkiaVk, TestReadBackType::kBitmap},
#endif // defined(ENABLE_CC_VULKAN_TESTS)
+#if defined(ENABLE_CC_DAWN_TESTS)
+ ReadbackTestConfig{TestRendererType::kSkiaDawn, TestReadBackType::kBitmap},
+#endif // defined(ENABLE_CC_DAWN_TESTS)
};
INSTANTIATE_TEST_SUITE_P(All,
@@ -438,19 +437,18 @@ INSTANTIATE_TEST_SUITE_P(All,
// TODO(crbug.com/974283): These tests are crashing with vulkan when TSan or
// MSan are used.
ReadbackTestConfig const kMaybeVulkanTestConfigs[] = {
- ReadbackTestConfig{LayerTreeTest::RENDERER_SOFTWARE, READBACK_BITMAP},
- ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_TEXTURE},
- ReadbackTestConfig{LayerTreeTest::RENDERER_GL, READBACK_BITMAP},
- // TODO(crbug.com/1046788): The skia readback path doesn't support
- // RGBA_TEXTURE readback requests yet. Don't run these tests on platforms
- // that have UseSkiaForGLReadback enabled by default.
- //
- // ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_TEXTURE},
- ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_GL, READBACK_BITMAP},
+ ReadbackTestConfig{TestRendererType::kSoftware, TestReadBackType::kBitmap},
+ ReadbackTestConfig{TestRendererType::kGL, TestReadBackType::kTexture},
+ ReadbackTestConfig{TestRendererType::kGL, TestReadBackType::kBitmap},
+ ReadbackTestConfig{TestRendererType::kSkiaGL, TestReadBackType::kTexture},
+ ReadbackTestConfig{TestRendererType::kSkiaGL, TestReadBackType::kBitmap},
#if defined(ENABLE_CC_VULKAN_TESTS) && !defined(THREAD_SANITIZER) && \
!defined(MEMORY_SANITIZER)
- ReadbackTestConfig{LayerTreeTest::RENDERER_SKIA_VK, READBACK_BITMAP},
+ ReadbackTestConfig{TestRendererType::kSkiaVk, TestReadBackType::kBitmap},
#endif
+#if defined(ENABLE_CC_DAWN_TESTS)
+ ReadbackTestConfig{TestRendererType::kSkiaDawn, TestReadBackType::kBitmap},
+#endif // defined(ENABLE_CC_DAWN_TESTS)
};
INSTANTIATE_TEST_SUITE_P(All,
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc
index 5dc2efdea40..f0d0388afcf 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_scrollbars.cc
@@ -25,11 +25,11 @@ namespace {
class LayerTreeHostScrollbarsPixelTest
: public LayerTreePixelTest,
- public ::testing::WithParamInterface<LayerTreeTest::RendererType> {
+ public ::testing::WithParamInterface<TestRendererType> {
protected:
LayerTreeHostScrollbarsPixelTest() : LayerTreePixelTest(renderer_type()) {}
- RendererType renderer_type() const { return GetParam(); }
+ TestRendererType renderer_type() const { return GetParam(); }
void SetupTree() override {
SetInitialDeviceScaleFactor(device_scale_factor_);
@@ -73,12 +73,15 @@ class PaintedScrollbar : public FakeScrollbar {
SkColor color_ = SK_ColorGREEN;
};
-LayerTreeTest::RendererType const kRendererTypes[] = {
- LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL,
+TestRendererType const kRendererTypes[] = {
+ TestRendererType::kGL,
+ TestRendererType::kSkiaGL,
#if defined(ENABLE_CC_VULKAN_TESTS)
- LayerTreeTest::RENDERER_SKIA_VK,
+ TestRendererType::kSkiaVk,
#endif // defined(ENABLE_CC_VULKAN_TESTS)
+#if defined(ENABLE_CC_DAWN_TESTS)
+ TestRendererType::kSkiaDawn,
+#endif // defined(ENABLE_CC_DAWN_TESTS)
};
INSTANTIATE_TEST_SUITE_P(All,
@@ -180,7 +183,8 @@ TEST_P(LayerTreeHostScrollbarsPixelTest, MAYBE_HugeTransformScale) {
scale_transform.Scale(scale, scale);
layer->SetTransform(scale_transform);
- if (renderer_type_ == RENDERER_SKIA_GL)
+ if (renderer_type_ == TestRendererType::kSkiaGL ||
+ renderer_type_ == TestRendererType::kSkiaDawn)
pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true);
RunPixelTest(background,
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc b/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc
index 34d9ef117a4..1f4fc7bfcdd 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc
@@ -17,17 +17,16 @@ namespace {
class LayerTreeHostSynchronousPixelTest
: public LayerTreePixelTest,
- public ::testing::WithParamInterface<LayerTreeTest::RendererType> {
+ public ::testing::WithParamInterface<TestRendererType> {
protected:
LayerTreeHostSynchronousPixelTest() : LayerTreePixelTest(renderer_type()) {}
void InitializeSettings(LayerTreeSettings* settings) override {
LayerTreePixelTest::InitializeSettings(settings);
settings->single_thread_proxy_scheduler = false;
- settings->use_zero_copy = use_zero_copy_;
}
- RendererType renderer_type() const { return GetParam(); }
+ TestRendererType renderer_type() const { return GetParam(); }
void BeginTest() override {
LayerTreePixelTest::BeginTest();
@@ -49,16 +48,17 @@ class LayerTreeHostSynchronousPixelTest
RunSingleThreadedPixelTest(root,
base::FilePath(FILE_PATH_LITERAL("green.png")));
}
-
- bool use_zero_copy_ = false;
};
-LayerTreeTest::RendererType const kRendererTypesGpu[] = {
- LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL,
+TestRendererType const kRendererTypesGpu[] = {
+ TestRendererType::kGL,
+ TestRendererType::kSkiaGL,
#if defined(ENABLE_CC_VULKAN_TESTS)
- LayerTreeTest::RENDERER_SKIA_VK,
+ TestRendererType::kSkiaVk,
#endif // defined(ENABLE_CC_VULKAN_TESTS)
+#if defined(ENABLE_CC_DAWN_TESTS)
+ TestRendererType::kSkiaDawn,
+#endif // defined(ENABLE_CC_DAWN_TESTS)
};
INSTANTIATE_TEST_SUITE_P(All,
@@ -66,12 +66,12 @@ INSTANTIATE_TEST_SUITE_P(All,
::testing::ValuesIn(kRendererTypesGpu));
TEST_P(LayerTreeHostSynchronousPixelTest, OneContentLayerZeroCopy) {
- use_zero_copy_ = true;
+ set_raster_type(TestRasterType::kZeroCopy);
DoContentLayerTest();
}
TEST_P(LayerTreeHostSynchronousPixelTest, OneContentLayerGpuRasterization) {
- set_gpu_rasterization();
+ set_raster_type(TestRasterType::kGpu);
DoContentLayerTest();
}
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc b/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc
index 9464beb4941..a2aa1aaf086 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_tiles.cc
@@ -20,16 +20,9 @@
namespace cc {
namespace {
-enum RasterMode {
- BITMAP,
- ONE_COPY,
- GPU,
- GPU_LOW_BIT_DEPTH,
-};
-
struct TilesTestConfig {
- LayerTreeTest::RendererType renderer_type;
- RasterMode raster_mode;
+ TestRendererType renderer_type;
+ TestRasterType raster_type;
};
class LayerTreeHostTilesPixelTest
@@ -37,33 +30,13 @@ class LayerTreeHostTilesPixelTest
public ::testing::WithParamInterface<TilesTestConfig> {
protected:
LayerTreeHostTilesPixelTest() : LayerTreePixelTest(renderer_type()) {
- switch (raster_mode()) {
- case GPU:
- case GPU_LOW_BIT_DEPTH:
- set_gpu_rasterization();
- break;
- default:
- break;
- }
+ set_raster_type(GetParam().raster_type);
}
- RendererType renderer_type() const { return GetParam().renderer_type; }
-
- RasterMode raster_mode() const { return GetParam().raster_mode; }
+ TestRendererType renderer_type() const { return GetParam().renderer_type; }
void InitializeSettings(LayerTreeSettings* settings) override {
LayerTreePixelTest::InitializeSettings(settings);
- switch (raster_mode()) {
- case ONE_COPY:
- settings->use_zero_copy = false;
- break;
- case GPU_LOW_BIT_DEPTH:
- settings->use_rgba_4444 = true;
- settings->unpremultiply_and_dither_low_bit_depth_tiles = true;
- break;
- default:
- break;
- }
settings->use_partial_raster = use_partial_raster_;
}
@@ -180,27 +153,36 @@ class LayerTreeHostTilesTestPartialInvalidation
};
std::vector<TilesTestConfig> const kTestCases = {
- {LayerTreeTest::RENDERER_SOFTWARE, BITMAP},
- {LayerTreeTest::RENDERER_GL, ONE_COPY},
- {LayerTreeTest::RENDERER_GL, GPU},
- {LayerTreeTest::RENDERER_SKIA_GL, ONE_COPY},
- {LayerTreeTest::RENDERER_SKIA_GL, GPU},
+ {TestRendererType::kSoftware, TestRasterType::kBitmap},
+ {TestRendererType::kGL, TestRasterType::kOneCopy},
+ {TestRendererType::kGL, TestRasterType::kGpu},
+ {TestRendererType::kSkiaGL, TestRasterType::kOneCopy},
+ {TestRendererType::kSkiaGL, TestRasterType::kGpu},
#if defined(ENABLE_CC_VULKAN_TESTS)
- {LayerTreeTest::RENDERER_SKIA_VK, ONE_COPY},
- {LayerTreeTest::RENDERER_SKIA_VK, GPU},
+ {TestRendererType::kSkiaVk, TestRasterType::kOop},
#endif // defined(ENABLE_CC_VULKAN_TESTS)
+#if defined(ENABLE_CC_DAWN_TESTS)
+ {TestRendererType::kSkiaDawn, TestRasterType::kOop},
+#endif // defined(ENABLE_CC_DAWN_TESTS)
};
INSTANTIATE_TEST_SUITE_P(All,
LayerTreeHostTilesTestPartialInvalidation,
::testing::ValuesIn(kTestCases));
-TEST_P(LayerTreeHostTilesTestPartialInvalidation, PartialRaster) {
+#if defined(OS_WIN) && defined(ADDRESS_SANITIZER)
+// Flaky on Windows ASAN https://crbug.com/1045521
+#define MAYBE_PartialRaster DISABLED_PartialRaster
+#else
+#define MAYBE_PartialRaster PartialRaster
+#endif
+TEST_P(LayerTreeHostTilesTestPartialInvalidation, MAYBE_PartialRaster) {
use_partial_raster_ = true;
RunSingleThreadedPixelTest(
picture_layer_,
base::FilePath(FILE_PATH_LITERAL("blue_yellow_partial_flipped.png")));
}
+#undef MAYBE_PartialRaster
TEST_P(LayerTreeHostTilesTestPartialInvalidation, FullRaster) {
RunSingleThreadedPixelTest(
@@ -209,11 +191,16 @@ TEST_P(LayerTreeHostTilesTestPartialInvalidation, FullRaster) {
}
std::vector<TilesTestConfig> const kTestCasesMultiThread = {
- {LayerTreeTest::RENDERER_GL, ONE_COPY},
- {LayerTreeTest::RENDERER_SKIA_GL, ONE_COPY},
+ {TestRendererType::kGL, TestRasterType::kOneCopy},
+ {TestRendererType::kSkiaGL, TestRasterType::kOneCopy},
#if defined(ENABLE_CC_VULKAN_TESTS)
- {LayerTreeTest::RENDERER_SKIA_VK, ONE_COPY},
+ // TODO(sgilhuly): Switch this to one copy raster once is is supported for
+ // Vulkan in these tests.
+ {TestRendererType::kSkiaVk, TestRasterType::kOop},
#endif // defined(ENABLE_CC_VULKAN_TESTS)
+#if defined(ENABLE_CC_DAWN_TESTS)
+ {TestRendererType::kSkiaDawn, TestRasterType::kOop},
+#endif // defined(ENABLE_CC_DAWN_TESTS)
};
using LayerTreeHostTilesTestPartialInvalidationMultiThread =
@@ -239,14 +226,22 @@ TEST_P(LayerTreeHostTilesTestPartialInvalidationMultiThread,
picture_layer_,
base::FilePath(FILE_PATH_LITERAL("blue_yellow_partial_flipped.png")));
}
+#undef MAYBE_PartialRaster
TEST_P(LayerTreeHostTilesTestPartialInvalidationMultiThread, FullRaster) {
RunPixelTest(picture_layer_,
base::FilePath(FILE_PATH_LITERAL("blue_yellow_flipped.png")));
}
-using LayerTreeHostTilesTestPartialInvalidationLowBitDepth =
- LayerTreeHostTilesTestPartialInvalidation;
+class LayerTreeHostTilesTestPartialInvalidationLowBitDepth
+ : public LayerTreeHostTilesTestPartialInvalidation {
+ protected:
+ void InitializeSettings(LayerTreeSettings* settings) override {
+ LayerTreeHostTilesPixelTest::InitializeSettings(settings);
+ settings->use_rgba_4444 = true;
+ settings->unpremultiply_and_dither_low_bit_depth_tiles = true;
+ }
+};
// This test doesn't work on Vulkan because on our hardware we can't render to
// RGBA4444 format using either SwiftShader or native Vulkan. See
@@ -255,8 +250,8 @@ INSTANTIATE_TEST_SUITE_P(
All,
LayerTreeHostTilesTestPartialInvalidationLowBitDepth,
::testing::Values(
- TilesTestConfig{LayerTreeTest::RENDERER_SKIA_GL, GPU_LOW_BIT_DEPTH},
- TilesTestConfig{LayerTreeTest::RENDERER_GL, GPU_LOW_BIT_DEPTH}));
+ TilesTestConfig{TestRendererType::kSkiaGL, TestRasterType::kGpu},
+ TilesTestConfig{TestRendererType::kGL, TestRasterType::kGpu}));
TEST_P(LayerTreeHostTilesTestPartialInvalidationLowBitDepth, PartialRaster) {
use_partial_raster_ = true;
diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc
index 2d94507a792..f5469543c99 100644
--- a/chromium/cc/trees/layer_tree_host_unittest.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest.cc
@@ -30,6 +30,7 @@
#include "cc/layers/picture_layer.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/layers/video_layer.h"
+#include "cc/metrics/events_metrics_manager.h"
#include "cc/paint/image_animation_count.h"
#include "cc/resources/ui_resource_manager.h"
#include "cc/test/fake_content_layer_client.h"
@@ -51,10 +52,10 @@
#include "cc/test/skia_common.h"
#include "cc/test/test_layer_tree_frame_sink.h"
#include "cc/trees/clip_node.h"
+#include "cc/trees/compositor_commit_data.h"
#include "cc/trees/effect_node.h"
#include "cc/trees/layer_tree_host_impl.h"
#include "cc/trees/layer_tree_impl.h"
-#include "cc/trees/scroll_and_scale_set.h"
#include "cc/trees/scroll_node.h"
#include "cc/trees/single_thread_proxy.h"
#include "cc/trees/swap_promise.h"
@@ -81,6 +82,12 @@
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
+#define EXPECT_SCOPED(statements) \
+ { \
+ SCOPED_TRACE(""); \
+ statements; \
+ }
+
using testing::_;
using testing::AnyNumber;
using testing::AtLeast;
@@ -1539,11 +1546,11 @@ class LayerTreeHostTestEarlyDamageCheckStops : public LayerTreeHostTest {
// Change the child layer each frame. Since the child layer is translated
// past the viewport, it should not cause damage, but webview will still
// invalidate if the frame doesn't check for damage early.
- child_->SetOpacity(1.0 / (float)(frame + 1));
+ child_->SetOpacity(1.0f / (frame + 1));
// For |damaged_frame_limit| consecutive frames, cause actual damage.
if (frame >= 3 && frame < (damaged_frame_limit_ + 3)) {
- root_->SetOpacity(1.0 / (float)frame);
+ root_->SetOpacity(1.0f / frame);
}
}
@@ -2672,8 +2679,8 @@ SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestDeviceScaleFactorChange);
class LayerTreeHostTestRasterColorSpaceChange : public LayerTreeHostTest {
public:
void SetupTree() override {
- space1_ = gfx::ColorSpace::CreateXYZD50();
- space2_ = gfx::ColorSpace::CreateSRGB();
+ space1_ = gfx::DisplayColorSpaces(gfx::ColorSpace::CreateXYZD50());
+ space2_ = gfx::DisplayColorSpaces(gfx::ColorSpace::CreateSRGB());
root_layer_ = Layer::Create();
root_layer_->SetBounds(gfx::Size(10, 20));
@@ -2683,7 +2690,7 @@ class LayerTreeHostTestRasterColorSpaceChange : public LayerTreeHostTest {
root_layer_->AddChild(child_layer_);
layer_tree_host()->SetRootLayer(root_layer_);
- layer_tree_host()->SetRasterColorSpace(space1_);
+ layer_tree_host()->SetDisplayColorSpaces(space1_);
LayerTreeHostTest::SetupTree();
client_.set_bounds(root_layer_->bounds());
}
@@ -2701,32 +2708,38 @@ class LayerTreeHostTestRasterColorSpaceChange : public LayerTreeHostTest {
// The first frame will have full damage, and should be in the initial
// color space.
EXPECT_FALSE(frame_data->has_no_damage);
- EXPECT_TRUE(space1_ == host_impl->active_tree()->raster_color_space());
+ EXPECT_TRUE(space1_ ==
+ host_impl->active_tree()->display_color_spaces());
break;
case 1:
// Empty commit.
EXPECT_TRUE(frame_data->has_no_damage);
- EXPECT_TRUE(space1_ == host_impl->active_tree()->raster_color_space());
+ EXPECT_TRUE(space1_ ==
+ host_impl->active_tree()->display_color_spaces());
break;
case 2:
// The change from space1 to space2 should cause full damage.
EXPECT_FALSE(frame_data->has_no_damage);
- EXPECT_TRUE(space2_ == host_impl->active_tree()->raster_color_space());
+ EXPECT_TRUE(space2_ ==
+ host_impl->active_tree()->display_color_spaces());
break;
case 3:
// Empty commit with the color space set to space2 redundantly.
EXPECT_TRUE(frame_data->has_no_damage);
- EXPECT_TRUE(space2_ == host_impl->active_tree()->raster_color_space());
+ EXPECT_TRUE(space2_ ==
+ host_impl->active_tree()->display_color_spaces());
break;
case 4:
// The change from space2 to space1 should cause full damage.
EXPECT_FALSE(frame_data->has_no_damage);
- EXPECT_TRUE(space1_ == host_impl->active_tree()->raster_color_space());
+ EXPECT_TRUE(space1_ ==
+ host_impl->active_tree()->display_color_spaces());
break;
case 5:
// Empty commit.
EXPECT_TRUE(frame_data->has_no_damage);
- EXPECT_TRUE(space1_ == host_impl->active_tree()->raster_color_space());
+ EXPECT_TRUE(space1_ ==
+ host_impl->active_tree()->display_color_spaces());
EndTest();
break;
default:
@@ -2751,19 +2764,19 @@ class LayerTreeHostTestRasterColorSpaceChange : public LayerTreeHostTest {
break;
case 2:
EXPECT_TRUE(child_layer_->update_rect().IsEmpty());
- layer_tree_host()->SetRasterColorSpace(space2_);
+ layer_tree_host()->SetDisplayColorSpaces(space2_);
EXPECT_FALSE(child_layer_->update_rect().IsEmpty());
break;
case 3:
// The redundant SetRasterColorSpace should cause no commit and no
// damage. Force a commit for the test to continue.
- layer_tree_host()->SetRasterColorSpace(space2_);
+ layer_tree_host()->SetDisplayColorSpaces(space2_);
PostSetNeedsCommitToMainThread();
EXPECT_TRUE(child_layer_->update_rect().IsEmpty());
break;
case 4:
EXPECT_TRUE(child_layer_->update_rect().IsEmpty());
- layer_tree_host()->SetRasterColorSpace(space1_);
+ layer_tree_host()->SetDisplayColorSpaces(space1_);
EXPECT_FALSE(child_layer_->update_rect().IsEmpty());
break;
case 5:
@@ -2779,8 +2792,8 @@ class LayerTreeHostTestRasterColorSpaceChange : public LayerTreeHostTest {
}
private:
- gfx::ColorSpace space1_;
- gfx::ColorSpace space2_;
+ gfx::DisplayColorSpaces space1_;
+ gfx::DisplayColorSpaces space2_;
FakeContentLayerClient client_;
scoped_refptr<Layer> root_layer_;
scoped_refptr<Layer> child_layer_;
@@ -3037,13 +3050,13 @@ class LayerTreeHostTestDamageWithScale : public LayerTreeHostTest {
host_impl->active_tree()->LayerById(child_layer_->id()));
// We remove tilings pretty aggressively if they are not ideal. Add this
// back in so that we can compare
- // child_layer_impl->GetEnclosingRectInTargetSpace to the damage.
+ // child_layer_impl->visible_drawable_content_rect() to the damage.
child_layer_impl->AddTilingUntilNextDraw(1.3f);
- EXPECT_EQ(gfx::Rect(26, 26), root_damage_rect);
- EXPECT_EQ(child_layer_impl->GetEnclosingRectInTargetSpace(),
+ EXPECT_EQ(gfx::Rect(25, 25), root_damage_rect);
+ EXPECT_EQ(child_layer_impl->visible_drawable_content_rect(),
root_damage_rect);
- EXPECT_TRUE(child_layer_impl->GetEnclosingRectInTargetSpace().Contains(
+ EXPECT_TRUE(child_layer_impl->visible_drawable_content_rect().Contains(
gfx::Rect(child_layer_->bounds())));
break;
}
@@ -3332,9 +3345,11 @@ class ViewportDeltasAppliedDuringPinch : public LayerTreeHostTest,
layer_tree_host()->InnerViewportScrollLayerForTesting();
EXPECT_EQ(scroll_layer->element_id(), last_scrolled_element_id_);
EXPECT_EQ(gfx::ScrollOffset(50, 50), last_scrolled_offset_);
- // The scroll offset in scroll tree needs update from blink which doesn't
- // exist in this test.
- EXPECT_EQ(gfx::ScrollOffset(), CurrentScrollOffset(scroll_layer));
+ // The scroll offset in the scroll tree is typically updated from blink
+ // which doesn't exist in this test. Because we preemptively apply the
+ // scroll offset in LayerTreeHost::UpdateScrollOffsetFromImpl, the current
+ // scroll offset will still be updated.
+ EXPECT_EQ(gfx::ScrollOffset(50, 50), CurrentScrollOffset(scroll_layer));
EndTest();
}
@@ -3947,94 +3962,6 @@ class LayerTreeHostTestCompositeImmediatelyStateTransitions
SINGLE_THREAD_TEST_F(LayerTreeHostTestCompositeImmediatelyStateTransitions);
-class LayerTreeHostTestLCDChange : public LayerTreeHostTest {
- public:
- void SetupTree() override {
- num_tiles_rastered_ = 0;
-
- scoped_refptr<Layer> root_layer = PictureLayer::Create(&client_);
- client_.set_fill_with_nonsolid_color(true);
- root_layer->SetIsDrawable(true);
- root_layer->SetBounds(gfx::Size(10, 10));
- root_layer->SetContentsOpaque(true);
-
- layer_tree_host()->SetRootLayer(root_layer);
-
- // The expectations are based on the assumption that the default
- // LCD settings are:
- EXPECT_TRUE(layer_tree_host()->GetSettings().can_use_lcd_text);
-
- LayerTreeHostTest::SetupTree();
- client_.set_bounds(root_layer->bounds());
- }
-
- void BeginTest() override { PostSetNeedsCommitToMainThread(); }
-
- void DidCommitAndDrawFrame() override {
- switch (layer_tree_host()->SourceFrameNumber()) {
- case 1:
- PostSetNeedsCommitToMainThread();
- break;
- case 2:
- // Change contents_opaque that should trigger lcd change.
- layer_tree_host()->root_layer()->SetContentsOpaque(false);
- break;
- case 3:
- // Change contents_opaque that should trigger lcd change.
- layer_tree_host()->root_layer()->SetContentsOpaque(true);
- break;
- case 4:
- EndTest();
- break;
- }
- }
-
- void NotifyTileStateChangedOnThread(LayerTreeHostImpl* host_impl,
- const Tile* tile) override {
- ++num_tiles_rastered_;
- }
-
- void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
- PictureLayerImpl* root_layer =
- static_cast<PictureLayerImpl*>(host_impl->active_tree()->root_layer());
- bool can_use_lcd_text =
- root_layer->ComputeLCDTextDisallowedReasonForTesting() ==
- LCDTextDisallowedReason::kNone;
- switch (host_impl->active_tree()->source_frame_number()) {
- case 0:
- // The first draw.
- EXPECT_EQ(1, num_tiles_rastered_);
- EXPECT_TRUE(can_use_lcd_text);
- EXPECT_TRUE(root_layer->can_use_lcd_text());
- break;
- case 1:
- // Nothing changed on the layer.
- EXPECT_EQ(1, num_tiles_rastered_);
- EXPECT_TRUE(can_use_lcd_text);
- EXPECT_TRUE(root_layer->can_use_lcd_text());
- break;
- case 2:
- // LCD text was disabled; it should be re-rastered with LCD text off.
- EXPECT_EQ(2, num_tiles_rastered_);
- EXPECT_FALSE(can_use_lcd_text);
- EXPECT_FALSE(root_layer->can_use_lcd_text());
- break;
- case 3:
- // LCD text was enabled, but it's sticky and stays off.
- EXPECT_EQ(2, num_tiles_rastered_);
- EXPECT_TRUE(can_use_lcd_text);
- EXPECT_FALSE(root_layer->can_use_lcd_text());
- break;
- }
- }
-
- private:
- FakeContentLayerClient client_;
- int num_tiles_rastered_;
-};
-
-SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestLCDChange);
-
class LayerTreeHostTestBeginFrameNotificationShutdownWhileEnabled
: public LayerTreeHostTest {
public:
@@ -4108,6 +4035,7 @@ class OnDrawLayerTreeFrameSink : public TestLayerTreeFrameSink {
scoped_refptr<viz::RasterContextProvider> worker_context_provider,
gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
const viz::RendererSettings& renderer_settings,
+ const viz::DebugRendererSettings* const debug_settings,
base::SingleThreadTaskRunner* task_runner,
bool synchronous_composite,
double refresh_rate,
@@ -4116,6 +4044,7 @@ class OnDrawLayerTreeFrameSink : public TestLayerTreeFrameSink {
std::move(worker_context_provider),
gpu_memory_buffer_manager,
renderer_settings,
+ debug_settings,
task_runner,
synchronous_composite,
false /* disable_display_vsync */,
@@ -4155,8 +4084,8 @@ class LayerTreeHostTestAbortedCommitDoesntStallSynchronousCompositor
base::Unretained(this));
auto frame_sink = std::make_unique<OnDrawLayerTreeFrameSink>(
compositor_context_provider, std::move(worker_context_provider),
- gpu_memory_buffer_manager(), renderer_settings, ImplThreadTaskRunner(),
- false /* synchronous_composite */, refresh_rate,
+ gpu_memory_buffer_manager(), renderer_settings, &debug_settings_,
+ ImplThreadTaskRunner(), false /* synchronous_composite */, refresh_rate,
std::move(on_draw_callback));
layer_tree_frame_sink_ = frame_sink.get();
return std::move(frame_sink);
@@ -4196,7 +4125,8 @@ class LayerTreeHostTestSynchronousCompositorActivateWithoutDraw
// Make |invalidate_callback| do nothing so there is no draw.
auto frame_sink = std::make_unique<OnDrawLayerTreeFrameSink>(
compositor_context_provider, std::move(worker_context_provider),
- gpu_memory_buffer_manager(), renderer_settings, ImplThreadTaskRunner(),
+ gpu_memory_buffer_manager(), renderer_settings, &debug_settings_,
+ ImplThreadTaskRunner(),
/*synchronous_composite=*/false, refresh_rate,
/*invalidate_callback=*/base::DoNothing());
return std::move(frame_sink);
@@ -6123,13 +6053,11 @@ SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestDeferSwapPromiseForVisibility);
class SimpleSwapPromiseMonitor : public SwapPromiseMonitor {
public:
SimpleSwapPromiseMonitor(LayerTreeHost* layer_tree_host,
- LayerTreeHostImpl* layer_tree_host_impl,
int* set_needs_commit_count,
int* set_needs_redraw_count)
- : SwapPromiseMonitor(
- (layer_tree_host ? layer_tree_host->GetSwapPromiseManager()
- : nullptr),
- layer_tree_host_impl),
+ : SwapPromiseMonitor(layer_tree_host
+ ? layer_tree_host->GetSwapPromiseManager()
+ : nullptr),
set_needs_commit_count_(set_needs_commit_count) {}
~SimpleSwapPromiseMonitor() override = default;
@@ -6159,7 +6087,7 @@ class LayerTreeHostTestSwapPromiseDuringCommit : public LayerTreeHostTest {
{
std::unique_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor(
- new SimpleSwapPromiseMonitor(layer_tree_host(), nullptr,
+ new SimpleSwapPromiseMonitor(layer_tree_host(),
&set_needs_commit_count,
&set_needs_redraw_count));
layer_tree_host()->QueueSwapPromise(std::move(swap_promise));
@@ -6182,7 +6110,7 @@ class LayerTreeHostTestSwapPromiseDuringCommit : public LayerTreeHostTest {
{
std::unique_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor(
- new SimpleSwapPromiseMonitor(layer_tree_host(), nullptr,
+ new SimpleSwapPromiseMonitor(layer_tree_host(),
&set_needs_commit_count,
&set_needs_redraw_count));
layer_tree_host()->QueueSwapPromise(std::move(swap_promise));
@@ -6212,7 +6140,7 @@ class LayerTreeHostTestSimpleSwapPromiseMonitor : public LayerTreeHostTest {
{
std::unique_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor(
- new SimpleSwapPromiseMonitor(layer_tree_host(), nullptr,
+ new SimpleSwapPromiseMonitor(layer_tree_host(),
&set_needs_commit_count,
&set_needs_redraw_count));
layer_tree_host()->SetNeedsCommit();
@@ -6228,7 +6156,7 @@ class LayerTreeHostTestSimpleSwapPromiseMonitor : public LayerTreeHostTest {
{
std::unique_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor(
- new SimpleSwapPromiseMonitor(layer_tree_host(), nullptr,
+ new SimpleSwapPromiseMonitor(layer_tree_host(),
&set_needs_commit_count,
&set_needs_redraw_count));
layer_tree_host()->SetNeedsUpdateLayers();
@@ -6238,7 +6166,7 @@ class LayerTreeHostTestSimpleSwapPromiseMonitor : public LayerTreeHostTest {
{
std::unique_ptr<SimpleSwapPromiseMonitor> swap_promise_monitor(
- new SimpleSwapPromiseMonitor(layer_tree_host(), nullptr,
+ new SimpleSwapPromiseMonitor(layer_tree_host(),
&set_needs_commit_count,
&set_needs_redraw_count));
layer_tree_host()->SetNeedsAnimate();
@@ -6785,8 +6713,9 @@ class LayerTreeHostTestSynchronousCompositeSwapPromise
!layer_tree_host()->GetSettings().single_thread_proxy_scheduler;
return std::make_unique<TestLayerTreeFrameSink>(
compositor_context_provider, std::move(worker_context_provider),
- gpu_memory_buffer_manager(), renderer_settings, ImplThreadTaskRunner(),
- synchronous_composite, disable_display_vsync, refresh_rate);
+ gpu_memory_buffer_manager(), renderer_settings, &debug_settings_,
+ ImplThreadTaskRunner(), synchronous_composite, disable_display_vsync,
+ refresh_rate);
}
void BeginTest() override {
@@ -6872,8 +6801,8 @@ class LayerTreeHostAcceptsDeltasFromImplWithoutRootLayer
void BeginTest() override {
layer_tree_host()->SetRootLayer(nullptr);
- info_.page_scale_delta = 3.14f;
- info_.top_controls_delta = 2.73f;
+ commit_data_.page_scale_delta = 3.14f;
+ commit_data_.top_controls_delta = 2.73f;
PostSetNeedsCommitToMainThread();
}
@@ -6881,21 +6810,21 @@ class LayerTreeHostAcceptsDeltasFromImplWithoutRootLayer
void BeginMainFrame(const viz::BeginFrameArgs& args) override {
EXPECT_EQ(nullptr, layer_tree_host()->root_layer());
- layer_tree_host()->ApplyScrollAndScale(&info_);
+ layer_tree_host()->ApplyCompositorChanges(&commit_data_);
EndTest();
}
void ApplyViewportChanges(const ApplyViewportChangesArgs& args) override {
- EXPECT_EQ(info_.page_scale_delta, args.page_scale_delta);
- EXPECT_EQ(info_.top_controls_delta, args.top_controls_delta);
- EXPECT_EQ(info_.browser_controls_constraint,
+ EXPECT_EQ(commit_data_.page_scale_delta, args.page_scale_delta);
+ EXPECT_EQ(commit_data_.top_controls_delta, args.top_controls_delta);
+ EXPECT_EQ(commit_data_.browser_controls_constraint,
args.browser_controls_constraint);
deltas_sent_to_client_ = true;
}
void AfterTest() override { EXPECT_TRUE(deltas_sent_to_client_); }
- ScrollAndScaleSet info_;
+ CompositorCommitData commit_data_;
bool deltas_sent_to_client_;
};
@@ -7096,7 +7025,7 @@ class LayerTreeHostTestCrispUpAfterPinchEndsWithOneCopy
viz::TestGLES2Interface* gl =
display_context_provider->UnboundTestContextGL();
gl->set_support_sync_query(true);
-#if defined(OS_MACOSX)
+#if defined(OS_MAC)
gl->set_support_texture_rectangle(true);
#endif
display_context_provider->BindToCurrentThread();
@@ -7945,15 +7874,15 @@ class LayerTreeHostTestSubmitFrameResources : public LayerTreeHostTest {
DrawResult draw_result) override {
frame->render_passes.clear();
- viz::RenderPass* child_pass =
- AddRenderPass(&frame->render_passes, 2, gfx::Rect(3, 3, 10, 10),
- gfx::Transform(), FilterOperations());
- std::vector<viz::ResourceId> child_resources =
- AddOneOfEveryQuadType(child_pass, host_impl->resource_provider(), 0);
+ viz::RenderPass* child_pass = AddRenderPass(
+ &frame->render_passes, viz::RenderPassId{2}, gfx::Rect(3, 3, 10, 10),
+ gfx::Transform(), FilterOperations());
+ std::vector<viz::ResourceId> child_resources = AddOneOfEveryQuadType(
+ child_pass, host_impl->resource_provider(), viz::RenderPassId{0});
- viz::RenderPass* pass =
- AddRenderPass(&frame->render_passes, 1, gfx::Rect(3, 3, 10, 10),
- gfx::Transform(), FilterOperations());
+ viz::RenderPass* pass = AddRenderPass(
+ &frame->render_passes, viz::RenderPassId{1}, gfx::Rect(3, 3, 10, 10),
+ gfx::Transform(), FilterOperations());
std::vector<viz::ResourceId> root_resources = AddOneOfEveryQuadType(
pass, host_impl->resource_provider(), child_pass->id);
@@ -8242,7 +8171,8 @@ SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestDiscardAckAfterRelease);
class LayerTreeHostTestImageAnimation : public LayerTreeHostTest {
public:
- explicit LayerTreeHostTestImageAnimation(RendererType type = RENDERER_GL)
+ explicit LayerTreeHostTestImageAnimation(
+ TestRendererType type = TestRendererType::kGL)
: LayerTreeHostTest(type) {}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
@@ -8280,10 +8210,10 @@ class LayerTreeHostTestImageAnimation : public LayerTreeHostTest {
static_cast<PictureLayerImpl*>(host_impl->active_tree()->root_layer());
switch (++draw_count_) {
case 1:
- // First draw, everything is invalid.
+ // First draw, everything is invalid. layer->update_rect() doesn't
+ // matter because a new layer always causes surface damage.
EXPECT_EQ(layer->InvalidationForTesting().bounds(),
gfx::Rect(layer->bounds()));
- EXPECT_EQ(layer->update_rect(), gfx::Rect(layer->bounds()));
EXPECT_EQ(generator_->frames_decoded().size(), 1u);
EXPECT_EQ(generator_->frames_decoded().count(0u), 1u);
break;
@@ -8323,7 +8253,7 @@ class LayerTreeHostTestImageAnimationDrawImage
: public LayerTreeHostTestImageAnimation {
public:
explicit LayerTreeHostTestImageAnimationDrawImage(
- RendererType type = RENDERER_GL)
+ TestRendererType type = TestRendererType::kGL)
: LayerTreeHostTestImageAnimation(type) {}
private:
@@ -8379,7 +8309,7 @@ class LayerTreeHostTestImageAnimationSynchronousScheduling
: public LayerTreeHostTestImageAnimationDrawImage {
public:
explicit LayerTreeHostTestImageAnimationSynchronousScheduling(
- RendererType type = RENDERER_GL)
+ TestRendererType type = TestRendererType::kGL)
: LayerTreeHostTestImageAnimationDrawImage(type) {}
void InitializeSettings(LayerTreeSettings* settings) override {
@@ -8395,7 +8325,7 @@ class LayerTreeHostTestImageAnimationSynchronousSchedulingSoftwareDraw
public:
LayerTreeHostTestImageAnimationSynchronousSchedulingSoftwareDraw()
: LayerTreeHostTestImageAnimationSynchronousScheduling(
- RENDERER_SOFTWARE) {}
+ TestRendererType::kSoftware) {}
void AfterTest() override {
LayerTreeHostTestImageAnimationSynchronousScheduling::AfterTest();
@@ -8500,7 +8430,7 @@ class LayerTreeHostTestCheckerboardUkm : public LayerTreeHostTest {
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
- if (!impl->pinch_gesture_active())
+ if (!impl->GetInputHandler().pinch_gesture_active())
return;
// We just drew a frame, stats for it should have been recorded. End the
@@ -8972,7 +8902,10 @@ class LayerTreeHostTestDelegatedInkMetadataOnAndOff
void DidCommitAndDrawFrame() override {
// Cause a redraw to occur.
- layer_->SetNeedsDisplay();
+ if (set_needs_display_) {
+ layer_->SetNeedsDisplay();
+ set_needs_display_ = false;
+ }
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
@@ -9000,8 +8933,6 @@ class LayerTreeHostTestDelegatedInkMetadataOnAndOff
}
}
- void AfterTest() override {}
-
// RenderFrameMetadataObserver implementation.
void BindToCurrentThread() override {}
void OnRenderFrameSubmission(
@@ -9016,9 +8947,356 @@ class LayerTreeHostTestDelegatedInkMetadataOnAndOff
base::Optional<viz::DelegatedInkMetadata> expected_metadata_;
FakeContentLayerClient client_;
scoped_refptr<Layer> layer_;
+ bool set_needs_display_ = true;
};
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestDelegatedInkMetadataOnAndOff);
+// Base class for EventMetrics-related tests.
+class LayerTreeHostTestEventsMetrics : public LayerTreeHostTest {
+ protected:
+ void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+
+ // Simulate an event being handled on the main thread.
+ void PostSimulateEvent() {
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&LayerTreeHostTestEventsMetrics::SimulateEventOnMain,
+ base::Unretained(this)));
+ }
+
+ // Verifies the number of saved events metrics on the main thread.
+ void PostVerifyMainSavedEventsMetricsCount(size_t count) const {
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE, base::BindOnce(&LayerTreeHostTestEventsMetrics::
+ VerifyMainSavedEventsMetricsCountOnMain,
+ base::Unretained(this), count));
+ }
+
+ // Verifies the number of events metrics copied from the main thread to the
+ // impl thread.
+ void VerifyImplEventsMetricsFromMainCount(LayerTreeHostImpl* impl,
+ size_t count) const {
+ EXPECT_EQ(count, impl->active_tree()
+ ->events_metrics_from_main_thread_count_for_testing());
+ }
+
+ private:
+ void SimulateEventOnMain() {
+ auto scoped_event_monitor =
+ layer_tree_host()->GetScopedEventMetricsMonitor(EventMetrics::Create(
+ ui::ET_GESTURE_SCROLL_UPDATE, base::TimeTicks::Now(),
+ ui::ScrollInputType::kWheel));
+ layer_tree_host()->SetNeedsAnimate();
+ EXPECT_SCOPED(VerifyMainSavedEventsMetricsCountOnMain(1));
+ }
+
+ void VerifyMainSavedEventsMetricsCountOnMain(size_t count) const {
+ EXPECT_EQ(count,
+ layer_tree_host()->saved_events_metrics_count_for_testing());
+ }
+};
+
+// Verifies that if the commit is aborted (deferred) due to LayerTreeHost being
+// hidden, events metrics are not thrown away to be used when it becomes
+// visible.
+class LayerTreeHostTestKeepEventsMetricsForVisibility
+ : public LayerTreeHostTestEventsMetrics {
+ protected:
+ void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl,
+ const viz::BeginFrameArgs& args) override {
+ // Skip if we have already received a begin-impl-frame and acted on it.
+ if (received_will_begin_impl_frame_)
+ return;
+ received_will_begin_impl_frame_ = true;
+
+ EXPECT_SCOPED(VerifyImplEventsMetricsFromMainCount(impl, 0));
+
+ // Simulate an event being handled on the main thread. Since the main frame
+ // is not yet scheduled, we will have events metrics when the main frame is
+ // processed.
+ PostSimulateEvent();
+
+ // Hide layer tree host. Since the main frame is not yet scheduled, layer
+ // tree host will be hidden when the main frame is processed, causing it to
+ // abort.
+ PostSetLayerTreeHostVisible(false);
+ }
+
+ void BeginMainFrameAbortedOnThread(LayerTreeHostImpl* impl,
+ CommitEarlyOutReason reason) override {
+ EXPECT_EQ(reason, CommitEarlyOutReason::ABORTED_NOT_VISIBLE);
+
+ // Since the main frame is aborted due to invisibility, events metrics
+ // should not have been thrown away.
+ PostVerifyMainSavedEventsMetricsCount(1);
+
+ // Make layer tree host visible so that the deferred commit is completed,
+ // causing events metrics being passed to the impl thread.
+ PostSetLayerTreeHostVisible(true);
+ }
+
+ void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
+ // Now that a commit is completed and activated, events metrics from main
+ // thread should have been moved to the impl thread.
+ PostVerifyMainSavedEventsMetricsCount(0);
+ EXPECT_SCOPED(VerifyImplEventsMetricsFromMainCount(impl, 1));
+
+ EndTest();
+ }
+
+ private:
+ void SetLayerTreeHostVisibleOnMain(bool visible) {
+ layer_tree_host()->SetVisible(visible);
+ }
+
+ void PostSetLayerTreeHostVisible(bool visible) {
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&LayerTreeHostTestKeepEventsMetricsForVisibility::
+ SetLayerTreeHostVisibleOnMain,
+ base::Unretained(this), visible));
+ }
+
+ bool received_will_begin_impl_frame_ = false;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestKeepEventsMetricsForVisibility);
+
+// Verifies that if the commit is aborted due to main frame update being
+// deferred, events metrics are not thrown away to be used when the actual
+// commit happens.
+class LayerTreeHostTestKeepEventsMetricsForDeferredMainFrameUpdate
+ : public LayerTreeHostTestEventsMetrics {
+ protected:
+ void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl,
+ const viz::BeginFrameArgs& args) override {
+ // Skip if we have already received a begin-impl-frame and acted on it.
+ if (received_will_begin_impl_frame_)
+ return;
+ received_will_begin_impl_frame_ = true;
+
+ EXPECT_SCOPED(VerifyImplEventsMetricsFromMainCount(impl, 0));
+
+ // Simulate an event being handled on the main thread. Since the main frame
+ // is not yet scheduled, we will have events metrics when the main frame is
+ // processed.
+ PostSimulateEvent();
+
+ // Defer main frame updates. Since the main frame is not yet scheduled, main
+ // frame updates will be deferred when the main frame is processed, causing
+ // it to abort.
+ PostDeferMainFrameUpdate();
+ }
+
+ void BeginMainFrameAbortedOnThread(LayerTreeHostImpl* impl,
+ CommitEarlyOutReason reason) override {
+ EXPECT_EQ(reason, CommitEarlyOutReason::ABORTED_DEFERRED_MAIN_FRAME_UPDATE);
+
+ // Since the main frame is aborted due to deferred main frame updates,
+ // events metrics should not have been thrown away.
+ PostVerifyMainSavedEventsMetricsCount(1);
+
+ // Stop deferring main frame updates so that the deferred commit is
+ // completed, causing events metrics being passed to the impl thread.
+ PostStopDeferringMainFrameUpdate();
+ }
+
+ void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
+ // Now that a commit is completed and activated, events metrics from main
+ // thread should have been moved to the impl thread.
+ PostVerifyMainSavedEventsMetricsCount(0);
+ EXPECT_SCOPED(VerifyImplEventsMetricsFromMainCount(impl, 1));
+
+ EndTest();
+ }
+
+ private:
+ void DeferMainFrameUpdateOnMain() {
+ scoped_defer_main_frame_update_ = layer_tree_host()->DeferMainFrameUpdate();
+ }
+
+ void PostDeferMainFrameUpdate() {
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &LayerTreeHostTestKeepEventsMetricsForDeferredMainFrameUpdate::
+ DeferMainFrameUpdateOnMain,
+ base::Unretained(this)));
+ }
+
+ void StopDeferringMainFrameUpdateOnMain() {
+ scoped_defer_main_frame_update_.reset();
+ }
+
+ void PostStopDeferringMainFrameUpdate() {
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &LayerTreeHostTestKeepEventsMetricsForDeferredMainFrameUpdate::
+ StopDeferringMainFrameUpdateOnMain,
+ base::Unretained(this)));
+ }
+
+ bool received_will_begin_impl_frame_ = false;
+ std::unique_ptr<ScopedDeferMainFrameUpdate> scoped_defer_main_frame_update_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostTestKeepEventsMetricsForDeferredMainFrameUpdate);
+
+// Verifies that if the commit is aborted (deferred), events metrics are not
+// thrown away to be used when the actual commit happens.
+class LayerTreeHostTestKeepEventsMetricsForDeferredCommit
+ : public LayerTreeHostTestEventsMetrics {
+ protected:
+ void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl,
+ const viz::BeginFrameArgs& args) override {
+ // Skip if we have already received a begin-impl-frame and acted on it.
+ if (received_will_begin_impl_frame_)
+ return;
+ received_will_begin_impl_frame_ = true;
+
+ EXPECT_SCOPED(VerifyImplEventsMetricsFromMainCount(impl, 0));
+
+ // Simulate an event being handled on the main thread. Since the main frame
+ // is not yet scheduled, we will have events metrics when the main frame is
+ // processed.
+ PostSimulateEvent();
+
+ // Defer commits. Since the main frame is not yet scheduled, commits will be
+ // deferred when the main frame is processed, causing it to abort.
+ PostDeferCommit();
+ }
+
+ void BeginMainFrameAbortedOnThread(LayerTreeHostImpl* impl,
+ CommitEarlyOutReason reason) override {
+ EXPECT_EQ(reason, CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT);
+
+ // Since the main frame is aborted due to deferred commits, events metrics
+ // should not have been thrown away.
+ PostVerifyMainSavedEventsMetricsCount(1);
+
+ // Stop deferring commits so that the deferred commit is completed, causing
+ // events metrics being passed to the impl thread.
+ PostStopDeferringCommit();
+ }
+
+ void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
+ // Now that a commit is completed and activated, events metrics from main
+ // thread should have been moved to the impl thread.
+ PostVerifyMainSavedEventsMetricsCount(0);
+ EXPECT_SCOPED(VerifyImplEventsMetricsFromMainCount(impl, 1));
+
+ EndTest();
+ }
+
+ private:
+ void DeferCommitOnMain() {
+ layer_tree_host()->StartDeferringCommits(base::TimeDelta::FromDays(1));
+ }
+
+ void PostDeferCommit() {
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&LayerTreeHostTestKeepEventsMetricsForDeferredCommit::
+ DeferCommitOnMain,
+ base::Unretained(this)));
+ }
+
+ void StopDeferringCommitOnMain() {
+ layer_tree_host()->StopDeferringCommits(
+ PaintHoldingCommitTrigger::kFirstContentfulPaint);
+ }
+
+ void PostStopDeferringCommit() {
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&LayerTreeHostTestKeepEventsMetricsForDeferredCommit::
+ StopDeferringCommitOnMain,
+ base::Unretained(this)));
+ }
+
+ bool received_will_begin_impl_frame_ = false;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostTestKeepEventsMetricsForDeferredCommit);
+
+// Verifies that if the commit is aborted due to no damage, events metrics are
+// thrown away, so there is nothing to report in the next commit.
+class LayerTreeHostTestIgnoreEventsMetricsForNoUpdate
+ : public LayerTreeHostTestEventsMetrics {
+ protected:
+ void WillBeginImplFrameOnThread(LayerTreeHostImpl* impl,
+ const viz::BeginFrameArgs& args) override {
+ // Continue only if we are waiting for the second frame's being-impl-frame.
+ // The first frame will end up in a commit which is not what we want.
+ if (state_ != State::kWaitingForSecondFrameBeginImpl)
+ return;
+ state_ = State::kReceivedSecondFrameBeginImpl;
+
+ EXPECT_SCOPED(VerifyImplEventsMetricsFromMainCount(impl, 0));
+
+ // Simulate an event being handled on the main thread. Since the main frame
+ // is not yet scheduled, we will have events metrics when the main frame is
+ // processed.
+ PostSimulateEvent();
+ }
+
+ void BeginMainFrameAbortedOnThread(LayerTreeHostImpl* impl,
+ CommitEarlyOutReason reason) override {
+ EXPECT_EQ(reason, CommitEarlyOutReason::FINISHED_NO_UPDATES);
+
+ // We should reach here only for the second frame.
+ EXPECT_EQ(state_, State::kReceivedSecondFrameBeginImpl);
+ state_ = State::kWaitingForThirdFrameActivation;
+
+ // Since the main frame is aborted due to no updates, events metrics should
+ // have been thrown away.
+ PostVerifyMainSavedEventsMetricsCount(0);
+
+ // Request another commit to make sure no events metrics is passed to the
+ // impl thread when it is complete.
+ PostSetNeedsCommitToMainThread();
+ }
+
+ void DidActivateTreeOnThread(LayerTreeHostImpl* impl) override {
+ switch (state_) {
+ case State::kWaitingForFirstFrameActivation:
+ // Now that the first frame's commit is completed and activated, request
+ // another begin-main-frame without requesting a full commit so that it
+ // aborts with no updates.
+ state_ = State::kWaitingForSecondFrameBeginImpl;
+ EXPECT_SCOPED(VerifyImplEventsMetricsFromMainCount(impl, 0));
+ PostSetNeedsUpdateLayersToMainThread();
+ break;
+ case State::kWaitingForThirdFrameActivation:
+ // Now that the third frame's commit is completed and activated there
+ // should be no events metrics on the main or impl thread as the events
+ // metrics were thrown away after second frame was aborted with no
+ // updates.
+ EXPECT_SCOPED(VerifyImplEventsMetricsFromMainCount(impl, 0));
+ PostVerifyMainSavedEventsMetricsCount(0);
+ EndTest();
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ private:
+ enum class State {
+ kWaitingForFirstFrameActivation,
+ kWaitingForSecondFrameBeginImpl,
+ kReceivedSecondFrameBeginImpl,
+ kWaitingForThirdFrameActivation,
+ };
+
+ State state_ = State::kWaitingForFirstFrameActivation;
+};
+
+MULTI_THREAD_TEST_F(LayerTreeHostTestIgnoreEventsMetricsForNoUpdate);
+
} // 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 7a188881491..a0191d0e899 100644
--- a/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc
@@ -7,6 +7,8 @@
#include "base/bind.h"
#include "base/location.h"
#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/test_timeouts.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "cc/layers/effect_tree_layer_list_iterator.h"
@@ -27,8 +29,7 @@
namespace cc {
namespace {
-auto CombineWithCompositorModes(
- const std::vector<LayerTreeTest::RendererType>& types) {
+auto CombineWithCompositorModes(const std::vector<TestRendererType>& types) {
return ::testing::Combine(::testing::ValuesIn(types),
::testing::Values(CompositorMode::SINGLE_THREADED,
CompositorMode::THREADED));
@@ -37,11 +38,13 @@ auto CombineWithCompositorModes(
class LayerTreeHostCopyRequestTest
: public LayerTreeTest,
public ::testing::WithParamInterface<
- ::testing::tuple<LayerTreeTest::RendererType, CompositorMode>> {
+ ::testing::tuple<TestRendererType, CompositorMode>> {
public:
LayerTreeHostCopyRequestTest() : LayerTreeTest(renderer_type()) {}
- RendererType renderer_type() const { return ::testing::get<0>(GetParam()); }
+ TestRendererType renderer_type() const {
+ return ::testing::get<0>(GetParam());
+ }
CompositorMode compositor_mode() const {
return ::testing::get<1>(GetParam());
@@ -162,12 +165,11 @@ class LayerTreeHostCopyRequestTestMultipleRequests
scoped_refptr<FakePictureLayer> grand_child;
};
-INSTANTIATE_TEST_SUITE_P(
- All,
- LayerTreeHostCopyRequestTestMultipleRequests,
- CombineWithCompositorModes({LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL,
- LayerTreeTest::RENDERER_SOFTWARE}));
+INSTANTIATE_TEST_SUITE_P(All,
+ LayerTreeHostCopyRequestTestMultipleRequests,
+ CombineWithCompositorModes(
+ {TestRendererType::kGL, TestRendererType::kSkiaGL,
+ TestRendererType::kSoftware}));
TEST_P(LayerTreeHostCopyRequestTestMultipleRequests, Test) {
RunTest(compositor_mode());
@@ -201,8 +203,8 @@ class LayerTreeHostCopyRequestTestMultipleRequestsOutOfOrder
INSTANTIATE_TEST_SUITE_P(
All,
LayerTreeHostCopyRequestTestMultipleRequestsOutOfOrder,
- CombineWithCompositorModes({LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL}));
+ CombineWithCompositorModes({TestRendererType::kGL,
+ TestRendererType::kSkiaGL}));
TEST_P(LayerTreeHostCopyRequestTestMultipleRequestsOutOfOrder, Test) {
RunTest(compositor_mode());
@@ -226,9 +228,7 @@ class LayerTreeHostCopyRequestCompletionCausesCommit
client_.set_bounds(root_->bounds());
}
- void BeginTest() override {
- PostSetNeedsCommitToMainThread();
- }
+ void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
int frame = layer_tree_host()->SourceFrameNumber();
@@ -262,8 +262,8 @@ class LayerTreeHostCopyRequestCompletionCausesCommit
INSTANTIATE_TEST_SUITE_P(
All,
LayerTreeHostCopyRequestCompletionCausesCommit,
- CombineWithCompositorModes({LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL}));
+ CombineWithCompositorModes({TestRendererType::kGL,
+ TestRendererType::kSkiaGL}));
TEST_P(LayerTreeHostCopyRequestCompletionCausesCommit, Test) {
RunTest(compositor_mode());
@@ -289,10 +289,7 @@ class LayerTreeHostCopyRequestTestLayerDestroyed
client_.set_bounds(root_->bounds());
}
- void BeginTest() override {
- callback_count_ = 0;
- PostSetNeedsCommitToMainThread();
- }
+ void BeginTest() override { PostSetNeedsCommitToMainThread(); }
void DidCommit() override {
int frame = layer_tree_host()->SourceFrameNumber();
@@ -303,76 +300,77 @@ class LayerTreeHostCopyRequestTestLayerDestroyed
viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
base::BindOnce(
&LayerTreeHostCopyRequestTestLayerDestroyed::CopyOutputCallback,
- base::Unretained(this))));
+ base::Unretained(this), &main_destroyed_event_)));
impl_destroyed_->RequestCopyOfOutput(std::make_unique<
viz::CopyOutputRequest>(
viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP,
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_);
+ base::Unretained(this), &impl_destroyed_event_)));
+ EXPECT_FALSE(main_destroyed_event_.IsSignaled());
+ EXPECT_FALSE(impl_destroyed_event_.IsSignaled());
// Destroy the main thread layer right away.
main_destroyed_->RemoveFromParent();
main_destroyed_.reset();
- // 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.
layer_tree_host()->SetViewportRectAndScale(
gfx::Rect(), 1.f, GetCurrentLocalSurfaceIdAllocation());
break;
case 2:
- // Flush the message loops and make sure the callbacks run.
+ // Flush the message loops.
layer_tree_host()->SetNeedsCommit();
break;
case 3:
- // No drawing means no readback yet.
- EXPECT_EQ(1, callback_count_);
+ // There is no timing promise of when we'll get the main callback but if
+ // we wait for it, we should receive it before we destroy the impl and
+ // before the impl callback. The resulting bitmap will be empty because
+ // we destroyed it in the first frame before it got a chance to draw.
+ EXPECT_TRUE(
+ main_destroyed_event_.TimedWait(TestTimeouts::action_timeout()));
+ EXPECT_FALSE(impl_destroyed_event_.IsSignaled());
// Destroy the impl thread layer.
impl_destroyed_->RemoveFromParent();
impl_destroyed_.reset();
-
- // No callback yet because it's on the impl side.
- EXPECT_EQ(1, callback_count_);
break;
case 4:
- // Flush the message loops and make sure the callbacks run.
+ // Flush the message loops.
layer_tree_host()->SetNeedsCommit();
break;
case 5:
- // We should get another callback with a NULL bitmap.
- EXPECT_EQ(2, callback_count_);
+ // There is no timing promise of when we'll get the impl callback but if
+ // we wait for it, we should receive it before the end of the test.
+ // The resulting bitmap will be empty because we called
+ // SetViewportRectAndScale() in the first frame before it got a chance
+ // to draw.
+ EXPECT_TRUE(
+ impl_destroyed_event_.TimedWait(TestTimeouts::action_timeout()));
EndTest();
break;
}
}
- void CopyOutputCallback(std::unique_ptr<viz::CopyOutputResult> result) {
+ void CopyOutputCallback(base::WaitableEvent* event,
+ std::unique_ptr<viz::CopyOutputResult> result) {
EXPECT_TRUE(result->IsEmpty());
- ++callback_count_;
+ event->Signal();
}
- int callback_count_;
FakeContentLayerClient client_;
scoped_refptr<FakePictureLayer> root_;
+ base::WaitableEvent main_destroyed_event_;
scoped_refptr<FakePictureLayer> main_destroyed_;
+ base::WaitableEvent impl_destroyed_event_;
scoped_refptr<FakePictureLayer> impl_destroyed_;
};
INSTANTIATE_TEST_SUITE_P(
All,
LayerTreeHostCopyRequestTestLayerDestroyed,
- CombineWithCompositorModes({LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL}));
+ CombineWithCompositorModes({TestRendererType::kGL,
+ TestRendererType::kSkiaGL}));
TEST_P(LayerTreeHostCopyRequestTestLayerDestroyed, Test) {
RunTest(compositor_mode());
@@ -477,8 +475,8 @@ class LayerTreeHostCopyRequestTestInHiddenSubtree
INSTANTIATE_TEST_SUITE_P(
All,
LayerTreeHostCopyRequestTestInHiddenSubtree,
- CombineWithCompositorModes({LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL}));
+ CombineWithCompositorModes({TestRendererType::kGL,
+ TestRendererType::kSkiaGL}));
TEST_P(LayerTreeHostCopyRequestTestInHiddenSubtree, Test) {
RunTest(compositor_mode());
@@ -584,8 +582,8 @@ class LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest
void AfterTest() override { EXPECT_TRUE(did_swap_); }
- viz::RenderPassId parent_render_pass_id = 0;
- viz::RenderPassId copy_layer_render_pass_id = 0;
+ viz::RenderPassId parent_render_pass_id;
+ viz::RenderPassId copy_layer_render_pass_id;
TestLayerTreeFrameSink* frame_sink_ = nullptr;
bool did_swap_ = false;
FakeContentLayerClient client_;
@@ -598,8 +596,8 @@ class LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest
INSTANTIATE_TEST_SUITE_P(
All,
LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest,
- CombineWithCompositorModes({LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL}));
+ CombineWithCompositorModes({TestRendererType::kGL,
+ TestRendererType::kSkiaGL}));
TEST_P(LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest, Test) {
RunTest(compositor_mode());
@@ -654,8 +652,8 @@ class LayerTreeHostCopyRequestTestClippedOut
INSTANTIATE_TEST_SUITE_P(
All,
LayerTreeHostCopyRequestTestClippedOut,
- CombineWithCompositorModes({LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL}));
+ CombineWithCompositorModes({TestRendererType::kGL,
+ TestRendererType::kSkiaGL}));
TEST_P(LayerTreeHostCopyRequestTestClippedOut, Test) {
RunTest(compositor_mode());
@@ -714,8 +712,8 @@ class LayerTreeHostCopyRequestTestScaledLayer
INSTANTIATE_TEST_SUITE_P(
All,
LayerTreeHostCopyRequestTestScaledLayer,
- CombineWithCompositorModes({LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL}));
+ CombineWithCompositorModes({TestRendererType::kGL,
+ TestRendererType::kSkiaGL}));
TEST_P(LayerTreeHostCopyRequestTestScaledLayer, Test) {
RunTest(compositor_mode());
@@ -809,8 +807,8 @@ class LayerTreeHostTestAsyncTwoReadbacksWithoutDraw
INSTANTIATE_TEST_SUITE_P(
All,
LayerTreeHostTestAsyncTwoReadbacksWithoutDraw,
- CombineWithCompositorModes({LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL}));
+ CombineWithCompositorModes({TestRendererType::kGL,
+ TestRendererType::kSkiaGL}));
TEST_P(LayerTreeHostTestAsyncTwoReadbacksWithoutDraw, Test) {
RunTest(compositor_mode());
@@ -952,8 +950,8 @@ class LayerTreeHostCopyRequestTestDeleteSharedImage
INSTANTIATE_TEST_SUITE_P(
All,
LayerTreeHostCopyRequestTestDeleteSharedImage,
- CombineWithCompositorModes({LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL}));
+ CombineWithCompositorModes({TestRendererType::kGL,
+ TestRendererType::kSkiaGL}));
TEST_P(LayerTreeHostCopyRequestTestDeleteSharedImage, Test) {
RunTest(compositor_mode());
@@ -1006,9 +1004,7 @@ class LayerTreeHostCopyRequestTestCountSharedImages
LayerTreeHostCopyRequestTest::SetupTree();
}
- void BeginTest() override {
- PostSetNeedsCommitToMainThread();
- }
+ void BeginTest() override { PostSetNeedsCommitToMainThread(); }
virtual void RequestCopy(Layer* layer) = 0;
@@ -1097,8 +1093,8 @@ class LayerTreeHostCopyRequestTestCreatesSharedImage
INSTANTIATE_TEST_SUITE_P(
All,
LayerTreeHostCopyRequestTestCreatesSharedImage,
- CombineWithCompositorModes({LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL}));
+ CombineWithCompositorModes({TestRendererType::kGL,
+ TestRendererType::kSkiaGL}));
TEST_P(LayerTreeHostCopyRequestTestCreatesSharedImage, Test) {
RunTest(compositor_mode());
@@ -1185,8 +1181,8 @@ class LayerTreeHostCopyRequestTestDestroyBeforeCopy
INSTANTIATE_TEST_SUITE_P(
All,
LayerTreeHostCopyRequestTestDestroyBeforeCopy,
- CombineWithCompositorModes({LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL}));
+ CombineWithCompositorModes({TestRendererType::kGL,
+ TestRendererType::kSkiaGL}));
TEST_P(LayerTreeHostCopyRequestTestDestroyBeforeCopy, Test) {
RunTest(compositor_mode());
@@ -1268,8 +1264,8 @@ class LayerTreeHostCopyRequestTestShutdownBeforeCopy
INSTANTIATE_TEST_SUITE_P(
All,
LayerTreeHostCopyRequestTestShutdownBeforeCopy,
- CombineWithCompositorModes({LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL}));
+ CombineWithCompositorModes({TestRendererType::kGL,
+ TestRendererType::kSkiaGL}));
TEST_P(LayerTreeHostCopyRequestTestShutdownBeforeCopy, Test) {
RunTest(compositor_mode());
@@ -1400,8 +1396,8 @@ class LayerTreeHostCopyRequestTestMultipleDrawsHiddenCopyRequest
INSTANTIATE_TEST_SUITE_P(
All,
LayerTreeHostCopyRequestTestMultipleDrawsHiddenCopyRequest,
- CombineWithCompositorModes({LayerTreeTest::RENDERER_GL,
- LayerTreeTest::RENDERER_SKIA_GL}));
+ CombineWithCompositorModes({TestRendererType::kGL,
+ TestRendererType::kSkiaGL}));
TEST_P(LayerTreeHostCopyRequestTestMultipleDrawsHiddenCopyRequest, Test) {
RunTest(compositor_mode());
diff --git a/chromium/cc/trees/layer_tree_host_unittest_record_gpu_histogram.cc b/chromium/cc/trees/layer_tree_host_unittest_record_gpu_histogram.cc
index 9d45733fa6d..01a9d5adc35 100644
--- a/chromium/cc/trees/layer_tree_host_unittest_record_gpu_histogram.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest_record_gpu_histogram.cc
@@ -20,6 +20,7 @@ TEST(LayerTreeHostRecordGpuHistogramTest, SingleThreaded) {
std::unique_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(
&host_client, &task_graph_runner, animation_host.get(), settings,
CompositorMode::SINGLE_THREADED);
+ host->CreateFakeLayerTreeHostImpl();
host->RecordGpuRasterizationHistogram(host->host_impl());
EXPECT_FALSE(host->gpu_rasterization_histogram_recorded());
}
@@ -32,6 +33,7 @@ TEST(LayerTreeHostRecordGpuHistogramTest, Threaded) {
std::unique_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create(
&host_client, &task_graph_runner, animation_host.get(), settings,
CompositorMode::THREADED);
+ host->CreateFakeLayerTreeHostImpl();
host->RecordGpuRasterizationHistogram(host->host_impl());
EXPECT_TRUE(host->gpu_rasterization_histogram_recorded());
}
diff --git a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc
index 3a95b182bf0..cb7c24cd27f 100644
--- a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -41,6 +41,8 @@
using ::testing::Mock;
+using ScrollThread = cc::InputHandler::ScrollThread;
+
namespace cc {
namespace {
@@ -693,7 +695,7 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest {
InputHandler::ScrollStatus status =
impl->ScrollBegin(BeginState(scroll_point, scroll_amount_).get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
impl->ScrollUpdate(UpdateState(gfx::Point(), scroll_amount_).get());
auto* scrolling_node = impl->CurrentlyScrollingNode();
CHECK(scrolling_node);
@@ -717,7 +719,7 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest {
InputHandler::ScrollStatus status =
impl->ScrollBegin(BeginState(scroll_point, scroll_amount_).get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
impl->ScrollUpdate(UpdateState(gfx::Point(), scroll_amount_).get());
impl->ScrollEnd();
@@ -1067,7 +1069,7 @@ MULTI_THREAD_TEST_F(LayerTreeHostScrollTestImplOnlyScroll);
// TODO(crbug.com/574283): Mac currently doesn't support smooth scrolling wheel
// events.
-#if !defined(OS_MACOSX)
+#if !defined(OS_MAC)
// 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.
@@ -1126,7 +1128,7 @@ class SmoothScrollAnimationEndNotification : public LayerTreeHostScrollTest {
ui::ScrollGranularity::kScrollByPixel;
InputHandler::ScrollStatus status = host_impl->ScrollBegin(
scroll_state.get(), ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
scroll_state = UpdateState(scroll_point, scroll_amount);
scroll_state->data()->delta_granularity =
ui::ScrollGranularity::kScrollByPixel;
@@ -1173,7 +1175,7 @@ class SmoothScrollAnimationEndNotification : public LayerTreeHostScrollTest {
};
MULTI_THREAD_TEST_F(SmoothScrollAnimationEndNotification);
-#endif // !defined(OS_MACOSX)
+#endif // !defined(OS_MAC)
void DoGestureScroll(LayerTreeHostImpl* host_impl,
const scoped_refptr<Layer>& scroller,
@@ -1187,7 +1189,7 @@ void DoGestureScroll(LayerTreeHostImpl* host_impl,
new ScrollState(begin_scroll_state_data));
auto scroll_status = host_impl->ScrollBegin(
begin_scroll_state.get(), ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, scroll_status.thread);
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, scroll_status.thread);
auto* scrolling_node = host_impl->CurrentlyScrollingNode();
EXPECT_TRUE(scrolling_node);
EXPECT_EQ(scrolling_node->element_id, scroller->element_id());
@@ -1268,10 +1270,12 @@ class LayerTreeHostScrollTestImplOnlyScrollSnap
DoGestureScroll(host_impl, scroller_, impl_thread_scroll_);
- EXPECT_TRUE(host_impl->IsAnimatingForSnap());
+ EXPECT_TRUE(
+ host_impl->GetInputHandler().animating_for_snap_for_testing());
EXPECT_VECTOR_EQ(impl_thread_scroll_, ScrollDelta(scroller_impl));
} else {
- snap_animation_finished_ = !host_impl->IsAnimatingForSnap();
+ snap_animation_finished_ =
+ !host_impl->GetInputHandler().animating_for_snap_for_testing();
}
}
@@ -1537,7 +1541,7 @@ class LayerTreeHostScrollTestScrollZeroMaxScrollOffset
ScrollState scroll_state(scroll_state_data);
InputHandler::ScrollStatus status =
impl->ScrollBegin(&scroll_state, ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread)
+ EXPECT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread)
<< "In Frame " << impl->active_tree()->source_frame_number();
switch (cur_step_) {
@@ -1604,11 +1608,11 @@ class LayerTreeHostScrollTestScrollNonDrawnLayer
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_EQ(ScrollThread::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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
status.main_thread_scrolling_reasons);
}
@@ -1616,7 +1620,7 @@ class LayerTreeHostScrollTestScrollNonDrawnLayer
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_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_FALSE(status.needs_main_thread_hit_test);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
@@ -1684,7 +1688,7 @@ class LayerTreeHostScrollTestImplScrollUnderMainThreadScrollingParent
// 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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kThreadedScrollingDisabled,
status.main_thread_scrolling_reasons);
}
@@ -1700,7 +1704,7 @@ class LayerTreeHostScrollTestImplScrollUnderMainThreadScrollingParent
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_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_FALSE(status.needs_main_thread_hit_test);
EXPECT_EQ(impl->CurrentlyScrollingNode(),
impl->OuterViewportScrollNode());
@@ -1708,7 +1712,7 @@ class LayerTreeHostScrollTestImplScrollUnderMainThreadScrollingParent
// 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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kThreadedScrollingDisabled,
status.main_thread_scrolling_reasons);
}
@@ -2681,13 +2685,13 @@ class NonScrollingNonFastScrollableRegion : public LayerTreeHostScrollTest {
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_EQ(ScrollThread::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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
status.main_thread_scrolling_reasons);
}
@@ -2700,10 +2704,10 @@ class NonScrollingNonFastScrollableRegion : public LayerTreeHostScrollTest {
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_EQ(ScrollThread::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(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
}
@@ -2720,7 +2724,7 @@ class NonScrollingNonFastScrollableRegion : public LayerTreeHostScrollTest {
// 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_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_FALSE(status.needs_main_thread_hit_test);
EXPECT_EQ(scroll_node, impl->CurrentlyScrollingNode());
impl->ScrollEnd();
@@ -2728,7 +2732,7 @@ class NonScrollingNonFastScrollableRegion : public LayerTreeHostScrollTest {
// 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(ScrollThread::SCROLL_ON_MAIN_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
status.main_thread_scrolling_reasons);
}
@@ -2787,7 +2791,7 @@ class UnifiedScrollingRepaintOnScroll : public LayerTreeTest {
BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 10)).get(),
ui::ScrollInputType::kTouchscreen);
- ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, status.thread);
ASSERT_EQ(layer_->scroll_tree_index(),
impl->CurrentlyScrollingNode()->id);
diff --git a/chromium/cc/trees/layer_tree_impl.cc b/chromium/cc/trees/layer_tree_impl.cc
index 7ab7bf797ef..f041671a252 100644
--- a/chromium/cc/trees/layer_tree_impl.cc
+++ b/chromium/cc/trees/layer_tree_impl.cc
@@ -12,6 +12,7 @@
#include <limits>
#include <memory>
#include <set>
+#include <unordered_set>
#include <utility>
#include "base/containers/adapters.h"
@@ -605,7 +606,7 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) {
target_tree->set_overscroll_behavior(overscroll_behavior_);
- target_tree->SetRasterColorSpace(raster_color_space_);
+ target_tree->SetDisplayColorSpaces(display_color_spaces_);
target_tree->elastic_overscroll()->PushPendingToActive();
target_tree->set_painted_device_scale_factor(painted_device_scale_factor());
@@ -1215,11 +1216,11 @@ gfx::Rect LayerTreeImpl::GetDeviceViewport() const {
return external_viewport;
}
-void LayerTreeImpl::SetRasterColorSpace(
- const gfx::ColorSpace& raster_color_space) {
- if (raster_color_space == raster_color_space_)
+void LayerTreeImpl::SetDisplayColorSpaces(
+ const gfx::DisplayColorSpaces& display_color_spaces) {
+ if (display_color_spaces_ == display_color_spaces)
return;
- raster_color_space_ = raster_color_space;
+ display_color_spaces_ = display_color_spaces;
}
void LayerTreeImpl::SetExternalPageScaleFactor(
@@ -1457,17 +1458,6 @@ bool LayerTreeImpl::UpdateDrawProperties(
return true;
}
-void LayerTreeImpl::UpdateCanUseLCDText() {
- // If this is not the sync tree, then it is not safe to update lcd text
- // as it causes invalidations and the tiles may be in use.
- DCHECK(IsSyncTree());
- bool tile_priorities_updated = false;
- for (auto* layer : picture_layers_)
- tile_priorities_updated |= layer->UpdateCanUseLCDTextAfterCommit();
- if (tile_priorities_updated)
- DidModifyTilePriorities();
-}
-
const RenderSurfaceList& LayerTreeImpl::GetRenderSurfaceList() const {
// If this assert triggers, then the list is dirty.
DCHECK(!needs_update_draw_properties_);
@@ -1666,7 +1656,7 @@ LayerImpl* LayerTreeImpl::FindPendingTreeLayerById(int id) {
}
bool LayerTreeImpl::PinchGestureActive() const {
- return host_impl_->pinch_gesture_active();
+ return host_impl_->GetInputHandler().pinch_gesture_active();
}
const viz::BeginFrameArgs& LayerTreeImpl::CurrentBeginFrameArgs() const {
@@ -2199,13 +2189,9 @@ LayerImpl* LayerTreeImpl::FindFirstScrollingLayerOrScrollbarThatIsHitByPoint(
FindClosestMatchingLayerState state;
LayerImpl* root_layer = layer_list_[0].get();
- auto HitTestScrollingLayerOrScrollbarFunctor =
- [this](const LayerImpl* layer) {
- return layer->HitTestable() &&
- (layer->IsScrollbarLayer() ||
- (property_trees()->scroll_tree.FindNodeFromElementId(
- layer->element_id())));
- };
+ auto HitTestScrollingLayerOrScrollbarFunctor = [](const LayerImpl* layer) {
+ return layer->HitTestable() && layer->IsScrollerOrScrollbar();
+ };
FindClosestMatchingLayer(screen_space_point, root_layer,
HitTestScrollingLayerOrScrollbarFunctor, &state);
return state.closest_match;
@@ -2296,6 +2282,91 @@ LayerTreeImpl::FindLayersHitByPointInNonFastScrollableRegion(
return layers;
}
+std::vector<const LayerImpl*>
+LayerTreeImpl::FindAllLayersUpToAndIncludingFirstScrollable(
+ const gfx::PointF& screen_space_point) {
+ std::vector<const LayerImpl*> layers;
+ if (layer_list_.empty())
+ return layers;
+ if (!UpdateDrawProperties())
+ return layers;
+
+ // If we hit a layer in a 3d context we can't rely on layer orders, we need
+ // to sort the layers by distance to hit. This is used only if the first_hit
+ // layer is in a 3d rendering context.
+ std::vector<std::pair<const LayerImpl*, float>> layers_3d;
+
+ const LayerImpl* first_hit = nullptr;
+
+ // We want to iterate from front to back when hit testing.
+ LayerImpl* root_layer = layer_list_[0].get();
+ for (const auto* layer : base::Reversed(*root_layer->layer_tree_impl())) {
+ if (!layer->HitTestable())
+ continue;
+
+ if (first_hit &&
+ layer->GetSortingContextId() != first_hit->GetSortingContextId())
+ continue;
+
+ float distance_to_intersection = 0.f;
+ bool hit = false;
+ if (layer->Is3dSorted()) {
+ hit =
+ PointHitsLayer(layer, screen_space_point, &distance_to_intersection);
+ } else {
+ hit = PointHitsLayer(layer, screen_space_point, nullptr);
+ }
+
+ if (!hit)
+ continue;
+
+ if (!first_hit)
+ first_hit = layer;
+
+ if (first_hit->Is3dSorted()) {
+ layers_3d.emplace_back(
+ std::pair<const LayerImpl*, float>(layer, distance_to_intersection));
+ } else {
+ layers.push_back(layer);
+ if (layer->IsScrollerOrScrollbar())
+ break;
+ }
+ }
+
+ if (!first_hit) {
+ DCHECK(layers.empty());
+ DCHECK(layers_3d.empty());
+ return layers;
+ }
+
+ if (first_hit->Is3dSorted()) {
+ DCHECK(layers.empty());
+ DCHECK(!layers_3d.empty());
+
+ // Since we hit a layer in a rendering context, we need to sort the layers
+ // based on their distance then add all until the first scrollable one to
+ // the return vector.
+ std::sort(layers_3d.begin(), layers_3d.end(),
+ [](const std::pair<const LayerImpl*, float>& a,
+ const std::pair<const LayerImpl*, float>& b) {
+ return a.second > b.second;
+ });
+
+ for (const auto& pair : layers_3d) {
+ const LayerImpl* layer = pair.first;
+
+ layers.push_back(layer);
+ if (layer->IsScrollerOrScrollbar())
+ break;
+ }
+ } else {
+ DCHECK(!layers.empty());
+ DCHECK(layers_3d.empty());
+ }
+
+ return layers;
+}
+
bool LayerTreeImpl::PointHitsNonFastScrollableRegion(
const gfx::PointF& screen_space_point,
const LayerImpl& layer) const {
@@ -2309,11 +2380,78 @@ bool LayerTreeImpl::PointHitsNonFastScrollableRegion(
layer.non_fast_scrollable_region(), &layer);
}
-struct HitTestFramedVisibleScrollableOrTouchableFunctor {
- bool operator()(LayerImpl* layer) const {
- return layer->HitTestable() && layer->frame_element_id();
+static ElementId GetFrameElementIdForLayer(const LayerImpl* layer) {
+ auto& transform_tree =
+ layer->layer_tree_impl()->property_trees()->transform_tree;
+ auto* node = transform_tree.Node(layer->transform_tree_index());
+ while (node && !node->visible_frame_element_id) {
+ node = transform_tree.Node(node->parent_frame_id);
}
-};
+ return node ? node->visible_frame_element_id : ElementId();
+}
+
+static void FindClosestMatchingLayerForAttribution(
+ const gfx::PointF& screen_space_point,
+ const LayerImpl* root_layer,
+ FindClosestMatchingLayerState* state) {
+ std::unordered_set<ElementId, ElementIdHash> hit_visible_frame_element_ids;
+ // We want to iterate from front to back when hit testing.
+ for (auto* layer : base::Reversed(*root_layer->layer_tree_impl())) {
+ if (!layer->HitTestable())
+ continue;
+
+ float distance_to_intersection = 0.f;
+ bool hit = false;
+ if (layer->Is3dSorted()) {
+ hit =
+ PointHitsLayer(layer, screen_space_point, &distance_to_intersection);
+ } else {
+ hit = PointHitsLayer(layer, screen_space_point, nullptr);
+ }
+
+ if (!hit)
+ continue;
+
+ bool in_front_of_previous_candidate =
+ state->closest_match &&
+ layer->GetSortingContextId() ==
+ state->closest_match->GetSortingContextId() &&
+ distance_to_intersection >
+ state->closest_distance + std::numeric_limits<float>::epsilon();
+
+ if (!state->closest_match || in_front_of_previous_candidate) {
+ state->closest_distance = distance_to_intersection;
+ state->closest_match = layer;
+ }
+
+ ElementId visible_frame_element_id = GetFrameElementIdForLayer(layer);
+ hit_visible_frame_element_ids.insert(visible_frame_element_id);
+ }
+
+ // Iterate through the transform tree of the hit layer in order to derive the
+ // frame path. If we hit any frame layer in our hit testing that belonged to
+ // a frame outside of this hierarchy, bail out.
+ //
+ // We explicitly allow occluding layers whose frames are parents of the
+ // targeted frame so that we can properly attribute the (common) parent ->
+ // child frame relationship. This is made possible since we can accurately
+ // hit test within layerized subframes, but not for all occluders.
+ if (auto* layer = state->closest_match) {
+ auto& transform_tree =
+ layer->layer_tree_impl()->property_trees()->transform_tree;
+ for (auto* node = transform_tree.Node(layer->transform_tree_index()); node;
+ node = transform_tree.Node(node->parent_frame_id)) {
+ hit_visible_frame_element_ids.erase(node->visible_frame_element_id);
+ if (hit_visible_frame_element_ids.size() == 0)
+ break;
+ }
+
+ if (hit_visible_frame_element_ids.size() > 0) {
+ state->closest_distance = 0.f;
+ state->closest_match = nullptr;
+ }
+ }
+}
ElementId LayerTreeImpl::FindFrameElementIdAtPoint(
const gfx::PointF& screen_space_point) {
@@ -2322,11 +2460,10 @@ ElementId LayerTreeImpl::FindFrameElementIdAtPoint(
if (!UpdateDrawProperties())
return {};
FindClosestMatchingLayerState state;
- FindClosestMatchingLayer(screen_space_point, layer_list_[0].get(),
- HitTestFramedVisibleScrollableOrTouchableFunctor(),
- &state);
+ FindClosestMatchingLayerForAttribution(screen_space_point,
+ layer_list_[0].get(), &state);
- if (auto* layer = state.closest_match) {
+ if (const auto* layer = state.closest_match) {
// TODO(https://crbug.com/1058870): Permit hit testing only if the framed
// element hit has a simple mask/clip. We don't have enough information
// about complex masks/clips on the impl-side to do accurate hit testing.
@@ -2335,8 +2472,9 @@ ElementId LayerTreeImpl::FindFrameElementIdAtPoint(
layer->effect_tree_index());
if (!layer_hit_test_region_is_masked)
- return layer->frame_element_id();
+ return GetFrameElementIdForLayer(layer);
}
+
return {};
}
@@ -2476,6 +2614,11 @@ int LayerTreeImpl::GetMSAASampleCountForRaster(
return host_impl_->GetMSAASampleCountForRaster(display_list);
}
+gfx::ColorSpace LayerTreeImpl::GetRasterColorSpace(
+ gfx::ContentColorUsage content_color_usage) const {
+ return host_impl_->GetRasterColorSpace(content_color_usage);
+}
+
void LayerTreeImpl::SetPendingPageScaleAnimation(
std::unique_ptr<PendingPageScaleAnimation> pending_animation) {
pending_page_scale_animation_ = std::move(pending_animation);
diff --git a/chromium/cc/trees/layer_tree_impl.h b/chromium/cc/trees/layer_tree_impl.h
index d4583970c74..2ea191c80e1 100644
--- a/chromium/cc/trees/layer_tree_impl.h
+++ b/chromium/cc/trees/layer_tree_impl.h
@@ -6,9 +6,11 @@
#define CC_TREES_LAYER_TREE_IMPL_H_
#include <map>
+#include <memory>
#include <set>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
#include "base/containers/flat_set.h"
@@ -156,6 +158,8 @@ class CC_EXPORT LayerTreeImpl {
decoding_mode_map);
int GetMSAASampleCountForRaster(
const scoped_refptr<DisplayItemList>& display_list);
+ gfx::ColorSpace GetRasterColorSpace(
+ gfx::ContentColorUsage content_color_usage) const;
// Tree specific methods exposed to layer-impl tree.
// ---------------------------------------------------------------------------
@@ -394,7 +398,9 @@ class CC_EXPORT LayerTreeImpl {
// internal (i.e. not external) device viewport.
gfx::Rect internal_device_viewport() { return device_viewport_rect_; }
- void SetRasterColorSpace(const gfx::ColorSpace& raster_color_space);
+ void SetDisplayColorSpaces(
+ const gfx::DisplayColorSpaces& display_color_spaces);
+
// OOPIFs need to know the page scale factor used in the main frame, but it
// is distributed differently (via VisualPropertiesSync), and used only to
// set raster-scale (page_scale_factor has geometry implications that are
@@ -403,18 +409,20 @@ class CC_EXPORT LayerTreeImpl {
float external_page_scale_factor() const {
return external_page_scale_factor_;
}
- // A function to provide page scale information for scaling scroll
- // deltas. In top-level frames we store this value in page_scale_factor_, but
- // for cross-process subframes it's stored in external_page_scale_factor_, so
- // that it only affects raster scale. These cases are mutually exclusive, so
- // only one of the values should ever vary from 1.f.
+ // A function to provide page scale information for scaling scroll deltas. In
+ // top-level frames we store this value in page_scale_factor_, but for
+ // cross-process subframes it's stored in external_page_scale_factor_, so
+ // that it only affects raster scale. These cases are mutually exclusive,
+ // except for a page hosted in a <portal>, so only one of the values should
+ // ever vary from 1.f.
float page_scale_factor_for_scroll() const {
DCHECK(external_page_scale_factor_ == 1.f ||
- current_page_scale_factor() == 1.f);
+ current_page_scale_factor() == 1.f ||
+ !settings().is_layer_tree_for_subframe);
return external_page_scale_factor_ * current_page_scale_factor();
}
- const gfx::ColorSpace& raster_color_space() const {
- return raster_color_space_;
+ const gfx::DisplayColorSpaces& display_color_spaces() const {
+ return display_color_spaces_;
}
SyncedElasticOverscroll* elastic_overscroll() {
@@ -448,7 +456,6 @@ class CC_EXPORT LayerTreeImpl {
bool UpdateDrawProperties(
bool update_image_animation_controller = true,
LayerImplList* output_update_layer_list_for_testing = nullptr);
- void UpdateCanUseLCDText();
void set_needs_update_draw_properties() {
needs_update_draw_properties_ = true;
@@ -590,6 +597,11 @@ 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);
+ // Returns all layers up to the first scroller or scrollbar layer, inclusive.
+ // The returned vector is sorted in order of top most come first. The back of
+ // the vector will be the scrollable layer if one was hit.
+ std::vector<const LayerImpl*> FindAllLayersUpToAndIncludingFirstScrollable(
+ const gfx::PointF& screen_space_point);
bool PointHitsNonFastScrollableRegion(const gfx::PointF& scree_space_point,
const LayerImpl& layer) const;
@@ -633,6 +645,9 @@ class CC_EXPORT LayerTreeImpl {
float bottom_controls_min_height() const {
return browser_controls_params_.bottom_controls_min_height;
}
+ bool only_expand_top_controls_at_page_top() const {
+ return browser_controls_params_.only_expand_top_controls_at_page_top;
+ }
void set_overscroll_behavior(const OverscrollBehavior& behavior);
OverscrollBehavior overscroll_behavior() const {
@@ -723,6 +738,10 @@ class CC_EXPORT LayerTreeImpl {
return std::move(delegated_ink_metadata_);
}
+ size_t events_metrics_from_main_thread_count_for_testing() const {
+ return events_metrics_from_main_thread_.size();
+ }
+
protected:
float ClampPageScaleFactorToLimits(float page_scale_factor) const;
void PushPageScaleFactorAndLimits(const float* page_scale_factor,
@@ -768,7 +787,7 @@ class CC_EXPORT LayerTreeImpl {
float device_scale_factor_;
float painted_device_scale_factor_;
- gfx::ColorSpace raster_color_space_;
+ gfx::DisplayColorSpaces display_color_spaces_;
viz::LocalSurfaceIdAllocation local_surface_id_allocation_from_parent_;
bool new_local_surface_id_request_ = false;
diff --git a/chromium/cc/trees/layer_tree_impl_unittest.cc b/chromium/cc/trees/layer_tree_impl_unittest.cc
index 883f35698a0..0e1e993131c 100644
--- a/chromium/cc/trees/layer_tree_impl_unittest.cc
+++ b/chromium/cc/trees/layer_tree_impl_unittest.cc
@@ -2199,12 +2199,13 @@ TEST_F(LayerTreeImplTest, DeviceScaleFactorNeedsDrawPropertiesUpdate) {
EXPECT_TRUE(host_impl().active_tree()->needs_update_draw_properties());
}
-TEST_F(LayerTreeImplTest, RasterColorSpaceDoesNotNeedDrawPropertiesUpdate) {
- host_impl().active_tree()->SetRasterColorSpace(
- gfx::ColorSpace::CreateXYZD50());
+TEST_F(LayerTreeImplTest, DisplayColorSpacesDoesNotNeedDrawPropertiesUpdate) {
+ host_impl().active_tree()->SetDisplayColorSpaces(
+ gfx::DisplayColorSpaces(gfx::ColorSpace::CreateXYZD50()));
host_impl().active_tree()->UpdateDrawProperties();
EXPECT_FALSE(host_impl().active_tree()->needs_update_draw_properties());
- host_impl().active_tree()->SetRasterColorSpace(gfx::ColorSpace::CreateSRGB());
+ host_impl().active_tree()->SetDisplayColorSpaces(
+ gfx::DisplayColorSpaces(gfx::ColorSpace::CreateSRGB()));
EXPECT_FALSE(host_impl().active_tree()->needs_update_draw_properties());
}
@@ -2549,91 +2550,6 @@ TEST_F(LayerTreeImplTest, ElementIdToAnimationMapsTrackOnlyOnSyncTree) {
EXPECT_EQ(filter_map.size(), 1u);
}
-TEST_F(LayerTreeImplTest, FrameElementIdHitTestSimple) {
- LayerImpl* frame_layer = AddLayer<LayerImpl>();
- frame_layer->SetBounds(gfx::Size(50, 50));
- frame_layer->SetDrawsContent(true);
- frame_layer->SetHitTestable(true);
- frame_layer->SetFrameElementId(ElementId(0x10));
- CopyProperties(root_layer(), frame_layer);
-
- UpdateDrawProperties(host_impl().active_tree());
-
- EXPECT_EQ(host_impl().FindFrameElementIdAtPoint(gfx::PointF(10, 10)),
- ElementId(0x10));
-}
-
-TEST_F(LayerTreeImplTest, FrameElementIdHitTestOverlap) {
- LayerImpl* frame_layer = AddLayer<LayerImpl>();
- frame_layer->SetBounds(gfx::Size(50, 50));
- frame_layer->SetHitTestable(true);
- frame_layer->SetFrameElementId(ElementId(0x10));
- CopyProperties(root_layer(), frame_layer);
-
- LayerImpl* occluding_frame_layer = AddLayer<LayerImpl>();
- occluding_frame_layer->SetBounds(gfx::Size(50, 50));
- occluding_frame_layer->SetHitTestable(true);
- occluding_frame_layer->SetFrameElementId(ElementId(0x20));
- CopyProperties(root_layer(), occluding_frame_layer);
- occluding_frame_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
-
- UpdateDrawProperties(host_impl().active_tree());
-
- EXPECT_EQ(host_impl().FindFrameElementIdAtPoint(gfx::PointF(30, 30)),
- ElementId(0x20));
-}
-
-TEST_F(LayerTreeImplTest, FrameElementIdHitTestOverlapSimpleClip) {
- LayerImpl* frame_layer = AddLayer<LayerImpl>();
- frame_layer->SetBounds(gfx::Size(50, 50));
- frame_layer->SetHitTestable(true);
- frame_layer->SetFrameElementId(ElementId(0x10));
- CopyProperties(root_layer(), frame_layer);
-
- LayerImpl* clipped_frame_layer = AddLayer<LayerImpl>();
- clipped_frame_layer->SetBounds(gfx::Size(50, 50));
- clipped_frame_layer->SetHitTestable(true);
- clipped_frame_layer->SetFrameElementId(ElementId(0x20));
- CopyProperties(root_layer(), clipped_frame_layer);
- clipped_frame_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
-
- // Create a clip excluding the overlapped region.
- auto& clip_node = CreateClipNode(clipped_frame_layer);
- clip_node.clip = gfx::RectF(40, 40, 10, 10);
-
- UpdateDrawProperties(host_impl().active_tree());
-
- // Ensure that the overlapping (clipped) layer isn't targeted.
- EXPECT_EQ(host_impl().FindFrameElementIdAtPoint(gfx::PointF(30, 30)),
- ElementId(0x10));
-}
-
-TEST_F(LayerTreeImplTest, FrameElementIdHitTestOverlapRoundedCorners) {
- LayerImpl* frame_layer = AddLayer<LayerImpl>();
- frame_layer->SetBounds(gfx::Size(50, 50));
- frame_layer->SetHitTestable(true);
- frame_layer->SetFrameElementId(ElementId(0x10));
- CopyProperties(root_layer(), frame_layer);
-
- LayerImpl* rounded_frame_layer = AddLayer<LayerImpl>();
- rounded_frame_layer->SetBounds(gfx::Size(50, 50));
- rounded_frame_layer->SetHitTestable(true);
- rounded_frame_layer->SetFrameElementId(ElementId(0x20));
- CopyProperties(root_layer(), rounded_frame_layer);
- rounded_frame_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
-
- // Add rounded corners to the layer, which are unable to be hit tested by the
- // simple quad-based logic.
- CreateEffectNode(rounded_frame_layer).rounded_corner_bounds =
- gfx::RRectF(25, 25, 50, 50, 5);
-
- UpdateDrawProperties(host_impl().active_tree());
-
- // The lookup should bail out in the presence of a complex clip/mask on the
- // target chain.
- EXPECT_FALSE(host_impl().FindFrameElementIdAtPoint(gfx::PointF(30, 30)));
-}
-
class LayerTreeImplOcclusionSettings : public LayerListSettings {
public:
explicit LayerTreeImplOcclusionSettings(bool enabled) {
diff --git a/chromium/cc/trees/layer_tree_settings.h b/chromium/cc/trees/layer_tree_settings.h
index 446d46764c5..eaff59cabf6 100644
--- a/chromium/cc/trees/layer_tree_settings.h
+++ b/chromium/cc/trees/layer_tree_settings.h
@@ -89,7 +89,6 @@ class CC_EXPORT LayerTreeSettings {
bool use_zero_copy = false;
bool use_partial_raster = false;
bool enable_elastic_overscroll = false;
- bool ignore_root_layer_flings = false;
size_t scheduled_raster_task_limit = 32;
bool use_occlusion_for_tile_prioritization = false;
bool use_layer_lists = false;
@@ -199,6 +198,11 @@ class CC_EXPORT LayerTreeSettings {
// When enabled, enforces new interoperable semantics for 3D transforms.
// See crbug.com/1008483.
bool enable_transform_interop = false;
+
+ // When enabled, the compositor specifies a frame rate preference that would
+ // allow the display to run at a low refresh rate matching the playback rate
+ // for videos updating onscreen.
+ bool force_preferred_interval_for_video = false;
};
class CC_EXPORT LayerListSettings : public LayerTreeSettings {
diff --git a/chromium/cc/trees/occlusion_tracker_unittest.cc b/chromium/cc/trees/occlusion_tracker_unittest.cc
index 920c746638d..c2dafbd27dd 100644
--- a/chromium/cc/trees/occlusion_tracker_unittest.cc
+++ b/chromium/cc/trees/occlusion_tracker_unittest.cc
@@ -6,6 +6,9 @@
#include <stddef.h>
+#include <memory>
+#include <utility>
+
#include "cc/animation/animation_host.h"
#include "cc/base/math_util.h"
#include "cc/layers/layer.h"
@@ -99,6 +102,7 @@ class OcclusionTrackerTest : public testing::Test {
animation_host_.get(),
LayerListSettings())),
next_layer_impl_id_(1) {
+ host_->CreateFakeLayerTreeHostImpl();
host_->host_impl()->InitializeFrameSink(layer_tree_frame_sink_.get());
}
diff --git a/chromium/cc/trees/property_tree.cc b/chromium/cc/trees/property_tree.cc
index 00ce2f2e65d..31e281766e5 100644
--- a/chromium/cc/trees/property_tree.cc
+++ b/chromium/cc/trees/property_tree.cc
@@ -4,18 +4,22 @@
#include <stddef.h>
+#include <algorithm>
#include <set>
+#include <string>
+#include <utility>
#include <vector>
#include "base/check_op.h"
#include "base/memory/ptr_util.h"
#include "base/numerics/checked_math.h"
+#include "base/numerics/safe_conversions.h"
#include "base/trace_event/traced_value.h"
#include "cc/trees/clip_node.h"
+#include "cc/trees/compositor_commit_data.h"
#include "cc/trees/effect_node.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/property_tree.h"
-#include "cc/trees/scroll_and_scale_set.h"
#include "cc/trees/scroll_node.h"
#include "cc/trees/transform_node.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
@@ -173,7 +177,6 @@ void TransformTree::UpdateTransforms(int id) {
UpdateScreenSpaceTransform(node, parent_node);
UpdateAnimationProperties(node, parent_node);
UpdateSnapping(node);
- UpdateNodeAndAncestorsHaveIntegerTranslations(node, parent_node);
UpdateTransformChanged(node, parent_node);
UpdateNodeAndAncestorsAreAnimatedOrInvertible(node, parent_node);
@@ -722,6 +725,14 @@ void EffectTree::UpdateEffectChanged(EffectNode* node,
}
}
+void EffectTree::UpdateHasFilters(EffectNode* node, EffectNode* parent_node) {
+ node->node_or_ancestor_has_filters = !node->filters.IsEmpty();
+ if (parent_node) {
+ node->node_or_ancestor_has_filters |=
+ parent_node->node_or_ancestor_has_filters;
+ }
+}
+
void EffectTree::UpdateBackfaceVisibility(EffectNode* node,
EffectNode* parent_node) {
if (parent_node && parent_node->hidden_by_backface_visibility) {
@@ -841,6 +852,7 @@ void EffectTree::UpdateEffects(int id) {
UpdateSubtreeHidden(node, parent_node);
UpdateIsDrawn(node, parent_node);
UpdateEffectChanged(node, parent_node);
+ UpdateHasFilters(node, parent_node);
UpdateBackfaceVisibility(node, parent_node);
UpdateHasMaskingChild(node, parent_node);
UpdateOnlyDrawsVisibleContent(node, parent_node);
@@ -949,8 +961,8 @@ void EffectTree::TakeCopyRequestsAndTransformToSurface(
.AssignIfValid(&scale_to_y)) {
continue;
}
- int scale_from_x = gfx::ToRoundedInt(scale_from_x_f);
- int scale_from_y = gfx::ToRoundedInt(scale_from_y_f);
+ int scale_from_x = base::ClampRound(scale_from_x_f);
+ int scale_from_y = base::ClampRound(scale_from_y_f);
if (scale_from_x <= 0 || scale_from_y <= 0 || scale_to_x <= 0 ||
scale_to_y <= 0) {
// Transformed scaling ratio became illegal. Drop the request to
@@ -1114,15 +1126,6 @@ bool EffectTree::HitTestMayBeAffectedByMask(int effect_id) const {
return false;
}
-void TransformTree::UpdateNodeAndAncestorsHaveIntegerTranslations(
- TransformNode* node,
- TransformNode* parent_node) {
- DCHECK(parent_node);
- node->node_and_ancestors_have_only_integer_translation =
- node->to_parent.IsIdentityOrIntegerTranslation() &&
- parent_node->node_and_ancestors_have_only_integer_translation;
-}
-
void ClipTree::SetViewportClip(gfx::RectF viewport_rect) {
if (size() < 2)
return;
@@ -1134,7 +1137,7 @@ void ClipTree::SetViewportClip(gfx::RectF viewport_rect) {
}
gfx::RectF ClipTree::ViewportClip() const {
- const unsigned long min_size = 1;
+ const size_t min_size = 1;
DCHECK_GT(size(), min_size);
return Node(kViewportNodeId)->clip;
}
@@ -1453,7 +1456,7 @@ gfx::ScrollOffset ScrollTree::PullDeltaForMainThread(
}
void ScrollTree::CollectScrollDeltas(
- ScrollAndScaleSet* scroll_info,
+ CompositorCommitData* commit_data,
ElementId inner_viewport_scroll_element_id,
bool use_fractional_deltas,
const base::flat_set<ElementId>& snapped_elements) {
@@ -1483,13 +1486,13 @@ void ScrollTree::CollectScrollDeltas(
TRACE_EVENT_INSTANT2("cc", "CollectScrollDeltas",
TRACE_EVENT_SCOPE_THREAD, "x", scroll_delta.x(), "y",
scroll_delta.y());
- ScrollAndScaleSet::ScrollUpdateInfo update(id, scroll_delta,
- snap_target_ids);
+ CompositorCommitData::ScrollUpdateInfo update(id, scroll_delta,
+ snap_target_ids);
if (id == inner_viewport_scroll_element_id) {
// Inner (visual) viewport is stored separately.
- scroll_info->inner_viewport_scroll = std::move(update);
+ commit_data->inner_viewport_scroll = std::move(update);
} else {
- scroll_info->scrolls.push_back(std::move(update));
+ commit_data->scrolls.push_back(std::move(update));
}
}
}
diff --git a/chromium/cc/trees/property_tree.h b/chromium/cc/trees/property_tree.h
index 6c41097cc7a..6e8d7d0fefe 100644
--- a/chromium/cc/trees/property_tree.h
+++ b/chromium/cc/trees/property_tree.h
@@ -8,6 +8,7 @@
#include <stddef.h>
#include <memory>
+#include <string>
#include <unordered_map>
#include <vector>
@@ -42,7 +43,7 @@ class LayerTreeImpl;
class RenderSurfaceImpl;
struct ClipNode;
struct EffectNode;
-struct ScrollAndScaleSet;
+struct CompositorCommitData;
struct ScrollNode;
struct TransformNode;
struct TransformCachedNodeData;
@@ -304,6 +305,8 @@ class CC_EXPORT EffectTree final : public PropertyTree<EffectNode> {
void UpdateEffectChanged(EffectNode* node, EffectNode* parent_node);
+ void UpdateHasFilters(EffectNode* node, EffectNode* parent_node);
+
void AddCopyRequest(int node_id,
std::unique_ptr<viz::CopyOutputRequest> request);
void PushCopyRequestsTo(EffectTree* other_tree);
@@ -432,7 +435,7 @@ class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> {
// Collects deltas for scroll changes on the impl thread that need to be
// reported to the main thread during the main frame. As such, should only be
// called on the impl thread side PropertyTrees.
- void CollectScrollDeltas(ScrollAndScaleSet* scroll_info,
+ void CollectScrollDeltas(CompositorCommitData* commit_data,
ElementId inner_viewport_scroll_element_id,
bool use_fractional_deltas,
const base::flat_set<ElementId>& snapped_elements);
@@ -453,6 +456,7 @@ class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> {
void SetBaseScrollOffset(ElementId id,
const gfx::ScrollOffset& scroll_offset);
+ // Returns true if the scroll offset is changed.
bool SetScrollOffset(ElementId id, const gfx::ScrollOffset& scroll_offset);
void SetScrollOffsetClobberActiveValue(ElementId id) {
GetOrCreateSyncedScrollOffset(id)->set_clobber_active_value();
diff --git a/chromium/cc/trees/property_tree_builder_unittest.cc b/chromium/cc/trees/property_tree_builder_unittest.cc
index 183c3342de0..7130f9547c9 100644
--- a/chromium/cc/trees/property_tree_builder_unittest.cc
+++ b/chromium/cc/trees/property_tree_builder_unittest.cc
@@ -4,6 +4,9 @@
#include "cc/trees/property_tree_builder.h"
+#include <memory>
+#include <utility>
+
#include "cc/animation/keyframed_animation_curve.h"
#include "cc/layers/layer.h"
#include "cc/layers/layer_impl.h"
@@ -200,7 +203,7 @@ TEST_F(PropertyTreeBuilderTest, RenderSurfaceListForTransparentChild) {
EXPECT_EQ(0, GetRenderSurfaceImpl(root)->num_contributors());
EXPECT_EQ(1U, GetRenderSurfaceList().size());
EXPECT_EQ(static_cast<viz::RenderPassId>(root->id()),
- GetRenderSurfaceList().at(0)->id());
+ GetRenderSurfaceList().at(0)->render_pass_id());
EXPECT_EQ(gfx::Rect(), ImplOf(root)->visible_drawable_content_rect());
}
diff --git a/chromium/cc/trees/property_tree_unittest.cc b/chromium/cc/trees/property_tree_unittest.cc
index 8ac81691db8..489ece12ea2 100644
--- a/chromium/cc/trees/property_tree_unittest.cc
+++ b/chromium/cc/trees/property_tree_unittest.cc
@@ -4,6 +4,8 @@
#include "cc/trees/property_tree.h"
+#include <utility>
+
#include "cc/input/main_thread_scrolling_reason.h"
#include "cc/test/geometry_test_utils.h"
#include "cc/trees/clip_node.h"
@@ -419,45 +421,6 @@ TEST(PropertyTreeTest, ScreenSpaceOpacityUpdateTest) {
EXPECT_EQ(tree.Node(child)->screen_space_opacity, 0.25f);
}
-TEST(PropertyTreeTest, NonIntegerTranslationTest) {
- // This tests that when a node has non-integer translation, the information
- // is propagated to the subtree.
- PropertyTrees property_trees;
- TransformTree& tree = property_trees.transform_tree;
-
- int parent = tree.Insert(TransformNode(), 0);
- tree.Node(parent)->local.Translate(1.5f, 1.5f);
-
- int child = tree.Insert(TransformNode(), parent);
- tree.Node(child)->local.Translate(1, 1);
- tree.set_needs_update(true);
- draw_property_utils::ComputeTransforms(&tree);
- EXPECT_FALSE(
- tree.Node(parent)->node_and_ancestors_have_only_integer_translation);
- EXPECT_FALSE(
- tree.Node(child)->node_and_ancestors_have_only_integer_translation);
-
- tree.Node(parent)->local.Translate(0.5f, 0.5f);
- tree.Node(child)->local.Translate(0.5f, 0.5f);
- tree.Node(parent)->needs_local_transform_update = true;
- tree.Node(child)->needs_local_transform_update = true;
- tree.set_needs_update(true);
- draw_property_utils::ComputeTransforms(&tree);
- EXPECT_TRUE(
- tree.Node(parent)->node_and_ancestors_have_only_integer_translation);
- EXPECT_FALSE(
- tree.Node(child)->node_and_ancestors_have_only_integer_translation);
-
- tree.Node(child)->local.Translate(0.5f, 0.5f);
- tree.Node(child)->needs_local_transform_update = true;
- tree.set_needs_update(true);
- draw_property_utils::ComputeTransforms(&tree);
- EXPECT_TRUE(
- tree.Node(parent)->node_and_ancestors_have_only_integer_translation);
- EXPECT_TRUE(
- tree.Node(child)->node_and_ancestors_have_only_integer_translation);
-}
-
TEST(PropertyTreeTest, SingularTransformSnapTest) {
// This tests that to_target transform is not snapped when it has a singular
// transform.
diff --git a/chromium/cc/trees/proxy.h b/chromium/cc/trees/proxy.h
index 9afdb987bfc..cf96398f0f4 100644
--- a/chromium/cc/trees/proxy.h
+++ b/chromium/cc/trees/proxy.h
@@ -96,6 +96,9 @@ class CC_EXPORT Proxy {
virtual void SetRenderFrameObserver(
std::unique_ptr<RenderFrameMetadataObserver> observer) = 0;
+
+ virtual void SetEnableFrameRateThrottling(
+ bool enable_frame_rate_throttling) = 0;
};
} // namespace cc
diff --git a/chromium/cc/trees/proxy_common.cc b/chromium/cc/trees/proxy_common.cc
index a04796a3aa0..c56a3362549 100644
--- a/chromium/cc/trees/proxy_common.cc
+++ b/chromium/cc/trees/proxy_common.cc
@@ -4,8 +4,8 @@
#include "cc/trees/proxy_common.h"
+#include "cc/trees/compositor_commit_data.h"
#include "cc/trees/layer_tree_host.h"
-#include "cc/trees/scroll_and_scale_set.h"
namespace cc {
diff --git a/chromium/cc/trees/proxy_common.h b/chromium/cc/trees/proxy_common.h
index 8dacb9c39d2..1a00129b49c 100644
--- a/chromium/cc/trees/proxy_common.h
+++ b/chromium/cc/trees/proxy_common.h
@@ -6,6 +6,9 @@
#define CC_TREES_PROXY_COMMON_H_
#include <stddef.h>
+#include <memory>
+#include <utility>
+#include <vector>
#include "base/callback_forward.h"
#include "cc/cc_export.h"
@@ -14,7 +17,7 @@
namespace cc {
-struct ScrollAndScaleSet;
+struct CompositorCommitData;
class MutatorEvents;
struct CC_EXPORT BeginMainFrameAndCommitState {
@@ -23,7 +26,7 @@ struct CC_EXPORT BeginMainFrameAndCommitState {
unsigned int begin_frame_id = 0;
viz::BeginFrameArgs begin_frame_args;
- std::unique_ptr<ScrollAndScaleSet> scroll_info;
+ std::unique_ptr<CompositorCommitData> commit_data;
size_t memory_allocation_limit_bytes = 0;
std::vector<std::pair<int, bool>> completed_image_decode_requests;
std::unique_ptr<MutatorEvents> mutator_events;
diff --git a/chromium/cc/trees/proxy_impl.cc b/chromium/cc/trees/proxy_impl.cc
index 24e222ffbe0..2a3679534b4 100644
--- a/chromium/cc/trees/proxy_impl.cc
+++ b/chromium/cc/trees/proxy_impl.cc
@@ -8,6 +8,8 @@
#include <algorithm>
#include <string>
+#include <utility>
+#include <vector>
#include "base/auto_reset.h"
#include "base/bind.h"
@@ -18,13 +20,13 @@
#include "cc/input/browser_controls_offset_manager.h"
#include "cc/metrics/compositor_timing_history.h"
#include "cc/paint/paint_worklet_layer_painter.h"
+#include "cc/trees/compositor_commit_data.h"
#include "cc/trees/layer_tree_frame_sink.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/mutator_host.h"
#include "cc/trees/proxy_main.h"
#include "cc/trees/render_frame_metadata_observer.h"
-#include "cc/trees/scroll_and_scale_set.h"
#include "cc/trees/task_runner_provider.h"
#include "components/viz/common/frame_sinks/delay_based_time_source.h"
#include "components/viz/common/frame_timing_details.h"
@@ -389,7 +391,7 @@ bool ProxyImpl::IsBeginMainFrameExpected() {
void ProxyImpl::RenewTreePriority() {
DCHECK(IsImplThread());
const bool user_interaction_in_progress =
- host_impl_->pinch_gesture_active() ||
+ host_impl_->GetInputHandler().pinch_gesture_active() ||
host_impl_->page_scale_animation_active() ||
host_impl_->IsActivelyPrecisionScrolling();
@@ -426,7 +428,7 @@ void ProxyImpl::RenewTreePriority() {
// handling scroll updates within the same frame. The tree itself is still
// kept in prefer smoothness mode to allow checkerboarding.
ScrollHandlerState scroll_handler_state =
- host_impl_->scroll_affects_scroll_handler()
+ host_impl_->ScrollAffectsScrollHandler()
? ScrollHandlerState::SCROLL_AFFECTS_SCROLL_HANDLER
: ScrollHandlerState::SCROLL_DOES_NOT_AFFECT_SCROLL_HANDLER;
scheduler_->SetTreePrioritiesAndScrollState(tree_priority,
@@ -580,7 +582,7 @@ void ProxyImpl::ScheduledActionSendBeginMainFrame(
new BeginMainFrameAndCommitState);
begin_main_frame_state->begin_frame_id = begin_frame_id;
begin_main_frame_state->begin_frame_args = args;
- begin_main_frame_state->scroll_info = host_impl_->ProcessScrollDeltas();
+ begin_main_frame_state->commit_data = host_impl_->ProcessCompositorDeltas();
begin_main_frame_state->completed_image_decode_requests =
host_impl_->TakeCompletedImageDecodeRequests();
begin_main_frame_state->mutator_events = host_impl_->TakeMutatorEvents();
@@ -803,4 +805,9 @@ void ProxyImpl::SetRenderFrameObserver(
host_impl_->SetRenderFrameObserver(std::move(observer));
}
+void ProxyImpl::SetEnableFrameRateThrottling(
+ bool enable_frame_rate_throttling) {
+ host_impl_->SetEnableFrameRateThrottling(enable_frame_rate_throttling);
+}
+
} // namespace cc
diff --git a/chromium/cc/trees/proxy_impl.h b/chromium/cc/trees/proxy_impl.h
index dbe9b20d3ba..e031aefa22f 100644
--- a/chromium/cc/trees/proxy_impl.h
+++ b/chromium/cc/trees/proxy_impl.h
@@ -67,6 +67,7 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient,
void ClearHistory();
void SetRenderFrameObserver(
std::unique_ptr<RenderFrameMetadataObserver> observer);
+ void SetEnableFrameRateThrottling(bool enable_frame_rate_throttling);
void MainFrameWillHappenOnImplForTesting(CompletionEvent* completion,
bool* main_frame_will_happen);
diff --git a/chromium/cc/trees/proxy_main.cc b/chromium/cc/trees/proxy_main.cc
index 1ad9795cd11..5c91b18ec49 100644
--- a/chromium/cc/trees/proxy_main.cc
+++ b/chromium/cc/trees/proxy_main.cc
@@ -5,7 +5,10 @@
#include "cc/trees/proxy_main.h"
#include <algorithm>
+#include <memory>
#include <string>
+#include <utility>
+#include <vector>
#include "base/bind.h"
#include "base/trace_event/trace_event.h"
@@ -147,6 +150,10 @@ void ProxyMain::BeginMainFrame(
// after tab becomes visible again.
if (!layer_tree_host_->IsVisible()) {
TRACE_EVENT_INSTANT0("cc", "EarlyOut_NotVisible", TRACE_EVENT_SCOPE_THREAD);
+
+ // In this case, since the commit is deferred to a later time, gathered
+ // events metrics are not discarded so that they can be reported if the
+ // commit happens in the future.
std::vector<std::unique_ptr<SwapPromise>> empty_swap_promises;
ImplThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&ProxyImpl::BeginMainFrameAbortedOnImpl,
@@ -177,6 +184,10 @@ void ProxyMain::BeginMainFrame(
if (skip_full_pipeline) {
TRACE_EVENT_INSTANT0("cc", "EarlyOut_DeferCommit",
TRACE_EVENT_SCOPE_THREAD);
+
+ // In this case, since the commit is deferred to a later time, gathered
+ // events metrics are not discarded so that they can be reported if the
+ // commit happens in the future.
std::vector<std::unique_ptr<SwapPromise>> empty_swap_promises;
ImplThreadTaskRunner()->PostTask(
FROM_HERE,
@@ -210,8 +221,8 @@ void ProxyMain::BeginMainFrame(
// the compositor thread to the main thread for both cc and and its client
// (e.g. Blink). Do not do this if we explicitly plan to not commit the
// layer tree, to prevent scroll offsets getting out of sync.
- layer_tree_host_->ApplyScrollAndScale(
- begin_main_frame_state->scroll_info.get());
+ layer_tree_host_->ApplyCompositorChanges(
+ begin_main_frame_state->commit_data.get());
}
layer_tree_host_->ApplyMutatorEvents(
@@ -260,6 +271,10 @@ void ProxyMain::BeginMainFrame(
layer_tree_host_->RecordEndOfFrameMetrics(
begin_main_frame_start_time,
begin_main_frame_state->active_sequence_trackers);
+
+ // In this case, since the commit is deferred to a later time, gathered
+ // events metrics are not discarded so that they can be reported if the
+ // commit happens in the future.
std::vector<std::unique_ptr<SwapPromise>> empty_swap_promises;
ImplThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&ProxyImpl::BeginMainFrameAbortedOnImpl,
@@ -309,8 +324,9 @@ void ProxyMain::BeginMainFrame(
std::vector<std::unique_ptr<SwapPromise>> swap_promises =
layer_tree_host_->GetSwapPromiseManager()->TakeSwapPromises();
- // Since the BeginMainFrame has been aborted, handling of events on the main
- // frame had no effect and no metrics should be reported for such events.
+ // Since the commit has been aborted due to no updates, handling of events
+ // on the main frame had no effect and no metrics should be reported for
+ // such events.
layer_tree_host_->ClearEventsMetrics();
ImplThreadTaskRunner()->PostTask(
@@ -699,4 +715,12 @@ void ProxyMain::SetRenderFrameObserver(
base::Unretained(proxy_impl_.get()), std::move(observer)));
}
+void ProxyMain::SetEnableFrameRateThrottling(
+ bool enable_frame_rate_throttling) {
+ ImplThreadTaskRunner()->PostTask(
+ FROM_HERE, base::BindOnce(&ProxyImpl::SetEnableFrameRateThrottling,
+ base::Unretained(proxy_impl_.get()),
+ enable_frame_rate_throttling));
+}
+
} // namespace cc
diff --git a/chromium/cc/trees/proxy_main.h b/chromium/cc/trees/proxy_main.h
index 050af82306b..c74f0240b57 100644
--- a/chromium/cc/trees/proxy_main.h
+++ b/chromium/cc/trees/proxy_main.h
@@ -107,6 +107,7 @@ class CC_EXPORT ProxyMain : public Proxy {
void ClearHistory() override;
void SetRenderFrameObserver(
std::unique_ptr<RenderFrameMetadataObserver> observer) override;
+ void SetEnableFrameRateThrottling(bool enable_frame_rate_throttling) override;
// Returns |true| if the request was actually sent, |false| if one was
// already outstanding.
diff --git a/chromium/cc/trees/single_thread_proxy.cc b/chromium/cc/trees/single_thread_proxy.cc
index c5449d5e85a..5a80a9e0592 100644
--- a/chromium/cc/trees/single_thread_proxy.cc
+++ b/chromium/cc/trees/single_thread_proxy.cc
@@ -4,6 +4,10 @@
#include "cc/trees/single_thread_proxy.h"
+#include <memory>
+#include <utility>
+#include <vector>
+
#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/memory/ptr_util.h"
@@ -16,6 +20,7 @@
#include "cc/resources/ui_resource_manager.h"
#include "cc/scheduler/commit_earlyout_reason.h"
#include "cc/scheduler/scheduler.h"
+#include "cc/trees/compositor_commit_data.h"
#include "cc/trees/latency_info_swap_promise.h"
#include "cc/trees/layer_tree_frame_sink.h"
#include "cc/trees/layer_tree_host.h"
@@ -24,7 +29,6 @@
#include "cc/trees/mutator_host.h"
#include "cc/trees/render_frame_metadata_observer.h"
#include "cc/trees/scoped_abort_remaining_swap_promises.h"
-#include "cc/trees/scroll_and_scale_set.h"
#include "components/viz/common/frame_sinks/delay_based_time_source.h"
#include "components/viz/common/frame_timing_details.h"
#include "components/viz/common/gpu/context_provider.h"
@@ -53,6 +57,7 @@ SingleThreadProxy::SingleThreadProxy(LayerTreeHost* layer_tree_host,
defer_main_frame_update_(false),
defer_commits_(false),
animate_requested_(false),
+ update_layers_requested_(false),
commit_requested_(false),
inside_synchronous_composite_(false),
needs_impl_frame_(false),
@@ -171,7 +176,12 @@ void SingleThreadProxy::SetNeedsAnimate() {
void SingleThreadProxy::SetNeedsUpdateLayers() {
TRACE_EVENT0("cc", "SingleThreadProxy::SetNeedsUpdateLayers");
DCHECK(task_runner_provider_->IsMainThread());
- SetNeedsCommit();
+ if (!RequestedAnimatePending()) {
+ DebugScopedSetImplThread impl(task_runner_provider_);
+ if (scheduler_on_impl_thread_)
+ scheduler_on_impl_thread_->SetNeedsBeginMainFrame();
+ }
+ update_layers_requested_ = true;
}
void SingleThreadProxy::DoCommit(const viz::BeginFrameArgs& commit_args) {
@@ -256,7 +266,8 @@ void SingleThreadProxy::SetNextCommitWaitsForActivation() {
}
bool SingleThreadProxy::RequestedAnimatePending() {
- return animate_requested_ || commit_requested_ || needs_impl_frame_;
+ return animate_requested_ || update_layers_requested_ || commit_requested_ ||
+ needs_impl_frame_;
}
void SingleThreadProxy::SetDeferMainFrameUpdate(bool defer_main_frame_update) {
@@ -785,6 +796,7 @@ void SingleThreadProxy::BeginMainFrame(
commit_requested_ = false;
needs_impl_frame_ = false;
animate_requested_ = false;
+ update_layers_requested_ = false;
if (defer_main_frame_update_) {
TRACE_EVENT_INSTANT0("cc", "EarlyOut_DeferBeginMainFrame",
@@ -840,9 +852,9 @@ void SingleThreadProxy::DoBeginMainFrame(
// The impl-side scroll deltas may be manipulated directly via the
// InputHandler on the UI thread and the scale deltas may change when they
// are clamped on the impl thread.
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl_->ProcessScrollDeltas();
- layer_tree_host_->ApplyScrollAndScale(scroll_info.get());
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl_->ProcessCompositorDeltas();
+ layer_tree_host_->ApplyCompositorChanges(commit_data.get());
}
layer_tree_host_->ApplyMutatorEvents(host_impl_->TakeMutatorEvents());
layer_tree_host_->WillBeginMainFrame();
@@ -853,6 +865,7 @@ void SingleThreadProxy::DoBeginMainFrame(
void SingleThreadProxy::DoPainting() {
layer_tree_host_->UpdateLayers();
+ update_layers_requested_ = false;
// TODO(enne): SingleThreadProxy does not support cancelling commits yet,
// search for CommitEarlyOutReason::FINISHED_NO_UPDATES inside
diff --git a/chromium/cc/trees/single_thread_proxy.h b/chromium/cc/trees/single_thread_proxy.h
index ee1915c5222..2be75a96000 100644
--- a/chromium/cc/trees/single_thread_proxy.h
+++ b/chromium/cc/trees/single_thread_proxy.h
@@ -70,6 +70,8 @@ class CC_EXPORT SingleThreadProxy : public Proxy,
void ClearHistory() override;
void SetRenderFrameObserver(
std::unique_ptr<RenderFrameMetadataObserver> observer) override;
+ void SetEnableFrameRateThrottling(
+ bool enable_frame_rate_throttling) override {}
void UpdateBrowserControlsState(BrowserControlsState constraints,
BrowserControlsState current,
@@ -200,6 +202,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy,
bool defer_main_frame_update_;
bool defer_commits_;
bool animate_requested_;
+ bool update_layers_requested_;
bool commit_requested_;
bool inside_synchronous_composite_;
bool needs_impl_frame_;
diff --git a/chromium/cc/trees/swap_promise_manager_unittest.cc b/chromium/cc/trees/swap_promise_manager_unittest.cc
index 5bf0c072971..b18c5a200b4 100644
--- a/chromium/cc/trees/swap_promise_manager_unittest.cc
+++ b/chromium/cc/trees/swap_promise_manager_unittest.cc
@@ -4,6 +4,9 @@
#include "cc/trees/swap_promise_manager.h"
+#include <memory>
+#include <utility>
+
#include "cc/trees/swap_promise_monitor.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -16,7 +19,7 @@ namespace {
class MockSwapPromiseMonitor : public SwapPromiseMonitor {
public:
explicit MockSwapPromiseMonitor(SwapPromiseManager* manager)
- : SwapPromiseMonitor(manager, nullptr) {}
+ : SwapPromiseMonitor(manager) {}
~MockSwapPromiseMonitor() override = default;
MOCK_METHOD0(OnSetNeedsCommitOnMain, void());
diff --git a/chromium/cc/trees/swap_promise_monitor.cc b/chromium/cc/trees/swap_promise_monitor.cc
index 0cb85ca54d0..b8fce20cdb5 100644
--- a/chromium/cc/trees/swap_promise_monitor.cc
+++ b/chromium/cc/trees/swap_promise_monitor.cc
@@ -9,15 +9,16 @@
namespace cc {
-SwapPromiseMonitor::SwapPromiseMonitor(SwapPromiseManager* swap_promise_manager,
- LayerTreeHostImpl* host_impl)
- : swap_promise_manager_(swap_promise_manager), host_impl_(host_impl) {
- DCHECK((swap_promise_manager && !host_impl) ||
- (!swap_promise_manager && host_impl));
- if (swap_promise_manager)
- swap_promise_manager->InsertSwapPromiseMonitor(this);
- if (host_impl_)
- host_impl_->InsertSwapPromiseMonitor(this);
+SwapPromiseMonitor::SwapPromiseMonitor(SwapPromiseManager* swap_promise_manager)
+ : swap_promise_manager_(swap_promise_manager), host_impl_(nullptr) {
+ DCHECK(swap_promise_manager);
+ swap_promise_manager_->InsertSwapPromiseMonitor(this);
+}
+
+SwapPromiseMonitor::SwapPromiseMonitor(LayerTreeHostImpl* host_impl)
+ : swap_promise_manager_(nullptr), host_impl_(host_impl) {
+ DCHECK(host_impl);
+ host_impl_->InsertSwapPromiseMonitor(this);
}
SwapPromiseMonitor::~SwapPromiseMonitor() {
diff --git a/chromium/cc/trees/swap_promise_monitor.h b/chromium/cc/trees/swap_promise_monitor.h
index 47821b25bca..29a2bf85f63 100644
--- a/chromium/cc/trees/swap_promise_monitor.h
+++ b/chromium/cc/trees/swap_promise_monitor.h
@@ -30,11 +30,10 @@ class LayerTreeHostImpl;
class CC_EXPORT SwapPromiseMonitor {
public:
// If the monitor lives on the main thread, pass in |swap_promise_manager|
- // tied to the LayerTreeHost and set |host_impl| to nullptr. If the monitor
- // lives on the impl thread, pass in |host_impl| and set |layer_tree_host| to
- // nullptr.
- SwapPromiseMonitor(SwapPromiseManager* swap_promise_managaer,
- LayerTreeHostImpl* host_impl);
+ // tied to the LayerTreeHost. If the monitor lives on the impl thread, pass in
+ // |host_impl|.
+ explicit SwapPromiseMonitor(SwapPromiseManager* swap_promise_managaer);
+ explicit SwapPromiseMonitor(LayerTreeHostImpl* host_impl);
virtual ~SwapPromiseMonitor();
virtual void OnSetNeedsCommitOnMain() = 0;
diff --git a/chromium/cc/trees/transform_node.cc b/chromium/cc/trees/transform_node.cc
index b19c820baab..85943440cd6 100644
--- a/chromium/cc/trees/transform_node.cc
+++ b/chromium/cc/trees/transform_node.cc
@@ -15,6 +15,7 @@ namespace cc {
TransformNode::TransformNode()
: id(TransformTree::kInvalidNodeId),
parent_id(TransformTree::kInvalidNodeId),
+ parent_frame_id(TransformTree::kInvalidNodeId),
sticky_position_constraint_id(-1),
sorting_context_id(0),
needs_local_transform_update(true),
@@ -26,7 +27,6 @@ TransformNode::TransformNode()
to_screen_is_potentially_animated(false),
flattens_inherited_transform(true),
node_and_ancestors_are_flat(true),
- node_and_ancestors_have_only_integer_translation(true),
scrolls(false),
should_be_snapped(false),
moved_by_outer_viewport_bounds_delta_y(false),
@@ -40,6 +40,7 @@ TransformNode::TransformNode(const TransformNode&) = default;
bool TransformNode::operator==(const TransformNode& other) const {
return id == other.id && parent_id == other.parent_id &&
+ parent_frame_id == other.parent_frame_id &&
element_id == other.element_id && local == other.local &&
origin == other.origin && post_translation == other.post_translation &&
to_parent == other.to_parent &&
@@ -55,8 +56,6 @@ bool TransformNode::operator==(const TransformNode& other) const {
other.to_screen_is_potentially_animated &&
flattens_inherited_transform == other.flattens_inherited_transform &&
node_and_ancestors_are_flat == other.node_and_ancestors_are_flat &&
- node_and_ancestors_have_only_integer_translation ==
- other.node_and_ancestors_have_only_integer_translation &&
scrolls == other.scrolls &&
should_be_snapped == other.should_be_snapped &&
moved_by_outer_viewport_bounds_delta_y ==
@@ -69,7 +68,8 @@ bool TransformNode::operator==(const TransformNode& other) const {
scroll_offset == other.scroll_offset &&
snap_amount == other.snap_amount &&
maximum_animation_scale == other.maximum_animation_scale &&
- starting_animation_scale == other.starting_animation_scale;
+ starting_animation_scale == other.starting_animation_scale &&
+ visible_frame_element_id == other.visible_frame_element_id;
}
void TransformNode::AsValueInto(base::trace_event::TracedValue* value) const {
diff --git a/chromium/cc/trees/transform_node.h b/chromium/cc/trees/transform_node.h
index 47bb059ea1c..bb3f19a7caf 100644
--- a/chromium/cc/trees/transform_node.h
+++ b/chromium/cc/trees/transform_node.h
@@ -28,6 +28,9 @@ struct CC_EXPORT TransformNode {
int id;
// The node index of the parent node in the transform tree node vector.
int parent_id;
+ // The node index of the nearest parent frame node in the transform tree node
+ // vector.
+ int parent_frame_id;
ElementId element_id;
@@ -84,9 +87,6 @@ struct CC_EXPORT TransformNode {
// root is flat.
bool node_and_ancestors_are_flat : 1;
- // This is needed to know if a layer can use lcd text.
- bool node_and_ancestors_have_only_integer_translation : 1;
-
bool scrolls : 1;
bool should_be_snapped : 1;
@@ -121,6 +121,10 @@ struct CC_EXPORT TransformNode {
float maximum_animation_scale;
float starting_animation_scale;
+ // Set to the element ID of containing document if this transform node is the
+ // root of a visible frame subtree.
+ ElementId visible_frame_element_id;
+
bool operator==(const TransformNode& other) const;
void set_to_parent(const gfx::Transform& transform) {
diff --git a/chromium/cc/trees/tree_synchronizer_unittest.cc b/chromium/cc/trees/tree_synchronizer_unittest.cc
index e3e1411fccb..755327d0da6 100644
--- a/chromium/cc/trees/tree_synchronizer_unittest.cc
+++ b/chromium/cc/trees/tree_synchronizer_unittest.cc
@@ -26,8 +26,8 @@
#include "cc/test/fake_rendering_stats_instrumentation.h"
#include "cc/test/stub_layer_tree_host_single_thread_client.h"
#include "cc/test/test_task_graph_runner.h"
+#include "cc/trees/compositor_commit_data.h"
#include "cc/trees/effect_node.h"
-#include "cc/trees/scroll_and_scale_set.h"
#include "cc/trees/scroll_node.h"
#include "cc/trees/single_thread_proxy.h"
#include "cc/trees/task_runner_provider.h"
@@ -117,6 +117,8 @@ class TreeSynchronizerTest : public testing::Test {
void ResetLayerTreeHost(const LayerTreeSettings& settings) {
host_ = FakeLayerTreeHost::Create(&client_, &task_graph_runner_,
animation_host_.get(), settings);
+ host_->InitializeSingleThreaded(&single_thread_client_,
+ base::ThreadTaskRunnerHandle::Get());
host_->host_impl()->CreatePendingTree();
}
@@ -591,8 +593,6 @@ TEST_F(TreeSynchronizerTest, SynchronizeCurrentlyScrollingNode) {
}
TEST_F(TreeSynchronizerTest, SynchronizeScrollTreeScrollOffsetMap) {
- host_->InitializeSingleThreaded(&single_thread_client_,
- base::ThreadTaskRunnerHandle::Get());
LayerTreeSettings settings;
FakeLayerTreeHostImplClient client;
FakeImplTaskRunnerProvider task_runner_provider;
@@ -661,12 +661,12 @@ TEST_F(TreeSynchronizerTest, SynchronizeScrollTreeScrollOffsetMap) {
gfx::ScrollOffset(20, 30));
// Pull ScrollOffset delta for main thread, and change offset on main thread
- std::unique_ptr<ScrollAndScaleSet> scroll_info(new ScrollAndScaleSet());
- scroll_tree.CollectScrollDeltas(scroll_info.get(), ElementId(),
+ std::unique_ptr<CompositorCommitData> commit_data(new CompositorCommitData());
+ scroll_tree.CollectScrollDeltas(commit_data.get(), ElementId(),
settings.commit_fractional_scroll_deltas,
base::flat_set<ElementId>());
host_->proxy()->SetNeedsCommit();
- host_->ApplyScrollAndScale(scroll_info.get());
+ host_->ApplyCompositorChanges(commit_data.get());
EXPECT_EQ(gfx::ScrollOffset(20, 30), scroll_layer->scroll_offset());
scroll_layer->SetScrollOffset(gfx::ScrollOffset(100, 100));
@@ -704,8 +704,6 @@ TEST_F(TreeSynchronizerTest, SynchronizeScrollTreeScrollOffsetMap) {
}
TEST_F(TreeSynchronizerTest, RefreshPropertyTreesCachedData) {
- host_->InitializeSingleThreaded(&single_thread_client_,
- base::ThreadTaskRunnerHandle::Get());
LayerTreeSettings settings;
FakeLayerTreeHostImplClient client;
FakeImplTaskRunnerProvider task_runner_provider;
@@ -752,9 +750,6 @@ TEST_F(TreeSynchronizerTest, RoundedScrollDeltasOnCommit) {
settings.commit_fractional_scroll_deltas = false;
ResetLayerTreeHost(settings);
- host_->InitializeSingleThreaded(&single_thread_client_,
- base::ThreadTaskRunnerHandle::Get());
-
scoped_refptr<Layer> scroll_layer = SetupScrollLayer();
// Scroll the layer by a fractional amount.
@@ -765,10 +760,10 @@ TEST_F(TreeSynchronizerTest, RoundedScrollDeltasOnCommit) {
// When we collect the scroll deltas, we should have truncated the fractional
// part because the commit_fractional_scroll_deltas setting is enabled.
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl->ProcessScrollDeltas();
- ASSERT_EQ(1u, scroll_info->scrolls.size());
- EXPECT_EQ(2.f, scroll_info->scrolls[0].scroll_delta.y());
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl->ProcessCompositorDeltas();
+ ASSERT_EQ(1u, commit_data->scrolls.size());
+ EXPECT_EQ(2.f, commit_data->scrolls[0].scroll_delta.y());
}
TEST_F(TreeSynchronizerTest, PreserveFractionalScrollDeltasOnCommit) {
@@ -776,9 +771,6 @@ TEST_F(TreeSynchronizerTest, PreserveFractionalScrollDeltasOnCommit) {
settings.commit_fractional_scroll_deltas = true;
ResetLayerTreeHost(settings);
- host_->InitializeSingleThreaded(&single_thread_client_,
- base::ThreadTaskRunnerHandle::Get());
-
scoped_refptr<Layer> scroll_layer = SetupScrollLayer();
// Scroll the layer by a fractional amount.
@@ -789,10 +781,10 @@ TEST_F(TreeSynchronizerTest, PreserveFractionalScrollDeltasOnCommit) {
// When we collect the scroll deltas, we should keep the fractional part
// because the commit_fractional_scroll_deltas setting is disabled.
- std::unique_ptr<ScrollAndScaleSet> scroll_info =
- host_impl->ProcessScrollDeltas();
- ASSERT_EQ(1u, scroll_info->scrolls.size());
- EXPECT_EQ(1.75f, scroll_info->scrolls[0].scroll_delta.y());
+ std::unique_ptr<CompositorCommitData> commit_data =
+ host_impl->ProcessCompositorDeltas();
+ ASSERT_EQ(1u, commit_data->scrolls.size());
+ EXPECT_EQ(1.75f, commit_data->scrolls[0].scroll_delta.y());
}
} // namespace
diff --git a/chromium/cc/trees/ukm_manager.cc b/chromium/cc/trees/ukm_manager.cc
index 9d4eab1315f..5b014c07dc6 100644
--- a/chromium/cc/trees/ukm_manager.cc
+++ b/chromium/cc/trees/ukm_manager.cc
@@ -112,6 +112,7 @@ void UkmManager::RecordThroughputUKM(
CASE_FOR_MAIN_THREAD_TRACKER(MainThreadAnimation);
CASE_FOR_MAIN_THREAD_TRACKER(PinchZoom);
CASE_FOR_MAIN_THREAD_TRACKER(RAF);
+ CASE_FOR_MAIN_THREAD_TRACKER(ScrollbarScroll);
CASE_FOR_MAIN_THREAD_TRACKER(TouchScroll);
CASE_FOR_MAIN_THREAD_TRACKER(Universal);
CASE_FOR_MAIN_THREAD_TRACKER(Video);
@@ -135,6 +136,7 @@ void UkmManager::RecordThroughputUKM(
CASE_FOR_COMPOSITOR_THREAD_TRACKER(MainThreadAnimation);
CASE_FOR_COMPOSITOR_THREAD_TRACKER(PinchZoom);
CASE_FOR_COMPOSITOR_THREAD_TRACKER(RAF);
+ CASE_FOR_COMPOSITOR_THREAD_TRACKER(ScrollbarScroll);
CASE_FOR_COMPOSITOR_THREAD_TRACKER(TouchScroll);
CASE_FOR_COMPOSITOR_THREAD_TRACKER(Universal);
CASE_FOR_COMPOSITOR_THREAD_TRACKER(Video);
@@ -157,6 +159,7 @@ void UkmManager::RecordThroughputUKM(
CASE_FOR_SLOWER_THREAD_TRACKER(MainThreadAnimation);
CASE_FOR_SLOWER_THREAD_TRACKER(PinchZoom);
CASE_FOR_SLOWER_THREAD_TRACKER(RAF);
+ CASE_FOR_SLOWER_THREAD_TRACKER(ScrollbarScroll);
CASE_FOR_SLOWER_THREAD_TRACKER(TouchScroll);
CASE_FOR_SLOWER_THREAD_TRACKER(Universal);
CASE_FOR_SLOWER_THREAD_TRACKER(Video);
@@ -280,6 +283,7 @@ void UkmManager::RecordCompositorLatencyUKM(
CASE_FOR_TRACKER(MainThreadAnimation);
CASE_FOR_TRACKER(PinchZoom);
CASE_FOR_TRACKER(RAF);
+ CASE_FOR_TRACKER(ScrollbarScroll);
CASE_FOR_TRACKER(TouchScroll);
CASE_FOR_TRACKER(Video);
CASE_FOR_TRACKER(WheelScroll);
@@ -309,8 +313,8 @@ void UkmManager::RecordEventLatencyUKM(
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())
+ builder.SetTotalLatencyToSwapBegin(
+ (viz_breakdown.swap_timings.swap_start - event_metrics.time_stamp())
.InMicroseconds());
}
}
diff --git a/chromium/cc/trees/ukm_manager_unittest.cc b/chromium/cc/trees/ukm_manager_unittest.cc
index 2b55162d45f..55eac741424 100644
--- a/chromium/cc/trees/ukm_manager_unittest.cc
+++ b/chromium/cc/trees/ukm_manager_unittest.cc
@@ -4,6 +4,7 @@
#include "cc/trees/ukm_manager.h"
+#include <utility>
#include <vector>
#include "base/time/time.h"
@@ -65,7 +66,7 @@ const char kVizBreakdownSwapStartToSwapEnd[] =
const char kVizBreakdownSwapEndToPresentationCompositorFrame[] =
"SubmitCompositorFrameToPresentationCompositorFrame."
"SwapEndToPresentationCompositorFrame";
-const char kTotalLatencyToSwapEnd[] = "TotalLatencyToSwapEnd";
+const char kTotalLatencyToSwapBegin[] = "TotalLatencyToSwapBegin";
const char kTotalLatency[] = "TotalLatency";
// Names of frame sequence types use in compositor latency UKM metrics (see
@@ -74,6 +75,7 @@ const char kCompositorAnimation[] = "CompositorAnimation";
const char kMainThreadAnimation[] = "MainThreadAnimation";
const char kPinchZoom[] = "PinchZoom";
const char kRAF[] = "RAF";
+const char kScrollbarScroll[] = "ScrollbarScroll";
const char kTouchScroll[] = "TouchScroll";
const char kUniversal[] = "Universal";
const char kVideo[] = "Video";
@@ -261,6 +263,8 @@ TEST_P(UkmManagerCompositorLatencyTest, CompositorLatency) {
CompositorFrameReporter::ActiveTrackers active_trackers;
active_trackers.set(
+ static_cast<size_t>(FrameSequenceTrackerType::kScrollbarScroll));
+ active_trackers.set(
static_cast<size_t>(FrameSequenceTrackerType::kTouchScroll));
active_trackers.set(
static_cast<size_t>(FrameSequenceTrackerType::kCompositorAnimation));
@@ -336,6 +340,7 @@ TEST_P(UkmManagerCompositorLatencyTest, CompositorLatency) {
test_ukm_recorder_->ExpectEntryMetric(entry, kCompositorAnimation, true);
test_ukm_recorder_->ExpectEntryMetric(entry, kTouchScroll, true);
+ test_ukm_recorder_->ExpectEntryMetric(entry, kScrollbarScroll, 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));
@@ -379,7 +384,7 @@ TEST_F(UkmManagerTest, EventLatency) {
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 swap_start_time = viz_breakdown.swap_timings.swap_start;
const base::TimeTicks present_time =
viz_breakdown.presentation_feedback.timestamp;
@@ -445,8 +450,8 @@ TEST_F(UkmManagerTest, EventLatency) {
entry, kSubmitCompositorFrameToPresentationCompositorFrame,
(present_time - submit_time).InMicroseconds());
test_ukm_recorder_->ExpectEntryMetric(
- entry, kTotalLatencyToSwapEnd,
- (swap_end_time - event_time).InMicroseconds());
+ entry, kTotalLatencyToSwapBegin,
+ (swap_start_time - event_time).InMicroseconds());
test_ukm_recorder_->ExpectEntryMetric(
entry, kTotalLatency, (present_time - event_time).InMicroseconds());
}