blob: 5697765400019e0d0265574a54a473b0ef8cdea7 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/viz/service/display/delegated_ink_point_renderer_base.h"
#include <algorithm>
#include "base/debug/dump_without_crashing.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/trace_event/trace_event.h"
#include "components/viz/service/display/delegated_ink_trail_data.h"
namespace viz {
DelegatedInkPointRendererBase::DelegatedInkPointRendererBase() = default;
DelegatedInkPointRendererBase::~DelegatedInkPointRendererBase() = default;
void DelegatedInkPointRendererBase::InitMessagePipeline(
mojo::PendingReceiver<gfx::mojom::DelegatedInkPointRenderer> receiver) {
// The remote end of this pipeline exists on a per-tab basis, so if tab A
// is using the feature and then tab B starts trying to use it, a new
// PendingReceiver will arrive here while |receiver_| is still bound to the
// remote in tab A. In this case, just reset |receiver_| so that tab A's
// remote is unbound and bind the new receiver to use the feature in tab B.
if (receiver_.is_bound()) {
receiver_.reset();
metadata_.reset();
pointer_ids_.clear();
}
receiver_.Bind(std::move(receiver));
}
void DelegatedInkPointRendererBase::SetDelegatedInkMetadata(
std::unique_ptr<gfx::DelegatedInkMetadata> metadata) {
// Frame time is set later than everything else due to what is available
// at time of creation, so confirm that it was actually set.
CHECK_NE(metadata->frame_time(), base::TimeTicks());
metadata_ = std::move(metadata);
const bool metadata_is_new = previous_metadata_ != *metadata_;
previous_metadata_ = *metadata_;
TRACE_EVENT_WITH_FLOW1(
"delegated_ink_trails",
"DelegatedInkPointRendererBase::SetDelegatedInkMetadata",
TRACE_ID_GLOBAL(metadata_->trace_id()), TRACE_EVENT_FLAG_FLOW_IN,
"metadata", metadata_->ToString());
// If we already have a cached pointer ID, check if the same pointer ID
// matches the new metadata.
if (pointer_id_.has_value() &&
pointer_ids_[pointer_id_.value()].ContainsMatchingPoint(
metadata_.get())) {
if (metadata_is_new) {
metadata_paint_time_ = pointer_ids_[pointer_id_.value()]
.GetMatchingPoint(metadata_.get())
.paint_timestamp();
}
return;
}
// If not, find the pointer ID that does match it, if any, and cache it.
for (auto& it : pointer_ids_) {
if (it.second.ContainsMatchingPoint(metadata_.get())) {
pointer_id_ = it.first;
metadata_paint_time_ =
it.second.GetMatchingPoint(metadata_.get()).paint_timestamp();
return;
}
}
// If we aren't able to find any matching point, set the pointer ID to null
// so that FilterPoints and PredictPoints can early out.
pointer_id_ = std::nullopt;
}
void DelegatedInkPointRendererBase::ResetPoints() {
CHECK(!metadata_);
pointer_ids_.clear();
pointer_id_.reset();
}
std::vector<gfx::DelegatedInkPoint>
DelegatedInkPointRendererBase::FilterPoints() {
if (pointer_ids_.empty()) {
return {};
}
CHECK(metadata_);
// Any stored point with a timestamp earlier than the metadata's has already
// been drawn as part of the ink stroke and therefore should not be part of
// the delegated ink trail. Do this before checking if |pointer_id_| is valid
// because it helps manage the number of DelegatedInkPoints that are being
// stored and isn't dependent on |pointer_id_| at all.
for (auto& it : pointer_ids_) {
it.second.ErasePointsOlderThanMetadata(metadata_.get());
}
// TODO(crbug.com/40118757): Add additional filtering to prevent points in
// |points_| from having a timestamp that is far ahead of |metadata_|'s
// timestamp. This could occur if the renderer stalls before sending a
// metadata while the browser continues to pump points through to viz. Then
// when the renderer starts back up again, the metadata it sends may be
// significantly older than the points stored here, resulting in a long
// possibly incorrect trail if the max number of points to store was reached.
// If no point with any pointer id exactly matches the metadata, then we can't
// confirm which set of points to use for the delegated ink trail, so just
// return an empty vector so that nothing will be drawn. This happens most
// often at the beginning of delegated ink trail use. The metadata is created
// using a PointerEvent earlier than any DelegatedInkPoint is created,
// resulting in the metadata having an earlier timestamp and a point that
// doesn't match anything that is sent here from viz. Even if only a single
// pointer ID is in use, we can't know with any certainty what happened
// between the metadata point and the earliest DelegatedInkPoint we have, so
// we choose to just not draw anything.
if (!pointer_id_.has_value()) {
return {};
}
DelegatedInkTrailData& trail_data = pointer_ids_[pointer_id_.value()];
// Make sure the metrics handler is provided the new real events to accurately
// measure the prediction later.
trail_data.UpdateMetrics(metadata_.get());
// Any remaining points must be the points that should be part of the
// delegated ink trail
std::vector<gfx::DelegatedInkPoint> points_to_draw;
for (const auto& [_, point] : trail_data.GetPoints()) {
points_to_draw.emplace_back(point);
TRACE_EVENT_WITH_FLOW1("delegated_ink_trails", "Filtering to draw point",
TRACE_ID_GLOBAL(point.trace_id()),
TRACE_EVENT_FLAG_FLOW_IN, "point", point.ToString());
}
if (!points_to_draw.front().MatchesDelegatedInkMetadata(metadata_.get())) {
base::debug::DumpWithoutCrashing();
}
return points_to_draw;
}
std::optional<AggregatedRenderPassId>
DelegatedInkPointRendererBase::GetLatestMetadataRenderPassId() const {
if (metadata_) {
return AggregatedRenderPassId::FromUnsafeValue(metadata_->render_pass_id());
}
return std::nullopt;
}
void DelegatedInkPointRendererBase::PredictPoints(
std::vector<gfx::DelegatedInkPoint>* ink_points_to_draw) {
DCHECK(metadata_);
if (!pointer_id_.has_value() ||
static_cast<int>(ink_points_to_draw->size()) == 0)
return;
pointer_ids_[pointer_id_.value()].PredictPoints(ink_points_to_draw,
metadata_.get());
}
void DelegatedInkPointRendererBase::ResetPrediction() {
for (auto& it : pointer_ids_)
it.second.Reset();
TRACE_EVENT_INSTANT0("delegated_ink_trails",
"Delegated ink prediction reset.",
TRACE_EVENT_SCOPE_THREAD);
}
void DelegatedInkPointRendererBase::ReportPointsDrawn() {
const base::TimeTicks now = base::TimeTicks::Now();
// If there is a point that matches the metadata and the histogram has not yet
// been fired, then this is the first frame that the metadata point will be
// recorded by `updateInkTrailStartPoint`.
if (metadata_paint_time_.has_value()) {
base::UmaHistogramCustomTimes(
"Renderer.DelegatedInkTrail.Skia.TimeFromDelegatedInkToApiPaint",
now - metadata_paint_time_.value(), base::Milliseconds(1),
base::Seconds(1), 50);
metadata_paint_time_ = std::nullopt;
}
if (!pointer_id_.has_value()) {
return;
}
CHECK(pointer_ids_.contains(pointer_id_.value()));
auto& points_trail = pointer_ids_.at(pointer_id_.value()).GetPoints();
base::UmaHistogramCounts100(
"Renderer.DelegatedInkTrail.Skia.OutstandingPointsToDraw",
points_trail.size());
for (auto& [_, point] : points_trail) {
UMA_HISTOGRAM_TIMES(
"Renderer.DelegatedInkTrail.Skia.TimeToDrawPointsMillis",
now - point.timestamp());
if (!point.paint_timestamp().has_value()) {
point.set_paint_timestamp(now);
}
}
}
void DelegatedInkPointRendererBase::StoreDelegatedInkPoint(
const gfx::DelegatedInkPoint& point) {
TRACE_EVENT_WITH_FLOW1(
"delegated_ink_trails",
"DelegatedInkPointRendererImpl::StoreDelegatedInkPoint",
TRACE_ID_GLOBAL(point.trace_id()),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "point",
point.ToString());
pointer_ids_[point.pointer_id()].AddPoint(point);
}
} // namespace viz