// Copyright 2018 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.

#ifndef UI_LATENCY_FRAME_METRICS_H_
#define UI_LATENCY_FRAME_METRICS_H_

#include "ui/latency/stream_analyzer.h"

#include <cstdint>

#include "base/containers/circular_deque.h"
#include "base/macros.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event_argument.h"
#include "ui/latency/skipped_frame_tracker.h"

namespace ui {
namespace frame_metrics {

class SkipClient : public frame_metrics::StreamAnalyzerClient {
  double TransformResult(double result) const override;
};

class LatencyClient : public frame_metrics::StreamAnalyzerClient {
  double TransformResult(double result) const override;
};

class LatencySpeedClient : public frame_metrics::StreamAnalyzerClient {
  double TransformResult(double result) const override;
};

class LatencyAccelerationClient : public frame_metrics::StreamAnalyzerClient {
  double TransformResult(double result) const override;
};

}  // namespace frame_metrics

enum class FrameMetricsSource {
  Unknown = 0,
  UnitTest = 1,
  RendererCompositor = 2,
  UiCompositor = 3,
};

enum class FrameMetricsSourceThread {
  Unknown = 0,
  Blink = 1,
  RendererCompositor = 2,
  Ui = 3,
  UiCompositor = 4,
  VizCompositor = 5,
};

enum class FrameMetricsCompileTarget {
  Unknown = 0,
  Chromium = 1,
  SynchronousCompositor = 2,
  Headless = 3,
};

struct FrameMetricsSettings {
  FrameMetricsSettings() = default;

  FrameMetricsSettings(FrameMetricsSource source,
                       FrameMetricsSourceThread source_thread,
                       FrameMetricsCompileTarget compile_target,
                       bool trace_results_every_frame = false,
                       size_t max_window_size = 60)
      : source(source),
        source_thread(source_thread),
        compile_target(compile_target),
        trace_results_every_frame(trace_results_every_frame),
        max_window_size(max_window_size) {}

  // Source configuration.
  FrameMetricsSource source;
  FrameMetricsSourceThread source_thread;
  FrameMetricsCompileTarget compile_target;

  // This is needed for telemetry results.
  bool trace_results_every_frame;

  // Maximum window size in number of samples.
  // This is forwarded to each WindowAnalyzer.
  size_t max_window_size;

  void AsValueInto(base::trace_event::TracedValue* state) const;
};

// Calculates all metrics for a frame source.
// Every frame source that we wish to instrument will own an instance of
// this class and will call AddFrameProduced and AddFrameDisplayed.
// Statistics will be reported automatically. Either periodically, based
// on the client interface, or on destruction if any samples were added since
// the last call to StartNewReportPeriod.
class FrameMetrics : public SkippedFrameTracker::Client {
 public:
  explicit FrameMetrics(FrameMetricsSettings settings);
  ~FrameMetrics() override;

  // Resets all data and history as if the class were just created.
  void Reset();

  // AddFrameProduced should be called every time a source produces a frame.
  // The information added here affects the number of frames skipped.
  // Note: If the FrameMetrics class is hooked up to an optional
  //   SkippedFrameTracker, the client should not call this directly.
  void AddFrameProduced(base::TimeTicks source_timestamp,
                        base::TimeDelta amount_produced,
                        base::TimeDelta amount_skipped) override;

  // AddFrameDisplayed should be called whenever a frame causes damage and
  // we know when the result became visible on the display.
  // This will affect all latency derived metrics, including latency speed,
  // latency acceleration, and latency itself.
  // If a frame is produced but not displayed, do not call this; there was
  // no change in the displayed result and thus no change to track the visual
  // latency of. Guessing a displayed time will only skew the results.
  void AddFrameDisplayed(base::TimeTicks source_timestamp,
                         base::TimeTicks display_timestamp);

 protected:
  void TraceStats() const;

  // virtual for testing.
  virtual base::TimeDelta ReportPeriod();

  // Starts a new reporting period that resets the various accumulators
  // and memory of worst regions encountered, but does not destroy recent
  // sample history in the windowed analyzers and in the derivatives
  // for latency speed and latency acceleration. This avoids small gaps
  // in coverage when starting a new reporting period.
  void StartNewReportPeriod();

  FrameMetricsSettings settings_;
  const char* source_name_;

  frame_metrics::SharedWindowedAnalyzerClient shared_skip_client_;
  base::circular_deque<base::TimeTicks> skip_timestamp_queue_;

  frame_metrics::SharedWindowedAnalyzerClient shared_latency_client_;
  base::circular_deque<base::TimeTicks> latency_timestamp_queue_;

  base::TimeDelta time_since_start_of_report_period_;
  uint32_t frames_produced_since_start_of_report_period_ = 0;

  uint64_t latencies_added_ = 0;
  base::TimeTicks source_timestamp_prev_;
  base::TimeDelta latency_prev_;
  base::TimeDelta source_duration_prev_;
  base::TimeDelta latency_delta_prev_;

  frame_metrics::SkipClient skip_client_;
  frame_metrics::LatencyClient latency_client_;
  frame_metrics::LatencySpeedClient latency_speed_client_;
  frame_metrics::LatencyAccelerationClient latency_acceleration_client_;

  frame_metrics::StreamAnalyzer frame_skips_analyzer_;
  frame_metrics::StreamAnalyzer latency_analyzer_;
  frame_metrics::StreamAnalyzer latency_speed_analyzer_;
  frame_metrics::StreamAnalyzer latency_acceleration_analyzer_;

  DISALLOW_COPY_AND_ASSIGN(FrameMetrics);
};

}  // namespace ui

#endif  // UI_LATENCY_FRAME_METRICS_H_
