diff --git a/cobalt/browser/web_module.cc b/cobalt/browser/web_module.cc index 9b122533d5c9..f1158ddb732c 100644 --- a/cobalt/browser/web_module.cc +++ b/cobalt/browser/web_module.cc @@ -564,6 +564,7 @@ WebModule::Impl::Impl(web::Context* web_context, const ConstructionData& data) data.options.loader_thread_priority)); animated_image_tracker_.reset(new loader::image::AnimatedImageTracker( + web_context_->name().c_str(), data.options.animated_image_decode_thread_priority)); DCHECK_LE(0, data.options.image_cache_capacity); @@ -1017,6 +1018,7 @@ void WebModule::Impl::ProcessOnRenderTreeRasterized( DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); web_module_stat_tracker_->OnRenderTreeRasterized(produced_time, rasterized_time); + animated_image_tracker_->OnRenderTreeRasterized(); if (produced_time >= last_render_tree_produced_time_) { is_render_tree_rasterization_pending_ = false; } diff --git a/cobalt/loader/image/animated_image_tracker.cc b/cobalt/loader/image/animated_image_tracker.cc index 98dadcad0dca..c7a3e73cefb5 100644 --- a/cobalt/loader/image/animated_image_tracker.cc +++ b/cobalt/loader/image/animated_image_tracker.cc @@ -15,7 +15,9 @@ #include "cobalt/loader/image/animated_image_tracker.h" #include +#include +#include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" #include "cobalt/base/polymorphic_downcast.h" @@ -24,8 +26,22 @@ namespace loader { namespace image { AnimatedImageTracker::AnimatedImageTracker( + const char* name, base::ThreadPriority animated_image_decode_thread_priority) - : animated_image_decode_thread_("AnimatedImage") { + : animated_image_decode_thread_("AnimatedImage"), + name_(name), + count_animated_images_active( + base::StringPrintf("Count.%s.AnimatedImage.Active", name), 0, + "Total number of active animated image decoders."), + count_animated_frames_decoded( + base::StringPrintf("Count.%s.AnimatedImage.DecodedFrames", name), 0, + "Total number of decoded animated image frames."), + count_animated_frames_decoding_underrun( + base::StringPrintf("Count.%s.AnimatedImage.DecodingUnderruns", name), + 0, "Number of underruns from decoding animated images"), + count_animated_frames_decoding_overrun( + base::StringPrintf("Count.%s.AnimatedImage.DecodingOverruns", name), + 0, "Number of overruns from decoding animated images") { TRACE_EVENT0("cobalt::loader::image", "AnimatedImageTracker::RecordImage()"); base::Thread::Options options(base::MessageLoop::TYPE_DEFAULT, 0 /* default stack size */); @@ -123,6 +139,17 @@ void AnimatedImageTracker::Reset() { playing_urls_.clear(); } +void AnimatedImageTracker::OnRenderTreeRasterized() { + count_animated_images_active = playing_urls_.size(); + for (const auto& playing_url : playing_urls_) { + auto image = image_map_[playing_url.first].get(); + auto stats = image->GetFrameDeltaStats(); + count_animated_frames_decoded += stats.frames_decoded; + count_animated_frames_decoding_underrun += stats.frames_underrun; + count_animated_frames_decoding_overrun += stats.frames_overrun; + } +} + } // namespace image } // namespace loader } // namespace cobalt diff --git a/cobalt/loader/image/animated_image_tracker.h b/cobalt/loader/image/animated_image_tracker.h index f6f38e4d4092..6df920b7a331 100644 --- a/cobalt/loader/image/animated_image_tracker.h +++ b/cobalt/loader/image/animated_image_tracker.h @@ -20,6 +20,7 @@ #include "base/containers/small_map.h" #include "base/threading/thread.h" +#include "cobalt/base/c_val.h" #include "cobalt/base/unused.h" #include "cobalt/loader/image/image.h" #include "url/gurl.h" @@ -33,7 +34,8 @@ namespace image { // playing status is updated hence decoding is turned on / off for it. class AnimatedImageTracker { public: - explicit AnimatedImageTracker( + AnimatedImageTracker( + const char* name, base::ThreadPriority animated_image_decode_thread_priority); ~AnimatedImageTracker(); @@ -54,6 +56,9 @@ class AnimatedImageTracker { // animations. void Reset(); + // Called from WebModule to compute image animation related stats. + void OnRenderTreeRasterized(); + private: void OnDisplayStart(loader::image::AnimatedImage* image); void OnDisplayEnd(loader::image::AnimatedImage* image); @@ -72,6 +77,15 @@ class AnimatedImageTracker { URLToIntMap current_url_counts_; URLSet playing_urls_; + // The name of the WebModule this AnimatedImage tracker belongs to, for CVals. + const std::string name_; + + // Animated image counters + base::CVal count_animated_images_active; + base::CVal count_animated_frames_decoded; + base::CVal count_animated_frames_decoding_underrun; + base::CVal count_animated_frames_decoding_overrun; + // Used to ensure that all AnimatedImageTracker methods are called on the // same thread (*not* |animated_image_decode_thread_|), the thread that we // were constructed on. diff --git a/cobalt/loader/image/animated_webp_image.cc b/cobalt/loader/image/animated_webp_image.cc index ed0b95936d41..86891682ea30 100644 --- a/cobalt/loader/image/animated_webp_image.cc +++ b/cobalt/loader/image/animated_webp_image.cc @@ -148,6 +148,8 @@ void AnimatedWebPImage::PlayInternal() { return; } is_playing_ = true; + current_stats.frames_underrun = 0; + current_stats.frames_overrun = 0; if (received_first_frame_) { StartDecoding(); @@ -171,7 +173,8 @@ void AnimatedWebPImage::StopInternal() { void AnimatedWebPImage::StartDecoding() { TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::StartDecoding()"); lock_.AssertAcquired(); - current_frame_time_ = base::TimeTicks::Now(); + decoding_start_time_ = current_frame_time_ = base::TimeTicks::Now(); + current_stats.frames_decoded = 0; if (task_runner_->BelongsToCurrentThread()) { DecodeFrames(); } else { @@ -269,6 +272,7 @@ bool AnimatedWebPImage::DecodeOneFrame(int frame_index) { LOG(ERROR) << "Failed to decode WebP image frame."; return false; } + current_stats.frames_decoded++; } // Alpha blend the current frame on top of the buffer. @@ -354,6 +358,7 @@ bool AnimatedWebPImage::AdvanceFrame() { // Always wait for a consumer to consume the previous frame before moving // forward with decoding the next frame. if (!frame_provider_->FrameConsumed()) { + current_stats.frames_overrun++; return false; } @@ -386,6 +391,7 @@ bool AnimatedWebPImage::AdvanceFrame() { if (next_frame_time_ < current_time) { // Don't let the animation fall back for more than a frame. next_frame_time_ = current_time; + current_stats.frames_underrun++; } return true; @@ -431,6 +437,25 @@ scoped_refptr AnimatedWebPImage::GetFrameForDebugging( return target_canvas; } +AnimatedImage::AnimatedImageDecodingStats +AnimatedWebPImage::GetFrameDeltaStats() { + AnimatedImageDecodingStats result; + if (current_stats.frames_decoded >= last_stats.frames_decoded) { + result.frames_decoded = + current_stats.frames_decoded - last_stats.frames_decoded; + result.frames_underrun = + current_stats.frames_underrun - last_stats.frames_underrun; + result.frames_overrun = + current_stats.frames_overrun - last_stats.frames_overrun; + } else { + // There was a reset somewhere + // Simply return total, this discards any overflow data we might have had. + result = current_stats; + } + last_stats = current_stats; + return result; +} + } // namespace image } // namespace loader } // namespace cobalt diff --git a/cobalt/loader/image/animated_webp_image.h b/cobalt/loader/image/animated_webp_image.h index 135bf6a600d1..4d1be0402d7d 100644 --- a/cobalt/loader/image/animated_webp_image.h +++ b/cobalt/loader/image/animated_webp_image.h @@ -66,6 +66,8 @@ class AnimatedWebPImage : public AnimatedImage { // Returns the render image of the frame for debugging scoped_refptr GetFrameForDebugging(int target_frame); + AnimatedImageDecodingStats GetFrameDeltaStats() override; + private: ~AnimatedWebPImage() override; @@ -119,12 +121,17 @@ class AnimatedWebPImage : public AnimatedImage { base::CancelableClosure decode_closure_; base::TimeTicks current_frame_time_; base::Optional next_frame_time_; + // The original encoded data. std::vector data_buffer_; scoped_refptr current_canvas_; scoped_refptr frame_provider_; base::Lock lock_; + base::TimeTicks decoding_start_time_; + AnimatedImageDecodingStats current_stats; + AnimatedImageDecodingStats last_stats; + // Makes sure that the thread that sets the task_runner is always consistent. // This is the thread sending Play()/Stop() calls, and is not necessarily // the same thread that the task_runner itself is running on. diff --git a/cobalt/loader/image/image.h b/cobalt/loader/image/image.h index af99363a3201..bac9849af3c3 100644 --- a/cobalt/loader/image/image.h +++ b/cobalt/loader/image/image.h @@ -145,6 +145,16 @@ class AnimatedImage : public Image { image_node_builder->destination_rect = destination_rect; image_node_builder->local_transform = local_transform; } + + // Frame counters for decoding. + struct AnimatedImageDecodingStats { + unsigned int frames_decoded = 0; + unsigned int frames_underrun = 0; + unsigned int frames_overrun = 0; + }; + + // Returns decoded frame stats since the last call, as a delta. + virtual AnimatedImageDecodingStats GetFrameDeltaStats() = 0; }; } // namespace image