blob: 638b035d6e5b89bb570be32c885710c15ffb9fe9 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/widget/input/compositor_thread_event_queue.h"
#include "base/trace_event/trace_event.h"
#include "cc/metrics/event_metrics.h"
namespace blink {
namespace {
// Sets |oldest_scroll_trace_id| or |oldest_pinch_trace_id| depending on the
// type of |event|.
void SetScrollOrPinchTraceId(EventWithCallback* event,
int64_t* oldest_scroll_trace_id,
int64_t* oldest_pinch_trace_id) {
if (event->event().GetType() == WebInputEvent::Type::kGestureScrollUpdate) {
DCHECK_EQ(-1, *oldest_scroll_trace_id);
*oldest_scroll_trace_id = event->latency_info().trace_id();
return;
}
DCHECK_EQ(WebInputEvent::Type::kGesturePinchUpdate, event->event().GetType());
DCHECK_EQ(-1, *oldest_pinch_trace_id);
*oldest_pinch_trace_id = event->latency_info().trace_id();
}
inline const WebGestureEvent& ToWebGestureEvent(const WebInputEvent& event) {
DCHECK(WebInputEvent::IsGestureEventType(event.GetType()));
return static_cast<const WebGestureEvent&>(event);
}
bool IsContinuousGestureEvent(WebInputEvent::Type type) {
switch (type) {
case WebGestureEvent::Type::kGestureScrollUpdate:
case WebGestureEvent::Type::kGesturePinchUpdate:
return true;
default:
return false;
}
}
} // namespace
CompositorThreadEventQueue::CompositorThreadEventQueue() {}
CompositorThreadEventQueue::~CompositorThreadEventQueue() {}
void CompositorThreadEventQueue::Queue(
std::unique_ptr<EventWithCallback> new_event,
base::TimeTicks timestamp_now) {
if (queue_.empty() ||
!IsContinuousGestureEvent(new_event->event().GetType()) ||
!(queue_.back()->CanCoalesceWith(*new_event) ||
WebGestureEvent::IsCompatibleScrollorPinch(
ToWebGestureEvent(new_event->event()),
ToWebGestureEvent(queue_.back()->event())))) {
if (new_event->first_original_event()) {
// Trace could be nested as there might be multiple events in queue.
// e.g. |ScrollUpdate|, |ScrollEnd|, and another scroll sequence.
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("input",
"CompositorThreadEventQueue::Queue",
new_event->first_original_event());
}
queue_.push_back(std::move(new_event));
return;
}
if (queue_.back()->CanCoalesceWith(*new_event)) {
queue_.back()->CoalesceWith(new_event.get(), timestamp_now);
return;
}
// We have only scrolls or pinches at this point (all other events are
// filtered out by the if statements above). We want to coalesce this event
// into the previous event(s) and represent it as a scroll and then a pinch.
DCHECK(IsContinuousGestureEvent(new_event->event().GetType()));
// If there is only one event in the queue we will still emit two events
// (scroll and pinch) but the |new_event| will still be coalesced into the
// |last_event|, but there will be only one LatencyInfo that should be traced
// for two events. In this case we will output an empty LatencyInfo.
//
// However with two events one will be a GesturePinchUpdate and one will be a
// GestureScrollUpdate and we will use the two non-coalesced event's trace_ids
// to instrument the flow through the system.
int64_t oldest_scroll_trace_id = -1;
int64_t oldest_pinch_trace_id = -1;
ui::LatencyInfo oldest_latency;
// Extract the last event in queue (again either a scroll or a pinch).
std::unique_ptr<EventWithCallback> last_event = std::move(queue_.back());
queue_.pop_back();
DCHECK(IsContinuousGestureEvent(last_event->event().GetType()));
DCHECK_LE(last_event->latency_info().trace_id(),
new_event->latency_info().trace_id());
SetScrollOrPinchTraceId(last_event.get(), &oldest_scroll_trace_id,
&oldest_pinch_trace_id);
oldest_latency = last_event->latency_info();
base::TimeTicks oldest_creation_timestamp = last_event->creation_timestamp();
EventWithCallback::OriginalEventList combined_original_events;
combined_original_events.splice(combined_original_events.end(),
last_event->original_events());
combined_original_events.splice(combined_original_events.end(),
new_event->original_events());
// Extract the second last event in queue IF it's a scroll or a pinch for the
// same target.
std::unique_ptr<EventWithCallback> second_last_event = nullptr;
if (!queue_.empty() && WebGestureEvent::IsCompatibleScrollorPinch(
ToWebGestureEvent(new_event->event()),
ToWebGestureEvent(queue_.back()->event()))) {
second_last_event = std::move(queue_.back());
queue_.pop_back();
// second_last_event's trace_id might not be less than last_event if we had
// both a scroll and a pinch previously in the queue and reordered them when
// coalescing with a new event to keep the invariant that a scroll happens
// before the pinch.
DCHECK_LE(second_last_event->latency_info().trace_id(),
new_event->latency_info().trace_id());
SetScrollOrPinchTraceId(second_last_event.get(), &oldest_scroll_trace_id,
&oldest_pinch_trace_id);
oldest_latency = second_last_event->latency_info();
oldest_creation_timestamp = second_last_event->creation_timestamp();
combined_original_events.splice(combined_original_events.begin(),
second_last_event->original_events());
}
// To ensure proper trace tracking we have to determine which event was the
// original non-coalesced event. If the event was artificially created (I.E it
// sprung into existence in CoalesceScrollAndPinch and isn't associated with a
// WebInputEvent that was in the queue) we will give it an empty LatencyInfo
// (so it won't have anything reported for it). This can be seen when a
// trace_id is equal to -1. We also move the original events into whichever
// one is the original non-coalesced event, defaulting to the pinch event if
// both are non-coalesced versions so it runs last.
ui::LatencyInfo scroll_latency;
EventWithCallback::OriginalEventList scroll_original_events;
ui::LatencyInfo pinch_latency;
EventWithCallback::OriginalEventList pinch_original_events;
DCHECK(oldest_pinch_trace_id == -1 || oldest_scroll_trace_id == -1);
if (oldest_scroll_trace_id != -1) {
scroll_latency = oldest_latency;
scroll_latency.set_trace_id(oldest_scroll_trace_id);
scroll_original_events = std::move(combined_original_events);
} else {
// In both the valid pinch event trace id case and scroll and pinch both
// have invalid trace_ids case, we will assign original_events to the
// pinch_event.
pinch_latency = oldest_latency;
pinch_latency.set_trace_id(oldest_pinch_trace_id);
pinch_original_events = std::move(combined_original_events);
}
TRACE_EVENT2("input", "CoalesceScrollAndPinch", "coalescedTraceId",
new_event->latency_info().trace_id(), "traceId",
scroll_latency.trace_id() != -1 ? scroll_latency.trace_id()
: pinch_latency.trace_id());
std::pair<std::unique_ptr<WebGestureEvent>, std::unique_ptr<WebGestureEvent>>
coalesced_events = WebGestureEvent::CoalesceScrollAndPinch(
second_last_event ? &ToWebGestureEvent(second_last_event->event())
: nullptr,
ToWebGestureEvent(last_event->event()),
ToWebGestureEvent(new_event->event()));
DCHECK(coalesced_events.first);
DCHECK(coalesced_events.second);
auto scroll_event = std::make_unique<EventWithCallback>(
std::make_unique<WebCoalescedInputEvent>(
std::move(coalesced_events.first), scroll_latency),
oldest_creation_timestamp, timestamp_now,
std::move(scroll_original_events));
scroll_event->set_coalesced_scroll_and_pinch();
auto pinch_event = std::make_unique<EventWithCallback>(
std::make_unique<WebCoalescedInputEvent>(
std::move(coalesced_events.second), pinch_latency),
oldest_creation_timestamp, timestamp_now,
std::move(pinch_original_events));
pinch_event->set_coalesced_scroll_and_pinch();
queue_.push_back(std::move(scroll_event));
queue_.push_back(std::move(pinch_event));
}
std::unique_ptr<EventWithCallback> CompositorThreadEventQueue::Pop() {
DCHECK(!queue_.empty());
std::unique_ptr<EventWithCallback> result = std::move(queue_.front());
queue_.pop_front();
if (result->first_original_event()) {
TRACE_EVENT_NESTABLE_ASYNC_END2(
"input", "CompositorThreadEventQueue::Queue",
result->first_original_event(), "type", result->event().GetType(),
"coalesced_count", result->coalesced_count());
}
return result;
}
} // namespace blink