[M94 Migration] Introduce event resampler 55/268255/1
authorSurya Kumar <surya.kumar7@samsung.com>
Tue, 7 Dec 2021 08:34:04 +0000 (14:04 +0530)
committerSurya Kumar <surya.kumar7@samsung.com>
Fri, 17 Dec 2021 10:34:18 +0000 (16:04 +0530)
1. Event resampler changes interval of touchmove, scroll, pinch event to
vsync and recalculates touchmove position to improve smoothness
2. This change fixes touch event does not work issue. When a problem
occurs, touch up event is handled by event_resampler. Because of this,
for statement was returned without processing the next up event. It
should be "continue" instead of "return"

Cherry-picked from: https://review.tizen.org/gerrit/267576

Change-Id: Id20a585160b499d347cc9ad77af04d19a37ba633
Signed-off-by: Surya Kumar <surya.kumar7@samsung.com>
tizen_src/chromium_impl/content/browser/browser_efl.gni
tizen_src/chromium_impl/content/browser/renderer_host/event_resampler.cc [new file with mode: 0644]
tizen_src/chromium_impl/content/browser/renderer_host/event_resampler.h [new file with mode: 0644]
tizen_src/chromium_impl/content/browser/renderer_host/render_widget_host_view_efl.cc
tizen_src/chromium_impl/content/browser/renderer_host/render_widget_host_view_efl.h

