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 { ...@@ -32,6 +32,7 @@ class MockImageDecodeCache : public ImageDecodeCache {
MOCK_METHOD0(ClearCache, void()); MOCK_METHOD0(ClearCache, void());
MOCK_METHOD2(GetOutOfRasterDecodeTaskForImageAndRef, MOCK_METHOD2(GetOutOfRasterDecodeTaskForImageAndRef,
bool(const DrawImage& image, scoped_refptr<TileTask>* task)); bool(const DrawImage& image, scoped_refptr<TileTask>* task));
MOCK_CONST_METHOD0(GetMaximumMemoryLimitBytes, size_t());
}; };
TEST(ImageHijackCanvasTest, NonLazyImagesSkipped) { TEST(ImageHijackCanvasTest, NonLazyImagesSkipped) {
......
...@@ -120,7 +120,9 @@ bool CheckerImageTracker::ShouldCheckerImage(const sk_sp<const SkImage>& image, ...@@ -120,7 +120,9 @@ bool CheckerImageTracker::ShouldCheckerImage(const sk_sp<const SkImage>& image,
std::pair<ImageId, DecodePolicy>(image_id, DecodePolicy::ASYNC)); std::pair<ImageId, DecodePolicy>(image_id, DecodePolicy::ASYNC));
auto it = insert_result.first; auto it = insert_result.first;
if (insert_result.second) { 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::ASYNC
: DecodePolicy::SYNC_PERMANENT; : DecodePolicy::SYNC_PERMANENT;
} }
......
...@@ -15,8 +15,13 @@ ...@@ -15,8 +15,13 @@
namespace cc { namespace cc {
namespace { namespace {
// 5MB max image cache size.
const size_t kMaxImageCacheSizeBytes = 5 * 1024 * 1024;
const int kCheckerableImageDimension = 512; 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 { class TestImageController : public ImageController {
public: public:
...@@ -24,7 +29,9 @@ class TestImageController : public ImageController { ...@@ -24,7 +29,9 @@ class TestImageController : public ImageController {
// the ImageController is over-ridden here. // the ImageController is over-ridden here.
TestImageController() TestImageController()
: ImageController(base::ThreadTaskRunnerHandle::Get().get(), : ImageController(base::ThreadTaskRunnerHandle::Get().get(),
base::ThreadTaskRunnerHandle::Get()) {} base::ThreadTaskRunnerHandle::Get()) {
SetMaxImageCacheLimitBytesForTesting(kMaxImageCacheSizeBytes);
}
~TestImageController() override { DCHECK_EQ(locked_images_.size(), 0U); } ~TestImageController() override { DCHECK_EQ(locked_images_.size(), 0U); }
...@@ -65,7 +72,11 @@ class TestImageController : public ImageController { ...@@ -65,7 +72,11 @@ class TestImageController : public ImageController {
class CheckerImageTrackerTest : public testing::Test, class CheckerImageTrackerTest : public testing::Test,
public CheckerImageTrackerClient { public CheckerImageTrackerClient {
public: public:
enum class ImageType { CHECKERABLE, NON_CHECKERABLE }; enum class ImageType {
CHECKERABLE,
SMALL_NON_CHECKERABLE,
LARGE_NON_CHECKERABLE
};
void SetUpTracker(bool checker_images_enabled) { void SetUpTracker(bool checker_images_enabled) {
checker_image_tracker_ = base::MakeUnique<CheckerImageTracker>( checker_image_tracker_ = base::MakeUnique<CheckerImageTracker>(
...@@ -75,9 +86,19 @@ class CheckerImageTrackerTest : public testing::Test, ...@@ -75,9 +86,19 @@ class CheckerImageTrackerTest : public testing::Test,
void TearDown() override { checker_image_tracker_.reset(); } void TearDown() override { checker_image_tracker_.reset(); }
DrawImage CreateImage(ImageType image_type) { DrawImage CreateImage(ImageType image_type) {
int dimension = image_type == ImageType::CHECKERABLE int dimension = 0;
? kCheckerableImageDimension switch (image_type) {
: kNonCheckerableImageDimension; 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 = sk_sp<SkImage> image =
CreateDiscardableImage(gfx::Size(dimension, dimension)); CreateDiscardableImage(gfx::Size(dimension, dimension));
gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateSRGB(); gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateSRGB();
...@@ -126,20 +147,22 @@ TEST_F(CheckerImageTrackerTest, UpdatesImagesAtomically) { ...@@ -126,20 +147,22 @@ TEST_F(CheckerImageTrackerTest, UpdatesImagesAtomically) {
SetUpTracker(true); SetUpTracker(true);
DrawImage checkerable_image = CreateImage(ImageType::CHECKERABLE); DrawImage checkerable_image = CreateImage(ImageType::CHECKERABLE);
DrawImage non_checkerable_image = CreateImage(ImageType::NON_CHECKERABLE); DrawImage small_non_checkerable_image =
std::vector<DrawImage> draw_images; CreateImage(ImageType::SMALL_NON_CHECKERABLE);
DrawImage large_non_checkerable_image =
CreateImage(ImageType::LARGE_NON_CHECKERABLE);
CheckerImageTracker::ImageDecodeQueue image_decode_queue; CheckerImageTracker::ImageDecodeQueue image_decode_queue;
// First request to filter images. // First request to filter images.
draw_images.push_back(checkerable_image); std::vector<DrawImage> draw_images = {
draw_images.push_back(non_checkerable_image); checkerable_image, small_non_checkerable_image,
draw_images.push_back(checkerable_image); large_non_checkerable_image, checkerable_image};
image_decode_queue = image_decode_queue =
BuildImageDecodeQueue(draw_images, WhichTree::PENDING_TREE); BuildImageDecodeQueue(draw_images, WhichTree::PENDING_TREE);
EXPECT_EQ(image_decode_queue.size(), 2U); ASSERT_EQ(2u, image_decode_queue.size());
EXPECT_EQ(image_decode_queue[0], checkerable_image.image()); EXPECT_EQ(checkerable_image.image(), image_decode_queue[0]);
EXPECT_EQ(image_decode_queue[1], checkerable_image.image()); EXPECT_EQ(checkerable_image.image(), image_decode_queue[1]);
checker_image_tracker_->ScheduleImageDecodeQueue(image_decode_queue); checker_image_tracker_->ScheduleImageDecodeQueue(image_decode_queue);
EXPECT_EQ(image_controller_.num_of_locked_images(), 1); EXPECT_EQ(image_controller_.num_of_locked_images(), 1);
...@@ -168,14 +191,18 @@ TEST_F(CheckerImageTrackerTest, UpdatesImagesAtomically) { ...@@ -168,14 +191,18 @@ TEST_F(CheckerImageTrackerTest, UpdatesImagesAtomically) {
EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage(
checkerable_image.image(), WhichTree::PENDING_TREE)); checkerable_image.image(), WhichTree::PENDING_TREE));
EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( 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 // Use this set to make the same request from the active tree, we should
// continue checkering this image on the active tree until activation. // continue checkering this image on the active tree until activation.
EXPECT_TRUE(checker_image_tracker_->ShouldCheckerImage( EXPECT_TRUE(checker_image_tracker_->ShouldCheckerImage(
checkerable_image.image(), WhichTree::ACTIVE_TREE)); checkerable_image.image(), WhichTree::ACTIVE_TREE));
EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( 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. // Activate the sync tree. The images should be unlocked upon activation.
EXPECT_EQ(image_controller_.num_of_locked_images(), 1); EXPECT_EQ(image_controller_.num_of_locked_images(), 1);
...@@ -188,11 +215,8 @@ TEST_F(CheckerImageTrackerTest, NoConsecutiveCheckeringForImage) { ...@@ -188,11 +215,8 @@ TEST_F(CheckerImageTrackerTest, NoConsecutiveCheckeringForImage) {
SetUpTracker(true); SetUpTracker(true);
DrawImage checkerable_image = CreateImage(ImageType::CHECKERABLE); DrawImage checkerable_image = CreateImage(ImageType::CHECKERABLE);
DrawImage non_checkerable_image = CreateImage(ImageType::NON_CHECKERABLE); std::vector<DrawImage> draw_images = {checkerable_image};
std::vector<DrawImage> draw_images;
draw_images.clear();
draw_images.push_back(checkerable_image);
CheckerImageTracker::ImageDecodeQueue image_decode_queue = CheckerImageTracker::ImageDecodeQueue image_decode_queue =
BuildImageDecodeQueue(draw_images, WhichTree::PENDING_TREE); BuildImageDecodeQueue(draw_images, WhichTree::PENDING_TREE);
EXPECT_EQ(image_decode_queue.size(), 1U); EXPECT_EQ(image_decode_queue.size(), 1U);
......
...@@ -616,6 +616,10 @@ void GpuImageDecodeCache::ClearCache() { ...@@ -616,6 +616,10 @@ void GpuImageDecodeCache::ClearCache() {
} }
} }
size_t GpuImageDecodeCache::GetMaximumMemoryLimitBytes() const {
return normal_max_cache_bytes_;
}
bool GpuImageDecodeCache::OnMemoryDump( bool GpuImageDecodeCache::OnMemoryDump(
const base::trace_event::MemoryDumpArgs& args, const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) { base::trace_event::ProcessMemoryDump* pmd) {
...@@ -1102,7 +1106,7 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image, ...@@ -1102,7 +1106,7 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image,
// scale. // scale.
SkPixmap image_pixmap(image_info.makeColorSpace(nullptr), SkPixmap image_pixmap(image_info.makeColorSpace(nullptr),
backing_memory->data(), image_info.minRowBytes()); 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. // no need to special case that as an optimization.
if (!draw_image.image()->scalePixels( if (!draw_image.image()->scalePixels(
image_pixmap, CalculateUploadScaleFilterQuality(draw_image), image_pixmap, CalculateUploadScaleFilterQuality(draw_image),
......
...@@ -125,6 +125,7 @@ class CC_EXPORT GpuImageDecodeCache ...@@ -125,6 +125,7 @@ class CC_EXPORT GpuImageDecodeCache
void SetShouldAggressivelyFreeResources( void SetShouldAggressivelyFreeResources(
bool aggressively_free_resources) override; bool aggressively_free_resources) override;
void ClearCache() override; void ClearCache() override;
size_t GetMaximumMemoryLimitBytes() const override;
// MemoryDumpProvider overrides. // MemoryDumpProvider overrides.
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
...@@ -347,6 +348,8 @@ class CC_EXPORT GpuImageDecodeCache ...@@ -347,6 +348,8 @@ class CC_EXPORT GpuImageDecodeCache
sk_sp<GrContextThreadSafeProxy> context_threadsafe_proxy_; sk_sp<GrContextThreadSafeProxy> context_threadsafe_proxy_;
// All members below this point must only be accessed while holding |lock_|. // 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_; base::Lock lock_;
// |persistent_cache_| represents the long-lived cache, keeping a certain // |persistent_cache_| represents the long-lived cache, keeping a certain
......
...@@ -127,12 +127,15 @@ void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) { ...@@ -127,12 +127,15 @@ void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) {
SetPredecodeImages(std::vector<DrawImage>(), SetPredecodeImages(std::vector<DrawImage>(),
ImageDecodeCache::TracingInfo()); ImageDecodeCache::TracingInfo());
StopWorkerTasks(); StopWorkerTasks();
image_cache_max_limit_bytes_ = 0u;
} }
cache_ = cache; cache_ = cache;
if (cache_) if (cache_) {
image_cache_max_limit_bytes_ = cache_->GetMaximumMemoryLimitBytes();
GenerateTasksForOrphanedRequests(); GenerateTasksForOrphanedRequests();
}
} }
void ImageController::GetTasksForImagesAndRef( void ImageController::GetTasksForImagesAndRef(
......
...@@ -56,6 +56,13 @@ class CC_EXPORT ImageController { ...@@ -56,6 +56,13 @@ class CC_EXPORT ImageController {
virtual ImageDecodeRequestId QueueImageDecode( virtual ImageDecodeRequestId QueueImageDecode(
sk_sp<const SkImage> image, sk_sp<const SkImage> image,
const ImageDecodedCallback& callback); 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: protected:
scoped_refptr<base::SequencedTaskRunner> worker_task_runner_; scoped_refptr<base::SequencedTaskRunner> worker_task_runner_;
...@@ -97,6 +104,7 @@ class CC_EXPORT ImageController { ...@@ -97,6 +104,7 @@ class CC_EXPORT ImageController {
base::flat_map<ImageDecodeRequestId, DrawImage> requested_locked_images_; base::flat_map<ImageDecodeRequestId, DrawImage> requested_locked_images_;
base::SequencedTaskRunner* origin_task_runner_ = nullptr; 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 // The variables defined below this lock (aside from weak_ptr_factory_) can
// only be accessed when the lock is acquired. // only be accessed when the lock is acquired.
......
...@@ -129,6 +129,9 @@ class TestableCache : public ImageDecodeCache { ...@@ -129,6 +129,9 @@ class TestableCache : public ImageDecodeCache {
void SetShouldAggressivelyFreeResources( void SetShouldAggressivelyFreeResources(
bool aggressively_free_resources) override {} bool aggressively_free_resources) override {}
void ClearCache() override {} void ClearCache() override {}
size_t GetMaximumMemoryLimitBytes() const override {
return 256 * 1024 * 1024;
}
int number_of_refs() const { return number_of_refs_; } int number_of_refs() const { return number_of_refs_; }
void SetTaskToUse(scoped_refptr<TileTask> task) { task_to_use_ = task; } void SetTaskToUse(scoped_refptr<TileTask> task) { task_to_use_ = task; }
......
...@@ -96,6 +96,12 @@ class CC_EXPORT ImageDecodeCache { ...@@ -96,6 +96,12 @@ class CC_EXPORT ImageDecodeCache {
// Clears all elements from the cache. // Clears all elements from the cache.
virtual void ClearCache() = 0; 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 } // namespace cc
......
...@@ -839,6 +839,10 @@ void SoftwareImageDecodeCache::ClearCache() { ...@@ -839,6 +839,10 @@ void SoftwareImageDecodeCache::ClearCache() {
ReduceCacheUsageUntilWithinLimit(0); ReduceCacheUsageUntilWithinLimit(0);
} }
size_t SoftwareImageDecodeCache::GetMaximumMemoryLimitBytes() const {
return locked_images_budget_.total_limit_bytes();
}
void SoftwareImageDecodeCache::RemovePendingTask(const ImageKey& key, void SoftwareImageDecodeCache::RemovePendingTask(const ImageKey& key,
DecodeTaskType task_type) { DecodeTaskType task_type) {
base::AutoLock lock(lock_); base::AutoLock lock(lock_);
......
...@@ -140,6 +140,7 @@ class CC_EXPORT SoftwareImageDecodeCache ...@@ -140,6 +140,7 @@ class CC_EXPORT SoftwareImageDecodeCache
void SetShouldAggressivelyFreeResources( void SetShouldAggressivelyFreeResources(
bool aggressively_free_resources) override {} bool aggressively_free_resources) override {}
void ClearCache() 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 // Decode the given image and store it in the cache. This is only called by an
// image decode task from a worker thread. // image decode task from a worker thread.
...@@ -220,7 +221,7 @@ class CC_EXPORT SoftwareImageDecodeCache ...@@ -220,7 +221,7 @@ class CC_EXPORT SoftwareImageDecodeCache
size_t GetCurrentUsageSafe() const; size_t GetCurrentUsageSafe() const;
private: private:
size_t limit_bytes_; const size_t limit_bytes_;
base::CheckedNumeric<size_t> current_usage_bytes_; base::CheckedNumeric<size_t> current_usage_bytes_;
}; };
...@@ -306,6 +307,8 @@ class CC_EXPORT SoftwareImageDecodeCache ...@@ -306,6 +307,8 @@ class CC_EXPORT SoftwareImageDecodeCache
// The members below this comment can only be accessed if the lock is held to // 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. // 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_; base::Lock lock_;
// Decoded images and ref counts (predecode path). // 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