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 @@ ...@@ -4,6 +4,7 @@
package org.chromium.chrome.browser.vr_shell; package org.chromium.chrome.browser.vr_shell;
import android.os.Handler;
import android.view.InputDevice; import android.view.InputDevice;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
...@@ -38,6 +39,19 @@ public class AndroidUiGestureTarget { ...@@ -38,6 +39,19 @@ public class AndroidUiGestureTarget {
0 /* index */, x, y, 0 /* id */, MotionEvent.TOOL_TYPE_STYLUS); 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 @CalledByNative
private long getNativeObject() { private long getNativeObject() {
return mNativePointer; return mNativePointer;
......
...@@ -16,6 +16,9 @@ using base::android::JavaParamRef; ...@@ -16,6 +16,9 @@ using base::android::JavaParamRef;
using base::android::JavaRef; using base::android::JavaRef;
using content::MotionEventAction; using content::MotionEventAction;
static constexpr int kFrameDurationMs = 16;
static constexpr int kScrollEventsPerFrame = 2;
namespace vr { namespace vr {
AndroidUiGestureTarget::AndroidUiGestureTarget(JNIEnv* env, AndroidUiGestureTarget::AndroidUiGestureTarget(JNIEnv* env,
...@@ -77,12 +80,21 @@ void AndroidUiGestureTarget::DispatchWebInputEvent( ...@@ -77,12 +80,21 @@ void AndroidUiGestureTarget::DispatchWebInputEvent(
SetPointer(scroll_x_, scroll_y_); SetPointer(scroll_x_, scroll_y_);
Inject(content::MOTION_EVENT_ACTION_END, event_time_ms); Inject(content::MOTION_EVENT_ACTION_END, event_time_ms);
break; break;
case blink::WebGestureEvent::kGestureScrollUpdate: case blink::WebGestureEvent::kGestureScrollUpdate: {
scroll_x_ += (scroll_ratio_ * gesture->data.scroll_update.delta_x); float scale = scroll_ratio_ / kScrollEventsPerFrame;
scroll_y_ += (scroll_ratio_ * gesture->data.scroll_update.delta_y); scroll_x_ += gesture->data.scroll_update.delta_x * scale;
scroll_y_ += gesture->data.scroll_update.delta_y * scale;
SetPointer(scroll_x_, scroll_y_); SetPointer(scroll_x_, scroll_y_);
Inject(content::MOTION_EVENT_ACTION_MOVE, event_time_ms); 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; break;
}
case blink::WebGestureEvent::kGestureTapDown: case blink::WebGestureEvent::kGestureTapDown:
SetPointer(gesture->PositionInWidget().x, gesture->PositionInWidget().y); SetPointer(gesture->PositionInWidget().x, gesture->PositionInWidget().y);
Inject(content::MOTION_EVENT_ACTION_START, event_time_ms); Inject(content::MOTION_EVENT_ACTION_START, event_time_ms);
...@@ -146,6 +158,21 @@ void AndroidUiGestureTarget::SetPointer(int x, int y) { ...@@ -146,6 +158,21 @@ void AndroidUiGestureTarget::SetPointer(int x, int y) {
y * scale_factor_); 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 // static
AndroidUiGestureTarget* AndroidUiGestureTarget::FromJavaObject( AndroidUiGestureTarget* AndroidUiGestureTarget::FromJavaObject(
const JavaRef<jobject>& obj) { const JavaRef<jobject>& obj) {
......
...@@ -34,6 +34,11 @@ class AndroidUiGestureTarget { ...@@ -34,6 +34,11 @@ class AndroidUiGestureTarget {
private: private:
void Inject(content::MotionEventAction action, int64_t time_ms); void Inject(content::MotionEventAction action, int64_t time_ms);
void SetPointer(int x, int y); 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_x_ = 0;
int scroll_y_ = 0; int scroll_y_ = 0;
......
...@@ -11,7 +11,11 @@ namespace vr { ...@@ -11,7 +11,11 @@ namespace vr {
namespace { 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; constexpr int kMaxNumOfExtrapolations = 2;
...@@ -50,7 +54,8 @@ std::unique_ptr<GestureList> GestureDetector::DetectGestures( ...@@ -50,7 +54,8 @@ std::unique_ptr<GestureList> GestureDetector::DetectGestures(
UpdateOverallVelocity(touch_info); UpdateOverallVelocity(touch_info);
auto gesture_list = std::make_unique<GestureList>(); 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); gesture->SetSourceDevice(blink::kWebGestureDeviceTouchpad);
if (gesture->GetType() == blink::WebInputEvent::kGestureScrollEnd) if (gesture->GetType() == blink::WebInputEvent::kGestureScrollEnd)
...@@ -63,7 +68,8 @@ std::unique_ptr<GestureList> GestureDetector::DetectGestures( ...@@ -63,7 +68,8 @@ std::unique_ptr<GestureList> GestureDetector::DetectGestures(
std::unique_ptr<blink::WebGestureEvent> std::unique_ptr<blink::WebGestureEvent>
GestureDetector::GetGestureFromTouchInfo(const TouchInfo& touch_info, GestureDetector::GetGestureFromTouchInfo(const TouchInfo& touch_info,
bool force_cancel) { bool force_cancel,
base::TimeTicks current_timestamp) {
auto gesture = std::make_unique<blink::WebGestureEvent>(); auto gesture = std::make_unique<blink::WebGestureEvent>();
gesture->SetTimeStamp(touch_info.touch_point.timestamp); gesture->SetTimeStamp(touch_info.touch_point.timestamp);
...@@ -78,12 +84,14 @@ GestureDetector::GetGestureFromTouchInfo(const TouchInfo& touch_info, ...@@ -78,12 +84,14 @@ GestureDetector::GetGestureFromTouchInfo(const TouchInfo& touch_info,
break; break;
// User is scrolling on touchpad // User is scrolling on touchpad
case SCROLLING: case SCROLLING:
HandleScrollingState(touch_info, force_cancel, gesture.get()); HandleScrollingState(touch_info, force_cancel, current_timestamp,
gesture.get());
break; break;
// The user has finished scrolling, but we'll hallucinate a few points // The user has finished scrolling, but we'll hallucinate a few points
// before really finishing. // before really finishing.
case POST_SCROLL: case POST_SCROLL:
HandlePostScrollingState(touch_info, force_cancel, gesture.get()); HandlePostScrollingState(touch_info, force_cancel, current_timestamp,
gesture.get());
break; break;
default: default:
NOTREACHED(); NOTREACHED();
...@@ -135,6 +143,7 @@ void GestureDetector::HandleDetectingState(const TouchInfo& touch_info, ...@@ -135,6 +143,7 @@ void GestureDetector::HandleDetectingState(const TouchInfo& touch_info,
void GestureDetector::HandleScrollingState(const TouchInfo& touch_info, void GestureDetector::HandleScrollingState(const TouchInfo& touch_info,
bool force_cancel, bool force_cancel,
base::TimeTicks current_timestamp,
blink::WebGestureEvent* gesture) { blink::WebGestureEvent* gesture) {
if (force_cancel) { if (force_cancel) {
gesture->SetType(blink::WebInputEvent::kGestureScrollEnd); gesture->SetType(blink::WebInputEvent::kGestureScrollEnd);
...@@ -147,13 +156,14 @@ void GestureDetector::HandleScrollingState(const TouchInfo& touch_info, ...@@ -147,13 +156,14 @@ void GestureDetector::HandleScrollingState(const TouchInfo& touch_info,
if (touch_position_changed_) { if (touch_position_changed_) {
gesture->SetType(blink::WebInputEvent::kGestureScrollUpdate); gesture->SetType(blink::WebInputEvent::kGestureScrollUpdate);
UpdateGestureParameters(touch_info); UpdateGestureParameters(touch_info);
UpdateGestureWithScrollDelta(gesture); UpdateGestureWithScrollDelta(gesture, current_timestamp);
} }
} }
void GestureDetector::HandlePostScrollingState( void GestureDetector::HandlePostScrollingState(
const TouchInfo& touch_info, const TouchInfo& touch_info,
bool force_cancel, bool force_cancel,
base::TimeTicks current_timestamp,
blink::WebGestureEvent* gesture) { blink::WebGestureEvent* gesture) {
if (extrapolated_touch_ == 0 || force_cancel) { if (extrapolated_touch_ == 0 || force_cancel) {
gesture->SetType(blink::WebInputEvent::kGestureScrollEnd); gesture->SetType(blink::WebInputEvent::kGestureScrollEnd);
...@@ -161,16 +171,40 @@ void GestureDetector::HandlePostScrollingState( ...@@ -161,16 +171,40 @@ void GestureDetector::HandlePostScrollingState(
} else { } else {
gesture->SetType(blink::WebInputEvent::kGestureScrollUpdate); gesture->SetType(blink::WebInputEvent::kGestureScrollUpdate);
UpdateGestureParameters(touch_info); UpdateGestureParameters(touch_info);
UpdateGestureWithScrollDelta(gesture); UpdateGestureWithScrollDelta(gesture, current_timestamp);
} }
} }
void GestureDetector::UpdateGestureWithScrollDelta( void GestureDetector::UpdateGestureWithScrollDelta(
blink::WebGestureEvent* gesture) { blink::WebGestureEvent* gesture,
base::TimeTicks current_timestamp) {
gesture->data.scroll_update.delta_x = gesture->data.scroll_update.delta_x =
state_->displacement.x() * kDisplacementScaleFactor; state_->displacement.x() * kDisplacementScaleFactor;
gesture->data.scroll_update.delta_y = gesture->data.scroll_update.delta_y =
state_->displacement.y() * kDisplacementScaleFactor; 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) { bool GestureDetector::UpdateCurrentTouchPoint(const TouchInfo& touch_info) {
......
...@@ -62,7 +62,8 @@ class VR_EXPORT GestureDetector { ...@@ -62,7 +62,8 @@ class VR_EXPORT GestureDetector {
std::unique_ptr<blink::WebGestureEvent> GetGestureFromTouchInfo( std::unique_ptr<blink::WebGestureEvent> GetGestureFromTouchInfo(
const TouchInfo& input_touch_info, const TouchInfo& input_touch_info,
bool force_cancel); bool force_cancel,
base::TimeTicks current_timestamp);
void HandleWaitingState(const TouchInfo& touch_info, void HandleWaitingState(const TouchInfo& touch_info,
blink::WebGestureEvent* gesture); blink::WebGestureEvent* gesture);
...@@ -71,12 +72,15 @@ class VR_EXPORT GestureDetector { ...@@ -71,12 +72,15 @@ class VR_EXPORT GestureDetector {
blink::WebGestureEvent* gesture); blink::WebGestureEvent* gesture);
void HandleScrollingState(const TouchInfo& touch_info, void HandleScrollingState(const TouchInfo& touch_info,
bool force_cancel, bool force_cancel,
base::TimeTicks current_timestamp,
blink::WebGestureEvent* gesture); blink::WebGestureEvent* gesture);
void HandlePostScrollingState(const TouchInfo& touch_info, void HandlePostScrollingState(const TouchInfo& touch_info,
bool force_cancel, bool force_cancel,
base::TimeTicks current_timestamp,
blink::WebGestureEvent* gesture); 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 // 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. // 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