Skip to content

Commit

Permalink
Adds 4 CVals to track WebP usage and performance (#1020)
Browse files Browse the repository at this point in the history
This PR adds tracking around AnimatedImage, which is currently only used
for WebP decoding.
We are adding 4 metrics
 - Total number of currently decoding WebP images
 - Cumulative total of frames decoded
 - Decoding underruns, when CPU bottleneck is hit
- Decoding overruns, when timing in decoding pipeline is trying to
decode too fast.

b/196887641
b/280381029
b/290130439
b/290060548

(cherry picked from commit d0684de)
  • Loading branch information
kaidokert committed Jul 26, 2023
1 parent 3eeba6d commit 95e793b
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 3 deletions.
2 changes: 2 additions & 0 deletions cobalt/browser/web_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
29 changes: 28 additions & 1 deletion cobalt/loader/image/animated_image_tracker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
#include "cobalt/loader/image/animated_image_tracker.h"

#include <algorithm>
#include <utility>

#include "base/strings/stringprintf.h"
#include "base/trace_event/trace_event.h"
#include "cobalt/base/polymorphic_downcast.h"

Expand All @@ -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 */);
Expand Down Expand Up @@ -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
16 changes: 15 additions & 1 deletion cobalt/loader/image/animated_image_tracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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();

Expand All @@ -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);
Expand All @@ -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<int, base::CValPublic> count_animated_images_active;
base::CVal<int, base::CValPublic> count_animated_frames_decoded;
base::CVal<int, base::CValPublic> count_animated_frames_decoding_underrun;
base::CVal<int, base::CValPublic> 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.
Expand Down
27 changes: 26 additions & 1 deletion cobalt/loader/image/animated_webp_image.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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 {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -431,6 +437,25 @@ scoped_refptr<render_tree::Image> 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
7 changes: 7 additions & 0 deletions cobalt/loader/image/animated_webp_image.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class AnimatedWebPImage : public AnimatedImage {
// Returns the render image of the frame for debugging
scoped_refptr<render_tree::Image> GetFrameForDebugging(int target_frame);

AnimatedImageDecodingStats GetFrameDeltaStats() override;

private:
~AnimatedWebPImage() override;

Expand Down Expand Up @@ -119,12 +121,17 @@ class AnimatedWebPImage : public AnimatedImage {
base::CancelableClosure decode_closure_;
base::TimeTicks current_frame_time_;
base::Optional<base::TimeTicks> next_frame_time_;

// The original encoded data.
std::vector<uint8> data_buffer_;
scoped_refptr<render_tree::Image> current_canvas_;
scoped_refptr<FrameProvider> 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.
Expand Down
10 changes: 10 additions & 0 deletions cobalt/loader/image/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 95e793b

Please sign in to comment.