Commit 8e795d5b authored by Sahir Vellani's avatar Sahir Vellani Committed by Commit Bot

FlingBoost with experimental fling animation curve

Currently, if physics based fling curve is enabled, even if the fling is
boosted, the scroll distance is capped by upper bound which is three
times the size of viewport. Thus it nullify the effect of fling boosting
and travels the same distance as it would if fling is not boosted.

This CL fixes fling boosting with physics based fling curve. It does
so by increasing the upper bound every time a fling is boosted.

Bug: 647137
Change-Id: Ib2dd51b37df91168ae1f1e8d33888e3b5dc43e23
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2118218Reviewed-by: default avatarNavid Zolghadr <nzolghadr@chromium.org>
Reviewed-by: default avatarDavid Bokan <bokan@chromium.org>
Reviewed-by: default avatarDaniel Libby <dlibby@microsoft.com>
Commit-Queue: Sahir Vellani <sahir.vellani@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#758878}
parent 44e88429
......@@ -407,13 +407,27 @@ bool FlingController::UpdateCurrentFlingState(
return false;
}
gfx::Vector2dF velocity_from_gfs(
fling_start_event.data.fling_start.velocity_x,
fling_start_event.data.fling_start.velocity_y);
float max_velocity_from_gfs =
std::max(fabs(velocity_from_gfs.x()), fabs(velocity_from_gfs.y()));
float max_velocity = std::max(fabs(current_fling_parameters_.velocity.x()),
fabs(current_fling_parameters_.velocity.y()));
// Scale the default bound multiplier to compute the maximum scroll distance a
// fling can travel based on physics based fling curve.
float boost_multiplier = max_velocity / max_velocity_from_gfs;
fling_curve_ = std::unique_ptr<blink::WebGestureCurve>(
ui::WebGestureCurveImpl::CreateFromDefaultPlatformCurve(
current_fling_parameters_.source_device,
current_fling_parameters_.velocity,
gfx::Vector2dF() /*initial_offset*/, false /*on_main_thread*/,
GetContentClient()->browser()->ShouldUseMobileFlingCurve(),
current_fling_parameters_.global_point, root_widget_viewport_size));
current_fling_parameters_.global_point, boost_multiplier,
root_widget_viewport_size));
return true;
}
......
......@@ -6,13 +6,17 @@
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "build/chromecast_buildflags.h"
#include "content/browser/renderer_host/input/gesture_event_queue.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/ui_base_features.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/blink/fling_booster.h"
#include "ui/events/gestures/physics_based_fling_curve.h"
#if defined(OS_WIN)
#include "ui/display/win/test/scoped_screen_win.h"
......@@ -21,6 +25,7 @@
using blink::WebGestureEvent;
using blink::WebInputEvent;
using blink::WebMouseWheelEvent;
using ui::PhysicsBasedFlingCurve;
namespace {
constexpr double kFrameDelta = 1000.0 / 60.0;
......@@ -702,4 +707,134 @@ TEST_P(FlingControllerTest, NoFlingStartAfterWheelEventConsumed) {
EXPECT_FALSE(FlingInProgress());
}
class FlingControllerWithPhysicsBasedFlingTest : public FlingControllerTest {
public:
// testing::Test
FlingControllerWithPhysicsBasedFlingTest() {
scoped_feature_list_.InitAndEnableFeature(
features::kExperimentalFlingAnimation);
}
~FlingControllerWithPhysicsBasedFlingTest() override = default;
private:
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(FlingControllerWithPhysicsBasedFlingTest);
};
INSTANTIATE_TEST_SUITE_P(All,
FlingControllerWithPhysicsBasedFlingTest,
testing::Bool());
// Ensure the bounding distance for boosted physics based flings is increased
// by a factor of the boost_multiplier and default multiplier
TEST_P(FlingControllerWithPhysicsBasedFlingTest,
ControllerBoostsTouchscreenFling) {
// We use a velocity of 4500 in this test because it yields a scroll delta
// that is greater than viewport * boost_multiplier * kDefaultBoundsMultiplier
// Android and Chromecast use Mobile fling curve so they are ignored
// for this test
bool use_mobile_fling_curve = false;
#if defined(OS_ANDROID) || BUILDFLAG(IS_CHROMECAST)
use_mobile_fling_curve = true;
#endif
if (use_mobile_fling_curve)
return;
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(4500, 0));
EXPECT_TRUE(FlingInProgress());
// Fling progress must send GSU events.
AdvanceTime();
ProgressFling(NowTicks());
ASSERT_EQ(WebInputEvent::kGestureScrollUpdate, last_sent_gesture_.GetType());
EXPECT_EQ(WebGestureEvent::InertialPhaseState::kMomentum,
last_sent_gesture_.data.scroll_update.inertial_phase);
EXPECT_GT(last_sent_gesture_.data.scroll_update.delta_x, 0.f);
// Now cancel the fling.
SimulateFlingCancel(blink::WebGestureDevice::kTouchscreen);
EXPECT_FALSE(FlingInProgress());
// The second GFS can be boosted so it should boost the just deactivated
// fling. To test that the correct bounds scale is used, the scroll delta
// is accumulated after each frame.
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(4500, 0));
EXPECT_TRUE(FlingInProgress());
if (NeedsBeginFrameForFlingProgress())
ProgressFling(NowTicks());
float total_scroll_delta = last_sent_gesture_.data.scroll_update.delta_x;
while (true) {
if (last_sent_gesture_.GetType() == WebInputEvent::kGestureScrollEnd) {
break;
}
AdvanceTime();
ProgressFling(NowTicks());
total_scroll_delta += last_sent_gesture_.data.scroll_update.delta_x;
}
// We expect the scroll delta to be the viewport * [boost_multiplier = 2] *
// multiplier
float expected_delta =
2 * PhysicsBasedFlingCurve::default_bounds_multiplier_for_testing() *
GetRootWidgetViewportSize().width();
EXPECT_EQ(ceilf(total_scroll_delta), roundf(expected_delta));
}
// Ensure that once a fling finishes, the next fling has a boost_multiplier of 1
TEST_P(FlingControllerWithPhysicsBasedFlingTest,
ControllerDoesntBoostFinishedFling) {
// Android and Chromecast use Mobile fling curve so they are ignored
// for this test
bool use_mobile_fling_curve = false;
#if defined(OS_ANDROID) || BUILDFLAG(IS_CHROMECAST)
use_mobile_fling_curve = true;
#endif
if (use_mobile_fling_curve)
return;
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(1000, 0), /*wait_before_processing=*/true);
EXPECT_TRUE(FlingInProgress());
AdvanceTime();
ProgressFling(NowTicks());
// Fast forward so that the fling ends.
double time_to_advance_ms = 1000.0;
AdvanceTime(time_to_advance_ms);
ProgressFling(NowTicks());
ASSERT_EQ(WebInputEvent::kGestureScrollEnd, last_sent_gesture_.GetType())
<< "Unexpected Last Sent Gesture: "
<< WebInputEvent::GetName(last_sent_gesture_.GetType());
EXPECT_EQ(fling_controller_->CurrentFlingVelocity().x(), 0);
EXPECT_FALSE(FlingInProgress());
// Now send a new fling, ensure boost_multiplier is 1
AdvanceTime();
SimulateFlingCancel(blink::WebGestureDevice::kTouchscreen);
SimulateFlingStart(blink::WebGestureDevice::kTouchscreen,
gfx::Vector2dF(10000, 0));
EXPECT_TRUE(FlingInProgress());
if (NeedsBeginFrameForFlingProgress())
ProgressFling(NowTicks());
float total_scroll_delta = last_sent_gesture_.data.scroll_update.delta_x;
while (true) {
if (last_sent_gesture_.GetType() == WebInputEvent::kGestureScrollEnd) {
break;
}
AdvanceTime();
ProgressFling(NowTicks());
total_scroll_delta += last_sent_gesture_.data.scroll_update.delta_x;
}
// We expect the scroll delta to be the viewport * [boost_multiplier = 1] *
// multiplier
float expected_delta =
PhysicsBasedFlingCurve::default_bounds_multiplier_for_testing() *
GetRootWidgetViewportSize().width();
EXPECT_EQ(ceilf(total_scroll_delta), roundf(expected_delta));
}
} // namespace content
......@@ -33,7 +33,8 @@ std::unique_ptr<GestureCurve> CreateDefaultPlatformCurve(
const gfx::Vector2dF& initial_velocity,
bool use_mobile_fling_curve,
const gfx::PointF& position_in_screen,
const gfx::Size& viewport_size) {
const float boost_multiplier,
const gfx::Size& bounding_size) {
if (device_source == blink::WebGestureDevice::kSyntheticAutoscroll) {
return std::make_unique<FixedVelocityCurve>(initial_velocity,
base::TimeTicks());
......@@ -62,7 +63,8 @@ std::unique_ptr<GestureCurve> CreateDefaultPlatformCurve(
display::win::ScreenWin::GetPixelsPerInch(position_in_screen);
#endif // define(OS_WIN)
return std::make_unique<PhysicsBasedFlingCurve>(
initial_velocity, base::TimeTicks(), pixels_per_inch, viewport_size);
initial_velocity, base::TimeTicks(), pixels_per_inch, boost_multiplier,
bounding_size);
}
return std::make_unique<FlingCurve>(initial_velocity, base::TimeTicks());
......@@ -79,11 +81,12 @@ WebGestureCurveImpl::CreateFromDefaultPlatformCurve(
bool on_main_thread,
bool use_mobile_fling_curve,
const gfx::PointF& position_in_screen,
const float boost_multiplier,
const gfx::Size& viewport_size) {
return std::unique_ptr<WebGestureCurve>(new WebGestureCurveImpl(
CreateDefaultPlatformCurve(device_source, initial_velocity,
use_mobile_fling_curve, position_in_screen,
viewport_size),
boost_multiplier, viewport_size),
initial_offset, on_main_thread ? ThreadType::MAIN : ThreadType::IMPL));
}
......
......@@ -24,12 +24,19 @@ class WebGestureCurveImpl : public blink::WebGestureCurve {
public:
static std::unique_ptr<blink::WebGestureCurve> CreateFromDefaultPlatformCurve(
blink::WebGestureDevice device_source,
// Initial velocity has boost_multiplier from fling booster already
// applied
const gfx::Vector2dF& initial_velocity,
const gfx::Vector2dF& initial_offset,
bool on_main_thread,
bool use_mobile_fling_curve,
const gfx::PointF& position_in_screen,
const gfx::Size& viewport_szie);
// Multiplier for fling distance based on fling boosting. Used in physics
// based fling curve
const float boost_multiplier,
// Maximum fling distance subject to boost_multiplier and default
// bounds multiplier. Used in physics based fling curve
const gfx::Size& bounding_size);
static std::unique_ptr<blink::WebGestureCurve> CreateFromUICurveForTesting(
std::unique_ptr<GestureCurve> curve,
const gfx::Vector2dF& initial_offset);
......
......@@ -54,7 +54,7 @@ gfx::Vector2dF GetDuration(const gfx::Vector2dF& velocity,
// generate fling animation curve.
gfx::Vector2dF CalculateEndPoint(const gfx::Vector2dF& pixels_per_inch,
const gfx::Vector2dF& velocity_pixels_per_ms,
const gfx::Size& viewport) {
const gfx::Size& bounding_size) {
// deceleration is in pixels/ ms^2.
gfx::Vector2dF deceleration = GetDecelerationInPixelsPerMs2(pixels_per_inch);
......@@ -66,18 +66,14 @@ gfx::Vector2dF CalculateEndPoint(const gfx::Vector2dF& pixels_per_inch,
GetOffset(velocity_pixels_per_ms.x(), deceleration.x(), duration.x()),
GetOffset(velocity_pixels_per_ms.y(), deceleration.y(), duration.y()));
// Upper bound for the scroll distance for a fling
gfx::Vector2dF max_end_point =
gfx::Vector2dF(3 * viewport.width(), 3 * viewport.height());
if (std::abs(offset_in_screen_coord_space.x()) > max_end_point.x()) {
if (std::abs(offset_in_screen_coord_space.x()) > bounding_size.width()) {
float sign = offset_in_screen_coord_space.x() > 0 ? 1 : -1;
offset_in_screen_coord_space.set_x(max_end_point.x() * sign);
offset_in_screen_coord_space.set_x(bounding_size.width() * sign);
}
if (std::abs(offset_in_screen_coord_space.y()) > max_end_point.y()) {
if (std::abs(offset_in_screen_coord_space.y()) > bounding_size.height()) {
float sign = offset_in_screen_coord_space.y() > 0 ? 1 : -1;
offset_in_screen_coord_space.set_y(max_end_point.y() * sign);
offset_in_screen_coord_space.set_y(bounding_size.height() * sign);
}
return offset_in_screen_coord_space;
......@@ -91,13 +87,16 @@ PhysicsBasedFlingCurve::PhysicsBasedFlingCurve(
const gfx::Vector2dF& velocity,
base::TimeTicks start_timestamp,
const gfx::Vector2dF& pixels_per_inch,
const gfx::Size& viewport)
const float boost_multiplier,
const gfx::Size& bounding_size)
: start_timestamp_(start_timestamp),
p1_(gfx::PointF(kDefaultP1X, kDefaultP1Y)),
p2_(gfx::PointF(kDefaultP2X, kDefaultP2Y)),
distance_(CalculateEndPoint(pixels_per_inch,
gfx::ScaleVector2d(velocity, 1 / 1000.0f),
viewport)),
distance_(CalculateEndPoint(
pixels_per_inch,
gfx::ScaleVector2d(velocity, 1 / 1000.0f),
ScaleToFlooredSize(bounding_size,
boost_multiplier * kDefaultBoundsMultiplier))),
curve_duration_(CalculateDurationAndConfigureControlPoints(velocity)),
bezier_(p1_.x(), p1_.y(), p2_.x(), p2_.y()),
previous_time_delta_(base::TimeDelta()) {
......
......@@ -21,10 +21,15 @@ namespace ui {
// suitable for touch screen-based flings.
class EVENTS_BASE_EXPORT PhysicsBasedFlingCurve : public GestureCurve {
public:
PhysicsBasedFlingCurve(const gfx::Vector2dF& velocity,
base::TimeTicks start_timestamp,
const gfx::Vector2dF& pixels_per_inch,
const gfx::Size& viewport);
PhysicsBasedFlingCurve(
const gfx::Vector2dF& velocity,
base::TimeTicks start_timestamp,
const gfx::Vector2dF& pixels_per_inch,
// Multiplier for fling distance based on fling boosting
const float boost_multiplier,
// Maximum fling distance subject to boost_multiplier and default
// bounds multiplier
const gfx::Size& bounding_size);
~PhysicsBasedFlingCurve() override;
// GestureCurve implementation.
......@@ -35,6 +40,9 @@ class EVENTS_BASE_EXPORT PhysicsBasedFlingCurve : public GestureCurve {
float curve_duration() const { return curve_duration_; }
const gfx::PointF& p1_for_testing() const { return p1_; }
const gfx::PointF& p2_for_testing() const { return p2_; }
static int default_bounds_multiplier_for_testing() {
return kDefaultBoundsMultiplier;
}
private:
// Time when fling curve is generated.
......@@ -50,6 +58,12 @@ class EVENTS_BASE_EXPORT PhysicsBasedFlingCurve : public GestureCurve {
// crrev.com/c/1865928 is merged.
// crbug.com/1028501
const float curve_duration_;
// Default value used to scale the viewport when it is passed in as a
// parameter in the generation of a physics based fling curve. This value
// increases the upper bound of the scroll distance for a fling.
constexpr static int kDefaultBoundsMultiplier = 3;
const gfx::CubicBezier bezier_;
base::TimeDelta previous_time_delta_;
gfx::Vector2dF cumulative_scroll_;
......
......@@ -9,6 +9,7 @@
namespace ui {
const float kDefaultPixelsPerInch = 96.f;
const float kBoostMultiplierUnboosted = 1.f;
TEST(PhysicsBasedFlingCurveTest, BasicFlingTestVelocityY) {
const gfx::Vector2dF fling_velocity(0, 5000);
......@@ -17,7 +18,8 @@ TEST(PhysicsBasedFlingCurveTest, BasicFlingTestVelocityY) {
kDefaultPixelsPerInch);
const gfx::Size viewport(1920, 1080);
PhysicsBasedFlingCurve curve(fling_velocity, now, pixels_per_inch, viewport);
PhysicsBasedFlingCurve curve(fling_velocity, now, pixels_per_inch,
kBoostMultiplierUnboosted, viewport);
gfx::Vector2dF offset;
gfx::Vector2dF velocity;
......@@ -66,7 +68,8 @@ TEST(PhysicsBasedFlingCurveTest, BasicFlingTestVelocityX) {
kDefaultPixelsPerInch);
const gfx::Size viewport(1920, 1080);
PhysicsBasedFlingCurve curve(fling_velocity, now, pixels_per_inch, viewport);
PhysicsBasedFlingCurve curve(fling_velocity, now, pixels_per_inch,
kBoostMultiplierUnboosted, viewport);
gfx::Vector2dF offset;
gfx::Vector2dF velocity;
......@@ -114,7 +117,8 @@ TEST(PhysicsBasedFlingCurveTest, BasicFlingTestVelocityXAndY) {
kDefaultPixelsPerInch);
const gfx::Size viewport(1920, 1080);
PhysicsBasedFlingCurve curve(fling_velocity, now, pixels_per_inch, viewport);
PhysicsBasedFlingCurve curve(fling_velocity, now, pixels_per_inch,
kBoostMultiplierUnboosted, viewport);
gfx::Vector2dF offset;
gfx::Vector2dF velocity;
......@@ -162,7 +166,8 @@ TEST(PhysicsBasedFlingCurveTest, ControlPointsWithSlopeLessThan1) {
kDefaultPixelsPerInch);
const gfx::Size viewport(1920, 1080);
PhysicsBasedFlingCurve curve(velocity, now, pixels_per_inch, viewport);
PhysicsBasedFlingCurve curve(velocity, now, pixels_per_inch,
kBoostMultiplierUnboosted, viewport);
EXPECT_EQ(0.20f, curve.p1_for_testing().x());
EXPECT_NEAR(curve.p1_for_testing().y(), 0.43f, 0.01f);
......@@ -177,7 +182,8 @@ TEST(PhysicsBasedFlingCurveTest, ControlPointsWithSlopeGreaterThan1) {
kDefaultPixelsPerInch);
const gfx::Size viewport(1920, 1080);
PhysicsBasedFlingCurve curve(velocity, now, pixels_per_inch, viewport);
PhysicsBasedFlingCurve curve(velocity, now, pixels_per_inch,
kBoostMultiplierUnboosted, viewport);
EXPECT_NEAR(curve.p1_for_testing().x(), 0.19f, 0.01f);
EXPECT_EQ(curve.p1_for_testing().y(), 1.0f);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment