123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- // Copyright 2022 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 "components/exo/shell_surface_presentation_time_recorder.h"
- #include <cstdint>
- #include <memory>
- #include "base/bind.h"
- #include "base/logging.h"
- #include "base/metrics/histogram.h"
- #include "base/time/time.h"
- #include "base/trace_event/typed_macros.h"
- #include "components/exo/shell_surface.h"
- #include "components/exo/shell_surface_util.h"
- #include "components/exo/surface.h"
- #include "third_party/perfetto/include/perfetto/tracing/track.h"
- #include "ui/gfx/presentation_feedback.h"
- namespace exo {
- namespace {
- constexpr char kTraceCategory[] = "benchmark,ui";
- base::HistogramBase* CreateTimesHistogram(const char* name) {
- return base::Histogram::FactoryTimeGet(
- name, base::Milliseconds(1), base::Milliseconds(200), 50,
- base::HistogramBase::kUmaTargetedHistogramFlag);
- }
- // HistogramReporter reports latency and optional max latency as UMA histograms.
- class HistogramReporter
- : public ShellSurfacePresentationTimeRecorder::Reporter {
- public:
- HistogramReporter(const char* latency_histogram_name,
- absl::optional<const char*> max_latency_histogram_name)
- : latency_histogram_(CreateTimesHistogram(latency_histogram_name)),
- max_latency_histogram_name_(max_latency_histogram_name) {}
- HistogramReporter(const HistogramReporter&) = delete;
- HistogramReporter& operator=(const HistogramReporter&) = delete;
- ~HistogramReporter() override {
- if (max_latency_histogram_name_.has_value()) {
- CreateTimesHistogram(max_latency_histogram_name_.value())
- ->AddTimeMillisecondsGranularity(max_latency_);
- }
- }
- // PresentationTimeRecorder::Reporter
- void ReportTime(base::TimeDelta delta) override {
- latency_histogram_->AddTimeMillisecondsGranularity(delta);
- if (max_latency_histogram_name_.has_value() && delta > max_latency_) {
- max_latency_ = delta;
- }
- }
- private:
- base::HistogramBase* const latency_histogram_;
- const absl::optional<const char*> max_latency_histogram_name_;
- base::TimeDelta max_latency_;
- };
- } // namespace
- // static
- std::unique_ptr<ShellSurfacePresentationTimeRecorder::Reporter>
- ShellSurfacePresentationTimeRecorder::CreateHistogramReporter(
- const char* latency_histogram_name,
- absl::optional<const char*> max_latency_histogram_name) {
- return std::make_unique<HistogramReporter>(latency_histogram_name,
- max_latency_histogram_name);
- }
- ShellSurfacePresentationTimeRecorder::ShellSurfacePresentationTimeRecorder(
- ShellSurface* shell_surface,
- std::unique_ptr<Reporter> reporter)
- : shell_surface_(shell_surface), reporter_(std::move(reporter)) {
- scoped_observation_.Observe(shell_surface_);
- }
- ShellSurfacePresentationTimeRecorder::~ShellSurfacePresentationTimeRecorder() =
- default;
- void ShellSurfacePresentationTimeRecorder::PrepareToRecord() {
- if (pending_request_.has_value())
- return;
- pending_request_ = absl::make_optional<Request>();
- pending_request_->request_id = next_request_id_++;
- }
- bool ShellSurfacePresentationTimeRecorder::RequestNext() {
- // Underlying ShellSurface must still be alive.
- DCHECK(shell_surface_);
- // `PrepareToRecord()` must have happened.
- DCHECK(pending_request_.has_value());
- // Early out if there is a pending request that does not get a Configure.
- if (!pending_request_->serial.has_value())
- return false;
- TRACE_EVENT_BEGIN(kTraceCategory, "ShellSurfacePresentationTimeRecorder",
- perfetto::Track(pending_request_->request_id), "serial",
- pending_request_->serial.value());
- pending_request_->request_time = base::TimeTicks::Now();
- requests_.emplace_back(pending_request_.value());
- pending_request_.reset();
- LOG_IF(WARNING, requests_.size() > 100u)
- << "Number of requests waiting for ack has reached: " << requests_.size();
- return true;
- }
- void ShellSurfacePresentationTimeRecorder::OnConfigure(uint32_t serial) {
- if (!pending_request_.has_value())
- return;
- pending_request_->serial = serial;
- }
- void ShellSurfacePresentationTimeRecorder::OnAcknowledgeConfigure(
- uint32_t serial) {
- // Must not ack a serial in `pending_request_`. RequestNext() should happen
- // to commit the `pending_request_` before the ack.
- DCHECK(!pending_request_.has_value() ||
- !pending_request_->serial.has_value() ||
- serial > pending_request_->serial);
- Surface* root_surface = shell_surface_->root_surface();
- while (!requests_.empty()) {
- Request request = requests_.front();
- requests_.pop_front();
- root_surface->RequestPresentationCallback(base::BindRepeating(
- &ShellSurfacePresentationTimeRecorder::OnFramePresented,
- weak_ptr_factory_.GetWeakPtr(), request));
- if (request.serial.value() == serial)
- break;
- }
- }
- void ShellSurfacePresentationTimeRecorder::OnShellSurfaceDestroyed() {
- scoped_observation_.Reset();
- }
- void ShellSurfacePresentationTimeRecorder::OnFramePresented(
- const Request& request,
- const gfx::PresentationFeedback& feedback) {
- TRACE_EVENT_END(kTraceCategory, perfetto::Track(request.request_id), "flags",
- feedback.flags, "serial", request.serial.value());
- if (feedback.flags & gfx::PresentationFeedback::kFailure) {
- LOG(WARNING) << "PresentationFailed (serial=" << *request.serial << "):"
- << ", flags=" << feedback.flags;
- return;
- }
- if (feedback.timestamp.is_null()) {
- // TODO(b/165951963): ideally feedback.timestamp should not be null.
- // Consider replacing this by DCHECK or CHECK.
- LOG(ERROR) << "Invalid feedback timestamp (serial=" << *request.serial
- << "):"
- << " timestamp is not set";
- return;
- }
- const base::TimeDelta delta = feedback.timestamp - request.request_time;
- if (delta.InMilliseconds() < 0) {
- LOG(ERROR) << "Invalid timestamp for presentation feedback (serial="
- << *request.serial
- << "): requested_time=" << request.request_time
- << " feedback.timestamp=" << feedback.timestamp;
- return;
- }
- reporter_->ReportTime(delta);
- }
- } // namespace exo
|