cc: Plumb info for computing low velocity fling
Info made available to LayerTreeHostImpl to compute info for detecthing (and thus throttling) low velocity fling: * has_raw_input. This is distinct from the existing has_input that it is not set during fling. * Report and remember scroll offset * Remember the last begin frame arg to compute the time difference between frames. Compute fling speed in pixels per second. Add a for the computed speed. Bug: 402442892 Change-Id: Id437aa33d597bc2e532758722fcb8e6fa3bd0467 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6408736 Reviewed-by: Jonathan Ross <jonross@chromium.org> Commit-Queue: Bo Liu <boliu@chromium.org> Reviewed-by: David Bokan <bokan@chromium.org> Cr-Commit-Position: refs/heads/main@{#1442338}
This commit is contained in:
cc
input
scheduler
test
trees
components/viz/common/quads
third_party/blink/renderer/platform/widget/input
@ -154,7 +154,7 @@ class CompositorDelegateForInput {
|
||||
virtual double PredictViewportBoundsDelta(
|
||||
double current_bounds_delta,
|
||||
gfx::Vector2dF scroll_distance) const = 0;
|
||||
virtual void NotifyInputEvent() = 0;
|
||||
virtual void NotifyInputEvent(bool is_fling) = 0;
|
||||
virtual bool ElementHasImplOnlyScrollAnimation(
|
||||
ElementId element_id) const = 0;
|
||||
virtual std::optional<gfx::PointF> UpdateImplAnimationScrollTargetWithDelta(
|
||||
@ -167,7 +167,6 @@ class CompositorDelegateForInput {
|
||||
virtual void SetNeedsFullViewportRedraw() = 0;
|
||||
virtual void SetDeferBeginMainFrame(bool defer_begin_main_frame) const = 0;
|
||||
virtual void DidUpdateScrollAnimationCurve() = 0;
|
||||
virtual void AccumulateScrollDeltaForTracing(const gfx::Vector2dF& delta) = 0;
|
||||
virtual void DidStartPinchZoom() = 0;
|
||||
virtual void DidUpdatePinchZoom() = 0;
|
||||
virtual void DidEndPinchZoom() = 0;
|
||||
@ -176,7 +175,9 @@ class CompositorDelegateForInput {
|
||||
virtual void DidMouseLeave() = 0;
|
||||
virtual bool IsInHighLatencyMode() const = 0;
|
||||
virtual void WillScrollContent(ElementId element_id) = 0;
|
||||
virtual void DidScrollContent(ElementId element_id, bool animated) = 0;
|
||||
virtual void DidScrollContent(ElementId element_id,
|
||||
bool animated,
|
||||
const gfx::Vector2dF& scroll_delta) = 0;
|
||||
virtual float DeviceScaleFactor() const = 0;
|
||||
virtual float PageScaleFactor() const = 0;
|
||||
virtual gfx::Size VisualDeviceViewportSize() const = 0;
|
||||
|
@ -353,9 +353,6 @@ InputHandlerScrollResult InputHandler::ScrollUpdate(
|
||||
ui::ScrollGranularity::kScrollByPixel;
|
||||
}
|
||||
|
||||
compositor_delegate_->AccumulateScrollDeltaForTracing(
|
||||
gfx::Vector2dF(scroll_state.delta_x(), scroll_state.delta_y()));
|
||||
|
||||
compositor_delegate_->WillScrollContent(scroll_node.element_id);
|
||||
|
||||
float initial_top_controls_offset =
|
||||
@ -371,8 +368,8 @@ InputHandlerScrollResult InputHandler::ScrollUpdate(
|
||||
bool did_scroll_content = did_scroll_x || did_scroll_y;
|
||||
if (did_scroll_content) {
|
||||
bool is_animated_scroll = ShouldAnimateScroll(scroll_state);
|
||||
compositor_delegate_->DidScrollContent(scroll_node.element_id,
|
||||
is_animated_scroll);
|
||||
compositor_delegate_->DidScrollContent(
|
||||
scroll_node.element_id, is_animated_scroll, resolvedScrollDelta);
|
||||
}
|
||||
|
||||
SetNeedsCommit();
|
||||
@ -685,18 +682,21 @@ void InputHandler::SetSynchronousInputHandlerRootScrollOffset(
|
||||
root_content_offset - 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)
|
||||
gfx::Vector2dF consumed_delta =
|
||||
GetViewport()
|
||||
.ScrollBy(physical_delta,
|
||||
/*viewport_point=*/gfx::Point(),
|
||||
/*is_direct_manipulation=*/false,
|
||||
/*affect_browser_controls=*/false,
|
||||
/*scroll_outer_viewport=*/true)
|
||||
.consumed_delta;
|
||||
if (consumed_delta.IsZero()) {
|
||||
return;
|
||||
}
|
||||
|
||||
compositor_delegate_->DidScrollContent(OuterViewportScrollNode()->element_id,
|
||||
/*is_animated_scroll=*/false);
|
||||
/*is_animated_scroll=*/false,
|
||||
consumed_delta);
|
||||
SetNeedsCommit();
|
||||
|
||||
// After applying the synchronous input handler's scroll offset, tell it what
|
||||
@ -1058,8 +1058,8 @@ void InputHandler::ScrollEndForSnapFling(bool did_finish) {
|
||||
ScrollEnd(true /* should_snap */);
|
||||
}
|
||||
|
||||
void InputHandler::NotifyInputEvent() {
|
||||
compositor_delegate_->NotifyInputEvent();
|
||||
void InputHandler::NotifyInputEvent(bool is_fling) {
|
||||
compositor_delegate_->NotifyInputEvent(is_fling);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -441,7 +441,7 @@ class CC_EXPORT InputHandler : public InputDelegateForCompositor {
|
||||
|
||||
// Notifies when any input event is received, irrespective of whether it is
|
||||
// being handled by the InputHandler or not.
|
||||
virtual void NotifyInputEvent();
|
||||
virtual void NotifyInputEvent(bool is_fling);
|
||||
|
||||
// Returns true if ScrollbarController is in the middle of a scroll operation.
|
||||
virtual bool ScrollbarScrollIsActive();
|
||||
|
@ -74,6 +74,12 @@ const viz::BeginFrameArgs& BeginFrameTracker::Last() const {
|
||||
return current_args_;
|
||||
}
|
||||
|
||||
bool BeginFrameTracker::HasLast() const {
|
||||
DCHECK(HasFinished())
|
||||
<< "Tried to use last viz::BeginFrameArgs before the frame is finished.";
|
||||
return current_args_.IsValid();
|
||||
}
|
||||
|
||||
base::TimeDelta BeginFrameTracker::Interval() const {
|
||||
base::TimeDelta interval = current_args_.interval;
|
||||
// Normal interval will be ~16ms, 200Hz (5ms) screens are the fastest
|
||||
|
@ -66,6 +66,7 @@ class CC_EXPORT BeginFrameTracker {
|
||||
// **Must** only be called when **not** between the start and finish method
|
||||
// calls.
|
||||
const viz::BeginFrameArgs& Last() const;
|
||||
bool HasLast() const;
|
||||
|
||||
// Helper method to try and return a valid interval property. Defaults to
|
||||
// BFA::DefaultInterval() is no other interval can be found. Can be called at
|
||||
|
@ -57,7 +57,7 @@ class FakeCompositorDelegateForInput : public CompositorDelegateForInput {
|
||||
std::unique_ptr<EventsMetricsManager::ScopedMonitor>
|
||||
GetScopedEventMetricsMonitor(
|
||||
EventsMetricsManager::ScopedMonitor::DoneCallback done_callback) override;
|
||||
void NotifyInputEvent() override {}
|
||||
void NotifyInputEvent(bool is_fling) override {}
|
||||
std::unique_ptr<LatencyInfoSwapPromiseMonitor>
|
||||
CreateLatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency) override;
|
||||
void SetNeedsAnimateInput() override {}
|
||||
@ -69,7 +69,6 @@ class FakeCompositorDelegateForInput : public CompositorDelegateForInput {
|
||||
void SetNeedsFullViewportRedraw() override {}
|
||||
void SetDeferBeginMainFrame(bool defer_begin_main_frame) const override {}
|
||||
void DidUpdateScrollAnimationCurve() override {}
|
||||
void AccumulateScrollDeltaForTracing(const gfx::Vector2dF& delta) override {}
|
||||
void DidStartPinchZoom() override {}
|
||||
void DidUpdatePinchZoom() override {}
|
||||
void DidEndPinchZoom() override {}
|
||||
@ -78,7 +77,9 @@ class FakeCompositorDelegateForInput : public CompositorDelegateForInput {
|
||||
void DidMouseLeave() override {}
|
||||
bool IsInHighLatencyMode() const override;
|
||||
void WillScrollContent(ElementId element_id) override {}
|
||||
void DidScrollContent(ElementId element_id, bool animated) override {}
|
||||
void DidScrollContent(ElementId element_id,
|
||||
bool animated,
|
||||
const gfx::Vector2dF& scroll_delta) override {}
|
||||
float DeviceScaleFactor() const override;
|
||||
float PageScaleFactor() const override;
|
||||
gfx::Size VisualDeviceViewportSize() const override;
|
||||
|
@ -62,7 +62,7 @@ class MockInputHandler : public InputHandler {
|
||||
MOCK_METHOD1(MouseUp,
|
||||
InputHandlerPointerResult(const gfx::PointF& mouse_position));
|
||||
MOCK_METHOD1(SetIsHandlingTouchSequence, void(bool));
|
||||
void NotifyInputEvent() override {}
|
||||
void NotifyInputEvent(bool is_fling) override {}
|
||||
|
||||
std::unique_ptr<LatencyInfoSwapPromiseMonitor>
|
||||
CreateLatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency) override {
|
||||
|
@ -317,11 +317,6 @@ void LayerTreeHostImpl::DidUpdateScrollAnimationCurve() {
|
||||
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);
|
||||
@ -1188,8 +1183,9 @@ LayerTreeHostImpl::GetScopedEventMetricsMonitor(
|
||||
return events_metrics_manager_.GetScopedMonitor(std::move(done_callback));
|
||||
}
|
||||
|
||||
void LayerTreeHostImpl::NotifyInputEvent() {
|
||||
void LayerTreeHostImpl::NotifyInputEvent(bool is_fling) {
|
||||
frame_rate_estimator_.NotifyInputEvent();
|
||||
has_non_fling_input_since_last_frame_ |= (!is_fling);
|
||||
}
|
||||
|
||||
void LayerTreeHostImpl::QueueSwapPromiseForMainThreadScrollUpdate(
|
||||
@ -3133,6 +3129,19 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame(
|
||||
CurrentBeginFrameArgs().frame_time;
|
||||
metadata.frame_interval_inputs.has_input =
|
||||
frame_rate_estimator_.input_priority_mode();
|
||||
has_non_fling_input_since_last_frame_ = false;
|
||||
|
||||
if (frame->damage_reasons.Has(DamageReason::kCompositorScroll)) {
|
||||
// Sanity check frame time delta.
|
||||
if (begin_frame_time_delta_.InMicroseconds() < 100 ||
|
||||
begin_frame_time_delta_.InMicroseconds() > 1000000) {
|
||||
metadata.frame_interval_inputs.major_scroll_speed_in_pixels_per_second =
|
||||
0.f;
|
||||
} else {
|
||||
metadata.frame_interval_inputs.major_scroll_speed_in_pixels_per_second =
|
||||
frame_max_scroll_delta_ / begin_frame_time_delta_.InSecondsF();
|
||||
}
|
||||
}
|
||||
|
||||
if (!frame->video_layer_preferred_intervals.empty() &&
|
||||
frame->damage_reasons.Has(DamageReason::kVideoLayer)) {
|
||||
@ -3445,6 +3454,15 @@ bool LayerTreeHostImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) {
|
||||
input_delegate_->IsCurrentlyScrolling() &&
|
||||
!input_delegate_->HasQueuedInput());
|
||||
}
|
||||
|
||||
frame_max_scroll_delta_ = 0.f;
|
||||
if (current_begin_frame_tracker_.HasLast()) {
|
||||
begin_frame_time_delta_ =
|
||||
args.frame_time - current_begin_frame_tracker_.Last().frame_time;
|
||||
} else {
|
||||
begin_frame_time_delta_ = base::TimeDelta();
|
||||
}
|
||||
|
||||
impl_thread_phase_ = ImplThreadPhase::INSIDE_IMPL_FRAME;
|
||||
current_begin_frame_tracker_.Start(args);
|
||||
frame_trackers_.NotifyBeginImplFrame(args);
|
||||
@ -4667,7 +4685,12 @@ void LayerTreeHostImpl::WillScrollContent(ElementId element_id) {
|
||||
}
|
||||
}
|
||||
|
||||
void LayerTreeHostImpl::DidScrollContent(ElementId element_id, bool animated) {
|
||||
void LayerTreeHostImpl::DidScrollContent(ElementId element_id,
|
||||
bool animated,
|
||||
const gfx::Vector2dF& scroll_delta) {
|
||||
scroll_accumulated_this_frame_ += scroll_delta;
|
||||
frame_max_scroll_delta_ =
|
||||
std::max(std::abs(scroll_delta.x()), std::abs(scroll_delta.y()));
|
||||
if (settings().scrollbar_flash_after_any_scroll_update) {
|
||||
FlashAllScrollbars(true);
|
||||
} else {
|
||||
|
@ -324,14 +324,13 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient,
|
||||
std::unique_ptr<EventsMetricsManager::ScopedMonitor>
|
||||
GetScopedEventMetricsMonitor(
|
||||
EventsMetricsManager::ScopedMonitor::DoneCallback done_callback) override;
|
||||
void NotifyInputEvent() override;
|
||||
void NotifyInputEvent(bool is_fling) override;
|
||||
bool HasAnimatedScrollbars() const override;
|
||||
// Already overridden for BrowserControlsOffsetManagerClient which declares a
|
||||
// method of the same name.
|
||||
// void SetNeedsCommit();
|
||||
void SetNeedsFullViewportRedraw() override;
|
||||
void DidUpdateScrollAnimationCurve() override;
|
||||
void AccumulateScrollDeltaForTracing(const gfx::Vector2dF& delta) override;
|
||||
void DidStartPinchZoom() override;
|
||||
void DidUpdatePinchZoom() override;
|
||||
void DidEndPinchZoom() override;
|
||||
@ -340,7 +339,9 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient,
|
||||
void DidMouseLeave() override;
|
||||
bool IsInHighLatencyMode() const override;
|
||||
void WillScrollContent(ElementId element_id) override;
|
||||
void DidScrollContent(ElementId element_id, bool animated) override;
|
||||
void DidScrollContent(ElementId element_id,
|
||||
bool animated,
|
||||
const gfx::Vector2dF& scroll_delta) override;
|
||||
float DeviceScaleFactor() const override;
|
||||
float PageScaleFactor() const override;
|
||||
gfx::Size VisualDeviceViewportSize() const override;
|
||||
@ -1288,6 +1289,7 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient,
|
||||
std::unique_ptr<LCDTextMetricsReporter> lcd_text_metrics_reporter_;
|
||||
|
||||
FrameRateEstimator frame_rate_estimator_;
|
||||
bool has_non_fling_input_since_last_frame_ = false;
|
||||
bool has_observed_first_scroll_delay_ = false;
|
||||
|
||||
// True if we are measuring smoothness in TotalFrameCounter and
|
||||
@ -1312,6 +1314,13 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient,
|
||||
|
||||
float top_controls_visible_height_ = 0.f;
|
||||
|
||||
// Maximum scroll delta update in x or y direction since last begin impl
|
||||
// frame.
|
||||
float frame_max_scroll_delta_ = 0.f;
|
||||
|
||||
// Time delta between last and current begin impl frame.
|
||||
base::TimeDelta begin_frame_time_delta_;
|
||||
|
||||
base::flat_set<ElementId> pending_invalidation_raster_inducing_scrolls_;
|
||||
|
||||
std::unordered_map<uint32_t, viz::ViewTransitionElementResourceRects>
|
||||
|
@ -12084,7 +12084,7 @@ TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
|
||||
|
||||
auto* fake_layer_tree_frame_sink =
|
||||
static_cast<FakeLayerTreeFrameSink*>(host_impl_->layer_tree_frame_sink());
|
||||
host_impl_->NotifyInputEvent();
|
||||
host_impl_->NotifyInputEvent(/*is_fling=*/false);
|
||||
host_impl_->SetFullViewportDamage();
|
||||
host_impl_->SetNeedsRedraw();
|
||||
auto args = viz::CreateBeginFrameArgsForTesting(
|
||||
@ -18493,7 +18493,7 @@ TEST_P(LayerTreeHostImplTest, NonCompositedScrollUsesRaster) {
|
||||
|
||||
// Draw the next frame of the scroll.
|
||||
{
|
||||
host_impl_->NotifyInputEvent();
|
||||
host_impl_->NotifyInputEvent(/*is_fling=*/false);
|
||||
host_impl_->SetFullViewportDamage();
|
||||
host_impl_->SetNeedsRedraw();
|
||||
TestFrameData frame;
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "cc/trees/transform_node.h"
|
||||
#include "components/viz/common/features.h"
|
||||
#include "components/viz/common/frame_sinks/begin_frame_source.h"
|
||||
#include "components/viz/common/quads/compositor_frame.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "ui/events/types/scroll_input_type.h"
|
||||
#include "ui/gfx/geometry/point_conversions.h"
|
||||
@ -3263,5 +3264,59 @@ class CommitWithoutSynchronizingScrollOffsets : public LayerTreeHostScrollTest {
|
||||
|
||||
MULTI_THREAD_TEST_F(CommitWithoutSynchronizingScrollOffsets);
|
||||
|
||||
class LayerTreeHostScrollTestScrollFrameIntervalInputs
|
||||
: public LayerTreeHostScrollTest {
|
||||
public:
|
||||
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
|
||||
|
||||
void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl,
|
||||
const viz::BeginFrameArgs& args,
|
||||
bool has_damage) override {
|
||||
frame_time_delta_ = args.frame_time - last_frame_time_;
|
||||
last_frame_time_ = args.frame_time;
|
||||
if (has_activated_ && !has_scrolled_) {
|
||||
// Scroll second frame.
|
||||
ScrollStateData scroll_state_data;
|
||||
scroll_state_data.is_beginning = true;
|
||||
scroll_state_data.delta_y_hint = kScrollDelta;
|
||||
ScrollState scroll_state(scroll_state_data);
|
||||
host_impl->GetInputHandler().ScrollBegin(
|
||||
&scroll_state, ui::ScrollInputType::kAutoscroll);
|
||||
host_impl->GetInputHandler().ScrollUpdate(
|
||||
UpdateState(gfx::Point(), gfx::Vector2d(0, kScrollDelta)));
|
||||
has_scrolled_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
|
||||
has_activated_ = true;
|
||||
}
|
||||
|
||||
void WillSubmitCompositorFrame(LayerTreeHostImpl* host_impl,
|
||||
const viz::CompositorFrame& frame) override {
|
||||
if (!has_scrolled_) {
|
||||
host_impl->SetNeedsRedraw();
|
||||
host_impl->SetFullViewportDamage();
|
||||
} else {
|
||||
int scroll_delta = kScrollDelta;
|
||||
float pixels_per_second = scroll_delta / frame_time_delta_.InSecondsF();
|
||||
|
||||
EXPECT_EQ(pixels_per_second,
|
||||
frame.metadata.frame_interval_inputs
|
||||
.major_scroll_speed_in_pixels_per_second);
|
||||
EndTest();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr static int kScrollDelta = 10;
|
||||
bool has_activated_ = false;
|
||||
bool has_scrolled_ = false;
|
||||
base::TimeTicks last_frame_time_;
|
||||
base::TimeDelta frame_time_delta_;
|
||||
};
|
||||
|
||||
MULTI_THREAD_TEST_F(LayerTreeHostScrollTestScrollFrameIntervalInputs);
|
||||
|
||||
} // namespace
|
||||
} // namespace cc
|
||||
|
@ -61,6 +61,10 @@ struct VIZ_COMMON_EXPORT FrameIntervalInputs {
|
||||
// as touch scrolling, in the future.
|
||||
bool has_input = false;
|
||||
|
||||
// The maximum of x or y scroll speed.
|
||||
// TODO(crbug.com/402442892): Serialize and use this.
|
||||
float major_scroll_speed_in_pixels_per_second = 0.f;
|
||||
|
||||
// Any content that has a fixed or specified content frame interval can be
|
||||
// added to `content_interval_info`. If `content_interval_info` contains
|
||||
// information on _all_ updating content in this client , then client can
|
||||
|
@ -322,8 +322,13 @@ void InputHandlerProxy::HandleInputEventWithLatencyInfo(
|
||||
ChromeLatencyInfo2::Step::STEP_HANDLE_INPUT_EVENT_IMPL);
|
||||
});
|
||||
|
||||
bool is_fling =
|
||||
WebInputEvent::Type::kGestureScrollUpdate == event->Event().GetType() &&
|
||||
static_cast<const WebGestureEvent&>(event->Event())
|
||||
.data.scroll_update.inertial_phase ==
|
||||
WebGestureEvent::InertialPhaseState::kMomentum;
|
||||
DCHECK(input_handler_);
|
||||
input_handler_->NotifyInputEvent();
|
||||
input_handler_->NotifyInputEvent(is_fling);
|
||||
|
||||
// Prevent the events to be counted into INP metrics if there is an active
|
||||
// scroll.
|
||||
|
Reference in New Issue
Block a user