blob: 6019d1e4dc44d6bad8eaa46fb4e7b57baf3a1c84 [file] [log] [blame]
// Copyright 2017 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.
#include "cc/tiles/checker_image_tracker.h"
#include "base/bind.h"
#include "base/stl_util.h"
#include "base/trace_event/trace_event.h"
namespace cc {
namespace {
// The minimum size of an image that we should consider checkering.
size_t kMinImageSizeToCheckerBytes = 512 * 1024;
size_t SafeSizeOfImage(const SkImage* image) {
base::CheckedNumeric<size_t> checked_size = 4;
checked_size *= image->width();
checked_size *= image->height();
return checked_size.ValueOrDefault(std::numeric_limits<size_t>::max());
}
} // namespace
CheckerImageTracker::CheckerImageTracker(ImageController* image_controller,
CheckerImageTrackerClient* client,
bool enable_checker_imaging)
: image_controller_(image_controller),
client_(client),
enable_checker_imaging_(enable_checker_imaging),
weak_factory_(this) {}
CheckerImageTracker::~CheckerImageTracker() {
// Unlock all images pending decode requests.
for (auto it : image_id_to_decode_request_id_)
image_controller_->UnlockImageDecode(it.second);
}
void CheckerImageTracker::ScheduleImageDecodeQueue(
ImageDecodeQueue image_decode_queue) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"CheckerImageTracker::ScheduleImageDecodeQueue");
// Only checker-imaged (async updated) images are decoded using the image
// decode service. If |enable_checker_imaging_| is false, no image should
// be checkered.
DCHECK(image_decode_queue.empty() || enable_checker_imaging_);
image_decode_queue_ = std::move(image_decode_queue);
ScheduleNextImageDecode();
}
const ImageIdFlatSet& CheckerImageTracker::TakeImagesToInvalidateOnSyncTree() {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"CheckerImageTracker::TakeImagesToInvalidateOnSyncTree");
DCHECK_EQ(invalidated_images_on_current_sync_tree_.size(), 0u)
<< "Sync tree can not be invalidated more than once";
invalidated_images_on_current_sync_tree_.swap(images_pending_invalidation_);
images_pending_invalidation_.clear();
return invalidated_images_on_current_sync_tree_;
}
void CheckerImageTracker::DidActivateSyncTree() {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"CheckerImageTracker::DidActivateSyncTree");
for (auto image_id : invalidated_images_on_current_sync_tree_) {
auto it = image_id_to_decode_request_id_.find(image_id);
image_controller_->UnlockImageDecode(it->second);
image_id_to_decode_request_id_.erase(it);
}
invalidated_images_on_current_sync_tree_.clear();
}
void CheckerImageTracker::DidFinishImageDecode(
ImageId image_id,
ImageController::ImageDecodeRequestId request_id,
ImageController::ImageDecodeResult result) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"CheckerImageTracker::DidFinishImageDecode");
TRACE_EVENT_ASYNC_END0("cc", "CheckerImageTracker::DeferImageDecode",
image_id);
DCHECK_NE(ImageController::ImageDecodeResult::DECODE_NOT_REQUIRED, result);
DCHECK_EQ(outstanding_image_decode_->uniqueID(), image_id);
outstanding_image_decode_ = nullptr;
image_async_decode_state_[image_id] = DecodePolicy::SYNC_DECODED_ONCE;
images_pending_invalidation_.insert(image_id);
ScheduleNextImageDecode();
client_->NeedsInvalidationForCheckerImagedTiles();
}
bool CheckerImageTracker::ShouldCheckerImage(const sk_sp<const SkImage>& image,
WhichTree tree) {
TRACE_EVENT1("cc", "CheckerImageTracker::ShouldCheckerImage", "image_id",
image->uniqueID());
if (!enable_checker_imaging_)
return false;
// If the image was invalidated on the current sync tree and the tile is
// for the active tree, continue checkering it on the active tree to ensure
// the image update is atomic for the frame.
if (invalidated_images_on_current_sync_tree_.count(image->uniqueID()) != 0 &&
tree == WhichTree::ACTIVE_TREE) {
return true;
}
// If the image is pending invalidation, continue checkering it. All tiles
// for these images will be invalidated on the next pending tree.
if (images_pending_invalidation_.find(image->uniqueID()) !=
images_pending_invalidation_.end()) {
return true;
}
ImageId image_id = image->uniqueID();
auto insert_result = image_async_decode_state_.insert(
std::pair<ImageId, DecodePolicy>(image_id, DecodePolicy::ASYNC));
auto it = insert_result.first;
if (insert_result.second) {
it->second = SafeSizeOfImage(image.get()) >= kMinImageSizeToCheckerBytes
? DecodePolicy::ASYNC
: DecodePolicy::SYNC_PERMANENT;
}
return it->second == DecodePolicy::ASYNC;
}
void CheckerImageTracker::ScheduleNextImageDecode() {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"CheckerImageTracker::ScheduleNextImageDecode");
// We can have only one outsanding decode pending completion with the decode
// service. We'll come back here when it is completed.
if (outstanding_image_decode_)
return;
while (!image_decode_queue_.empty()) {
auto candidate = std::move(image_decode_queue_.front());
image_decode_queue_.erase(image_decode_queue_.begin());
// Once an image has been decoded, it can still be present in the decode
// queue (duplicate entries), or while an image is still being skipped on
// the active tree. Check if the image is still ASYNC to see if a decode is
// needed.
ImageId image_id = candidate->uniqueID();
auto it = image_async_decode_state_.find(image_id);
DCHECK(it != image_async_decode_state_.end());
if (it->second != DecodePolicy::ASYNC)
continue;
outstanding_image_decode_ = std::move(candidate);
break;
}
// We either found an image to decode or we reached the end of the queue. If
// we couldn't find an image, we're done.
if (!outstanding_image_decode_) {
DCHECK(image_decode_queue_.empty());
return;
}
ImageId image_id = outstanding_image_decode_->uniqueID();
DCHECK_EQ(image_id_to_decode_request_id_.count(image_id), 0u);
TRACE_EVENT_ASYNC_BEGIN0("cc", "CheckerImageTracker::DeferImageDecode",
image_id);
image_id_to_decode_request_id_[image_id] =
image_controller_->QueueImageDecode(
outstanding_image_decode_,
base::Bind(&CheckerImageTracker::DidFinishImageDecode,
weak_factory_.GetWeakPtr(), image_id));
}
} // namespace cc