#include "cc/tiles/decoded_image_tracker.h"
#include <algorithm>
#include "base/task/sequenced_task_runner.h"
#include "base/time/default_tick_clock.h"
#include "base/trace_event/trace_event.h"
namespace cc {
namespace {
const int kNumCommitsToLock = 3;
const int64_t kTimeoutDurationMs = 4000;
}
DecodedImageTracker::ImageLock::ImageLock(
DecodedImageTracker* tracker,
ImageController::ImageDecodeRequestId request_id,
int expiration_frame,
base::TimeTicks expiration_time)
: tracker_(tracker),
request_id_(request_id),
expiration_frame_(expiration_frame),
expiration_time_(expiration_time) {}
DecodedImageTracker::ImageLock::~ImageLock() {
tracker_->image_controller_->UnlockImageDecode(request_id_);
}
DecodedImageTracker::DecodedImageTracker(
ImageController* controller,
scoped_refptr<base::SequencedTaskRunner> task_runner)
: image_controller_(controller),
tick_clock_(base::DefaultTickClock::GetInstance()),
expiration_timer_(std::make_unique<base::RepeatingTimer>(tick_clock_)) {
DCHECK(image_controller_);
expiration_timer_->SetTaskRunner(task_runner);
timer_closure_ = std::make_unique<base::RepeatingClosure>(
base::BindRepeating(&DecodedImageTracker::CheckForExpiredDecodes,
weak_ptr_factory_.GetWeakPtr()));
}
DecodedImageTracker::~DecodedImageTracker() {
UnlockAllImages();
}
void DecodedImageTracker::QueueImageDecode(
const DrawImage& image,
base::OnceCallback<void(bool)> callback,
bool speculative) {
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"DecodedImageTracker::QueueImageDecode", "frame_key",
image.frame_key().ToString());
DCHECK(image_controller_);
image_controller_->QueueImageDecode(
image,
base::BindOnce(&DecodedImageTracker::ImageDecodeFinished,
base::Unretained(this), std::move(callback),
image.paint_image().stable_id()),
speculative);
}
void DecodedImageTracker::UnlockAllImages() {
locked_images_.clear();
}
void DecodedImageTracker::OnImagesUsedInDraw(
const std::vector<DrawImage>& draw_images) {
for (const DrawImage& draw_image : draw_images)
locked_images_.erase(draw_image.paint_image().stable_id());
}
void DecodedImageTracker::SetSyncTreeFrameNumber(int frame_number) {
if (sync_tree_frame_number_ != frame_number) {
sync_tree_frame_number_ = frame_number;
CheckForExpiredDecodes();
}
}
void DecodedImageTracker::CheckForExpiredDecodes() {
if (locked_images_.empty()) {
return;
}
int current_frame = sync_tree_frame_number_;
auto now = tick_clock_->NowTicks();
base::TimeDelta new_delay = base::TimeDelta::Max();
base::EraseIf(
locked_images_,
[now, current_frame, &new_delay](
const std::pair<PaintImage::Id, std::unique_ptr<ImageLock>>& entry)
-> bool {
if (entry.second->expiration_frame() < current_frame ||
entry.second->expiration_time() <= now) {
return true;
}
new_delay = std::min(new_delay, entry.second->expiration_time() - now);
return false;
});
StartTimer(new_delay);
}
void DecodedImageTracker::SetTickClockForTesting(
const base::TickClock* tick_clock,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
tick_clock_ = tick_clock;
expiration_timer_ = std::make_unique<base::RepeatingTimer>(tick_clock_);
expiration_timer_->SetTaskRunner(task_runner);
}
void DecodedImageTracker::ImageDecodeFinished(
base::OnceCallback<void(bool)> callback,
PaintImage::Id image_id,
ImageController::ImageDecodeRequestId request_id,
ImageController::ImageDecodeResult result) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"DecodedImageTracker::ImageDecodeFinished");
if (result == ImageController::ImageDecodeResult::SUCCESS) {
locked_images_.erase(image_id);
auto delay = base::Milliseconds(kTimeoutDurationMs);
locked_images_.emplace(
image_id,
std::make_unique<ImageLock>(this, request_id,
sync_tree_frame_number_ + kNumCommitsToLock,
tick_clock_->NowTicks() + delay));
if (!expiration_timer_->IsRunning()) {
StartTimer(delay);
}
}
bool decode_succeeded =
result == ImageController::ImageDecodeResult::SUCCESS ||
result == ImageController::ImageDecodeResult::DECODE_NOT_REQUIRED;
std::move(callback).Run(decode_succeeded);
}
void DecodedImageTracker::StartTimer(base::TimeDelta delay) {
expiration_timer_->Stop();
if (delay == base::TimeDelta::Max()) {
return;
}
expiration_timer_->Start(FROM_HERE, delay, *timer_closure_);
}
}