// Copyright 2013 The Flutter 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 "flutter/flow/frame_timings.h" #include #include #include "flutter/common/settings.h" #include "flutter/fml/logging.h" #include "flutter/fml/time/time_point.h" namespace flutter { namespace { const char* StateToString(FrameTimingsRecorder::State state) { #ifndef NDEBUG switch (state) { case FrameTimingsRecorder::State::kUninitialized: return "kUninitialized"; case FrameTimingsRecorder::State::kVsync: return "kVsync"; case FrameTimingsRecorder::State::kBuildStart: return "kBuildStart"; case FrameTimingsRecorder::State::kBuildEnd: return "kBuildEnd"; case FrameTimingsRecorder::State::kRasterStart: return "kRasterStart"; case FrameTimingsRecorder::State::kRasterEnd: return "kRasterEnd"; }; FML_UNREACHABLE(); #endif return ""; } } // namespace std::atomic FrameTimingsRecorder::frame_number_gen_ = {1}; FrameTimingsRecorder::FrameTimingsRecorder() : frame_number_(frame_number_gen_++), frame_number_trace_arg_val_(std::to_string(frame_number_)) {} FrameTimingsRecorder::FrameTimingsRecorder(uint64_t frame_number) : frame_number_(frame_number), frame_number_trace_arg_val_(std::to_string(frame_number_)) {} FrameTimingsRecorder::~FrameTimingsRecorder() = default; fml::TimePoint FrameTimingsRecorder::GetVsyncStartTime() const { std::scoped_lock state_lock(state_mutex_); FML_DCHECK(state_ >= State::kVsync); return vsync_start_; } fml::TimePoint FrameTimingsRecorder::GetVsyncTargetTime() const { std::scoped_lock state_lock(state_mutex_); FML_DCHECK(state_ >= State::kVsync); return vsync_target_; } fml::TimePoint FrameTimingsRecorder::GetBuildStartTime() const { std::scoped_lock state_lock(state_mutex_); FML_DCHECK(state_ >= State::kBuildStart); return build_start_; } fml::TimePoint FrameTimingsRecorder::GetBuildEndTime() const { std::scoped_lock state_lock(state_mutex_); FML_DCHECK(state_ >= State::kBuildEnd); return build_end_; } fml::TimePoint FrameTimingsRecorder::GetRasterStartTime() const { std::scoped_lock state_lock(state_mutex_); FML_DCHECK(state_ >= State::kRasterStart); return raster_start_; } fml::TimePoint FrameTimingsRecorder::GetRasterEndTime() const { std::scoped_lock state_lock(state_mutex_); FML_DCHECK(state_ >= State::kRasterEnd); return raster_end_; } fml::TimePoint FrameTimingsRecorder::GetRasterEndWallTime() const { std::scoped_lock state_lock(state_mutex_); FML_DCHECK(state_ >= State::kRasterEnd); return raster_end_wall_time_; } fml::TimeDelta FrameTimingsRecorder::GetBuildDuration() const { std::scoped_lock state_lock(state_mutex_); FML_DCHECK(state_ >= State::kBuildEnd); return build_end_ - build_start_; } /// Count of the layer cache entries size_t FrameTimingsRecorder::GetLayerCacheCount() const { std::scoped_lock state_lock(state_mutex_); FML_DCHECK(state_ >= State::kRasterEnd); return layer_cache_count_; } /// Total bytes in all layer cache entries size_t FrameTimingsRecorder::GetLayerCacheBytes() const { std::scoped_lock state_lock(state_mutex_); FML_DCHECK(state_ >= State::kRasterEnd); return layer_cache_bytes_; } /// Count of the picture cache entries size_t FrameTimingsRecorder::GetPictureCacheCount() const { std::scoped_lock state_lock(state_mutex_); FML_DCHECK(state_ >= State::kRasterEnd); return picture_cache_count_; } /// Total bytes in all picture cache entries size_t FrameTimingsRecorder::GetPictureCacheBytes() const { std::scoped_lock state_lock(state_mutex_); FML_DCHECK(state_ >= State::kRasterEnd); return picture_cache_bytes_; } void FrameTimingsRecorder::RecordVsync(fml::TimePoint vsync_start, fml::TimePoint vsync_target) { fml::Status status = RecordVsyncImpl(vsync_start, vsync_target); FML_DCHECK(status.ok()); (void)status; } void FrameTimingsRecorder::RecordBuildStart(fml::TimePoint build_start) { fml::Status status = RecordBuildStartImpl(build_start); FML_DCHECK(status.ok()); (void)status; } void FrameTimingsRecorder::RecordBuildEnd(fml::TimePoint build_end) { fml::Status status = RecordBuildEndImpl(build_end); FML_DCHECK(status.ok()); (void)status; } void FrameTimingsRecorder::RecordRasterStart(fml::TimePoint raster_start) { fml::Status status = RecordRasterStartImpl(raster_start); FML_DCHECK(status.ok()); (void)status; } fml::Status FrameTimingsRecorder::RecordVsyncImpl(fml::TimePoint vsync_start, fml::TimePoint vsync_target) { std::scoped_lock state_lock(state_mutex_); if (state_ != State::kUninitialized) { return fml::Status(fml::StatusCode::kFailedPrecondition, "Check failed: state_ == State::kUninitialized."); } state_ = State::kVsync; vsync_start_ = vsync_start; vsync_target_ = vsync_target; return fml::Status(); } fml::Status FrameTimingsRecorder::RecordBuildStartImpl( fml::TimePoint build_start) { std::scoped_lock state_lock(state_mutex_); if (state_ != State::kVsync) { return fml::Status(fml::StatusCode::kFailedPrecondition, "Check failed: state_ == State::kVsync."); } state_ = State::kBuildStart; build_start_ = build_start; return fml::Status(); } fml::Status FrameTimingsRecorder::RecordBuildEndImpl(fml::TimePoint build_end) { std::scoped_lock state_lock(state_mutex_); if (state_ != State::kBuildStart) { return fml::Status(fml::StatusCode::kFailedPrecondition, "Check failed: state_ == State::kBuildStart."); } state_ = State::kBuildEnd; build_end_ = build_end; return fml::Status(); } fml::Status FrameTimingsRecorder::RecordRasterStartImpl( fml::TimePoint raster_start) { std::scoped_lock state_lock(state_mutex_); if (state_ != State::kBuildEnd) { return fml::Status(fml::StatusCode::kFailedPrecondition, "Check failed: state_ == State::kBuildEnd."); } state_ = State::kRasterStart; raster_start_ = raster_start; return fml::Status(); } FrameTiming FrameTimingsRecorder::RecordRasterEnd(const RasterCache* cache) { std::scoped_lock state_lock(state_mutex_); FML_DCHECK(state_ == State::kRasterStart); state_ = State::kRasterEnd; raster_end_ = fml::TimePoint::Now(); raster_end_wall_time_ = fml::TimePoint::CurrentWallTime(); if (cache) { #if !SLIMPELLER const RasterCacheMetrics& layer_metrics = cache->layer_metrics(); const RasterCacheMetrics& picture_metrics = cache->picture_metrics(); layer_cache_count_ = layer_metrics.total_count(); layer_cache_bytes_ = layer_metrics.total_bytes(); picture_cache_count_ = picture_metrics.total_count(); picture_cache_bytes_ = picture_metrics.total_bytes(); #endif // !SLIMPELLER } else { layer_cache_count_ = layer_cache_bytes_ = picture_cache_count_ = picture_cache_bytes_ = 0; } timing_.Set(FrameTiming::kVsyncStart, vsync_start_); timing_.Set(FrameTiming::kBuildStart, build_start_); timing_.Set(FrameTiming::kBuildFinish, build_end_); timing_.Set(FrameTiming::kRasterStart, raster_start_); timing_.Set(FrameTiming::kRasterFinish, raster_end_); timing_.Set(FrameTiming::kRasterFinishWallTime, raster_end_wall_time_); timing_.SetFrameNumber(GetFrameNumber()); timing_.SetRasterCacheStatistics(layer_cache_count_, layer_cache_bytes_, picture_cache_count_, picture_cache_bytes_); return timing_; } FrameTiming FrameTimingsRecorder::GetRecordedTime() const { std::scoped_lock state_lock(state_mutex_); FML_DCHECK(state_ == State::kRasterEnd); return timing_; } std::unique_ptr FrameTimingsRecorder::CloneUntil( State state) { std::scoped_lock state_lock(state_mutex_); std::unique_ptr recorder = std::make_unique(frame_number_); FML_DCHECK(state_ >= state); recorder->state_ = state; if (state >= State::kVsync) { recorder->vsync_start_ = vsync_start_; recorder->vsync_target_ = vsync_target_; } if (state >= State::kBuildStart) { recorder->build_start_ = build_start_; } if (state >= State::kBuildEnd) { recorder->build_end_ = build_end_; } if (state >= State::kRasterStart) { recorder->raster_start_ = raster_start_; } if (state >= State::kRasterEnd) { recorder->raster_end_ = raster_end_; recorder->raster_end_wall_time_ = raster_end_wall_time_; recorder->layer_cache_count_ = layer_cache_count_; recorder->layer_cache_bytes_ = layer_cache_bytes_; recorder->picture_cache_count_ = picture_cache_count_; recorder->picture_cache_bytes_ = picture_cache_bytes_; } return recorder; } uint64_t FrameTimingsRecorder::GetFrameNumber() const { return frame_number_; } const char* FrameTimingsRecorder::GetFrameNumberTraceArg() const { return frame_number_trace_arg_val_.c_str(); } void FrameTimingsRecorder::AssertInState(State state) const { FML_DCHECK(state_ == state) << "Expected state " << StateToString(state) << ", actual state " << StateToString(state_); } } // namespace flutter