Commit 13ee9736 authored by vmpstr's avatar vmpstr Committed by Commit bot

cc: Make images that can't possibly fit in cache not checker.

This patch ensures that we don't even try to checker images that are
too big to fit into our cache.

R=khushalsagar@chromium.org
CQ_INCLUDE_TRYBOTS=master.tryserver.blink:linux_trusty_blink_rel

Review-Url: https://codereview.chromium.org/2844273004
Cr-Commit-Position: refs/heads/master@{#468186}
parent fc4b4d8f
......@@ -32,6 +32,7 @@ class MockImageDecodeCache : public ImageDecodeCache {
MOCK_METHOD0(ClearCache, void());
MOCK_METHOD2(GetOutOfRasterDecodeTaskForImageAndRef,
bool(const DrawImage& image, scoped_refptr<TileTask>* task));
MOCK_CONST_METHOD0(GetMaximumMemoryLimitBytes, size_t());
};
TEST(ImageHijackCanvasTest, NonLazyImagesSkipped) {
......
......@@ -120,7 +120,9 @@ bool CheckerImageTracker::ShouldCheckerImage(const sk_sp<const SkImage>& image,
std::pair<ImageId, DecodePolicy>(image_id, DecodePolicy::ASYNC));
auto it = insert_result.first;
if (insert_result.second) {
it->second = SafeSizeOfImage(image.get()) >= kMinImageSizeToCheckerBytes
size_t size = SafeSizeOfImage(image.get());
it->second = (size >= kMinImageSizeToCheckerBytes &&
size <= image_controller_->image_cache_max_limit_bytes())
? DecodePolicy::ASYNC
: DecodePolicy::SYNC_PERMANENT;
}
......
......@@ -15,8 +15,13 @@
namespace cc {
namespace {
// 5MB max image cache size.
const size_t kMaxImageCacheSizeBytes = 5 * 1024 * 1024;
const int kCheckerableImageDimension = 512;
const int kNonCheckerableImageDimension = 16;
// This size will result in an image just over kMaxImageCacheSizeBytes.
const int kLargeNonCheckerableImageDimension = 1145;
const int kSmallNonCheckerableImageDimension = 16;
class TestImageController : public ImageController {
public:
......@@ -24,7 +29,9 @@ class TestImageController : public ImageController {
// the ImageController is over-ridden here.
TestImageController()
: ImageController(base::ThreadTaskRunnerHandle::Get().get(),
base::ThreadTaskRunnerHandle::Get()) {}
base::ThreadTaskRunnerHandle::Get()) {
SetMaxImageCacheLimitBytesForTesting(kMaxImageCacheSizeBytes);
}
~TestImageController() override { DCHECK_EQ(locked_images_.size(), 0U); }
......@@ -65,7 +72,11 @@ class TestImageController : public ImageController {
class CheckerImageTrackerTest : public testing::Test,
public CheckerImageTrackerClient {
public:
enum class ImageType { CHECKERABLE, NON_CHECKERABLE };
enum class ImageType {
CHECKERABLE,
SMALL_NON_CHECKERABLE,
LARGE_NON_CHECKERABLE
};
void SetUpTracker(bool checker_images_enabled) {
checker_image_tracker_ = base::MakeUnique<CheckerImageTracker>(
......@@ -75,9 +86,19 @@ class CheckerImageTrackerTest : public testing::Test,
void TearDown() override { checker_image_tracker_.reset(); }
DrawImage CreateImage(ImageType image_type) {
int dimension = image_type == ImageType::CHECKERABLE
? kCheckerableImageDimension
: kNonCheckerableImageDimension;
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;
}
sk_sp<SkImage> image =
CreateDiscardableImage(gfx::Size(dimension, dimension));
gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateSRGB();
......@@ -126,20 +147,22 @@ TEST_F(CheckerImageTrackerTest, UpdatesImagesAtomically) {
SetUpTracker(true);
DrawImage checkerable_image = CreateImage(ImageType::CHECKERABLE);
DrawImage non_checkerable_image = CreateImage(ImageType::NON_CHECKERABLE);
std::vector<DrawImage> draw_images;
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.
draw_images.push_back(checkerable_image);
draw_images.push_back(non_checkerable_image);
draw_images.push_back(checkerable_image);
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);
EXPECT_EQ(image_decode_queue.size(), 2U);
EXPECT_EQ(image_decode_queue[0], checkerable_image.image());
EXPECT_EQ(image_decode_queue[1], checkerable_image.image());
ASSERT_EQ(2u, image_decode_queue.size());
EXPECT_EQ(checkerable_image.image(), image_decode_queue[0]);
EXPECT_EQ(checkerable_image.image(), image_decode_queue[1]);
checker_image_tracker_->ScheduleImageDecodeQueue(image_decode_queue);
EXPECT_EQ(image_controller_.num_of_locked_images(), 1);
......@@ -168,14 +191,18 @@ TEST_F(CheckerImageTrackerTest, UpdatesImagesAtomically) {
EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage(
checkerable_image.image(), WhichTree::PENDING_TREE));
EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage(
non_checkerable_image.image(), WhichTree::PENDING_TREE));
small_non_checkerable_image.image(), WhichTree::PENDING_TREE));
EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage(
large_non_checkerable_image.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(checker_image_tracker_->ShouldCheckerImage(
checkerable_image.image(), WhichTree::ACTIVE_TREE));
EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage(
non_checkerable_image.image(), WhichTree::ACTIVE_TREE));
small_non_checkerable_image.image(), WhichTree::ACTIVE_TREE));
EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage(
large_non_checkerable_image.image(), WhichTree::ACTIVE_TREE));
// Activate the sync tree. The images should be unlocked upon activation.
EXPECT_EQ(image_controller_.num_of_locked_images(), 1);
......@@ -188,11 +215,8 @@ TEST_F(CheckerImageTrackerTest, NoConsecutiveCheckeringForImage) {
SetUpTracker(true);
DrawImage checkerable_image = CreateImage(ImageType::CHECKERABLE);
DrawImage non_checkerable_image = CreateImage(ImageType::NON_CHECKERABLE);
std::vector<DrawImage> draw_images;
std::vector<DrawImage> draw_images = {checkerable_image};
draw_images.clear();
draw_images.push_back(checkerable_image);
CheckerImageTracker::ImageDecodeQueue image_decode_queue =
BuildImageDecodeQueue(draw_images, WhichTree::PENDING_TREE);
EXPECT_EQ(image_decode_queue.size(), 1U);
......
......@@ -616,6 +616,10 @@ void GpuImageDecodeCache::ClearCache() {
}
}
size_t GpuImageDecodeCache::GetMaximumMemoryLimitBytes() const {
return normal_max_cache_bytes_;
}
bool GpuImageDecodeCache::OnMemoryDump(
const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) {
......@@ -1102,7 +1106,7 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image,
// scale.
SkPixmap image_pixmap(image_info.makeColorSpace(nullptr),
backing_memory->data(), image_info.minRowBytes());
// Note that scalePixels falls back to readPixels if the sale is 1x, so
// Note that scalePixels falls back to readPixels if the scale is 1x, so
// no need to special case that as an optimization.
if (!draw_image.image()->scalePixels(
image_pixmap, CalculateUploadScaleFilterQuality(draw_image),
......
......@@ -125,6 +125,7 @@ class CC_EXPORT GpuImageDecodeCache
void SetShouldAggressivelyFreeResources(
bool aggressively_free_resources) override;
void ClearCache() override;
size_t GetMaximumMemoryLimitBytes() const override;
// MemoryDumpProvider overrides.
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
......@@ -347,6 +348,8 @@ class CC_EXPORT GpuImageDecodeCache
sk_sp<GrContextThreadSafeProxy> context_threadsafe_proxy_;
// All members below this point must only be accessed while holding |lock_|.
// The exception are const members like |normal_max_cache_bytes_| that can
// be accessed without a lock since they are thread safe.
base::Lock lock_;
// |persistent_cache_| represents the long-lived cache, keeping a certain
......
......@@ -127,12 +127,15 @@ void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) {
SetPredecodeImages(std::vector<DrawImage>(),
ImageDecodeCache::TracingInfo());
StopWorkerTasks();
image_cache_max_limit_bytes_ = 0u;
}
cache_ = cache;
if (cache_)
if (cache_) {
image_cache_max_limit_bytes_ = cache_->GetMaximumMemoryLimitBytes();
GenerateTasksForOrphanedRequests();
}
}
void ImageController::GetTasksForImagesAndRef(
......
......@@ -56,6 +56,13 @@ class CC_EXPORT ImageController {
virtual ImageDecodeRequestId QueueImageDecode(
sk_sp<const SkImage> image,
const ImageDecodedCallback& callback);
size_t image_cache_max_limit_bytes() const {
return image_cache_max_limit_bytes_;
}
void SetMaxImageCacheLimitBytesForTesting(size_t bytes) {
image_cache_max_limit_bytes_ = bytes;
}
protected:
scoped_refptr<base::SequencedTaskRunner> worker_task_runner_;
......@@ -97,6 +104,7 @@ class CC_EXPORT ImageController {
base::flat_map<ImageDecodeRequestId, DrawImage> requested_locked_images_;
base::SequencedTaskRunner* origin_task_runner_ = nullptr;
size_t image_cache_max_limit_bytes_ = 0u;
// The variables defined below this lock (aside from weak_ptr_factory_) can
// only be accessed when the lock is acquired.
......
......@@ -129,6 +129,9 @@ class TestableCache : public ImageDecodeCache {
void SetShouldAggressivelyFreeResources(
bool aggressively_free_resources) override {}
void ClearCache() override {}
size_t GetMaximumMemoryLimitBytes() const override {
return 256 * 1024 * 1024;
}
int number_of_refs() const { return number_of_refs_; }
void SetTaskToUse(scoped_refptr<TileTask> task) { task_to_use_ = task; }
......
......@@ -96,6 +96,12 @@ class CC_EXPORT ImageDecodeCache {
// Clears all elements from the cache.
virtual void ClearCache() = 0;
// Returns the maximum amount of memory we would be able to lock. This ignores
// any temporary states, such as throttled, and return the maximum possible
// memory. It is used as an esimate of whether an image can fit into the
// locked budget before creating a task.
virtual size_t GetMaximumMemoryLimitBytes() const = 0;
};
} // namespace cc
......
......@@ -839,6 +839,10 @@ void SoftwareImageDecodeCache::ClearCache() {
ReduceCacheUsageUntilWithinLimit(0);
}
size_t SoftwareImageDecodeCache::GetMaximumMemoryLimitBytes() const {
return locked_images_budget_.total_limit_bytes();
}
void SoftwareImageDecodeCache::RemovePendingTask(const ImageKey& key,
DecodeTaskType task_type) {
base::AutoLock lock(lock_);
......
......@@ -140,6 +140,7 @@ class CC_EXPORT SoftwareImageDecodeCache
void SetShouldAggressivelyFreeResources(
bool aggressively_free_resources) override {}
void ClearCache() override;
size_t GetMaximumMemoryLimitBytes() const override;
// Decode the given image and store it in the cache. This is only called by an
// image decode task from a worker thread.
......@@ -220,7 +221,7 @@ class CC_EXPORT SoftwareImageDecodeCache
size_t GetCurrentUsageSafe() const;
private:
size_t limit_bytes_;
const size_t limit_bytes_;
base::CheckedNumeric<size_t> current_usage_bytes_;
};
......@@ -306,6 +307,8 @@ class CC_EXPORT SoftwareImageDecodeCache
// The members below this comment can only be accessed if the lock is held to
// ensure that they are safe to access on multiple threads.
// The exception is accessing |locked_images_budget_.total_limit_bytes()|,
// which is const and thread safe.
base::Lock lock_;
// Decoded images and ref counts (predecode path).
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment