Commit 0e9a15da authored by Ian Vollick's avatar Ian Vollick Committed by Commit Bot

[vr] Tune scroll event stream

This change applies three heuristics to make trackpad scrolling feel
smoother. We increase the frequency at which we submit scroll events to
the platform, we also decrease the scroll scale factor (to make the
scrolls less jumpy), and we do some simple filtering of our scroll
values.

I pair-programmed much of this change with asimjour@ and he wrote the
initial code to interpolate events in the android ui gesture target.

Bug: 859075
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:linux_vr;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: I2fe42940214d01d99618ddacd182f202ff271dad
Reviewed-on: https://chromium-review.googlesource.com/1120425Reviewed-by: default avatarMichael Thiessen <mthiesse@chromium.org>
Commit-Queue: Ian Vollick <vollick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#571535}
parent 5ef1ac29
......@@ -4,6 +4,7 @@
package org.chromium.chrome.browser.vr_shell;
import android.os.Handler;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
......@@ -38,6 +39,19 @@ public class AndroidUiGestureTarget {
0 /* index */, x, y, 0 /* id */, MotionEvent.TOOL_TYPE_STYLUS);
}
@CalledByNative
private void setDelayedEvent(int x, int y, int action, long timeInMs, int delayMs) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mMotionEventSynthesizer.setPointer(
0 /* index */, x, y, 0 /* id */, MotionEvent.TOOL_TYPE_STYLUS);
mMotionEventSynthesizer.inject(action, 1 /* pointerCount */, timeInMs + delayMs,
InputDevice.SOURCE_CLASS_POINTER);
}
}, delayMs);
}
@CalledByNative
private long getNativeObject() {
return mNativePointer;
......
......@@ -16,6 +16,9 @@ using base::android::JavaParamRef;
using base::android::JavaRef;
using content::MotionEventAction;
static constexpr int kFrameDurationMs = 16;
static constexpr int kScrollEventsPerFrame = 2;
namespace vr {
AndroidUiGestureTarget::AndroidUiGestureTarget(JNIEnv* env,
......@@ -77,12 +80,21 @@ void AndroidUiGestureTarget::DispatchWebInputEvent(
SetPointer(scroll_x_, scroll_y_);
Inject(content::MOTION_EVENT_ACTION_END, event_time_ms);
break;
case blink::WebGestureEvent::kGestureScrollUpdate:
scroll_x_ += (scroll_ratio_ * gesture->data.scroll_update.delta_x);
scroll_y_ += (scroll_ratio_ * gesture->data.scroll_update.delta_y);
case blink::WebGestureEvent::kGestureScrollUpdate: {
float scale = scroll_ratio_ / kScrollEventsPerFrame;
scroll_x_ += gesture->data.scroll_update.delta_x * scale;
scroll_y_ += gesture->data.scroll_update.delta_y * scale;
SetPointer(scroll_x_, scroll_y_);
Inject(content::MOTION_EVENT_ACTION_MOVE, event_time_ms);
scroll_x_ += gesture->data.scroll_update.delta_x * scale;
scroll_y_ += gesture->data.scroll_update.delta_y * scale;
SetDelayedEvent(scroll_x_, scroll_y_, content::MOTION_EVENT_ACTION_MOVE,
event_time_ms, kFrameDurationMs / kScrollEventsPerFrame);
break;
}
case blink::WebGestureEvent::kGestureTapDown:
SetPointer(gesture->PositionInWidget().x, gesture->PositionInWidget().y);
Inject(content::MOTION_EVENT_ACTION_START, event_time_ms);
......@@ -146,6 +158,21 @@ void AndroidUiGestureTarget::SetPointer(int x, int y) {
y * scale_factor_);
}
void AndroidUiGestureTarget::SetDelayedEvent(int x,
int y,
MotionEventAction action,
int64_t time_ms,
int delay_ms) {
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
return;
Java_AndroidUiGestureTarget_setDelayedEvent(env, obj, x * scale_factor_,
y * scale_factor_, action,
time_ms, delay_ms);
}
// static
AndroidUiGestureTarget* AndroidUiGestureTarget::FromJavaObject(
const JavaRef<jobject>& obj) {
......
......@@ -34,6 +34,11 @@ class AndroidUiGestureTarget {
private:
void Inject(content::MotionEventAction action, int64_t time_ms);
void SetPointer(int x, int y);
void SetDelayedEvent(int x,
int y,
content::MotionEventAction action,
int64_t time_ms,
int delay_ms);
int scroll_x_ = 0;
int scroll_y_ = 0;
......
......@@ -11,7 +11,11 @@ namespace vr {
namespace {
constexpr float kDisplacementScaleFactor = 300.0f;
constexpr float kDisplacementScaleFactor = 215.0f;
// This varies on the range [0, 1] and represents how much we favor predicted
// positions over measured positions.
constexpr float kSmoothingBias = 0.4f;
constexpr int kMaxNumOfExtrapolations = 2;
......@@ -50,7 +54,8 @@ std::unique_ptr<GestureList> GestureDetector::DetectGestures(
UpdateOverallVelocity(touch_info);
auto gesture_list = std::make_unique<GestureList>();
auto gesture = GetGestureFromTouchInfo(touch_info, force_cancel);
auto gesture =
GetGestureFromTouchInfo(touch_info, force_cancel, current_timestamp);
gesture->SetSourceDevice(blink::kWebGestureDeviceTouchpad);
if (gesture->GetType() == blink::WebInputEvent::kGestureScrollEnd)
......@@ -63,7 +68,8 @@ std::unique_ptr<GestureList> GestureDetector::DetectGestures(
std::unique_ptr<blink::WebGestureEvent>
GestureDetector::GetGestureFromTouchInfo(const TouchInfo& touch_info,
bool force_cancel) {
bool force_cancel,
base::TimeTicks current_timestamp) {
auto gesture = std::make_unique<blink::WebGestureEvent>();
gesture->SetTimeStamp(touch_info.touch_point.timestamp);
......@@ -78,12 +84,14 @@ GestureDetector::GetGestureFromTouchInfo(const TouchInfo& touch_info,
break;
// User is scrolling on touchpad
case SCROLLING:
HandleScrollingState(touch_info, force_cancel, gesture.get());
HandleScrollingState(touch_info, force_cancel, current_timestamp,
gesture.get());
break;
// The user has finished scrolling, but we'll hallucinate a few points
// before really finishing.
case POST_SCROLL:
HandlePostScrollingState(touch_info, force_cancel, gesture.get());
HandlePostScrollingState(touch_info, force_cancel, current_timestamp,
gesture.get());
break;
default:
NOTREACHED();
......@@ -135,6 +143,7 @@ void GestureDetector::HandleDetectingState(const TouchInfo& touch_info,
void GestureDetector::HandleScrollingState(const TouchInfo& touch_info,
bool force_cancel,
base::TimeTicks current_timestamp,
blink::WebGestureEvent* gesture) {
if (force_cancel) {
gesture->SetType(blink::WebInputEvent::kGestureScrollEnd);
......@@ -147,13 +156,14 @@ void GestureDetector::HandleScrollingState(const TouchInfo& touch_info,
if (touch_position_changed_) {
gesture->SetType(blink::WebInputEvent::kGestureScrollUpdate);
UpdateGestureParameters(touch_info);
UpdateGestureWithScrollDelta(gesture);
UpdateGestureWithScrollDelta(gesture, current_timestamp);
}
}
void GestureDetector::HandlePostScrollingState(
const TouchInfo& touch_info,
bool force_cancel,
base::TimeTicks current_timestamp,
blink::WebGestureEvent* gesture) {
if (extrapolated_touch_ == 0 || force_cancel) {
gesture->SetType(blink::WebInputEvent::kGestureScrollEnd);
......@@ -161,16 +171,40 @@ void GestureDetector::HandlePostScrollingState(
} else {
gesture->SetType(blink::WebInputEvent::kGestureScrollUpdate);
UpdateGestureParameters(touch_info);
UpdateGestureWithScrollDelta(gesture);
UpdateGestureWithScrollDelta(gesture, current_timestamp);
}
}
void GestureDetector::UpdateGestureWithScrollDelta(
blink::WebGestureEvent* gesture) {
blink::WebGestureEvent* gesture,
base::TimeTicks current_timestamp) {
gesture->data.scroll_update.delta_x =
state_->displacement.x() * kDisplacementScaleFactor;
gesture->data.scroll_update.delta_y =
state_->displacement.y() * kDisplacementScaleFactor;
// Attempt to smooth the scroll deltas. This depends on a velocity vector.
// If we have one, we will use it to compute a predicted scroll and,
// ultimately, we will produce a scroll update that blends the predicted and
// measured scroll deltas per |kSmoothingBias|.
if (!state_->overall_velocity.IsZero()) {
gfx::PointF predicted_point;
float duration = (current_timestamp - last_timestamp_).InSecondsF();
predicted_point.set_x(state_->overall_velocity.x() * duration *
kDisplacementScaleFactor);
predicted_point.set_y(state_->overall_velocity.y() * duration *
kDisplacementScaleFactor);
gfx::PointF measured_point(gesture->data.scroll_update.delta_x,
gesture->data.scroll_update.delta_y);
gfx::Vector2dF delta =
ScaleVector2d(predicted_point - measured_point, kSmoothingBias);
gfx::PointF interpolated_point = measured_point + delta;
gesture->data.scroll_update.delta_x = interpolated_point.x();
gesture->data.scroll_update.delta_y = interpolated_point.y();
}
}
bool GestureDetector::UpdateCurrentTouchPoint(const TouchInfo& touch_info) {
......
......@@ -62,7 +62,8 @@ class VR_EXPORT GestureDetector {
std::unique_ptr<blink::WebGestureEvent> GetGestureFromTouchInfo(
const TouchInfo& input_touch_info,
bool force_cancel);
bool force_cancel,
base::TimeTicks current_timestamp);
void HandleWaitingState(const TouchInfo& touch_info,
blink::WebGestureEvent* gesture);
......@@ -71,12 +72,15 @@ class VR_EXPORT GestureDetector {
blink::WebGestureEvent* gesture);
void HandleScrollingState(const TouchInfo& touch_info,
bool force_cancel,
base::TimeTicks current_timestamp,
blink::WebGestureEvent* gesture);
void HandlePostScrollingState(const TouchInfo& touch_info,
bool force_cancel,
base::TimeTicks current_timestamp,
blink::WebGestureEvent* gesture);
void UpdateGestureWithScrollDelta(blink::WebGestureEvent* gesture);
void UpdateGestureWithScrollDelta(blink::WebGestureEvent* gesture,
base::TimeTicks current_timestamp);
// If the user is touching the touch pad and the touch point is different from
// before, update the touch point and return true. Otherwise, return false.
......
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