index 6ad1ca9..b463e0d 100644 (file)
@@ -79,6 +79,8 @@ external_content_browser_efl_sources = [
   "//tizen_src/chromium_impl/content/browser/renderer_host/disambiguation_popup_efl.h",
   "//tizen_src/chromium_impl/content/browser/renderer_host/edge_effect.cc",
   "//tizen_src/chromium_impl/content/browser/renderer_host/edge_effect.h",
+  "//tizen_src/chromium_impl/content/browser/renderer_host/event_resampler.cc",
+  "//tizen_src/chromium_impl/content/browser/renderer_host/event_resampler.h",
   "//tizen_src/chromium_impl/content/browser/renderer_host/im_context_efl.cc",
   "//tizen_src/chromium_impl/content/browser/renderer_host/im_context_efl.h",
   "//tizen_src/chromium_impl/content/browser/renderer_host/native_web_keyboard_event_efl.cc",
diff --git a/tizen_src/chromium_impl/content/browser/renderer_host/event_resampler.cc b/tizen_src/chromium_impl/content/browser/renderer_host/event_resampler.cc
new file mode 100644 (file)
index 0000000..c043f2e
--- /dev/null
@@ -0,0 +1,240 @@
+// Copyright 2021 Samsung Electronics. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/event_resampler.h"
+
+#include "content/browser/renderer_host/render_widget_host_view_efl.h"
+#include "ui/events/event_constants.h"
+
+namespace {
+// Currently we can resample two fingers only.
+const int kIdentifiableTouchCount = 2;
+
+// Two latest touchmove events are used for resampling.
+const unsigned kTouchMoveEventQueueSize = 2;
+
+// Latency added during resampling.  A few milliseconds doesn't hurt much but
+// reduces the impact of mispredicted touch positions.
+const float kResampleLatency = 5;
+
+// Minimum time difference between consecutive samples before attempting to
+// resample.
+const float kResampleMinDelta = 2;
+
+// Maximum time to predict forward from the last known state, to avoid
+// predicting too far into the future.  This time is further bounded by 50% of
+// the last time delta.
+const float kResampleMaxPrediction = 8;
+}  // namespace
+
+namespace content {
+
+EventResampler::EventResampler(EventResamplerClient* client)
+    : client_(client) {}
+
+EventResampler::~EventResampler() {
+  if (!vsync_animator_)
+    return;
+
+  ecore_animator_del(vsync_animator_);
+  vsync_animator_ = nullptr;
+}
+
+bool EventResampler::HandleTouchMoveEvent(ui::TouchEvent event) {
+  if (event.type() != ui::EventType::ET_TOUCH_MOVED)
+    return false;
+
+  int id = event.pointer_details().id;
+  if (id >= kIdentifiableTouchCount)
+    return false;
+
+  if (!vsync_animator_) {
+    vsync_animator_ = ecore_animator_add(VsyncAnimatorCallback, this);
+    return false;
+  }
+
+  touch_move_event_queue_[id].push(event);
+  if (touch_move_event_queue_[id].size() > kTouchMoveEventQueueSize)
+    touch_move_event_queue_[id].pop();
+
+  has_pending_touch_move_event_[id] = true;
+
+  return true;
+}
+
+bool EventResampler::HandleGestureEvent(blink::WebGestureEvent& event) {
+  switch (event.GetType()) {
+    case blink::WebInputEvent::Type::kGestureScrollBegin:
+      has_pending_scroll_event_ = false;
+      pending_scroll_delta_.SetPoint(0.0, 0.0);
+      break;
+    case blink::WebInputEvent::Type::kGestureScrollUpdate:
+      last_scroll_update_event_ = event;
+      pending_scroll_delta_.Offset(event.data.scroll_update.delta_x,
+                                   event.data.scroll_update.delta_y);
+      has_pending_scroll_event_ = true;
+      if (!is_scroll_processed_in_last_callback_) {
+        ResampleScrollUpdate();
+        is_scroll_processed_in_last_callback_ = true;
+      }
+      return true;
+    case blink::WebInputEvent::Type::kGesturePinchBegin:
+      has_pending_pinch_event_ = false;
+      pending_pinch_scale_ = 1.0;
+      break;
+    case blink::WebInputEvent::Type::kGesturePinchUpdate:
+      last_pinch_update_event_ = event;
+      pending_pinch_scale_ *= event.data.pinch_update.scale;
+      has_pending_pinch_event_ = true;
+
+      if (!is_pinch_processed_in_last_callback_) {
+        ResamplePinchUpdate();
+        is_pinch_processed_in_last_callback_ = true;
+      }
+      return true;
+    case blink::WebInputEvent::Type::kGestureFlingStart:
+      if (has_pending_scroll_event_)
+        ResampleScrollUpdate();
+      break;
+    default:
+      break;
+  }
+
+  return false;
+}
+
+// static
+Eina_Bool EventResampler::VsyncAnimatorCallback(void* data) {
+  auto resampler = static_cast<EventResampler*>(data);
+  for (unsigned i = 0; i < kIdentifiableTouchCount; i++) {
+    if (resampler->has_pending_touch_move_event_[i])
+      resampler->ResampleTouchMove(i);
+  }
+
+  resampler->is_scroll_processed_in_last_callback_ = false;
+  if (resampler->has_pending_scroll_event_) {
+    resampler->ResampleScrollUpdate();
+    resampler->is_scroll_processed_in_last_callback_ = true;
+  }
+
+  resampler->is_pinch_processed_in_last_callback_ = false;
+  if (resampler->has_pending_pinch_event_) {
+    resampler->ResamplePinchUpdate();
+    resampler->is_pinch_processed_in_last_callback_ = true;
+  }
+
+  if (resampler->CanDeleteAnimator()) {
+    resampler->ClearTouchMoveEventQueue();
+    resampler->vsync_animator_ = nullptr;
+    return ECORE_CALLBACK_CANCEL;
+  }
+
+  return ECORE_CALLBACK_RENEW;
+}
+
+bool EventResampler::CanDeleteAnimator() {
+  if (client_->TouchPointCount() > 0)
+    return false;
+
+  for (unsigned i = 0; i < kIdentifiableTouchCount; i++) {
+    if (has_pending_touch_move_event_[i])
+      return false;
+  }
+
+  return !has_pending_scroll_event_ && !has_pending_pinch_event_ &&
+         !is_scroll_processed_in_last_callback_ &&
+         !is_pinch_processed_in_last_callback_;
+}
+
+void EventResampler::ResampleTouchMove(int id) {
+  if (touch_move_event_queue_[id].empty())
+    return;
+
+  has_pending_touch_move_event_[id] = false;
+
+  ui::TouchEvent last_event = touch_move_event_queue_[id].back();
+  if (touch_move_event_queue_[id].size() < kTouchMoveEventQueueSize) {
+    client_->ForwardTouchEvent(&last_event);
+    return;
+  }
+
+  ui::TouchEvent first_event = touch_move_event_queue_[id].front();
+
+  float last_event_timestamp =
+      (last_event.time_stamp() - base::TimeTicks()).InMillisecondsF();
+  float first_event_timestamp =
+      (first_event.time_stamp() - base::TimeTicks()).InMillisecondsF();
+
+  float delta = last_event_timestamp - first_event_timestamp;
+  if (delta < kResampleMinDelta) {
+    client_->ForwardTouchEvent(&last_event);
+    return;
+  }
+
+  float x, y;
+  float sample_time = ecore_loop_time_get() * 1000 - kResampleLatency;
+  if (last_event_timestamp > sample_time) {
+    // Interpolation.
+    float alpha = (sample_time - first_event_timestamp) / delta;
+    x = first_event.location().x() +
+        (last_event.location().x() - first_event.location().x()) * alpha;
+    y = first_event.location().y() +
+        (last_event.location().y() - first_event.location().y()) * alpha;
+  } else {
+    // Extrapolation.
+    float max_predict =
+        last_event_timestamp + std::min(delta / 2, kResampleMaxPrediction);
+    sample_time = std::min(sample_time, max_predict);
+
+    float alpha = (last_event_timestamp - sample_time) / delta;
+    x = last_event.location().x() +
+        (first_event.location().x() - last_event.location().x()) * alpha;
+    y = last_event.location().y() +
+        (first_event.location().y() - last_event.location().y()) * alpha;
+  }
+
+  last_event.set_location_f(gfx::PointF(x, y));
+  client_->ForwardTouchEvent(&last_event);
+}
+
+void EventResampler::ResampleScrollUpdate() {
+  blink::WebGestureEvent event = last_scroll_update_event_;
+
+  double scroll_delta_x = pending_scroll_delta_.x();
+  double scroll_delta_y = pending_scroll_delta_.y();
+
+  // Convert delta type to Int for accuracy of top controls offset.
+  if (client_->TouchPointCount() == 1) {
+    scroll_delta_x = (scroll_delta_x > 0) ? std::floor(scroll_delta_x)
+                                          : std::ceil(scroll_delta_x);
+
+    scroll_delta_y = (scroll_delta_y > 0) ? std::floor(scroll_delta_y)
+                                          : std::ceil(scroll_delta_y);
+  }
+
+  pending_scroll_delta_.Offset(-scroll_delta_x, -scroll_delta_y);
+  event.data.scroll_update.delta_x = scroll_delta_x;
+  event.data.scroll_update.delta_y = scroll_delta_y;
+  has_pending_scroll_event_ = false;
+
+  client_->ForwardGestureEvent(event);
+}
+
+void EventResampler::ResamplePinchUpdate() {
+  blink::WebGestureEvent event = last_pinch_update_event_;
+  event.data.pinch_update.scale = pending_pinch_scale_;
+  pending_pinch_scale_ = 1.0;
+  has_pending_pinch_event_ = false;
+
+  client_->ForwardGestureEvent(event);
+}
+
+void EventResampler::ClearTouchMoveEventQueue() {
+  for (unsigned i = 0; i < kIdentifiableTouchCount; i++) {
+    std::queue<ui::TouchEvent> empty;
+    std::swap(touch_move_event_queue_[i], empty);
+  }
+}
+
+}  // namespace content
\ No newline at end of file
diff --git a/tizen_src/chromium_impl/content/browser/renderer_host/event_resampler.h b/tizen_src/chromium_impl/content/browser/renderer_host/event_resampler.h
new file mode 100644 (file)
index 0000000..b2f9a86
--- /dev/null
@@ -0,0 +1,74 @@
+// Copyright 2021 Samsung Electronics. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EVENT_RESAMPLER
+#define EVENT_RESAMPLER
+
+#include <Ecore.h>
+#include <queue>
+
+#include "third_party/blink/public/common/input/web_gesture_event.h"
+#include "ui/events/event.h"
+#include "ui/gfx/geometry/point_f.h"
+
+#define MAX_TOUCH_COUNT 2
+
+namespace content {
+
+class RenderWidgetHostViewEfl;
+
+class EventResamplerClient {
+ public:
+  // Forward resampled touch event.
+  virtual void ForwardTouchEvent(ui::TouchEvent*) {}
+
+  // Forward resampled gesture event.
+  virtual void ForwardGestureEvent(blink::WebGestureEvent) {}
+
+  // Return number of touch point.
+  virtual unsigned TouchPointCount() const { return 0; }
+};
+
+class EventResampler {
+ public:
+  explicit EventResampler(EventResamplerClient* client);
+  ~EventResampler();
+
+  bool HandleTouchMoveEvent(ui::TouchEvent event);
+  bool HandleGestureEvent(blink::WebGestureEvent& event);
+
+ private:
+  static Eina_Bool VsyncAnimatorCallback(void* data);
+
+  void ResampleTouchMove(int id);
+  void ResampleScrollUpdate();
+  void ResamplePinchUpdate();
+
+  void ClearTouchMoveEventQueue();
+  bool CanDeleteAnimator();
+
+  EventResamplerClient* client_;
+
+  Ecore_Animator* vsync_animator_ = nullptr;
+  bool has_pending_touch_move_event_[MAX_TOUCH_COUNT] = {
+      false,
+  };
+  bool has_pending_scroll_event_ = false;
+  bool has_pending_pinch_event_ = false;
+
+  // To process without delay if event isn't processed in last callback.
+  bool is_scroll_processed_in_last_callback_ = false;
+  bool is_pinch_processed_in_last_callback_ = false;
+
+  std::queue<ui::TouchEvent> touch_move_event_queue_[MAX_TOUCH_COUNT];
+  blink::WebGestureEvent last_scroll_update_event_;
+  blink::WebGestureEvent last_pinch_update_event_;
+
+  gfx::PointF pending_scroll_delta_;
+  float pending_pinch_scale_ = 0.0;
+};
+
+}  // namespace content
+
+#endif  // EVENT_RESAMPLER
\ No newline at end of file
index 3ff61a0..7802845 100644 (file)
@@ -144,6 +144,7 @@ RenderWidgetHostViewEfl::RenderWidgetHostViewEfl(RenderWidgetHost* widget_host,
       touch_events_enabled_(false),
       background_color_(SK_ColorWHITE),
       web_contents_(web_contents),
+      event_resampler_(new EventResampler(this)),
       weak_factory_(this) {
   parent_view_ = static_cast<Evas_Object*>(web_contents.GetNativeView());
   evas_ = evas_object_evas_get(parent_view_);
@@ -1158,7 +1159,7 @@ void RenderWidgetHostViewEfl::OnMultiTouchDownEvent(void* data,
   RenderWidgetHostViewEfl* rwhv = static_cast<RenderWidgetHostViewEfl*>(data);
   CHECK(rwhv->touch_events_enabled_);
   rwhv->ProcessTouchEvents(
-      static_cast<Evas_Event_Multi_Down*>(event_info)->timestamp);
+      static_cast<Evas_Event_Multi_Down*>(event_info)->timestamp, true);
 }
 
 void RenderWidgetHostViewEfl::OnMultiTouchMoveEvent(void* data,
@@ -1168,7 +1169,7 @@ void RenderWidgetHostViewEfl::OnMultiTouchMoveEvent(void* data,
   RenderWidgetHostViewEfl* rwhv = static_cast<RenderWidgetHostViewEfl*>(data);
   CHECK(rwhv->touch_events_enabled_);
   rwhv->ProcessTouchEvents(
-      static_cast<Evas_Event_Multi_Move*>(event_info)->timestamp);
+      static_cast<Evas_Event_Multi_Move*>(event_info)->timestamp, true);
 }
 
 void RenderWidgetHostViewEfl::OnMultiTouchUpEvent(void* data,
@@ -1178,7 +1179,7 @@ void RenderWidgetHostViewEfl::OnMultiTouchUpEvent(void* data,
   RenderWidgetHostViewEfl* rwhv = static_cast<RenderWidgetHostViewEfl*>(data);
   CHECK(rwhv->touch_events_enabled_);
   rwhv->ProcessTouchEvents(
-      static_cast<Evas_Event_Multi_Up*>(event_info)->timestamp);
+      static_cast<Evas_Event_Multi_Up*>(event_info)->timestamp, true);
 }
 
 void RenderWidgetHostViewEfl::OnKeyDown(void* data,
@@ -1272,7 +1273,8 @@ void RenderWidgetHostViewEfl::OnMouseWheel(void* data,
   }
 }
 
-void RenderWidgetHostViewEfl::ProcessTouchEvents(unsigned int timestamp) {
+void RenderWidgetHostViewEfl::ProcessTouchEvents(unsigned int timestamp,
+                                                 bool is_multi_touch) {
   // These constants are used to map multi touch's touch id(s).
   // The poorly-written Tizen API document says:
   //  "0 for Mouse Event and device id for Multi Event."
@@ -1318,6 +1320,15 @@ void RenderWidgetHostViewEfl::ProcessTouchEvents(unsigned int timestamp) {
 
     ui::TouchEvent touch_event =
         MakeTouchEvent(pt, state, id, content_image_, timestamp);
+
+    if (state == EVAS_TOUCH_POINT_MOVE) {
+      if ((id == 0 && is_multi_touch) || (id == 1 && !is_multi_touch))
+        continue;
+
+      if (event_resampler_->HandleTouchMoveEvent(touch_event))
+        continue;
+    }
+
     HandleTouchEvent(&touch_event);
   }
 }
@@ -1367,9 +1378,11 @@ ui::LatencyInfo CreateLatencyInfo(const blink::WebInputEvent& event) {
 void RenderWidgetHostViewEfl::SendGestureEvent(blink::WebGestureEvent& event) {
   HandleGesture(event);
   blink::WebInputEvent::Type event_type = event.GetType();
-  if (magnifier_ &&
-      event_type == blink::WebInputEvent::Type::kGestureScrollUpdate)
+  if ((magnifier_ && event_type ==
+      blink::WebInputEvent::Type::kGestureScrollUpdate) ||
+      event_resampler_->HandleGestureEvent(event))
     return;
+
   if (host_ && event_type != blink::WebInputEvent::Type::kUndefined)
     host_->ForwardGestureEventWithLatencyInfo(event, CreateLatencyInfo(event));
 }
@@ -1491,6 +1504,20 @@ RenderWidgetHostViewEfl::CreateSyntheticGestureTarget() {
   return nullptr;
 }
 
+void RenderWidgetHostViewEfl::ForwardTouchEvent(ui::TouchEvent* event) {
+  HandleTouchEvent(event);
+}
+
+void RenderWidgetHostViewEfl::ForwardGestureEvent(
+    blink::WebGestureEvent event) {
+  if (host_ && event.GetType() != blink::WebInputEvent::Type::kUndefined)
+    host_->ForwardGestureEventWithLatencyInfo(event, CreateLatencyInfo(event));
+}
+
+unsigned RenderWidgetHostViewEfl::TouchPointCount() const {
+  return PointerStatePointerCount();
+}
+
 EdgeEffect& RenderWidgetHostViewEfl::EnsureEdgeEffect() {
   if (!edge_effect_)
     edge_effect_ = base::WrapUnique(new EdgeEffect(content_image_elm_host_));
index 71d8fd1..fdbd287 100644 (file)
@@ -20,6 +20,7 @@
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/compositor/evasgl_delegated_frame_host.h"
 #include "content/browser/renderer_host/evas_event_handler.h"
+#include "content/browser/renderer_host/event_resampler.h"
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
 #include "content/browser/selection/selection_controller_efl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
@@ -73,6 +74,7 @@ struct TextInputState;
 class CONTENT_EXPORT RenderWidgetHostViewEfl
     : public RenderWidgetHostViewBase,
       public EvasGLDelegatedFrameHostClient,
+      public EventResamplerClient,
       public ui::GestureConsumer,
       public ui::GestureEventHelper,
       public base::SupportsWeakPtr<RenderWidgetHostViewEfl>,
@@ -183,6 +185,11 @@ class CONTENT_EXPORT RenderWidgetHostViewEfl
   // IPC::Sender implementation:
   bool Send(IPC::Message*) override;
 
+  // EventResamplerClient implementation.
+  void ForwardTouchEvent(ui::TouchEvent*) override;
+  void ForwardGestureEvent(blink::WebGestureEvent) override;
+  unsigned TouchPointCount() const override;
+
   void FilterInputMotion(const blink::WebGestureEvent& gesture_event);
 
   void EvasToBlinkCords(int x, int y, int* view_x, int* view_y);
@@ -286,7 +293,7 @@ class CONTENT_EXPORT RenderWidgetHostViewEfl
   static void OnHWBackEvent(void*, Evas_Object*, void*);
 #endif
 
-  void ProcessTouchEvents(unsigned int timestamp);
+  void ProcessTouchEvents(unsigned int timestamp, bool is_multi_touch = false);
   void SetDoubleTapSupportEnabled(bool enabled);
 
   void OnOrientationChangeEvent(int);
@@ -366,6 +373,7 @@ class CONTENT_EXPORT RenderWidgetHostViewEfl
       screen_capture_cb_map_;
 
   std::unique_ptr<SelectionControllerEfl> selection_controller_;
+  std::unique_ptr<EventResampler> event_resampler_;
 
   viz::mojom::CompositorFrameSinkClient* renderer_compositor_frame_sink_ =
       nullptr;