blob: 44cba670c23db2d282399f604cda2d1e73b92395 [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/run_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/test/skia_common.h"
#include "cc/tiles/image_controller.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
namespace {
// 5MB max image cache size.
const size_t kMaxImageCacheSizeBytes = 5 * 1024 * 1024;
const int kCheckerableImageDimension = 512;
// This size will result in an image just over kMaxImageCacheSizeBytes.
const int kLargeNonCheckerableImageDimension = 1145;
const int kSmallNonCheckerableImageDimension = 16;
class TestImageController : public ImageController {
public:
// We can use the same thread for the image worker because all use of it in
// the ImageController is over-ridden here.
TestImageController()
: ImageController(base::ThreadTaskRunnerHandle::Get().get(),
base::ThreadTaskRunnerHandle::Get()) {
SetMaxImageCacheLimitBytesForTesting(kMaxImageCacheSizeBytes);
}
~TestImageController() override { DCHECK_EQ(locked_images_.size(), 0U); }
int num_of_locked_images() const { return locked_images_.size(); }
const PaintImageIdFlatSet& decodes_requested() const {
return decodes_requested_;
}
const std::vector<DrawImage>& decoded_images() const {
return decoded_images_;
}
void UnlockImageDecode(ImageDecodeRequestId id) override {
DCHECK_EQ(locked_images_.count(id), 1U);
locked_images_.erase(id);
}
ImageDecodeRequestId QueueImageDecode(
const DrawImage& image,
ImageDecodedCallback callback) override {
ImageDecodeRequestId request_id = next_image_request_id_++;
decoded_images_.push_back(image);
decodes_requested_.insert(image.paint_image().stable_id());
locked_images_.insert(request_id);
// Post the callback asynchronously to match the behaviour in
// ImageController.
worker_task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), request_id,
ImageDecodeResult::SUCCESS));
return request_id;
}
private:
ImageDecodeRequestId next_image_request_id_ = 1U;
std::unordered_set<ImageDecodeRequestId> locked_images_;
PaintImageIdFlatSet decodes_requested_;
std::vector<DrawImage> decoded_images_;
};
class CheckerImageTrackerTest : public testing::Test,
public CheckerImageTrackerClient {
public:
enum class ImageType {
CHECKERABLE,
SMALL_NON_CHECKERABLE,
LARGE_NON_CHECKERABLE
};
void SetUpTracker(bool checker_images_enabled) {
size_t size_to_checker = 512 * 1024;
checker_image_tracker_ = std::make_unique<CheckerImageTracker>(
&image_controller_, this, checker_images_enabled, size_to_checker);
checker_image_tracker_->SetMaxDecodePriorityAllowed(
CheckerImageTracker::DecodeType::kPreDecode);
}
void TearDown() override { checker_image_tracker_.reset(); }
DrawImage CreateImage(
ImageType image_type,
PaintImage::AnimationType animation = PaintImage::AnimationType::STATIC,
PaintImage::CompletionState completion =
PaintImage::CompletionState::DONE,
bool is_multipart = false) {
int dimension = 0;
switch (image_type) {
case ImageType::CHECKERABLE:
dimension = kCheckerableImageDimension;
break;
case ImageType::SMALL_NON_CHECKERABLE:
dimension = kSmallNonCheckerableImageDimension;
break;
case ImageType::LARGE_NON_CHECKERABLE:
dimension = kLargeNonCheckerableImageDimension;
break;
}
auto generator = CreatePaintImageGenerator(gfx::Size(dimension, dimension));
auto id = PaintImage::GetNextId();
checker_image_tracker_->UpdateImageDecodingHints(
{{id, PaintImage::DecodingMode::kAsync}});
return DrawImage(PaintImageBuilder::WithDefault()
.set_id(id)
.set_paint_image_generator(std::move(generator))
.set_animation_type(animation)
.set_completion_state(completion)
.set_is_multipart(is_multipart)
.set_decoding_mode(PaintImage::DecodingMode::kAsync)
.TakePaintImage(),
SkIRect::MakeWH(dimension, dimension),
kNone_SkFilterQuality, SkMatrix::I(),
PaintImage::kDefaultFrameIndex);
}
bool ShouldCheckerImage(const DrawImage& draw_image, WhichTree tree) {
return checker_image_tracker_->ShouldCheckerImage(draw_image, tree);
}
CheckerImageTracker::ImageDecodeQueue BuildImageDecodeQueue(
std::vector<DrawImage> images,
WhichTree tree) {
CheckerImageTracker::ImageDecodeQueue decode_queue;
for (const auto& image : images) {
if (ShouldCheckerImage(image, tree))
decode_queue.push_back(CheckerImageTracker::ImageDecodeRequest(
image.paint_image(), CheckerImageTracker::DecodeType::kRaster));
}
return decode_queue;
}
// CheckerImageTrackerClient implementation.
void NeedsInvalidationForCheckerImagedTiles() override {
invalidation_request_pending_ = true;
}
protected:
TestImageController image_controller_;
std::unique_ptr<CheckerImageTracker> checker_image_tracker_;
bool invalidation_request_pending_ = false;
};
TEST_F(CheckerImageTrackerTest, CheckerImagesDisabled) {
// Ensures that the tracker doesn't filter any images for checkering if it is
// disabled.
SetUpTracker(false);
PaintImageIdFlatSet checkered_images;
DrawImage draw_image = CreateImage(ImageType::CHECKERABLE);
EXPECT_FALSE(ShouldCheckerImage(draw_image, WhichTree::PENDING_TREE));
EXPECT_EQ(image_controller_.num_of_locked_images(), 0);
}
TEST_F(CheckerImageTrackerTest, UpdatesImagesAtomically) {
// Ensures that the tracker updates images atomically for each frame.
SetUpTracker(true);
DrawImage checkerable_image = CreateImage(ImageType::CHECKERABLE);
DrawImage small_non_checkerable_image =
CreateImage(ImageType::SMALL_NON_CHECKERABLE);
DrawImage large_non_checkerable_image =
CreateImage(ImageType::LARGE_NON_CHECKERABLE);
CheckerImageTracker::ImageDecodeQueue image_decode_queue;
// First request to filter images.
std::vector<DrawImage> draw_images = {
checkerable_image, small_non_checkerable_image,
large_non_checkerable_image, checkerable_image};
image_decode_queue =
BuildImageDecodeQueue(draw_images, WhichTree::PENDING_TREE);
ASSERT_EQ(2u, image_decode_queue.size());
EXPECT_EQ(checkerable_image.paint_image(), image_decode_queue[0].paint_image);
EXPECT_EQ(checkerable_image.paint_image(), image_decode_queue[1].paint_image);
checker_image_tracker_->ScheduleImageDecodeQueue(image_decode_queue);
EXPECT_EQ(image_controller_.num_of_locked_images(), 1);
// Run pending task to indicate completion of decode request to the tracker.
// This should send an impl-side invalidation request to the client. The
// images must remain locked until the sync tree to which the invalidations
// are added is activated.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(invalidation_request_pending_);
EXPECT_EQ(image_controller_.num_of_locked_images(), 1);
// Continue checkering the image until the set of images to invalidate is
// pulled.
EXPECT_TRUE(ShouldCheckerImage(checkerable_image, WhichTree::PENDING_TREE));
PaintImageIdFlatSet invalidated_images =
checker_image_tracker_->TakeImagesToInvalidateOnSyncTree();
EXPECT_EQ(invalidated_images.size(), 1U);
EXPECT_EQ(
invalidated_images.count(checkerable_image.paint_image().stable_id()),
1U);
// Use the same set of draw images to ensure that they are not checkered on
// the pending tree now.
EXPECT_FALSE(ShouldCheckerImage(checkerable_image, WhichTree::PENDING_TREE));
EXPECT_FALSE(
ShouldCheckerImage(small_non_checkerable_image, WhichTree::PENDING_TREE));
EXPECT_FALSE(
ShouldCheckerImage(large_non_checkerable_image, WhichTree::PENDING_TREE));
// Use this set to make the same request from the active tree, we should
// continue checkering this image on the active tree until activation.
EXPECT_TRUE(ShouldCheckerImage(checkerable_image, WhichTree::ACTIVE_TREE));
EXPECT_FALSE(
ShouldCheckerImage(small_non_checkerable_image, WhichTree::ACTIVE_TREE));
EXPECT_FALSE(
ShouldCheckerImage(large_non_checkerable_image, WhichTree::ACTIVE_TREE));
// Activate the sync tree. The images should be unlocked upon activation.
EXPECT_EQ(image_controller_.num_of_locked_images(), 1);
checker_image_tracker_->DidActivateSyncTree();
}
TEST_F(CheckerImageTrackerTest, NoConsecutiveCheckeringForImage) {
// Ensures that if an image is decoded and invalidated once, it is not
// checkered again in subsequent frames.
SetUpTracker(true);
DrawImage checkerable_image = CreateImage(ImageType::CHECKERABLE);
std::vector<DrawImage> draw_images = {checkerable_image};
CheckerImageTracker::ImageDecodeQueue image_decode_queue =
BuildImageDecodeQueue(draw_images, WhichTree::PENDING_TREE);
EXPECT_EQ(image_decode_queue.size(), 1U);
checker_image_tracker_->ScheduleImageDecodeQueue(image_decode_queue);
// Trigger decode completion, take images to invalidate and activate the sync
// tree.
base::RunLoop().RunUntilIdle();
checker_image_tracker_->TakeImagesToInvalidateOnSyncTree();
checker_image_tracker_->DidActivateSyncTree();
// Subsequent requests for this image should not be checkered.
EXPECT_FALSE(ShouldCheckerImage(checkerable_image, WhichTree::PENDING_TREE));
}
TEST_F(CheckerImageTrackerTest,
TracksCheckeredImagesSeperatelyInConsecutiveFrames) {
// Ensures that the set of images being checkered on the pending tree, and the
// active tree are tracked correctly.
SetUpTracker(true);
DrawImage checkerable_image1 = CreateImage(ImageType::CHECKERABLE);
std::vector<DrawImage> draw_images;
CheckerImageTracker::ImageDecodeQueue image_decode_queue;
// First request to filter images on the pending and active tree.
draw_images.push_back(checkerable_image1);
image_decode_queue =
BuildImageDecodeQueue(draw_images, WhichTree::PENDING_TREE);
EXPECT_EQ(image_decode_queue.size(), 1U);
checker_image_tracker_->ScheduleImageDecodeQueue(image_decode_queue);
// The image is also checkered on the active tree while a decode request is
// pending.
EXPECT_TRUE(ShouldCheckerImage(checkerable_image1, WhichTree::ACTIVE_TREE));
// Trigger decode completion and take images to invalidate on the sync tree.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(invalidation_request_pending_);
PaintImageIdFlatSet invalidated_images =
checker_image_tracker_->TakeImagesToInvalidateOnSyncTree();
EXPECT_EQ(invalidated_images.size(), 1U);
EXPECT_EQ(
invalidated_images.count(checkerable_image1.paint_image().stable_id()),
1U);
// Second request to filter the same image on the pending and active tree. It
// should be checkered on the active tree, but not the pending tree.
EXPECT_TRUE(ShouldCheckerImage(checkerable_image1, WhichTree::ACTIVE_TREE));
EXPECT_FALSE(ShouldCheckerImage(checkerable_image1, WhichTree::PENDING_TREE));
// New checkerable image on the pending tree.
DrawImage checkerable_image2 = CreateImage(ImageType::CHECKERABLE);
EXPECT_TRUE(ShouldCheckerImage(checkerable_image2, WhichTree::PENDING_TREE));
// Activate the sync tree. The initial image should no longer be checkered on
// the active tree.
checker_image_tracker_->DidActivateSyncTree();
EXPECT_FALSE(ShouldCheckerImage(checkerable_image1, WhichTree::ACTIVE_TREE));
}
TEST_F(CheckerImageTrackerTest, CancelsScheduledDecodes) {
SetUpTracker(true);
DrawImage checkerable_image1 = CreateImage(ImageType::CHECKERABLE);
DrawImage checkerable_image2 = CreateImage(ImageType::CHECKERABLE);
std::vector<DrawImage> draw_images = {checkerable_image1, checkerable_image2};
CheckerImageTracker::ImageDecodeQueue image_decode_queue;
image_decode_queue =
BuildImageDecodeQueue(draw_images, WhichTree::PENDING_TREE);
EXPECT_EQ(image_decode_queue.size(), 2U);
checker_image_tracker_->ScheduleImageDecodeQueue(
std::move(image_decode_queue));
// Only the first image in the queue should have been decoded.
EXPECT_EQ(image_controller_.decodes_requested().size(), 1U);
EXPECT_EQ(image_controller_.decodes_requested().count(
checkerable_image1.paint_image().stable_id()),
1U);
// Rebuild the queue before the tracker is notified of decode completion,
// removing the second image and adding a new one.
DrawImage checkerable_image3 = CreateImage(ImageType::CHECKERABLE);
draw_images = {checkerable_image1, checkerable_image3};
image_decode_queue =
BuildImageDecodeQueue(draw_images, WhichTree::PENDING_TREE);
// The queue has 2 decodes because we are still checkering on the first one.
EXPECT_EQ(image_decode_queue.size(), 2U);
checker_image_tracker_->ScheduleImageDecodeQueue(
std::move(image_decode_queue));
// We still have only one decode because the tracker keeps only one decode
// pending at a time.
EXPECT_EQ(image_controller_.decodes_requested().size(), 1U);
EXPECT_EQ(image_controller_.decodes_requested().count(
checkerable_image1.paint_image().stable_id()),
1U);
// Trigger completion for all decodes. Only 2 images should have been decoded
// since the second image was cancelled.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(image_controller_.decodes_requested().size(), 2U);
EXPECT_EQ(image_controller_.decodes_requested().count(
checkerable_image3.paint_image().stable_id()),
1U);
EXPECT_EQ(image_controller_.num_of_locked_images(), 2);
}
TEST_F(CheckerImageTrackerTest, ClearsTracker) {
SetUpTracker(true);
DrawImage checkerable_image = CreateImage(ImageType::CHECKERABLE);
CheckerImageTracker::ImageDecodeQueue image_decode_queue =
BuildImageDecodeQueue({checkerable_image}, WhichTree::PENDING_TREE);
EXPECT_EQ(image_decode_queue.size(), 1U);
checker_image_tracker_->ScheduleImageDecodeQueue(
std::move(image_decode_queue));
base::RunLoop().RunUntilIdle();
checker_image_tracker_->TakeImagesToInvalidateOnSyncTree();
// The image is no longer checkered on the pending tree.
image_decode_queue =
BuildImageDecodeQueue({checkerable_image}, WhichTree::PENDING_TREE);
EXPECT_EQ(image_decode_queue.size(), 0U);
EXPECT_EQ(image_controller_.num_of_locked_images(), 1);
// Clear the tracker without clearing the async decode tracking. This should
// drop the decode but the image should not be checkered.
bool can_clear_decode_policy_tracking = false;
checker_image_tracker_->ClearTracker(can_clear_decode_policy_tracking);
EXPECT_EQ(image_controller_.num_of_locked_images(), 0);
image_decode_queue =
BuildImageDecodeQueue({checkerable_image}, WhichTree::PENDING_TREE);
EXPECT_EQ(image_decode_queue.size(), 0U);
checker_image_tracker_->DidActivateSyncTree();
// Now clear the decode tracking as well. The image will be re-checkered.
can_clear_decode_policy_tracking = true;
checker_image_tracker_->ClearTracker(can_clear_decode_policy_tracking);
image_decode_queue =
BuildImageDecodeQueue({checkerable_image}, WhichTree::PENDING_TREE);
EXPECT_EQ(image_decode_queue.size(), 1U);
// If an image had been decoded and tracker was cleared after it, we should
// continue checkering it.
DrawImage checkerable_image2 = CreateImage(ImageType::CHECKERABLE);
image_decode_queue =
BuildImageDecodeQueue({checkerable_image}, WhichTree::PENDING_TREE);
EXPECT_EQ(image_decode_queue.size(), 1U);
checker_image_tracker_->ScheduleImageDecodeQueue(
std::move(image_decode_queue));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(image_controller_.num_of_locked_images(), 1);
can_clear_decode_policy_tracking = false;
checker_image_tracker_->ClearTracker(can_clear_decode_policy_tracking);
EXPECT_EQ(image_controller_.num_of_locked_images(), 0);
image_decode_queue =
BuildImageDecodeQueue({checkerable_image}, WhichTree::PENDING_TREE);
EXPECT_EQ(image_decode_queue.size(), 1U);
}
TEST_F(CheckerImageTrackerTest, CheckersOnlyStaticCompletedImages) {
SetUpTracker(true);
DrawImage static_image = CreateImage(ImageType::CHECKERABLE);
DrawImage animated_image =
CreateImage(ImageType::CHECKERABLE, PaintImage::AnimationType::ANIMATED);
DrawImage partial_image =
CreateImage(ImageType::CHECKERABLE, PaintImage::AnimationType::STATIC,
PaintImage::CompletionState::PARTIALLY_DONE);
DrawImage video_image =
CreateImage(ImageType::CHECKERABLE, PaintImage::AnimationType::VIDEO);
std::vector<DrawImage> draw_images = {static_image, animated_image,
partial_image, video_image};
CheckerImageTracker::ImageDecodeQueue image_decode_queue =
BuildImageDecodeQueue(draw_images, WhichTree::PENDING_TREE);
EXPECT_EQ(image_decode_queue.size(), 1U);
EXPECT_EQ(image_decode_queue[0].paint_image, static_image.paint_image());
// Change the partial image to complete and try again. It should sstill not
// be checkered.
gfx::Size image_size = gfx::Size(partial_image.paint_image().width(),
partial_image.paint_image().height());
DrawImage completed_paint_image = DrawImage(
PaintImageBuilder::WithDefault()
.set_id(partial_image.paint_image().stable_id())
.set_paint_image_generator(CreatePaintImageGenerator(image_size))
.TakePaintImage(),
SkIRect::MakeWH(image_size.width(), image_size.height()),
kNone_SkFilterQuality, SkMatrix::I(), PaintImage::kDefaultFrameIndex);
EXPECT_FALSE(
ShouldCheckerImage(completed_paint_image, WhichTree::PENDING_TREE));
}
TEST_F(CheckerImageTrackerTest, DontCheckerDisallowedImages) {
SetUpTracker(true);
DrawImage image = CreateImage(ImageType::CHECKERABLE);
EXPECT_TRUE(ShouldCheckerImage(image, WhichTree::PENDING_TREE));
checker_image_tracker_->DisallowCheckeringForImage(image.paint_image());
// Since the tracker already saw the image, even disallowing it would still
// ensure that we checker it until it's completed.
EXPECT_TRUE(ShouldCheckerImage(image, WhichTree::PENDING_TREE));
// Reset the tracker.
checker_image_tracker_->ClearTracker(true);
// If we haven't seen the image and disallow it first, then it's not
// checkerable anymore.
checker_image_tracker_->DisallowCheckeringForImage(image.paint_image());
EXPECT_FALSE(ShouldCheckerImage(image, WhichTree::PENDING_TREE));
}
TEST_F(CheckerImageTrackerTest, ChoosesMaxScaleAndQuality) {
SetUpTracker(true);
DrawImage image = CreateImage(ImageType::CHECKERABLE);
DrawImage scaled_image1(image, 0.5f, PaintImage::kDefaultFrameIndex);
DrawImage scaled_image2 =
DrawImage(image.paint_image(), image.src_rect(), kHigh_SkFilterQuality,
SkMatrix::MakeScale(1.8f), PaintImage::kDefaultFrameIndex);
std::vector<DrawImage> draw_images = {scaled_image1, scaled_image2};
CheckerImageTracker::ImageDecodeQueue image_decode_queue =
BuildImageDecodeQueue(draw_images, WhichTree::PENDING_TREE);
checker_image_tracker_->ScheduleImageDecodeQueue(image_decode_queue);
EXPECT_EQ(image_controller_.decoded_images().size(), 1u);
EXPECT_EQ(image_controller_.decoded_images()[0].scale(),
SkSize::Make(1.8f, 1.8f));
EXPECT_EQ(image_controller_.decoded_images()[0].filter_quality(),
kHigh_SkFilterQuality);
}
TEST_F(CheckerImageTrackerTest, DontCheckerMultiPartImages) {
SetUpTracker(true);
DrawImage image = CreateImage(ImageType::CHECKERABLE);
EXPECT_FALSE(image.paint_image().is_multipart());
DrawImage multi_part_image =
CreateImage(ImageType::CHECKERABLE, PaintImage::AnimationType::STATIC,
PaintImage::CompletionState::DONE, true);
EXPECT_TRUE(multi_part_image.paint_image().is_multipart());
EXPECT_TRUE(ShouldCheckerImage(image, WhichTree::PENDING_TREE));
EXPECT_FALSE(ShouldCheckerImage(multi_part_image, WhichTree::PENDING_TREE));
}
TEST_F(CheckerImageTrackerTest, RespectsDecodePriority) {
SetUpTracker(true);
DrawImage image1 = CreateImage(ImageType::CHECKERABLE);
DrawImage image2 = CreateImage(ImageType::CHECKERABLE);
DrawImage image3 = CreateImage(ImageType::CHECKERABLE);
DrawImage image4 = CreateImage(ImageType::CHECKERABLE);
CheckerImageTracker::ImageDecodeQueue image_decode_queue =
BuildImageDecodeQueue({image1, image2, image3, image4},
WhichTree::PENDING_TREE);
// Mark the last 2 images as pre-decode.
EXPECT_EQ(image_decode_queue.size(), 4u);
image_decode_queue[2].type = CheckerImageTracker::DecodeType::kPreDecode;
image_decode_queue[3].type = CheckerImageTracker::DecodeType::kPreDecode;
// No decodes allowed. Nothing should be scheduled.
EXPECT_EQ(image_controller_.decoded_images().size(), 0u);
checker_image_tracker_->SetNoDecodesAllowed();
checker_image_tracker_->ScheduleImageDecodeQueue(image_decode_queue);
EXPECT_EQ(image_controller_.decoded_images().size(), 0u);
// Raster decodes allowed. Only those should be scheduled.
checker_image_tracker_->SetMaxDecodePriorityAllowed(
CheckerImageTracker::DecodeType::kRaster);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(image_controller_.decoded_images().size(), 2u);
EXPECT_EQ(image_controller_.decoded_images()[0], image1);
EXPECT_EQ(image_controller_.decoded_images()[1], image2);
// All decodes allowed. The complete queue should be flushed.
checker_image_tracker_->SetMaxDecodePriorityAllowed(
CheckerImageTracker::DecodeType::kPreDecode);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(image_controller_.decoded_images()[0], image1);
EXPECT_EQ(image_controller_.decoded_images()[1], image2);
EXPECT_EQ(image_controller_.decoded_images()[2], image3);
EXPECT_EQ(image_controller_.decoded_images()[3], image4);
}
TEST_F(CheckerImageTrackerTest, UseSrcRectForSize) {
SetUpTracker(true);
// Create an image with checkerable dimensions and subrect it. It should not
// be checkered.
DrawImage image = CreateImage(ImageType::CHECKERABLE);
image = DrawImage(image.paint_image(), SkIRect::MakeWH(200, 200),
image.filter_quality(), SkMatrix::I(),
PaintImage::kDefaultFrameIndex);
EXPECT_FALSE(ShouldCheckerImage(image, WhichTree::PENDING_TREE));
}
TEST_F(CheckerImageTrackerTest, DisableForSoftwareRaster) {
SetUpTracker(true);
// Should checker when not disabled.
checker_image_tracker_->set_force_disabled(false);
DrawImage image1 = CreateImage(ImageType::CHECKERABLE);
EXPECT_TRUE(ShouldCheckerImage(image1, WhichTree::PENDING_TREE));
// Toggle disable. If we were already checkering this image, we need to
// continue it.
checker_image_tracker_->set_force_disabled(true);
EXPECT_TRUE(ShouldCheckerImage(image1, WhichTree::PENDING_TREE));
// New image should not be checkered while disabled.
DrawImage image2 = CreateImage(ImageType::CHECKERABLE);
EXPECT_FALSE(ShouldCheckerImage(image2, WhichTree::PENDING_TREE));
}
TEST_F(CheckerImageTrackerTest, DecodingModeHints) {
SetUpTracker(true);
base::flat_map<PaintImage::Id, PaintImage::DecodingMode> hints = {
{1, PaintImage::DecodingMode::kUnspecified},
{2, PaintImage::DecodingMode::kSync},
{3, PaintImage::DecodingMode::kAsync}};
checker_image_tracker_->UpdateImageDecodingHints(hints);
EXPECT_EQ(PaintImage::DecodingMode::kUnspecified,
checker_image_tracker_->get_decoding_mode_hint_for_testing(1));
EXPECT_EQ(PaintImage::DecodingMode::kSync,
checker_image_tracker_->get_decoding_mode_hint_for_testing(2));
EXPECT_EQ(PaintImage::DecodingMode::kAsync,
checker_image_tracker_->get_decoding_mode_hint_for_testing(3));
hints = {{1, PaintImage::DecodingMode::kAsync},
{2, PaintImage::DecodingMode::kAsync},
{3, PaintImage::DecodingMode::kAsync}};
checker_image_tracker_->UpdateImageDecodingHints(hints);
// The more conservative state should persist.
EXPECT_EQ(PaintImage::DecodingMode::kUnspecified,
checker_image_tracker_->get_decoding_mode_hint_for_testing(1));
EXPECT_EQ(PaintImage::DecodingMode::kSync,
checker_image_tracker_->get_decoding_mode_hint_for_testing(2));
EXPECT_EQ(PaintImage::DecodingMode::kAsync,
checker_image_tracker_->get_decoding_mode_hint_for_testing(3));
hints = {{1, PaintImage::DecodingMode::kUnspecified},
{2, PaintImage::DecodingMode::kUnspecified},
{3, PaintImage::DecodingMode::kUnspecified}};
checker_image_tracker_->UpdateImageDecodingHints(hints);
EXPECT_EQ(PaintImage::DecodingMode::kUnspecified,
checker_image_tracker_->get_decoding_mode_hint_for_testing(1));
EXPECT_EQ(PaintImage::DecodingMode::kSync,
checker_image_tracker_->get_decoding_mode_hint_for_testing(2));
EXPECT_EQ(PaintImage::DecodingMode::kUnspecified,
checker_image_tracker_->get_decoding_mode_hint_for_testing(3));
hints = {{1, PaintImage::DecodingMode::kSync}};
checker_image_tracker_->UpdateImageDecodingHints(hints);
EXPECT_EQ(PaintImage::DecodingMode::kSync,
checker_image_tracker_->get_decoding_mode_hint_for_testing(1));
EXPECT_EQ(PaintImage::DecodingMode::kSync,
checker_image_tracker_->get_decoding_mode_hint_for_testing(2));
EXPECT_EQ(PaintImage::DecodingMode::kUnspecified,
checker_image_tracker_->get_decoding_mode_hint_for_testing(3));
}
TEST_F(CheckerImageTrackerTest, UsageHintsMakeImagesSync) {
SetUpTracker(true);
DrawImage image = CreateImage(ImageType::CHECKERABLE);
EXPECT_TRUE(ShouldCheckerImage(image, WhichTree::PENDING_TREE));
base::flat_map<PaintImage::Id, PaintImage::DecodingMode> hints = {
{image.paint_image().stable_id(), PaintImage::DecodingMode::kSync}};
checker_image_tracker_->UpdateImageDecodingHints(hints);
auto invalidated = checker_image_tracker_->TakeImagesToInvalidateOnSyncTree();
ASSERT_EQ(invalidated.size(), 1u);
EXPECT_EQ(*invalidated.begin(), image.paint_image().stable_id());
EXPECT_FALSE(ShouldCheckerImage(image, WhichTree::PENDING_TREE));
// We should still continue checkering on the active tree, since we want
// atomic updates from the pending tree.
EXPECT_TRUE(ShouldCheckerImage(image, WhichTree::ACTIVE_TREE));
}
} // namespace
} // namespace cc