diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-29 10:46:47 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-11-02 12:02:10 +0000 |
commit | 99677208ff3b216fdfec551fbe548da5520cd6fb (patch) | |
tree | 476a4865c10320249360e859d8fdd3e01833b03a /chromium/cc | |
parent | c30a6232df03e1efbd9f3b226777b07e087a1122 (diff) | |
download | qtwebengine-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')
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()); } |