diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-07-17 13:57:45 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-07-19 13:44:40 +0000 |
commit | 6ec7b8da05d21a3878bd21c691b41e675d74bb1c (patch) | |
tree | b87f250bc19413750b9bb9cdbf2da20ef5014820 /chromium/cc/tiles | |
parent | ec02ee4181c49b61fce1c8fb99292dbb8139cc90 (diff) | |
download | qtwebengine-chromium-6ec7b8da05d21a3878bd21c691b41e675d74bb1c.tar.gz |
BASELINE: Update Chromium to 60.0.3112.70
Change-Id: I9911c2280a014d4632f254857876a395d4baed2d
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/cc/tiles')
28 files changed, 1864 insertions, 1022 deletions
diff --git a/chromium/cc/tiles/checker_image_tracker.cc b/chromium/cc/tiles/checker_image_tracker.cc index 8bbcbebe839..022269d87a9 100644 --- a/chromium/cc/tiles/checker_image_tracker.cc +++ b/chromium/cc/tiles/checker_image_tracker.cc @@ -5,6 +5,7 @@ #include "cc/tiles/checker_image_tracker.h" #include "base/bind.h" +#include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "base/trace_event/trace_event.h" @@ -30,35 +31,23 @@ CheckerImageTracker::CheckerImageTracker(ImageController* image_controller, 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); -} +CheckerImageTracker::~CheckerImageTracker() = default; -void CheckerImageTracker::FilterImagesForCheckeringForTile( - std::vector<DrawImage>* images, - ImageIdFlatSet* checkered_images, - WhichTree tree) { - TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), - "CheckerImageTracker::FilterImagesForCheckeringForTile", "tree", - tree); - DCHECK(checkered_images->empty()); - - base::EraseIf(*images, - [this, tree, &checkered_images](const DrawImage& draw_image) { - const sk_sp<const SkImage>& image = draw_image.image(); - DCHECK(image->isLazyGenerated()); - if (ShouldCheckerImage(image, tree)) { - ScheduleImageDecodeIfNecessary(image); - checkered_images->insert(image->uniqueID()); - return true; - } - return false; - }); +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() { +const PaintImageIdFlatSet& +CheckerImageTracker::TakeImagesToInvalidateOnSyncTree() { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "CheckerImageTracker::TakeImagesToInvalidateOnSyncTree"); DCHECK_EQ(invalidated_images_on_current_sync_tree_.size(), 0u) @@ -72,17 +61,45 @@ const ImageIdFlatSet& CheckerImageTracker::TakeImagesToInvalidateOnSyncTree() { 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); - } - + for (auto image_id : invalidated_images_on_current_sync_tree_) + image_id_to_decode_.erase(image_id); invalidated_images_on_current_sync_tree_.clear(); } +void CheckerImageTracker::ClearTracker(bool can_clear_decode_policy_tracking) { + // Unlock all images and tracking for images pending invalidation. The + // |images_invalidated_on_current_sync_tree_| will be cleared when the sync + // tree is activated. + // + // Note that we assume that any images with DecodePolicy::ASYNC, which may be + // checkered, are safe to stop tracking here and will either be re-checkered + // and invalidated when the decode completes or be invalidated externally. + // This is because the policy decision for checkering an image is based on + // inputs received from a PaintImage in the DisplayItemList. The policy chosen + // for a PaintImage should remain unchanged. + // If the external inputs for deciding the decode policy for an image change, + // they should be accompanied with an invalidation during paint. + image_id_to_decode_.clear(); + + if (can_clear_decode_policy_tracking) { + image_async_decode_state_.clear(); + } else { + // If we can't clear the decode policy, we need to make sure we still + // re-decode and checker images that were pending invalidation. + for (auto image_id : images_pending_invalidation_) { + auto it = image_async_decode_state_.find(image_id); + + DCHECK(it != image_async_decode_state_.end()); + DCHECK_EQ(it->second, DecodePolicy::SYNC_DECODED_ONCE); + + it->second = DecodePolicy::ASYNC; + } + } + images_pending_invalidation_.clear(); +} + void CheckerImageTracker::DidFinishImageDecode( - ImageId image_id, + PaintImage::Id image_id, ImageController::ImageDecodeRequestId request_id, ImageController::ImageDecodeResult result) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), @@ -90,75 +107,114 @@ void CheckerImageTracker::DidFinishImageDecode( TRACE_EVENT_ASYNC_END0("cc", "CheckerImageTracker::DeferImageDecode", image_id); - DCHECK_NE(result, ImageController::ImageDecodeResult::DECODE_NOT_REQUIRED); - DCHECK_NE(pending_image_decodes_.count(image_id), 0u); - pending_image_decodes_.erase(image_id); + DCHECK_NE(ImageController::ImageDecodeResult::DECODE_NOT_REQUIRED, result); + DCHECK_EQ(outstanding_image_decode_.value().stable_id(), image_id); + outstanding_image_decode_.reset(); + + // The async decode state may have been cleared if the tracker was cleared + // before this decode could be finished. + auto it = image_async_decode_state_.find(image_id); + if (it == image_async_decode_state_.end()) { + DCHECK_EQ(image_id_to_decode_.count(image_id), 0u); + return; + } - images_decoded_once_.insert(image_id); + it->second = DecodePolicy::SYNC_DECODED_ONCE; images_pending_invalidation_.insert(image_id); + ScheduleNextImageDecode(); client_->NeedsInvalidationForCheckerImagedTiles(); } -bool CheckerImageTracker::ShouldCheckerImage(const sk_sp<const SkImage>& image, - WhichTree tree) const { +bool CheckerImageTracker::ShouldCheckerImage(const PaintImage& image, + WhichTree tree) { TRACE_EVENT1("cc", "CheckerImageTracker::ShouldCheckerImage", "image_id", - image->uniqueID()); + image.stable_id()); if (!enable_checker_imaging_) return false; + PaintImage::Id image_id = image.stable_id(); + // 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 && + if (invalidated_images_on_current_sync_tree_.count(image_id) != 0 && tree == WhichTree::ACTIVE_TREE) { return true; } - // If a decode request is pending for this image, continue checkering it. - if (pending_image_decodes_.find(image->uniqueID()) != - pending_image_decodes_.end()) { - 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()) != + if (images_pending_invalidation_.find(image_id) != images_pending_invalidation_.end()) { return true; } - // If the image has been decoded once before, don't checker it again. - if (images_decoded_once_.find(image->uniqueID()) != - images_decoded_once_.end()) { - return false; + auto insert_result = + image_async_decode_state_.insert(std::pair<PaintImage::Id, DecodePolicy>( + image_id, DecodePolicy::SYNC_PERMANENT)); + auto it = insert_result.first; + if (insert_result.second) { + bool can_checker_image = + image.animation_type() == PaintImage::AnimationType::STATIC && + image.completion_state() == PaintImage::CompletionState::DONE; + if (can_checker_image) { + size_t size = SafeSizeOfImage(image.sk_image().get()); + it->second = (size >= kMinImageSizeToCheckerBytes && + size <= image_controller_->image_cache_max_limit_bytes()) + ? DecodePolicy::ASYNC + : DecodePolicy::SYNC_PERMANENT; + } } - return SafeSizeOfImage(image.get()) >= kMinImageSizeToCheckerBytes; + return it->second == DecodePolicy::ASYNC; } -void CheckerImageTracker::ScheduleImageDecodeIfNecessary( - const sk_sp<const SkImage>& image) { +void CheckerImageTracker::ScheduleNextImageDecode() { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), - "CheckerImageTracker::ScheduleImageDecodeIfNecessary"); - ImageId image_id = image->uniqueID(); + "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_.has_value()) + 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. + PaintImage::Id image_id = candidate.stable_id(); + 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_.emplace(candidate); + break; + } - // If the image has already been decoded, or a decode request is pending, we - // don't need to schedule another decode. - if (images_decoded_once_.count(image_id) != 0 || - pending_image_decodes_.count(image_id) != 0) { + // 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_.has_value()) { + DCHECK(image_decode_queue_.empty()); return; } + PaintImage::Id image_id = outstanding_image_decode_.value().stable_id(); + DCHECK_EQ(image_id_to_decode_.count(image_id), 0u); TRACE_EVENT_ASYNC_BEGIN0("cc", "CheckerImageTracker::DeferImageDecode", image_id); - DCHECK_EQ(image_id_to_decode_request_id_.count(image_id), 0U); - - image_id_to_decode_request_id_[image_id] = + ImageController::ImageDecodeRequestId request_id = image_controller_->QueueImageDecode( - image, base::Bind(&CheckerImageTracker::DidFinishImageDecode, - weak_factory_.GetWeakPtr(), image_id)); - pending_image_decodes_.insert(image_id); + outstanding_image_decode_.value().sk_image(), + base::Bind(&CheckerImageTracker::DidFinishImageDecode, + weak_factory_.GetWeakPtr(), image_id)); + + image_id_to_decode_.emplace(image_id, base::MakeUnique<ScopedDecodeHolder>( + image_controller_, request_id)); } } // namespace cc diff --git a/chromium/cc/tiles/checker_image_tracker.h b/chromium/cc/tiles/checker_image_tracker.h index 3bc29f55294..9f64d8d3f06 100644 --- a/chromium/cc/tiles/checker_image_tracker.h +++ b/chromium/cc/tiles/checker_image_tracker.h @@ -8,6 +8,7 @@ #include <unordered_map> #include <vector> +#include "base/optional.h" #include "cc/cc_export.h" #include "cc/paint/image_id.h" #include "cc/tiles/image_controller.h" @@ -37,33 +38,62 @@ class CC_EXPORT CheckerImageTracker { bool enable_checker_imaging); ~CheckerImageTracker(); - // Given the |images| for a tile, filters the images which will be deferred - // asynchronously using the image decoded service, eliminating them from - // |images| adds them to the |checkered_images| set, so they can be skipped - // during the rasterization of this tile. - // The entries remaining in |images| are for images for which a cached decode - // from the image decode service is available, or which must be decoded before - // before this tile can be rasterized. - void FilterImagesForCheckeringForTile(std::vector<DrawImage>* images, - ImageIdFlatSet* checkered_images, - WhichTree tree); + // Returns true if the decode for |image| will be deferred to the image decode + // service and it should be be skipped during raster. + bool ShouldCheckerImage(const PaintImage& image, WhichTree tree); + + using ImageDecodeQueue = std::vector<PaintImage>; + void ScheduleImageDecodeQueue(ImageDecodeQueue image_decode_queue); // Returns the set of images to invalidate on the sync tree. - const ImageIdFlatSet& TakeImagesToInvalidateOnSyncTree(); + const PaintImageIdFlatSet& TakeImagesToInvalidateOnSyncTree(); + // Called when the sync tree is activated. Each call to + // TakeImagesToInvalidateOnSyncTree() must be followed by this when the + // invalidated sync tree is activated. void DidActivateSyncTree(); + // Called to reset the tracker state on navigation. This will release all + // cached images. Setting |can_clear_decode_policy_tracking| will also result + // in re-checkering any images already decoded by the tracker. + void ClearTracker(bool can_clear_decode_policy_tracking); + private: - void DidFinishImageDecode(ImageId image_id, + enum class DecodePolicy { + // The image can be decoded asynchronously from raster. When set, the image + // is always skipped during rasterization of content that includes this + // image until it has been decoded using the decode service. + ASYNC, + // The image has been decoded asynchronously once and should now be + // synchronously rasterized with the content. + SYNC_DECODED_ONCE, + // The image has been permanently vetoed from being decoded async. + SYNC_PERMANENT, + }; + + // Wrapper to unlock an image decode requested from the ImageController on + // destruction. + class ScopedDecodeHolder { + public: + ScopedDecodeHolder(ImageController* controller, + ImageController::ImageDecodeRequestId request_id) + : controller_(controller), request_id_(request_id) {} + ~ScopedDecodeHolder() { controller_->UnlockImageDecode(request_id_); } + + private: + ImageController* controller_; + ImageController::ImageDecodeRequestId request_id_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDecodeHolder); + }; + + void DidFinishImageDecode(PaintImage::Id image_id, ImageController::ImageDecodeRequestId request_id, ImageController::ImageDecodeResult result); - // Returns true if the decode for |image| will be deferred to the image decode - // service and it should be be skipped during raster. - bool ShouldCheckerImage(const sk_sp<const SkImage>& image, - WhichTree tree) const; - - void ScheduleImageDecodeIfNecessary(const sk_sp<const SkImage>& image); + // Called when the next request in the |image_decode_queue_| should be + // scheduled with the image decode service. + void ScheduleNextImageDecode(); ImageController* image_controller_; CheckerImageTrackerClient* client_; @@ -71,27 +101,26 @@ class CC_EXPORT CheckerImageTracker { // A set of images which have been decoded and are pending invalidation for // raster on the checkered tiles. - ImageIdFlatSet images_pending_invalidation_; + PaintImageIdFlatSet images_pending_invalidation_; // A set of images which were invalidated on the current sync tree. - ImageIdFlatSet invalidated_images_on_current_sync_tree_; + PaintImageIdFlatSet invalidated_images_on_current_sync_tree_; + + // The queue of images pending decode. We maintain a queue to ensure that the + // order in which images are decoded is aligned with the priority of the tiles + // dependent on these images. + ImageDecodeQueue image_decode_queue_; - // A set of images which are currently pending decode from the image decode - // service. - // TODO(khushalsagar): This should be a queue that gets re-built each time we - // do a PrepareTiles? See crbug.com/689184. - ImageIdFlatSet pending_image_decodes_; + // The currently outstanding image decode that has been scheduled with the + // decode service. There can be only one outstanding decode at a time. + base::Optional<PaintImage> outstanding_image_decode_; - // A set of images which have been decoded at least once from the - // ImageDecodeService and should not be checkered again. - // TODO(khushalsagar): Limit the size of this set. - // TODO(khushalsagar): Plumb navigation changes here to reset this. See - // crbug.com/693228. - std::unordered_set<ImageId> images_decoded_once_; + // A map of ImageId to its DecodePolicy. + std::unordered_map<PaintImage::Id, DecodePolicy> image_async_decode_state_; // A map of image id to image decode request id for images to be unlocked. - std::unordered_map<ImageId, ImageController::ImageDecodeRequestId> - image_id_to_decode_request_id_; + std::unordered_map<PaintImage::Id, std::unique_ptr<ScopedDecodeHolder>> + image_id_to_decode_; base::WeakPtrFactory<CheckerImageTracker> weak_factory_; }; diff --git a/chromium/cc/tiles/checker_image_tracker_unittest.cc b/chromium/cc/tiles/checker_image_tracker_unittest.cc index 9ca8d39f344..38de60dfd80 100644 --- a/chromium/cc/tiles/checker_image_tracker_unittest.cc +++ b/chromium/cc/tiles/checker_image_tracker_unittest.cc @@ -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,11 +29,16 @@ 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); } int num_of_locked_images() const { return locked_images_.size(); } + const PaintImageIdFlatSet& decodes_requested() const { + return decodes_requested_; + } void UnlockImageDecode(ImageDecodeRequestId id) override { DCHECK_EQ(locked_images_.count(id), 1U); @@ -40,10 +50,7 @@ class TestImageController : public ImageController { const ImageDecodedCallback& callback) override { ImageDecodeRequestId request_id = next_image_request_id_++; - // The tracker should request a decode only once. - EXPECT_EQ(decodes_requested_.count(image->uniqueID()), 0u); decodes_requested_.insert(image->uniqueID()); - locked_images_.insert(request_id); // Post the callback asynchronously to match the behaviour in @@ -58,13 +65,17 @@ class TestImageController : public ImageController { private: ImageDecodeRequestId next_image_request_id_ = 1U; std::unordered_set<ImageDecodeRequestId> locked_images_; - ImageIdFlatSet decodes_requested_; + PaintImageIdFlatSet decodes_requested_; }; 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>( @@ -73,15 +84,38 @@ 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; + PaintImage CreateImage( + ImageType image_type, + PaintImage::AnimationType animation = PaintImage::AnimationType::STATIC, + PaintImage::CompletionState completion = + PaintImage::CompletionState::DONE) { + 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(); - return DrawImage(image, SkIRect::MakeWH(image->width(), image->height()), - kNone_SkFilterQuality, SkMatrix::I(), target_color_space); + return PaintImage(PaintImage::GetNextId(), image, animation, completion); + } + + CheckerImageTracker::ImageDecodeQueue BuildImageDecodeQueue( + std::vector<PaintImage> images, + WhichTree tree) { + CheckerImageTracker::ImageDecodeQueue decode_queue; + for (const auto& image : images) { + if (checker_image_tracker_->ShouldCheckerImage(image, tree)) + decode_queue.push_back(image); + } + return decode_queue; } // CheckerImageTrackerClient implementation. @@ -101,13 +135,10 @@ TEST_F(CheckerImageTrackerTest, CheckerImagesDisabled) { // disabled. SetUpTracker(false); - std::vector<DrawImage> draw_images; - ImageIdFlatSet checkered_images; - draw_images.push_back(CreateImage(ImageType::CHECKERABLE)); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 1U); - EXPECT_EQ(checkered_images.size(), 0U); + PaintImageIdFlatSet checkered_images; + PaintImage paint_image = CreateImage(ImageType::CHECKERABLE); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + paint_image, WhichTree::PENDING_TREE)); EXPECT_EQ(image_controller_.num_of_locked_images(), 0); } @@ -115,22 +146,25 @@ TEST_F(CheckerImageTrackerTest, UpdatesImagesAtomically) { // Ensures that the tracker updates images atomically for each frame. SetUpTracker(true); - DrawImage checkerable_image = CreateImage(ImageType::CHECKERABLE); - DrawImage non_checkerable_image = CreateImage(ImageType::NON_CHECKERABLE); - ImageIdFlatSet checkered_images; - std::vector<DrawImage> draw_images; + PaintImage checkerable_image = CreateImage(ImageType::CHECKERABLE); + PaintImage small_non_checkerable_image = + CreateImage(ImageType::SMALL_NON_CHECKERABLE); + PaintImage 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); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - - EXPECT_EQ(draw_images.size(), 1U); - EXPECT_EQ(draw_images[0].image(), non_checkerable_image.image()); - EXPECT_EQ(checkered_images.size(), 1U); - EXPECT_EQ(checkered_images.count(checkerable_image.image()->uniqueID()), 1U); + std::vector<PaintImage> paint_images = { + checkerable_image, small_non_checkerable_image, + large_non_checkerable_image, checkerable_image}; + image_decode_queue = + BuildImageDecodeQueue(paint_images, WhichTree::PENDING_TREE); + + ASSERT_EQ(2u, image_decode_queue.size()); + EXPECT_EQ(checkerable_image, image_decode_queue[0]); + EXPECT_EQ(checkerable_image, image_decode_queue[1]); + + 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. @@ -143,44 +177,31 @@ TEST_F(CheckerImageTrackerTest, UpdatesImagesAtomically) { // Continue checkering the image until the set of images to invalidate is // pulled. - draw_images.clear(); - draw_images.push_back(checkerable_image); - checkered_images.clear(); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 0U); - EXPECT_EQ(checkered_images.size(), 1U); - EXPECT_EQ(image_controller_.num_of_locked_images(), 1); + EXPECT_TRUE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image, WhichTree::PENDING_TREE)); - ImageIdFlatSet invalidated_images = + PaintImageIdFlatSet invalidated_images = checker_image_tracker_->TakeImagesToInvalidateOnSyncTree(); EXPECT_EQ(invalidated_images.size(), 1U); - EXPECT_EQ(invalidated_images.count(checkerable_image.image()->uniqueID()), - 1U); + EXPECT_EQ(invalidated_images.count(checkerable_image.stable_id()), 1U); // Use the same set of draw images to ensure that they are not checkered on // the pending tree now. - draw_images.clear(); - draw_images.push_back(checkerable_image); - draw_images.push_back(non_checkerable_image); - checkered_images.clear(); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 2U); - EXPECT_EQ(checkered_images.size(), 0U); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image, WhichTree::PENDING_TREE)); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + small_non_checkerable_image, WhichTree::PENDING_TREE)); + EXPECT_FALSE(checker_image_tracker_->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. - draw_images.clear(); - draw_images.push_back(checkerable_image); - draw_images.push_back(non_checkerable_image); - checkered_images.clear(); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::ACTIVE_TREE); - EXPECT_EQ(draw_images.size(), 1U); - EXPECT_EQ(draw_images[0].image(), non_checkerable_image.image()); - EXPECT_EQ(checkered_images.size(), 1U); - EXPECT_EQ(checkered_images.count(checkerable_image.image()->uniqueID()), 1U); + EXPECT_TRUE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image, WhichTree::ACTIVE_TREE)); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + small_non_checkerable_image, WhichTree::ACTIVE_TREE)); + EXPECT_FALSE(checker_image_tracker_->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); @@ -192,18 +213,13 @@ TEST_F(CheckerImageTrackerTest, NoConsecutiveCheckeringForImage) { // checkered again in subsequent frames. SetUpTracker(true); - DrawImage checkerable_image = CreateImage(ImageType::CHECKERABLE); - DrawImage non_checkerable_image = CreateImage(ImageType::NON_CHECKERABLE); - ImageIdFlatSet checkered_images; - std::vector<DrawImage> draw_images; + PaintImage checkerable_image = CreateImage(ImageType::CHECKERABLE); + std::vector<PaintImage> paint_images = {checkerable_image}; - draw_images.clear(); - draw_images.push_back(checkerable_image); - checkered_images.clear(); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 0U); - EXPECT_EQ(checkered_images.size(), 1U); + CheckerImageTracker::ImageDecodeQueue image_decode_queue = + BuildImageDecodeQueue(paint_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. @@ -212,13 +228,8 @@ TEST_F(CheckerImageTrackerTest, NoConsecutiveCheckeringForImage) { checker_image_tracker_->DidActivateSyncTree(); // Subsequent requests for this image should not be checkered. - draw_images.clear(); - draw_images.push_back(checkerable_image); - checkered_images.clear(); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 1U); - EXPECT_EQ(checkered_images.size(), 0U); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image, WhichTree::PENDING_TREE)); } TEST_F(CheckerImageTrackerTest, @@ -227,72 +238,182 @@ TEST_F(CheckerImageTrackerTest, // active tree are tracked correctly. SetUpTracker(true); - DrawImage checkerable_image1 = CreateImage(ImageType::CHECKERABLE); - ImageIdFlatSet checkered_images; - std::vector<DrawImage> draw_images; + PaintImage checkerable_image1 = CreateImage(ImageType::CHECKERABLE); + std::vector<PaintImage> paint_images; + CheckerImageTracker::ImageDecodeQueue image_decode_queue; // First request to filter images on the pending and active tree. - draw_images.push_back(checkerable_image1); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 0U); - EXPECT_EQ(checkered_images.size(), 1U); + paint_images.push_back(checkerable_image1); + image_decode_queue = + BuildImageDecodeQueue(paint_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. - draw_images.clear(); - checkered_images.clear(); - draw_images.push_back(checkerable_image1); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::ACTIVE_TREE); - EXPECT_EQ(draw_images.size(), 0U); - EXPECT_EQ(checkered_images.size(), 1U); + EXPECT_TRUE(checker_image_tracker_->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_); - ImageIdFlatSet invalidated_images = + PaintImageIdFlatSet invalidated_images = checker_image_tracker_->TakeImagesToInvalidateOnSyncTree(); EXPECT_EQ(invalidated_images.size(), 1U); - EXPECT_EQ(invalidated_images.count(checkerable_image1.image()->uniqueID()), - 1U); + EXPECT_EQ(invalidated_images.count(checkerable_image1.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. - draw_images.clear(); - checkered_images.clear(); - draw_images.push_back(checkerable_image1); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 1U); - EXPECT_EQ(checkered_images.size(), 0U); - - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::ACTIVE_TREE); - EXPECT_EQ(draw_images.size(), 0U); - EXPECT_EQ(checkered_images.size(), 1U); + EXPECT_TRUE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image1, WhichTree::ACTIVE_TREE)); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + checkerable_image1, WhichTree::PENDING_TREE)); // New checkerable image on the pending tree. - DrawImage checkerable_image2 = CreateImage(ImageType::CHECKERABLE); - draw_images.clear(); - checkered_images.clear(); - draw_images.push_back(checkerable_image2); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::PENDING_TREE); - EXPECT_EQ(draw_images.size(), 0U); - EXPECT_EQ(checkered_images.size(), 1U); + PaintImage checkerable_image2 = CreateImage(ImageType::CHECKERABLE); + EXPECT_TRUE(checker_image_tracker_->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(checker_image_tracker_->ShouldCheckerImage( + checkerable_image1, WhichTree::ACTIVE_TREE)); +} + +TEST_F(CheckerImageTrackerTest, CancelsScheduledDecodes) { + SetUpTracker(true); + + PaintImage checkerable_image1 = CreateImage(ImageType::CHECKERABLE); + PaintImage checkerable_image2 = CreateImage(ImageType::CHECKERABLE); + std::vector<PaintImage> paint_images = {checkerable_image1, + checkerable_image2}; + + CheckerImageTracker::ImageDecodeQueue image_decode_queue; + image_decode_queue = + BuildImageDecodeQueue(paint_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.sk_image()->uniqueID()), + 1U); + + // Rebuild the queue before the tracker is notified of decode completion, + // removing the second image and adding a new one. + PaintImage checkerable_image3 = CreateImage(ImageType::CHECKERABLE); + paint_images = {checkerable_image1, checkerable_image3}; + image_decode_queue = + BuildImageDecodeQueue(paint_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.sk_image()->uniqueID()), + 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.sk_image()->uniqueID()), + 1U); + EXPECT_EQ(image_controller_.num_of_locked_images(), 2); +} + +TEST_F(CheckerImageTrackerTest, ClearsTracker) { + SetUpTracker(true); + + PaintImage 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); + 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. + PaintImage 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); - draw_images.clear(); - checkered_images.clear(); - draw_images.push_back(checkerable_image1); - checker_image_tracker_->FilterImagesForCheckeringForTile( - &draw_images, &checkered_images, WhichTree::ACTIVE_TREE); - EXPECT_EQ(draw_images.size(), 1U); - EXPECT_EQ(checkered_images.size(), 0U); + PaintImage static_image = CreateImage(ImageType::CHECKERABLE); + PaintImage animated_image = + CreateImage(ImageType::CHECKERABLE, PaintImage::AnimationType::ANIMATED); + PaintImage partial_image = + CreateImage(ImageType::CHECKERABLE, PaintImage::AnimationType::STATIC, + PaintImage::CompletionState::PARTIALLY_DONE); + PaintImage video_image = + CreateImage(ImageType::CHECKERABLE, PaintImage::AnimationType::VIDEO); + std::vector<PaintImage> paint_images = {static_image, animated_image, + partial_image, video_image}; + + CheckerImageTracker::ImageDecodeQueue image_decode_queue = + BuildImageDecodeQueue(paint_images, WhichTree::PENDING_TREE); + EXPECT_EQ(image_decode_queue.size(), 1U); + EXPECT_EQ(image_decode_queue[0], static_image); + + // Change the partial image to complete and try again. It should still not + // be checkered. + gfx::Size image_size = gfx::Size(partial_image.sk_image()->width(), + partial_image.sk_image()->height()); + PaintImage completed_paint_image = + PaintImage(partial_image.stable_id(), CreateDiscardableImage(image_size)); + EXPECT_FALSE(checker_image_tracker_->ShouldCheckerImage( + completed_paint_image, WhichTree::PENDING_TREE)); } } // namespace diff --git a/chromium/cc/tiles/gpu_image_decode_cache.cc b/chromium/cc/tiles/gpu_image_decode_cache.cc index 39a822617db..b30d9da1c6f 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache.cc @@ -110,6 +110,38 @@ gfx::Size CalculateSizeForMipLevel(const DrawImage& draw_image, int mip_level) { return MipMapUtil::GetSizeForLevel(base_size, mip_level); } +// Draws and scales the provided |draw_image| into the |target_pixmap|. If the +// draw/scale can be done directly, calls directly into SkImage::scalePixels, +// if not, decodes to a compatible temporary pixmap and then converts that into +// the |target_pixmap|. +bool DrawAndScaleImage(const DrawImage& draw_image, SkPixmap* target_pixmap) { + const SkImage* image = draw_image.image().get(); + if (image->dimensions() == target_pixmap->bounds().size() || + target_pixmap->info().colorType() == kN32_SkColorType) { + // If no scaling is occurring, or if the target colortype is already N32, + // just scale directly. + return image->scalePixels(*target_pixmap, + CalculateUploadScaleFilterQuality(draw_image), + SkImage::kDisallow_CachingHint); + } + + // If the target colortype is not N32, it may be impossible to scale + // directly. Instead scale into an N32 pixmap, and convert that into the + // |target_pixmap|. + SkImageInfo decode_info = + target_pixmap->info().makeColorType(kN32_SkColorType); + SkBitmap decode_bitmap; + if (!decode_bitmap.tryAllocPixels(decode_info)) + return false; + SkPixmap decode_pixmap(decode_bitmap.info(), decode_bitmap.getPixels(), + decode_bitmap.rowBytes()); + if (!image->scalePixels(decode_pixmap, + CalculateUploadScaleFilterQuality(draw_image), + SkImage::kDisallow_CachingHint)) + return false; + return decode_pixmap.readPixels(*target_pixmap); +} + } // namespace // static @@ -616,6 +648,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) { @@ -1100,13 +1136,11 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image, // In order to match GPU scaling quality (which uses mip-maps at high // quality), we want to use at most medium filter quality for the // scale. - SkPixmap image_pixmap(image_info, backing_memory->data(), - image_info.minRowBytes()); - // Note that scalePixels falls back to readPixels if the sale is 1x, so + SkPixmap image_pixmap(image_info.makeColorSpace(nullptr), + backing_memory->data(), image_info.minRowBytes()); + // 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), - SkImage::kDisallow_CachingHint)) { + if (!DrawAndScaleImage(draw_image, &image_pixmap)) { DLOG(ERROR) << "scalePixels failed."; backing_memory->Unlock(); backing_memory.reset(); @@ -1119,7 +1153,8 @@ void GpuImageDecodeCache::DecodeImageIfNecessary(const DrawImage& draw_image, // DCHECKs here to enforce this. if (!draw_image.image()->getDeferredTextureImageData( *context_threadsafe_proxy_.get(), &image_data->upload_params, 1, - backing_memory->data(), nullptr)) { + backing_memory->data(), nullptr, + ResourceFormatToClosestSkColorType(format_))) { DLOG(ERROR) << "getDeferredTextureImageData failed despite params " << "having validated."; backing_memory->Unlock(); @@ -1219,7 +1254,8 @@ GpuImageDecodeCache::CreateImageData(const DrawImage& draw_image) { draw_image.matrix(), CalculateUploadScaleFilterQuality(draw_image), upload_scale_mip_level); size_t data_size = draw_image.image()->getDeferredTextureImageData( - *context_threadsafe_proxy_.get(), ¶ms, 1, nullptr, nullptr); + *context_threadsafe_proxy_.get(), ¶ms, 1, nullptr, nullptr, + ResourceFormatToClosestSkColorType(format_)); if (data_size == 0) { // Can't upload image, too large or other failure. Try to use SW fallback. diff --git a/chromium/cc/tiles/gpu_image_decode_cache.h b/chromium/cc/tiles/gpu_image_decode_cache.h index 391f048c2e9..f60157742b4 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache.h +++ b/chromium/cc/tiles/gpu_image_decode_cache.h @@ -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, @@ -230,7 +231,7 @@ class CC_EXPORT GpuImageDecodeCache UsageStats usage_stats_; }; - struct ImageData : public base::RefCounted<ImageData> { + struct ImageData : public base::RefCountedThreadSafe<ImageData> { ImageData(DecodedDataMode mode, size_t size, const gfx::ColorSpace& target_color_space, @@ -250,7 +251,7 @@ class CC_EXPORT GpuImageDecodeCache UploadedImageData upload; private: - friend class base::RefCounted<ImageData>; + friend class base::RefCountedThreadSafe<ImageData>; ~ImageData(); }; @@ -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 diff --git a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc index 91a08d1f40c..fb2f91bceaf 100644 --- a/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc +++ b/chromium/cc/tiles/gpu_image_decode_cache_unittest.cc @@ -8,6 +8,7 @@ #include "cc/test/test_context_provider.h" #include "cc/test/test_tile_task_runner.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkImageGenerator.h" #include "third_party/skia/include/core/SkRefCnt.h" namespace cc { @@ -17,22 +18,48 @@ gfx::ColorSpace DefaultColorSpace() { return gfx::ColorSpace::CreateSRGB(); } +PaintImage::Id s_paint_image_id = PaintImage::GetNextId(); + +PaintImage CreatePaintImage(sk_sp<SkImage> image) { + return PaintImage(s_paint_image_id, image); +} + size_t kGpuMemoryLimitBytes = 96 * 1024 * 1024; class TestGpuImageDecodeCache : public GpuImageDecodeCache { public: - explicit TestGpuImageDecodeCache(ContextProvider* context) + explicit TestGpuImageDecodeCache(ContextProvider* context, + ResourceFormat format) : GpuImageDecodeCache(context, - ResourceFormat::RGBA_8888, + format, kGpuMemoryLimitBytes, kGpuMemoryLimitBytes) {} }; +class TestImageGenerator : public SkImageGenerator { + public: + explicit TestImageGenerator(const SkImageInfo& info) + : SkImageGenerator(info), + image_backing_memory_(info.getSafeSize(info.minRowBytes()), 0), + image_pixmap_(info, image_backing_memory_.data(), info.minRowBytes()) {} + + protected: + bool onGetPixels(const SkImageInfo& info, + void* pixels, + size_t rowBytes, + const Options&) override { + return image_pixmap_.readPixels(info, pixels, rowBytes, 0, 0); + } + + private: + std::vector<uint8_t> image_backing_memory_; + SkPixmap image_pixmap_; +}; + sk_sp<SkImage> CreateImage(int width, int height) { - SkBitmap bitmap; gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB(); - bitmap.allocPixels( - SkImageInfo::MakeN32Premul(width, height, color_space.ToSkColorSpace())); - return SkImage::MakeFromBitmap(bitmap); + std::unique_ptr<TestImageGenerator> generator(new TestImageGenerator( + SkImageInfo::MakeN32Premul(width, height, color_space.ToSkColorSpace()))); + return SkImage::MakeFromGenerator(std::move(generator)); } SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) { @@ -47,18 +74,20 @@ SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) { return matrix; } -TEST(GpuImageDecodeCacheTest, GetTaskForImageSameImage) { +using GpuImageDecodeCacheTest = ::testing::TestWithParam<ResourceFormat>; + +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageSameImage) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); sk_sp<SkImage> image = CreateImage(100, 100); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -66,8 +95,8 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageSameImage) { EXPECT_TRUE(task); DrawImage another_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> another_task; need_unref = cache.GetTaskForImageAndRef( @@ -82,18 +111,18 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageSameImage) { cache.UnrefImage(draw_image); } -TEST(GpuImageDecodeCacheTest, GetTaskForImageSmallerScale) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageSmallerScale) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); sk_sp<SkImage> image = CreateImage(100, 100); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -101,8 +130,8 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageSmallerScale) { EXPECT_TRUE(task); DrawImage another_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> another_task; need_unref = cache.GetTaskForImageAndRef( @@ -117,21 +146,16 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageSmallerScale) { cache.UnrefImage(another_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageLowerQuality DISABLED_GetTaskForImageLowerQuality -#else -#define MAYBE_GetTaskForImageLowerQuality GetTaskForImageLowerQuality -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLowerQuality) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageLowerQuality) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); sk_sp<SkImage> image = CreateImage(100, 100); bool is_decomposable = true; SkMatrix matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f), is_decomposable); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), kHigh_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -140,7 +164,7 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLowerQuality) { EXPECT_TRUE(task); DrawImage another_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), kLow_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> another_task; need_unref = cache.GetTaskForImageAndRef( @@ -155,24 +179,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLowerQuality) { cache.UnrefImage(another_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageDifferentImage \ - DISABLED_GetTaskForImageDifferentImage -#else -#define MAYBE_GetTaskForImageDifferentImage GetTaskForImageDifferentImage -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageDifferentImage) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageDifferentImage) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -182,7 +200,7 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageDifferentImage) { sk_sp<SkImage> second_image = CreateImage(100, 100); DrawImage second_draw_image( - second_image, + CreatePaintImage(second_image), SkIRect::MakeWH(second_image->width(), second_image->height()), quality, CreateMatrix(SkSize::Make(0.25f, 0.25f), is_decomposable), DefaultColorSpace()); @@ -202,23 +220,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageDifferentImage) { cache.UnrefImage(second_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageLargerScale DISABLED_GetTaskForImageLargerScale -#else -#define MAYBE_GetTaskForImageLargerScale GetTaskForImageLargerScale -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScale) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageLargerScale) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -232,8 +245,9 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScale) { cache.UnrefImage(first_draw_image); DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> second_task; need_unref = cache.GetTaskForImageAndRef( @@ -243,8 +257,9 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScale) { EXPECT_TRUE(first_task.get() != second_task.get()); DrawImage third_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> third_task; need_unref = cache.GetTaskForImageAndRef( @@ -259,25 +274,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScale) { cache.UnrefImage(third_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageLargerScaleNoReuse \ - DISABLED_GetTaskForImageLargerScaleNoReuse -#else -#define MAYBE_GetTaskForImageLargerScaleNoReuse \ - GetTaskForImageLargerScaleNoReuse -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScaleNoReuse) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageLargerScaleNoReuse) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -286,8 +294,9 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScaleNoReuse) { EXPECT_TRUE(first_task); DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> second_task; need_unref = cache.GetTaskForImageAndRef( @@ -297,8 +306,9 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScaleNoReuse) { EXPECT_TRUE(first_task.get() != second_task.get()); DrawImage third_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> third_task; need_unref = cache.GetTaskForImageAndRef( @@ -316,22 +326,17 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageLargerScaleNoReuse) { cache.UnrefImage(third_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageHigherQuality DISABLED_GetTaskForImageHigherQuality -#else -#define MAYBE_GetTaskForImageHigherQuality GetTaskForImageHigherQuality -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageHigherQuality) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageHigherQuality) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkMatrix matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f), is_decomposable); sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), kLow_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -345,7 +350,8 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageHigherQuality) { cache.UnrefImage(first_draw_image); DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), kHigh_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> second_task; need_unref = cache.GetTaskForImageAndRef( @@ -360,26 +366,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageHigherQuality) { cache.UnrefImage(second_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageAlreadyDecodedAndLocked \ - DISABLED_GetTaskForImageAlreadyDecodedAndLocked -#else -#define MAYBE_GetTaskForImageAlreadyDecodedAndLocked \ - GetTaskForImageAlreadyDecodedAndLocked -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageAlreadyDecodedAndLocked) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageAlreadyDecodedAndLocked) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -414,26 +412,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageAlreadyDecodedAndLocked) { cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageAlreadyDecodedNotLocked \ - DISABLED_GetTaskForImageAlreadyDecodedNotLocked -#else -#define MAYBE_GetTaskForImageAlreadyDecodedNotLocked \ - GetTaskForImageAlreadyDecodedNotLocked -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageAlreadyDecodedNotLocked) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageAlreadyDecodedNotLocked) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -468,25 +458,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageAlreadyDecodedNotLocked) { cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageAlreadyUploaded \ - DISABLED_GetTaskForImageAlreadyUploaded -#else -#define MAYBE_GetTaskForImageAlreadyUploaded GetTaskForImageAlreadyUploaded -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageAlreadyUploaded) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageAlreadyUploaded) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -511,26 +494,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageAlreadyUploaded) { cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageCanceledGetsNewTask \ - DISABLED_GetTaskForImageCanceledGetsNewTask -#else -#define MAYBE_GetTaskForImageCanceledGetsNewTask \ - GetTaskForImageCanceledGetsNewTask -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageCanceledGetsNewTask) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageCanceledGetsNewTask) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -567,27 +542,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetTaskForImageCanceledGetsNewTask) { cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetTaskForImageCanceledWhileReffedGetsNewTask \ - DISABLED_GetTaskForImageCanceledWhileReffedGetsNewTask -#else -#define MAYBE_GetTaskForImageCanceledWhileReffedGetsNewTask \ - GetTaskForImageCanceledWhileReffedGetsNewTask -#endif -TEST(GpuImageDecodeCacheTest, - MAYBE_GetTaskForImageCanceledWhileReffedGetsNewTask) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageCanceledWhileReffedGetsNewTask) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -628,26 +594,18 @@ TEST(GpuImageDecodeCacheTest, cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_NoTaskForImageAlreadyFailedDecoding \ - DISABLED_NoTaskForImageAlreadyFailedDecoding -#else -#define MAYBE_NoTaskForImageAlreadyFailedDecoding \ - NoTaskForImageAlreadyFailedDecoding -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_NoTaskForImageAlreadyFailedDecoding) { +TEST_P(GpuImageDecodeCacheTest, NoTaskForImageAlreadyFailedDecoding) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -670,24 +628,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_NoTaskForImageAlreadyFailedDecoding) { cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetDecodedImageForDraw DISABLED_GetDecodedImageForDraw -#else -#define MAYBE_GetDecodedImageForDraw GetDecodedImageForDraw -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDraw) { +TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDraw) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -711,18 +663,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDraw) { cache.UnrefImage(draw_image); } -TEST(GpuImageDecodeCacheTest, GetLargeDecodedImageForDraw) { +TEST_P(GpuImageDecodeCacheTest, GetLargeDecodedImageForDraw) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(1, 24000); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -747,20 +699,20 @@ TEST(GpuImageDecodeCacheTest, GetLargeDecodedImageForDraw) { EXPECT_FALSE(cache.DiscardableIsLockedForTesting(draw_image)); } -TEST(GpuImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecode) { +TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecode) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; cache.SetAllByteLimitsForTesting(0); sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -781,26 +733,18 @@ TEST(GpuImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecode) { cache.DrawWithImageFinished(draw_image, decoded_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetDecodedImageForDrawLargerScale \ - DISABLED_GetDecodedImageForDrawLargerScale -#else -#define MAYBE_GetDecodedImageForDrawLargerScale \ - GetDecodedImageForDrawLargerScale -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawLargerScale) { +TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawLargerScale) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -811,8 +755,8 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawLargerScale) { TestTileTaskRunner::ProcessTask(task.get()); DrawImage larger_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> larger_task; bool larger_need_unref = cache.GetTaskForImageAndRef( @@ -848,23 +792,16 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawLargerScale) { cache.UnrefImage(larger_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetDecodedImageForDrawHigherQuality \ - DISABLED_GetDecodedImageForDrawHigherQuality -#else -#define MAYBE_GetDecodedImageForDrawHigherQuality \ - GetDecodedImageForDrawHigherQuality -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawHigherQuality) { +TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawHigherQuality) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkMatrix matrix = CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable); sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), kLow_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -876,7 +813,7 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawHigherQuality) { TestTileTaskRunner::ProcessTask(task.get()); DrawImage higher_quality_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), kHigh_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> hq_task; bool hq_needs_unref = cache.GetTaskForImageAndRef( @@ -913,25 +850,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawHigherQuality) { cache.UnrefImage(higher_quality_draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetDecodedImageForDrawNegative \ - DISABLED_GetDecodedImageForDrawNegative -#else -#define MAYBE_GetDecodedImageForDrawNegative GetDecodedImageForDrawNegative -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawNegative) { +TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawNegative) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(-0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(-0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -957,26 +887,18 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetDecodedImageForDrawNegative) { cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetLargeScaledDecodedImageForDraw \ - DISABLED_GetLargeScaledDecodedImageForDraw -#else -#define MAYBE_GetLargeScaledDecodedImageForDraw \ - GetLargeScaledDecodedImageForDraw -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_GetLargeScaledDecodedImageForDraw) { +TEST_P(GpuImageDecodeCacheTest, GetLargeScaledDecodedImageForDraw) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(1, 48000); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -1004,28 +926,20 @@ TEST(GpuImageDecodeCacheTest, MAYBE_GetLargeScaledDecodedImageForDraw) { EXPECT_FALSE(cache.DiscardableIsLockedForTesting(draw_image)); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_AtRasterUsedDirectlyIfSpaceAllows \ - DISABLED_AtRasterUsedDirectlyIfSpaceAllows -#else -#define MAYBE_AtRasterUsedDirectlyIfSpaceAllows \ - AtRasterUsedDirectlyIfSpaceAllows -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_AtRasterUsedDirectlyIfSpaceAllows) { +TEST_P(GpuImageDecodeCacheTest, AtRasterUsedDirectlyIfSpaceAllows) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; cache.SetAllByteLimitsForTesting(0); sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1057,29 +971,21 @@ TEST(GpuImageDecodeCacheTest, MAYBE_AtRasterUsedDirectlyIfSpaceAllows) { cache.UnrefImage(draw_image); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_GetDecodedImageForDrawAtRasterDecodeMultipleTimes \ - DISABLED_GetDecodedImageForDrawAtRasterDecodeMultipleTimes -#else -#define MAYBE_GetDecodedImageForDrawAtRasterDecodeMultipleTimes \ - GetDecodedImageForDrawAtRasterDecodeMultipleTimes -#endif -TEST(GpuImageDecodeCacheTest, - MAYBE_GetDecodedImageForDrawAtRasterDecodeMultipleTimes) { +TEST_P(GpuImageDecodeCacheTest, + GetDecodedImageForDrawAtRasterDecodeMultipleTimes) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; cache.SetAllByteLimitsForTesting(0); sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); // Must hold context lock before calling GetDecodedImageForDraw / // DrawWithImageFinished. @@ -1100,19 +1006,19 @@ TEST(GpuImageDecodeCacheTest, cache.DrawWithImageFinished(draw_image, another_decoded_draw_image); } -TEST(GpuImageDecodeCacheTest, - GetLargeDecodedImageForDrawAtRasterDecodeMultipleTimes) { +TEST_P(GpuImageDecodeCacheTest, + GetLargeDecodedImageForDrawAtRasterDecodeMultipleTimes) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(1, 24000); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + DefaultColorSpace()); // Must hold context lock before calling GetDecodedImageForDraw / // DrawWithImageFinished. @@ -1138,18 +1044,18 @@ TEST(GpuImageDecodeCacheTest, EXPECT_FALSE(cache.DiscardableIsLockedForTesting(draw_image)); } -TEST(GpuImageDecodeCacheTest, ZeroSizedImagesAreSkipped) { +TEST_P(GpuImageDecodeCacheTest, ZeroSizedImagesAreSkipped) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.f, 0.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.f, 0.f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1167,17 +1073,18 @@ TEST(GpuImageDecodeCacheTest, ZeroSizedImagesAreSkipped) { cache.DrawWithImageFinished(draw_image, decoded_draw_image); } -TEST(GpuImageDecodeCacheTest, NonOverlappingSrcRectImagesAreSkipped) { +TEST_P(GpuImageDecodeCacheTest, NonOverlappingSrcRectImagesAreSkipped) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); DrawImage draw_image( - image, SkIRect::MakeXYWH(150, 150, image->width(), image->height()), - quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + CreatePaintImage(image), + SkIRect::MakeXYWH(150, 150, image->width(), image->height()), quality, + CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1196,18 +1103,19 @@ TEST(GpuImageDecodeCacheTest, NonOverlappingSrcRectImagesAreSkipped) { cache.DrawWithImageFinished(draw_image, decoded_draw_image); } -TEST(GpuImageDecodeCacheTest, CanceledTasksDoNotCountAgainstBudget) { +TEST_P(GpuImageDecodeCacheTest, CanceledTasksDoNotCountAgainstBudget) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image( - image, SkIRect::MakeXYWH(0, 0, image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeXYWH(0, 0, image->width(), image->height()), + quality, + CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1225,25 +1133,18 @@ TEST(GpuImageDecodeCacheTest, CanceledTasksDoNotCountAgainstBudget) { EXPECT_EQ(0u, cache.GetBytesUsedForTesting()); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_ShouldAggressivelyFreeResources \ - DISABLED_ShouldAggressivelyFreeResources -#else -#define MAYBE_ShouldAggressivelyFreeResources ShouldAggressivelyFreeResources -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_ShouldAggressivelyFreeResources) { +TEST_P(GpuImageDecodeCacheTest, ShouldAggressivelyFreeResources) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; { bool need_unref = cache.GetTaskForImageAndRef( @@ -1296,26 +1197,19 @@ TEST(GpuImageDecodeCacheTest, MAYBE_ShouldAggressivelyFreeResources) { } } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_OrphanedImagesFreeOnReachingZeroRefs \ - DISABLED_OrphanedImagesFreeOnReachingZeroRefs -#else -#define MAYBE_OrphanedImagesFreeOnReachingZeroRefs \ - OrphanedImagesFreeOnReachingZeroRefs -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_OrphanedImagesFreeOnReachingZeroRefs) { +TEST_P(GpuImageDecodeCacheTest, OrphanedImagesFreeOnReachingZeroRefs) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; // Create a downscaled image. sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1330,8 +1224,9 @@ TEST(GpuImageDecodeCacheTest, MAYBE_OrphanedImagesFreeOnReachingZeroRefs) { // Create a larger version of |first_image|, this should immediately free the // memory used by |first_image| for the smaller scale. DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> second_task; need_unref = cache.GetTaskForImageAndRef( @@ -1361,26 +1256,19 @@ TEST(GpuImageDecodeCacheTest, MAYBE_OrphanedImagesFreeOnReachingZeroRefs) { cache.GetDrawImageSizeForTesting(second_draw_image)); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_OrphanedZeroRefImagesImmediatelyDeleted \ - DISABLED_OrphanedZeroRefImagesImmediatelyDeleted -#else -#define MAYBE_OrphanedZeroRefImagesImmediatelyDeleted \ - OrphanedZeroRefImagesImmediatelyDeleted -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_OrphanedZeroRefImagesImmediatelyDeleted) { +TEST_P(GpuImageDecodeCacheTest, OrphanedZeroRefImagesImmediatelyDeleted) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; // Create a downscaled image. sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1399,8 +1287,9 @@ TEST(GpuImageDecodeCacheTest, MAYBE_OrphanedZeroRefImagesImmediatelyDeleted) { // Create a larger version of |first_image|, this should immediately free the // memory used by |first_image| for the smaller scale. DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> second_task; need_unref = cache.GetTaskForImageAndRef( @@ -1419,22 +1308,16 @@ TEST(GpuImageDecodeCacheTest, MAYBE_OrphanedZeroRefImagesImmediatelyDeleted) { cache.GetDrawImageSizeForTesting(second_draw_image)); } -// crbug.com/709341. -#if defined(MEMORY_SANITIZER) -#define MAYBE_QualityCappedAtMedium DISABLED_QualityCappedAtMedium -#else -#define MAYBE_QualityCappedAtMedium QualityCappedAtMedium -#endif -TEST(GpuImageDecodeCacheTest, MAYBE_QualityCappedAtMedium) { +TEST_P(GpuImageDecodeCacheTest, QualityCappedAtMedium) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); sk_sp<SkImage> image = CreateImage(100, 100); bool is_decomposable = true; SkMatrix matrix = CreateMatrix(SkSize::Make(0.4f, 0.4f), is_decomposable); // Create an image with kLow_FilterQuality. - DrawImage low_draw_image(image, + DrawImage low_draw_image(CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), kLow_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> low_task; @@ -1446,7 +1329,7 @@ TEST(GpuImageDecodeCacheTest, MAYBE_QualityCappedAtMedium) { // Get the same image at kMedium_FilterQuality. We can't re-use low, so we // should get a new task/ref. DrawImage medium_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), kMedium_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> medium_task; need_unref = cache.GetTaskForImageAndRef( @@ -1457,7 +1340,7 @@ TEST(GpuImageDecodeCacheTest, MAYBE_QualityCappedAtMedium) { // Get the same image at kHigh_FilterQuality. We should re-use medium. DrawImage large_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), kHigh_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> large_task; need_unref = cache.GetTaskForImageAndRef( @@ -1477,19 +1360,19 @@ TEST(GpuImageDecodeCacheTest, MAYBE_QualityCappedAtMedium) { // Ensure that switching to a mipped version of an image after the initial // cache entry creation doesn't cause a buffer overflow/crash. -TEST(GpuImageDecodeCacheTest, GetDecodedImageForDrawMipUsageChange) { +TEST_P(GpuImageDecodeCacheTest, GetDecodedImageForDrawMipUsageChange) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; // Create an image decode task and cache entry that does not need mips. sk_sp<SkImage> image = CreateImage(4000, 4000); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -1510,24 +1393,25 @@ TEST(GpuImageDecodeCacheTest, GetDecodedImageForDrawMipUsageChange) { // Do an at-raster decode of the above image that *does* require mips. DrawImage draw_image_mips( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.6f, 0.6f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.6f, 0.6f), is_decomposable), DefaultColorSpace()); DecodedDrawImage decoded_draw_image = cache.GetDecodedImageForDraw(draw_image_mips); cache.DrawWithImageFinished(draw_image_mips, decoded_draw_image); } -TEST(GpuImageDecodeCacheTest, MemoryStateSuspended) { +TEST_P(GpuImageDecodeCacheTest, MemoryStateSuspended) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); // First Insert an image into our cache. sk_sp<SkImage> image = CreateImage(1, 1); bool is_decomposable = true; SkMatrix matrix = CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), kLow_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1588,15 +1472,16 @@ TEST(GpuImageDecodeCacheTest, MemoryStateSuspended) { cache.UnrefImage(draw_image); } -TEST(GpuImageDecodeCacheTest, OutOfRasterDecodeTask) { +TEST_P(GpuImageDecodeCacheTest, OutOfRasterDecodeTask) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); sk_sp<SkImage> image = CreateImage(1, 1); bool is_decomposable = true; SkMatrix matrix = CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), kLow_SkFilterQuality, matrix, DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1614,7 +1499,7 @@ TEST(GpuImageDecodeCacheTest, OutOfRasterDecodeTask) { cache.UnrefImage(draw_image); } -TEST(GpuImageDecodeCacheTest, ZeroCacheNormalWorkingSet) { +TEST_P(GpuImageDecodeCacheTest, ZeroCacheNormalWorkingSet) { // Setup - Image cache has a normal working set, but zero cache size. auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); @@ -1626,10 +1511,10 @@ TEST(GpuImageDecodeCacheTest, ZeroCacheNormalWorkingSet) { // Add an image to the cache. Due to normal working set, this should produce // a task and a ref. sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -1669,7 +1554,7 @@ TEST(GpuImageDecodeCacheTest, ZeroCacheNormalWorkingSet) { cache.UnrefImage(draw_image); } -TEST(GpuImageDecodeCacheTest, SmallCacheNormalWorkingSet) { +TEST_P(GpuImageDecodeCacheTest, SmallCacheNormalWorkingSet) { // Cache will fit one (but not two) 100x100 images. size_t cache_size = 190 * 100 * 4; @@ -1681,16 +1566,17 @@ TEST(GpuImageDecodeCacheTest, SmallCacheNormalWorkingSet) { SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + DefaultColorSpace()); sk_sp<SkImage> image2 = CreateImage(100, 100); - DrawImage draw_image2( - image2, SkIRect::MakeWH(image2->width(), image2->height()), quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image2(CreatePaintImage(image2), + SkIRect::MakeWH(image2->width(), image2->height()), + quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + DefaultColorSpace()); // Add an image to the cache and un-ref it. { @@ -1762,17 +1648,18 @@ TEST(GpuImageDecodeCacheTest, SmallCacheNormalWorkingSet) { } } -TEST(GpuImageDecodeCacheTest, ClearCache) { +TEST_P(GpuImageDecodeCacheTest, ClearCache) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; for (int i = 0; i < 10; ++i) { sk_sp<SkImage> image = CreateImage(100, 100); DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, + CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1797,10 +1684,10 @@ TEST(GpuImageDecodeCacheTest, ClearCache) { EXPECT_EQ(cache.GetNumCacheEntriesForTesting(), 0u); } -TEST(GpuImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { +TEST_P(GpuImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { auto context_provider = TestContextProvider::Create(); context_provider->BindToCurrentThread(); - TestGpuImageDecodeCache cache(context_provider.get()); + TestGpuImageDecodeCache cache(context_provider.get(), GetParam()); bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; @@ -1809,9 +1696,9 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - color_space_a); + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), color_space_a); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( first_draw_image, ImageDecodeCache::TracingInfo(), &first_task); @@ -1819,9 +1706,9 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { EXPECT_TRUE(first_task); DrawImage second_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - color_space_b); + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), color_space_b); scoped_refptr<TileTask> second_task; need_unref = cache.GetTaskForImageAndRef( second_draw_image, ImageDecodeCache::TracingInfo(), &second_task); @@ -1830,9 +1717,9 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { EXPECT_TRUE(first_task.get() != second_task.get()); DrawImage third_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), - color_space_a); + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), color_space_a); scoped_refptr<TileTask> third_task; need_unref = cache.GetTaskForImageAndRef( third_draw_image, ImageDecodeCache::TracingInfo(), &third_task); @@ -1849,5 +1736,10 @@ TEST(GpuImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { cache.UnrefImage(third_draw_image); } +INSTANTIATE_TEST_CASE_P(GpuImageDecodeCacheTests, + GpuImageDecodeCacheTest, + ::testing::Values(ResourceFormat::RGBA_8888, + ResourceFormat::RGBA_4444)); + } // namespace } // namespace cc diff --git a/chromium/cc/tiles/image_controller.cc b/chromium/cc/tiles/image_controller.cc index ae81e64f96d..e8021e4264e 100644 --- a/chromium/cc/tiles/image_controller.cc +++ b/chromium/cc/tiles/image_controller.cc @@ -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( @@ -192,8 +195,12 @@ ImageController::ImageDecodeRequestId ImageController::QueueImageDecode( DCHECK(image); bool is_image_lazy = image->isLazyGenerated(); auto image_bounds = image->bounds(); - DrawImage draw_image(std::move(image), image_bounds, kNone_SkFilterQuality, - SkMatrix::I(), target_color_space); + // TODO(khushalsagar): Eliminate the use of an incorrect id here and have all + // call-sites provide PaintImage to the ImageController. + DrawImage draw_image( + PaintImage(PaintImage::kUnknownStableId, + sk_sp<SkImage>(const_cast<SkImage*>(image.release()))), + image_bounds, kNone_SkFilterQuality, SkMatrix::I(), target_color_space); // Get the tasks for this decode. scoped_refptr<TileTask> task; diff --git a/chromium/cc/tiles/image_controller.h b/chromium/cc/tiles/image_controller.h index d9253368bb8..30e93f68b18 100644 --- a/chromium/cc/tiles/image_controller.h +++ b/chromium/cc/tiles/image_controller.h @@ -9,9 +9,11 @@ #include <vector> #include "base/callback.h" +#include "base/containers/flat_map.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +#include "base/sequenced_task_runner.h" #include "base/threading/simple_thread.h" #include "cc/base/unique_notifier.h" #include "cc/cc_export.h" @@ -55,6 +57,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_; @@ -93,9 +102,10 @@ class CC_EXPORT ImageController { std::vector<DrawImage> predecode_locked_images_; static ImageDecodeRequestId s_next_image_decode_queue_id_; - std::unordered_map<ImageDecodeRequestId, DrawImage> requested_locked_images_; + 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. diff --git a/chromium/cc/tiles/image_controller_unittest.cc b/chromium/cc/tiles/image_controller_unittest.cc index 3a89afb2b64..a4a565774a4 100644 --- a/chromium/cc/tiles/image_controller_unittest.cc +++ b/chromium/cc/tiles/image_controller_unittest.cc @@ -80,7 +80,7 @@ class WorkerTaskRunner : public base::SequencedTaskRunner { return true; } - bool RunsTasksOnCurrentThread() const override { return false; } + bool RunsTasksInCurrentSequence() const override { return false; } protected: ~WorkerTaskRunner() override { @@ -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; } diff --git a/chromium/cc/tiles/image_decode_cache.h b/chromium/cc/tiles/image_decode_cache.h index ca1f41e4cd9..1b550a6b06e 100644 --- a/chromium/cc/tiles/image_decode_cache.h +++ b/chromium/cc/tiles/image_decode_cache.h @@ -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 diff --git a/chromium/cc/tiles/picture_layer_tiling.cc b/chromium/cc/tiles/picture_layer_tiling.cc index 344cb32b6c0..2a20ddcc2e9 100644 --- a/chromium/cc/tiles/picture_layer_tiling.cc +++ b/chromium/cc/tiles/picture_layer_tiling.cc @@ -11,7 +11,7 @@ #include <limits> #include <set> -#include "base/containers/small_map.h" +#include "base/containers/flat_map.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/numerics/safe_conversions.h" @@ -257,12 +257,8 @@ void PictureLayerTiling::RemoveTilesInRegion(const Region& layer_invalidation, // twin, so it's slated for removal in the future. if (live_tiles_rect_.IsEmpty()) return; - // Pick 16 for the size of the SmallMap before it promotes to a unordered_map. - // 4x4 tiles should cover most small invalidations, and walking a vector of - // 16 is fast enough. If an invalidation is huge we will fall back to a - // unordered_map instead of a vector in the SmallMap. - base::SmallMap<std::unordered_map<TileMapKey, gfx::Rect, TileMapKeyHash>, 16> - remove_tiles; + + base::flat_map<TileMapKey, gfx::Rect> remove_tiles; gfx::Rect expanded_live_tiles_rect = tiling_data_.ExpandRectToTileBounds(live_tiles_rect_); for (Region::Iterator iter(layer_invalidation); iter.has_rect(); @@ -380,9 +376,8 @@ PictureLayerTiling::CoverageIterator::CoverageIterator( const gfx::Rect& coverage_rect) : tiling_(tiling), coverage_rect_(coverage_rect), - coverage_to_content_( - gfx::PreScaleAxisTransform2d(tiling->raster_transform(), - 1.f / coverage_scale)) { + coverage_to_content_(tiling->raster_transform().scale() / coverage_scale, + tiling->raster_transform().translation()) { DCHECK(tiling_); // In order to avoid artifacts in geometry_rect scaling and clamping to ints, // the |coverage_scale| should always be at least as big as the tiling's @@ -439,25 +434,70 @@ PictureLayerTiling::CoverageIterator::operator++() { return *this; bool first_time = tile_i_ < left_; - bool new_row = false; - tile_i_++; - if (tile_i_ > right_) { - tile_i_ = left_; - tile_j_++; - new_row = true; - if (tile_j_ > bottom_) { - current_tile_ = NULL; - return *this; + while (true) { + bool new_row = false; + tile_i_++; + if (tile_i_ > right_) { + tile_i_ = left_; + tile_j_++; + new_row = true; + if (tile_j_ > bottom_) { + current_tile_ = NULL; + break; + } } - } - current_tile_ = tiling_->TileAt(tile_i_, tile_j_); + DCHECK_LT(tile_i_, tiling_->tiling_data_.num_tiles_x()); + DCHECK_LT(tile_j_, tiling_->tiling_data_.num_tiles_y()); + current_tile_ = tiling_->TileAt(tile_i_, tile_j_); + + gfx::Rect geometry_rect_candidate = ComputeGeometryRect(); + + // This can happen due to floating point inprecision when calculating the + // |wanted_texels| area in the constructor. + if (geometry_rect_candidate.IsEmpty()) + continue; + + gfx::Rect last_geometry_rect = current_geometry_rect_; + current_geometry_rect_ = geometry_rect_candidate; + + if (first_time) + break; + + // Iteration happens left->right, top->bottom. Running off the bottom-right + // edge is handled by the intersection above with dest_rect_. Here we make + // sure that the new current geometry rect doesn't overlap with the last. + int min_left; + int min_top; + if (new_row) { + min_left = coverage_rect_.x(); + min_top = last_geometry_rect.bottom(); + } else { + min_left = last_geometry_rect.right(); + min_top = last_geometry_rect.y(); + } + + int inset_left = std::max(0, min_left - current_geometry_rect_.x()); + int inset_top = std::max(0, min_top - current_geometry_rect_.y()); + current_geometry_rect_.Inset(inset_left, inset_top, 0, 0); + +#if DCHECK_IS_ON() + if (!new_row) { + DCHECK_EQ(last_geometry_rect.right(), current_geometry_rect_.x()); + DCHECK_EQ(last_geometry_rect.bottom(), current_geometry_rect_.bottom()); + DCHECK_EQ(last_geometry_rect.y(), current_geometry_rect_.y()); + } +#endif + break; + } + return *this; +} + +gfx::Rect PictureLayerTiling::CoverageIterator::ComputeGeometryRect() const { // Calculate the current geometry rect. As we reserved overlap between tiles // to accommodate bilinear filtering and rounding errors in destination // space, the geometry rect might overlap on the edges. - gfx::Rect last_geometry_rect = current_geometry_rect_; - gfx::RectF texel_extent = tiling_->tiling_data_.TexelExtent(tile_i_, tile_j_); { // Adjust tile extent to accommodate numerical errors. @@ -474,7 +514,7 @@ PictureLayerTiling::CoverageIterator::operator++() { // Convert texel_extent to coverage scale, which is what we have to report // geometry_rect in. - current_geometry_rect_ = + gfx::Rect candidate = gfx::ToEnclosedRect(coverage_to_content_.InverseMapRect(texel_extent)); { // Adjust external edges to cover the whole layer in dest space. @@ -486,48 +526,18 @@ PictureLayerTiling::CoverageIterator::operator++() { // sampled as the AA fragment shader clamps sample coordinate and // antialiasing itself. const TilingData& data = tiling_->tiling_data_; - current_geometry_rect_.Inset(tile_i_ ? 0 : -current_geometry_rect_.x(), - tile_j_ ? 0 : -current_geometry_rect_.y(), - (tile_i_ != data.num_tiles_x() - 1) - ? 0 - : current_geometry_rect_.right() - - coverage_rect_max_bounds_.width(), - (tile_j_ != data.num_tiles_y() - 1) - ? 0 - : current_geometry_rect_.bottom() - - coverage_rect_max_bounds_.height()); + candidate.Inset( + tile_i_ ? 0 : -candidate.x(), tile_j_ ? 0 : -candidate.y(), + (tile_i_ != data.num_tiles_x() - 1) + ? 0 + : candidate.right() - coverage_rect_max_bounds_.width(), + (tile_j_ != data.num_tiles_y() - 1) + ? 0 + : candidate.bottom() - coverage_rect_max_bounds_.height()); } - current_geometry_rect_.Intersect(coverage_rect_); - DCHECK(!current_geometry_rect_.IsEmpty()); - - if (first_time) - return *this; - - // Iteration happens left->right, top->bottom. Running off the bottom-right - // edge is handled by the intersection above with dest_rect_. Here we make - // sure that the new current geometry rect doesn't overlap with the last. - int min_left; - int min_top; - if (new_row) { - min_left = coverage_rect_.x(); - min_top = last_geometry_rect.bottom(); - } else { - min_left = last_geometry_rect.right(); - min_top = last_geometry_rect.y(); - } - - int inset_left = std::max(0, min_left - current_geometry_rect_.x()); - int inset_top = std::max(0, min_top - current_geometry_rect_.y()); - current_geometry_rect_.Inset(inset_left, inset_top, 0, 0); - - if (!new_row) { - DCHECK_EQ(last_geometry_rect.right(), current_geometry_rect_.x()); - DCHECK_EQ(last_geometry_rect.bottom(), current_geometry_rect_.bottom()); - DCHECK_EQ(last_geometry_rect.y(), current_geometry_rect_.y()); - } - - return *this; + candidate.Intersect(coverage_rect_); + return candidate; } gfx::Rect PictureLayerTiling::CoverageIterator::geometry_rect() const { @@ -808,6 +818,39 @@ bool PictureLayerTiling::IsTileRequiredForDraw(const Tile* tile) const { return true; } +bool PictureLayerTiling::ShouldDecodeCheckeredImagesForTile( + const Tile* tile) const { + // If this is the pending tree and the tile is not occluded, any checkered + // images on this tile should be decoded. + if (tree_ == PENDING_TREE) + return !IsTileOccludedOnCurrentTree(tile); + + DCHECK_EQ(tree_, ACTIVE_TREE); + const PictureLayerTiling* pending_twin = + client_->GetPendingOrActiveTwinTiling(this); + + // If we don't have a pending twin, then 2 cases are possible. Either we don't + // have a pending tree, in which case we should be decoding images for tiles + // which are unoccluded. + // If we do have a pending tree, then not having a twin implies that this + // tiling will be evicted upon activation. TODO(khushalsagar): Plumb this + // information here and return false for this case. + if (!pending_twin) + return !IsTileOccludedOnCurrentTree(tile); + + // If the tile will be replaced upon activation, then we don't need to process + // it for checkered images. Since once the pending tree is activated, it is + // the new active tree's content that we will invalidate and replace once the + // decode finishes. + if (!TilingMatchesTileIndices(pending_twin) || + pending_twin->TileAt(tile->tiling_i_index(), tile->tiling_j_index())) { + return false; + } + + // Ask the pending twin if this tile will become occluded upon activation. + return !pending_twin->IsTileOccludedOnCurrentTree(tile); +} + void PictureLayerTiling::UpdateRequiredStatesOnTile(Tile* tile) const { tile->set_required_for_activation(IsTileRequiredForActivation(tile)); tile->set_required_for_draw(IsTileRequiredForDraw(tile)); @@ -836,7 +879,8 @@ PrioritizedTile PictureLayerTiling::MakePrioritizedTile( tile_priority.distance_to_visible > 0.5f * max_skewport_extent_in_screen_space_); return PrioritizedTile(tile, this, tile_priority, IsTileOccluded(tile), - process_for_images_only); + process_for_images_only, + ShouldDecodeCheckeredImagesForTile(tile)); } std::map<const Tile*, PrioritizedTile> diff --git a/chromium/cc/tiles/picture_layer_tiling.h b/chromium/cc/tiles/picture_layer_tiling.h index ecf1974efa8..d49d22ed23f 100644 --- a/chromium/cc/tiles/picture_layer_tiling.h +++ b/chromium/cc/tiles/picture_layer_tiling.h @@ -63,6 +63,9 @@ struct TileMapKey { bool operator==(const TileMapKey& other) const { return index_x == other.index_x && index_y == other.index_y; } + bool operator<(const TileMapKey& other) const { + return std::tie(index_x, index_y) < std::tie(other.index_x, other.index_y); + } int index_x; int index_y; @@ -105,6 +108,10 @@ class CC_EXPORT PictureLayerTiling { bool IsTileRequiredForActivation(const Tile* tile) const; bool IsTileRequiredForDraw(const Tile* tile) const; + // Returns true if the tile should be processed for decoding images skipped + // during rasterization. + bool ShouldDecodeCheckeredImagesForTile(const Tile* tile) const; + void set_resolution(TileResolution resolution) { resolution_ = resolution; may_contain_low_resolution_tiles_ |= resolution == LOW_RESOLUTION; @@ -230,6 +237,8 @@ class CC_EXPORT PictureLayerTiling { int j() const { return tile_j_; } private: + gfx::Rect ComputeGeometryRect() const; + const PictureLayerTiling* tiling_ = nullptr; gfx::Size coverage_rect_max_bounds_; gfx::Rect coverage_rect_; diff --git a/chromium/cc/tiles/picture_layer_tiling_set.h b/chromium/cc/tiles/picture_layer_tiling_set.h index 30c1d2967d7..ed71afa177d 100644 --- a/chromium/cc/tiles/picture_layer_tiling_set.h +++ b/chromium/cc/tiles/picture_layer_tiling_set.h @@ -107,6 +107,9 @@ class CC_EXPORT PictureLayerTilingSet { // the aspect ratio. float GetMaximumContentsScale() const; + // Remove one tiling. + void Remove(PictureLayerTiling* tiling); + // Removes all tilings with a contents scale key < |minimum_scale_key|. void RemoveTilingsBelowScaleKey(float minimum_scale_key); @@ -226,8 +229,6 @@ class CC_EXPORT PictureLayerTilingSet { scoped_refptr<RasterSource> raster_source, const Region& layer_invalidation); - // Remove one tiling. - void Remove(PictureLayerTiling* tiling); void VerifyTilings(const PictureLayerTilingSet* pending_twin_set) const; bool TilingsNeedUpdate(const gfx::Rect& required_rect_in_layer_space, diff --git a/chromium/cc/tiles/picture_layer_tiling_unittest.cc b/chromium/cc/tiles/picture_layer_tiling_unittest.cc index f6185bf3638..39d5867a0a9 100644 --- a/chromium/cc/tiles/picture_layer_tiling_unittest.cc +++ b/chromium/cc/tiles/picture_layer_tiling_unittest.cc @@ -1255,5 +1255,33 @@ TEST_F(PictureLayerTilingIteratorTest, FractionalTranslatedTilingOverflow) { EXPECT_FALSE(++iter); } +TEST_F(PictureLayerTilingIteratorTest, EdgeCaseLargeIntBounds) { + gfx::Size tile_size(256, 256); + float scale = 7352.331055f; + gfx::Size layer_bounds(292082, 26910); + gfx::Rect coverage_rect(2104641536, 522015, 29440, 66172); + Initialize(tile_size, scale, layer_bounds); + int count = 0; + for (PictureLayerTiling::CoverageIterator + iter(tiling_.get(), scale, coverage_rect); + iter && count < 200; ++count, ++iter) { + EXPECT_FALSE(iter.geometry_rect().IsEmpty()); + } +} + +TEST_F(PictureLayerTilingIteratorTest, EdgeCaseLargeIntBounds2) { + gfx::RectF rect(2104670720.f, 522014.5f, 192.f, 1.f); + gfx::Size tile_size(256, 256); + float scale = 7352.331055f; + gfx::Size layer_bounds(292082, 26910); + gfx::Rect coverage_rect(2104670720, 522015, 192, 1); + Initialize(tile_size, scale, layer_bounds); + for (PictureLayerTiling::CoverageIterator iter(tiling_.get(), scale, + coverage_rect); + iter; ++iter) { + EXPECT_FALSE(iter.geometry_rect().IsEmpty()); + } +} + } // namespace } // namespace cc diff --git a/chromium/cc/tiles/prioritized_tile.cc b/chromium/cc/tiles/prioritized_tile.cc index a76963d340e..cd6e901959d 100644 --- a/chromium/cc/tiles/prioritized_tile.cc +++ b/chromium/cc/tiles/prioritized_tile.cc @@ -15,12 +15,15 @@ PrioritizedTile::PrioritizedTile(Tile* tile, const PictureLayerTiling* source_tiling, const TilePriority& priority, bool is_occluded, - bool is_process_for_images_only) + bool is_process_for_images_only, + bool should_decode_checkered_images_for_tile) : tile_(tile), source_tiling_(source_tiling), priority_(priority), is_occluded_(is_occluded), - is_process_for_images_only_(is_process_for_images_only) {} + is_process_for_images_only_(is_process_for_images_only), + should_decode_checkered_images_for_tile_( + should_decode_checkered_images_for_tile) {} PrioritizedTile::~PrioritizedTile() = default; diff --git a/chromium/cc/tiles/prioritized_tile.h b/chromium/cc/tiles/prioritized_tile.h index 7decc73cca1..a2996602c7f 100644 --- a/chromium/cc/tiles/prioritized_tile.h +++ b/chromium/cc/tiles/prioritized_tile.h @@ -24,7 +24,8 @@ class CC_EXPORT PrioritizedTile { const PictureLayerTiling* source_tiling, const TilePriority& priority, bool is_occluded, - bool is_process_for_images_only); + bool is_process_for_images_only, + bool should_decode_checkered_images_for_tile); ~PrioritizedTile(); Tile* tile() const { return tile_; } @@ -36,6 +37,9 @@ class CC_EXPORT PrioritizedTile { bool is_process_for_images_only() const { return is_process_for_images_only_; } + bool should_decode_checkered_images_for_tile() const { + return should_decode_checkered_images_for_tile_; + } void AsValueInto(base::trace_event::TracedValue* value) const; @@ -45,6 +49,7 @@ class CC_EXPORT PrioritizedTile { TilePriority priority_; bool is_occluded_ = false; bool is_process_for_images_only_ = false; + bool should_decode_checkered_images_for_tile_ = false; }; } // namespace cc diff --git a/chromium/cc/tiles/software_image_decode_cache.cc b/chromium/cc/tiles/software_image_decode_cache.cc index 716d1f45ef3..c3b6eb7d4f0 100644 --- a/chromium/cc/tiles/software_image_decode_cache.cc +++ b/chromium/cc/tiles/software_image_decode_cache.cc @@ -250,7 +250,7 @@ bool SoftwareImageDecodeCache::GetTaskForImageAndRefInternal( // image does not fit into the budget, then we don't ref this image, since it // will be decoded at raster time which is when it will be temporarily put in // the cache. - ImageKey key = ImageKey::FromDrawImage(image); + ImageKey key = ImageKey::FromDrawImage(image, format_); TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "SoftwareImageDecodeCache::GetTaskForImageAndRef", "key", key.ToString()); @@ -345,7 +345,7 @@ void SoftwareImageDecodeCache::UnrefImage(const DrawImage& image) { // 2a. The image isn't in the locked cache because we didn't get to decode // it yet (or failed to decode it). // 2b. Unlock the image but keep it in list. - const ImageKey& key = ImageKey::FromDrawImage(image); + const ImageKey& key = ImageKey::FromDrawImage(image, format_); TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "SoftwareImageDecodeCache::UnrefImage", "key", key.ToString()); @@ -445,11 +445,11 @@ SoftwareImageDecodeCache::DecodeImageInternal(const ImageKey& key, case kNone_SkFilterQuality: case kLow_SkFilterQuality: if (key.should_use_subrect()) - return GetSubrectImageDecode(key, std::move(image)); + return GetSubrectImageDecode(key, draw_image.paint_image()); return GetOriginalSizeImageDecode(key, std::move(image)); case kMedium_SkFilterQuality: case kHigh_SkFilterQuality: - return GetScaledImageDecode(key, std::move(image)); + return GetScaledImageDecode(key, draw_image.paint_image()); default: NOTREACHED(); return nullptr; @@ -458,7 +458,7 @@ SoftwareImageDecodeCache::DecodeImageInternal(const ImageKey& key, DecodedDrawImage SoftwareImageDecodeCache::GetDecodedImageForDraw( const DrawImage& draw_image) { - ImageKey key = ImageKey::FromDrawImage(draw_image); + ImageKey key = ImageKey::FromDrawImage(draw_image, format_); TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "SoftwareImageDecodeCache::GetDecodedImageForDraw", "key", key.ToString()); @@ -611,15 +611,16 @@ SoftwareImageDecodeCache::GetOriginalSizeImageDecode( std::unique_ptr<SoftwareImageDecodeCache::DecodedImage> SoftwareImageDecodeCache::GetSubrectImageDecode(const ImageKey& key, - sk_sp<const SkImage> image) { + const PaintImage& image) { // Construct a key to use in GetDecodedImageForDrawInternal(). // This allows us to reuse an image in any cache if available. - gfx::Rect full_image_rect(image->width(), image->height()); - DrawImage original_size_draw_image( - std::move(image), gfx::RectToSkIRect(full_image_rect), - kNone_SkFilterQuality, SkMatrix::I(), key.target_color_space()); + gfx::Rect full_image_rect(image.sk_image()->width(), + image.sk_image()->height()); + DrawImage original_size_draw_image(image, gfx::RectToSkIRect(full_image_rect), + kNone_SkFilterQuality, SkMatrix::I(), + key.target_color_space()); ImageKey original_size_key = - ImageKey::FromDrawImage(original_size_draw_image); + ImageKey::FromDrawImage(original_size_draw_image, format_); sk_sp<SkColorSpace> target_color_space = key.target_color_space().ToSkColorSpace(); @@ -674,15 +675,16 @@ SoftwareImageDecodeCache::GetSubrectImageDecode(const ImageKey& key, std::unique_ptr<SoftwareImageDecodeCache::DecodedImage> SoftwareImageDecodeCache::GetScaledImageDecode(const ImageKey& key, - sk_sp<const SkImage> image) { + const PaintImage& image) { // Construct a key to use in GetDecodedImageForDrawInternal(). // This allows us to reuse an image in any cache if available. - gfx::Rect full_image_rect(image->width(), image->height()); - DrawImage original_size_draw_image( - std::move(image), gfx::RectToSkIRect(full_image_rect), - kNone_SkFilterQuality, SkMatrix::I(), key.target_color_space()); + gfx::Rect full_image_rect(image.sk_image()->width(), + image.sk_image()->height()); + DrawImage original_size_draw_image(image, gfx::RectToSkIRect(full_image_rect), + kNone_SkFilterQuality, SkMatrix::I(), + key.target_color_space()); ImageKey original_size_key = - ImageKey::FromDrawImage(original_size_draw_image); + ImageKey::FromDrawImage(original_size_draw_image, format_); sk_sp<SkColorSpace> target_color_space = key.target_color_space().ToSkColorSpace(); @@ -745,8 +747,8 @@ void SoftwareImageDecodeCache::DrawWithImageFinished( const DecodedDrawImage& decoded_image) { TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "SoftwareImageDecodeCache::DrawWithImageFinished", "key", - ImageKey::FromDrawImage(image).ToString()); - ImageKey key = ImageKey::FromDrawImage(image); + ImageKey::FromDrawImage(image, format_).ToString()); + ImageKey key = ImageKey::FromDrawImage(image, format_); if (!decoded_image.image()) return; @@ -839,6 +841,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_); @@ -898,7 +904,8 @@ void SoftwareImageDecodeCache::DumpImageMemoryForCache( } // SoftwareImageDecodeCacheKey -ImageDecodeCacheKey ImageDecodeCacheKey::FromDrawImage(const DrawImage& image) { +ImageDecodeCacheKey ImageDecodeCacheKey::FromDrawImage(const DrawImage& image, + ResourceFormat format) { const SkSize& scale = image.scale(); // If the src_rect falls outside of the image, we need to clip it since // otherwise we might end up with uninitialized memory in the decode process. @@ -915,16 +922,22 @@ ImageDecodeCacheKey ImageDecodeCacheKey::FromDrawImage(const DrawImage& image) { // If we're not going to do a scale, we can use low filter quality. Note that // checking if the sizes are the same is better than checking if scale is 1.f, // because even non-1 scale can result in the same (rounded) width/height. - // If either dimension is a downscale, then use mipmaps (medium filter - // quality). + // If either dimension is a downscale, and the quality is not None (in which + // case we need to preserve the pixelated scale), then use mipmaps (medium + // filter quality). if (target_size.width() == src_rect.width() && target_size.height() == src_rect.height()) { quality = std::min(quality, kLow_SkFilterQuality); - } else if (target_size.width() < src_rect.width() || - target_size.height() < src_rect.height()) { - quality = std::min(quality, kMedium_SkFilterQuality); + } else if (quality != kNone_SkFilterQuality && + (target_size.width() < src_rect.width() || + target_size.height() < src_rect.height())) { + quality = kMedium_SkFilterQuality; } + // Skia doesn't scale an RGBA_4444 format, so always use the original decode. + if (format == RGBA_4444) + quality = std::min(quality, kLow_SkFilterQuality); + // Drop from high to medium if the the matrix we applied wasn't decomposable, // or if the scaled image will be too large. if (quality == kHigh_SkFilterQuality) { diff --git a/chromium/cc/tiles/software_image_decode_cache.h b/chromium/cc/tiles/software_image_decode_cache.h index 1d3e2ac75c4..755770b4bce 100644 --- a/chromium/cc/tiles/software_image_decode_cache.h +++ b/chromium/cc/tiles/software_image_decode_cache.h @@ -36,7 +36,8 @@ namespace cc { // in the cache multiple times at different scales and filter qualities. class CC_EXPORT ImageDecodeCacheKey { public: - static ImageDecodeCacheKey FromDrawImage(const DrawImage& image); + static ImageDecodeCacheKey FromDrawImage(const DrawImage& image, + ResourceFormat format); ImageDecodeCacheKey(const ImageDecodeCacheKey& other); @@ -140,6 +141,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 +222,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_; }; @@ -263,17 +265,15 @@ class CC_EXPORT SoftwareImageDecodeCache // data, which ensures that we cache an unlocked version of the original image // in case we need to extract multiple subrects (as would be the case in an // atlas). - std::unique_ptr<DecodedImage> GetSubrectImageDecode( - const ImageKey& key, - sk_sp<const SkImage> image); + std::unique_ptr<DecodedImage> GetSubrectImageDecode(const ImageKey& key, + const PaintImage& image); // GetScaledImageDecode is called by DecodeImageInternal when the quality // requires the image be scaled. Like DecodeImageInternal, it should be // called with no lock acquired and it returns nullptr if the decoding or // scaling failed. - std::unique_ptr<DecodedImage> GetScaledImageDecode( - const ImageKey& key, - sk_sp<const SkImage> image); + std::unique_ptr<DecodedImage> GetScaledImageDecode(const ImageKey& key, + const PaintImage& image); void RefImage(const ImageKey& key); void RefAtRasterImage(const ImageKey& key); @@ -306,6 +306,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). diff --git a/chromium/cc/tiles/software_image_decode_cache_perftest.cc b/chromium/cc/tiles/software_image_decode_cache_perftest.cc index 0267f20d4f4..3a4b93ff28a 100644 --- a/chromium/cc/tiles/software_image_decode_cache_perftest.cc +++ b/chromium/cc/tiles/software_image_decode_cache_perftest.cc @@ -56,7 +56,9 @@ class SoftwareImageDecodeCachePerfTest : public testing::Test { auto& subrect = rect_subrect.second; for (auto& scale : scales) { images.emplace_back( - CreateImage(rect.width(), rect.height()), subrect, quality, + PaintImage(PaintImage::GetNextId(), + CreateImage(rect.width(), rect.height())), + subrect, quality, CreateMatrix(SkSize::Make(scale.first, scale.second)), gfx::ColorSpace()); } @@ -66,7 +68,7 @@ class SoftwareImageDecodeCachePerfTest : public testing::Test { timer_.Reset(); do { for (auto& image : images) - ImageDecodeCacheKey::FromDrawImage(image); + ImageDecodeCacheKey::FromDrawImage(image, RGBA_8888); timer_.NextLap(); } while (!timer_.HasTimeLimitExpired()); diff --git a/chromium/cc/tiles/software_image_decode_cache_unittest.cc b/chromium/cc/tiles/software_image_decode_cache_unittest.cc index 7d26359ba3d..90aa66e48a2 100644 --- a/chromium/cc/tiles/software_image_decode_cache_unittest.cc +++ b/chromium/cc/tiles/software_image_decode_cache_unittest.cc @@ -50,39 +50,116 @@ SkMatrix CreateMatrix(const SkSize& scale, bool is_decomposable) { return matrix; } -TEST(SoftwareImageDecodeCacheTest, ImageKeyLowQuality) { +PaintImage::Id s_paint_image_id = PaintImage::GetNextId(); + +PaintImage CreatePaintImage(sk_sp<SkImage> image) { + return PaintImage(s_paint_image_id, image); +} + +TEST(SoftwareImageDecodeCacheTest, ImageKeyNoneQuality) { sk_sp<SkImage> image = CreateImage(100, 100); bool is_decomposable = true; - SkFilterQuality qualities[] = {kNone_SkFilterQuality, kLow_SkFilterQuality}; - for (auto quality : qualities) { - DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), + kNone_SkFilterQuality, + CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); - EXPECT_EQ(image->uniqueID(), key.image_id()); - EXPECT_EQ(quality, key.filter_quality()); - EXPECT_EQ(100, key.target_size().width()); - EXPECT_EQ(100, key.target_size().height()); - EXPECT_TRUE(key.can_use_original_size_decode()); - // Since the original decode will be used, the locked_bytes is that of the - // original image. - EXPECT_EQ(100u * 100u * 4u, key.locked_bytes()); - } + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); + EXPECT_EQ(image->uniqueID(), key.image_id()); + EXPECT_EQ(kNone_SkFilterQuality, key.filter_quality()); + EXPECT_EQ(100, key.target_size().width()); + EXPECT_EQ(100, key.target_size().height()); + EXPECT_TRUE(key.can_use_original_size_decode()); + // Since the original decode will be used, the locked_bytes is that of the + // original image. + EXPECT_EQ(100u * 100u * 4u, key.locked_bytes()); } -TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQuality) { +TEST(SoftwareImageDecodeCacheTest, + ImageKeyLowQualityIncreasedToMediumIfDownscale) { sk_sp<SkImage> image = CreateImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kMedium_SkFilterQuality; + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), + kLow_SkFilterQuality, + CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); + EXPECT_EQ(image->uniqueID(), key.image_id()); + EXPECT_EQ(kMedium_SkFilterQuality, key.filter_quality()); + EXPECT_EQ(100, key.target_size().width()); + EXPECT_EQ(100, key.target_size().height()); + EXPECT_FALSE(key.can_use_original_size_decode()); + EXPECT_EQ(100u * 100u * 4u, key.locked_bytes()); +} + +TEST(SoftwareImageDecodeCacheTest, LowUnscalableFormatStaysLow) { + sk_sp<SkImage> image = CreateImage(100, 100); + bool is_decomposable = true; + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), + kLow_SkFilterQuality, CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_4444); + EXPECT_EQ(image->uniqueID(), key.image_id()); + EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); + EXPECT_EQ(100, key.target_size().width()); + EXPECT_EQ(100, key.target_size().height()); + EXPECT_TRUE(key.can_use_original_size_decode()); + EXPECT_EQ(100u * 100u * 4u, key.locked_bytes()); +} + +TEST(SoftwareImageDecodeCacheTest, HighUnscalableFormatBecomesLow) { + sk_sp<SkImage> image = CreateImage(100, 100); + bool is_decomposable = true; + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), + kHigh_SkFilterQuality, + CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + DefaultColorSpace()); + + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_4444); + EXPECT_EQ(image->uniqueID(), key.image_id()); + EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); + EXPECT_EQ(100, key.target_size().width()); + EXPECT_EQ(100, key.target_size().height()); + EXPECT_TRUE(key.can_use_original_size_decode()); + EXPECT_EQ(100u * 100u * 4u, key.locked_bytes()); +} + +TEST(SoftwareImageDecodeCacheTest, ImageKeyLowQualityKeptLowIfUpscale) { + sk_sp<SkImage> image = CreateImage(100, 100); + bool is_decomposable = true; + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), + kLow_SkFilterQuality, + CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + DefaultColorSpace()); + + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); + EXPECT_EQ(image->uniqueID(), key.image_id()); + EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); + EXPECT_EQ(100, key.target_size().width()); + EXPECT_EQ(100, key.target_size().height()); + EXPECT_TRUE(key.can_use_original_size_decode()); + EXPECT_EQ(100u * 100u * 4u, key.locked_bytes()); +} + +TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQuality) { + sk_sp<SkImage> image = CreateImage(100, 100); + bool is_decomposable = true; + SkFilterQuality quality = kMedium_SkFilterQuality; + + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), + DefaultColorSpace()); + + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(quality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -96,12 +173,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityDropToLowIfEnlarging) { bool is_decomposable = true; SkFilterQuality quality = kMedium_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -115,12 +192,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityDropToLowIfIdentity) { bool is_decomposable = true; SkFilterQuality quality = kMedium_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -136,11 +213,11 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kMedium_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.001f, 1.001f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.001f, 1.001f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -156,11 +233,11 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kMedium_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.999f, 0.999f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.999f, 0.999f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -175,12 +252,12 @@ TEST(SoftwareImageDecodeCacheTest, bool is_decomposable = false; SkFilterQuality quality = kMedium_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -194,12 +271,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt1_5Scale) { bool is_decomposable = true; SkFilterQuality quality = kMedium_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(500, key.target_size().width()); @@ -213,12 +290,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt1_0cale) { bool is_decomposable = true; SkFilterQuality quality = kMedium_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(500, key.target_size().width()); @@ -233,11 +310,11 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_75Scale) { SkFilterQuality quality = kMedium_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.75f, 0.75f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.75f, 0.75f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(quality, key.filter_quality()); EXPECT_EQ(500, key.target_size().width()); @@ -251,12 +328,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_5Scale) { bool is_decomposable = true; SkFilterQuality quality = kMedium_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(quality, key.filter_quality()); EXPECT_EQ(250, key.target_size().width()); @@ -271,11 +348,11 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_49Scale) { SkFilterQuality quality = kMedium_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(quality, key.filter_quality()); EXPECT_EQ(250, key.target_size().width()); @@ -289,12 +366,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_1Scale) { bool is_decomposable = true; SkFilterQuality quality = kMedium_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.1f, 0.1f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.1f, 0.1f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(quality, key.filter_quality()); EXPECT_EQ(62, key.target_size().width()); @@ -309,11 +386,11 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyMediumQualityAt0_01Scale) { SkFilterQuality quality = kMedium_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.01f, 0.01f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.01f, 0.01f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(quality, key.filter_quality()); EXPECT_EQ(7, key.target_size().width()); @@ -328,12 +405,12 @@ TEST(SoftwareImageDecodeCacheTest, bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kMedium_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -348,12 +425,12 @@ TEST(SoftwareImageDecodeCacheTest, bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.2f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.2f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kMedium_SkFilterQuality, key.filter_quality()); EXPECT_EQ(50, key.target_size().width()); @@ -367,12 +444,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyDowscalesHighQuality) { bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(2.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(2.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(quality, key.filter_quality()); EXPECT_EQ(250, key.target_size().width()); @@ -389,12 +466,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyHighQualityDropToMediumIfTooLarge) { // At least one dimension should scale down, so that medium quality doesn't // become low. - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.9f, 2.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.9f, 2.f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kMedium_SkFilterQuality, key.filter_quality()); EXPECT_EQ(4555, key.target_size().width()); @@ -409,12 +486,12 @@ TEST(SoftwareImageDecodeCacheTest, bool is_decomposable = false; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 1.5f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -428,12 +505,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageKeyHighQualityDropToLowIfIdentity) { bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -449,11 +526,11 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kHigh_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.001f, 1.001f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.001f, 1.001f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -469,11 +546,11 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kHigh_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.999f, 0.999f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.999f, 0.999f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -485,29 +562,30 @@ TEST(SoftwareImageDecodeCacheTest, TEST(SoftwareImageDecodeCacheTest, OriginalDecodesAreEqual) { sk_sp<SkImage> image = CreateImage(100, 100); bool is_decomposable = true; - SkFilterQuality quality = kLow_SkFilterQuality; + SkFilterQuality quality = kNone_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5), is_decomposable), + DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); - EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); + EXPECT_EQ(kNone_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); EXPECT_EQ(100, key.target_size().height()); EXPECT_TRUE(key.can_use_original_size_decode()); EXPECT_EQ(100u * 100u * 4u, key.locked_bytes()); DrawImage another_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.5f, 1.5), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5), is_decomposable), DefaultColorSpace()); - auto another_key = ImageDecodeCacheKey::FromDrawImage(another_draw_image); + auto another_key = + ImageDecodeCacheKey::FromDrawImage(another_draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), another_key.image_id()); - EXPECT_EQ(kLow_SkFilterQuality, another_key.filter_quality()); + EXPECT_EQ(kNone_SkFilterQuality, another_key.filter_quality()); EXPECT_EQ(100, another_key.target_size().width()); EXPECT_EQ(100, another_key.target_size().height()); EXPECT_TRUE(another_key.can_use_original_size_decode()); @@ -522,11 +600,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageRectDoesNotContainSrcRect) { SkFilterQuality quality = kHigh_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeXYWH(25, 35, image->width(), image->height()), - quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + CreatePaintImage(image), + SkIRect::MakeXYWH(25, 35, image->width(), image->height()), quality, + CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kLow_SkFilterQuality, key.filter_quality()); EXPECT_EQ(100, key.target_size().width()); @@ -541,11 +620,12 @@ TEST(SoftwareImageDecodeCacheTest, ImageRectDoesNotContainSrcRectWithScale) { SkFilterQuality quality = kHigh_SkFilterQuality; DrawImage draw_image( - image, SkIRect::MakeXYWH(20, 30, image->width(), image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(image), + SkIRect::MakeXYWH(20, 30, image->width(), image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); - auto key = ImageDecodeCacheKey::FromDrawImage(draw_image); + auto key = ImageDecodeCacheKey::FromDrawImage(draw_image, RGBA_8888); EXPECT_EQ(image->uniqueID(), key.image_id()); EXPECT_EQ(kMedium_SkFilterQuality, key.filter_quality()); EXPECT_EQ(40, key.target_size().width()); @@ -560,10 +640,10 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImage) { bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -571,8 +651,8 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImage) { EXPECT_TRUE(task); DrawImage another_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> another_task; need_unref = cache.GetTaskForImageAndRef( @@ -592,7 +672,7 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImageDifferentQuality) { bool is_decomposable = true; DrawImage high_quality_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), kHigh_SkFilterQuality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); @@ -603,24 +683,24 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImageDifferentQuality) { EXPECT_TRUE(need_unref); EXPECT_TRUE(high_quality_task); - DrawImage low_quality_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), - kLow_SkFilterQuality, + DrawImage none_quality_draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + kNone_SkFilterQuality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); - scoped_refptr<TileTask> low_quality_task; - need_unref = cache.GetTaskForImageAndRef(low_quality_draw_image, + scoped_refptr<TileTask> none_quality_task; + need_unref = cache.GetTaskForImageAndRef(none_quality_draw_image, ImageDecodeCache::TracingInfo(), - &low_quality_task); + &none_quality_task); EXPECT_TRUE(need_unref); - EXPECT_TRUE(low_quality_task); - EXPECT_TRUE(high_quality_task.get() != low_quality_task.get()); + EXPECT_TRUE(none_quality_task); + EXPECT_TRUE(high_quality_task.get() != none_quality_task.get()); TestTileTaskRunner::ProcessTask(high_quality_task.get()); - TestTileTaskRunner::ProcessTask(low_quality_task.get()); + TestTileTaskRunner::ProcessTask(none_quality_task.get()); cache.UnrefImage(high_quality_draw_image); - cache.UnrefImage(low_quality_draw_image); + cache.UnrefImage(none_quality_draw_image); } TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImageDifferentSize) { @@ -630,8 +710,8 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImageDifferentSize) { SkFilterQuality quality = kHigh_SkFilterQuality; DrawImage half_size_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> half_size_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -640,8 +720,8 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageSameImageDifferentSize) { EXPECT_TRUE(half_size_task); DrawImage quarter_size_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.25f, 0.25f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.25f, 0.25f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> quarter_size_task; need_unref = cache.GetTaskForImageAndRef(quarter_size_draw_image, @@ -665,8 +745,9 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentImage) { sk_sp<SkImage> first_image = CreateImage(100, 100); DrawImage first_draw_image( - first_image, SkIRect::MakeWH(first_image->width(), first_image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(first_image), + SkIRect::MakeWH(first_image->width(), first_image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( @@ -676,7 +757,7 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentImage) { sk_sp<SkImage> second_image = CreateImage(100, 100); DrawImage second_draw_image( - second_image, + CreatePaintImage(second_image), SkIRect::MakeWH(second_image->width(), second_image->height()), quality, CreateMatrix(SkSize::Make(0.25f, 0.25f), is_decomposable), DefaultColorSpace()); @@ -694,7 +775,15 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentImage) { cache.UnrefImage(second_draw_image); } -TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { +// crbug.com/709341 +#if defined(MEMORY_SANITIZER) +#define MAYBE_GetTaskForImageDifferentColorSpace \ + DISABLED_GetTaskForImageDifferentColorSpace +#else +#define MAYBE_GetTaskForImageDifferentColorSpace \ + GetTaskForImageDifferentColorSpace +#endif +TEST(SoftwareImageDecodeCacheTest, MAYBE_GetTaskForImageDifferentColorSpace) { TestSoftwareImageDecodeCache cache; bool is_decomposable = true; SkFilterQuality quality = kHigh_SkFilterQuality; @@ -707,8 +796,9 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { sk_sp<SkImage> image = CreateImageWithColorSpace(100, 100, color_space_a); DrawImage first_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), color_space_b); + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + color_space_b); scoped_refptr<TileTask> first_task; bool need_unref = cache.GetTaskForImageAndRef( first_draw_image, ImageDecodeCache::TracingInfo(), &first_task); @@ -716,8 +806,9 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { EXPECT_TRUE(first_task); DrawImage second_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), color_space_c); + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + color_space_c); scoped_refptr<TileTask> second_task; need_unref = cache.GetTaskForImageAndRef( second_draw_image, ImageDecodeCache::TracingInfo(), &second_task); @@ -726,8 +817,9 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageDifferentColorSpace) { EXPECT_TRUE(first_task.get() != second_task.get()); DrawImage third_draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), color_space_b); + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), + color_space_b); scoped_refptr<TileTask> third_task; need_unref = cache.GetTaskForImageAndRef( third_draw_image, ImageDecodeCache::TracingInfo(), &third_task); @@ -749,10 +841,10 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageAlreadyDecoded) { SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -780,10 +872,10 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageAlreadyPrerolled) { SkFilterQuality quality = kLow_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -818,10 +910,10 @@ TEST(SoftwareImageDecodeCacheTest, GetTaskForImageCanceledGetsNewTask) { SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -862,10 +954,10 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -905,10 +997,10 @@ TEST(SoftwareImageDecodeCacheTest, GetDecodedImageForDraw) { SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( draw_image, ImageDecodeCache::TracingInfo(), &task); @@ -940,8 +1032,9 @@ TEST(SoftwareImageDecodeCacheTest, sk_sp<SkImage> image = CreateImage(100, 100); DrawImage draw_image( - image, SkIRect::MakeXYWH(20, 30, image->width(), image->height()), - quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(image), + SkIRect::MakeXYWH(20, 30, image->width(), image->height()), quality, + CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -972,10 +1065,10 @@ TEST(SoftwareImageDecodeCacheTest, GetDecodedImageForDrawAtRasterDecode) { SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); DecodedDrawImage decoded_draw_image = cache.GetDecodedImageForDraw(draw_image); @@ -998,10 +1091,10 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); DecodedDrawImage decoded_draw_image = cache.GetDecodedImageForDraw(draw_image); @@ -1030,10 +1123,10 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); DecodedDrawImage decoded_draw_image = cache.GetDecodedImageForDraw(draw_image); @@ -1076,10 +1169,10 @@ TEST(SoftwareImageDecodeCacheTest, SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); DecodedDrawImage decoded_draw_image = cache.GetDecodedImageForDraw(draw_image); @@ -1122,10 +1215,10 @@ TEST(SoftwareImageDecodeCacheTest, ZeroSizedImagesAreSkipped) { SkFilterQuality quality = kHigh_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.f, 0.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.f, 0.f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1147,8 +1240,9 @@ TEST(SoftwareImageDecodeCacheTest, NonOverlappingSrcRectImagesAreSkipped) { sk_sp<SkImage> image = CreateImage(100, 100); DrawImage draw_image( - image, SkIRect::MakeXYWH(150, 150, image->width(), image->height()), - quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + CreatePaintImage(image), + SkIRect::MakeXYWH(150, 150, image->width(), image->height()), quality, + CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1170,10 +1264,10 @@ TEST(SoftwareImageDecodeCacheTest, LowQualityFilterIsHandled) { SkFilterQuality quality = kLow_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1200,7 +1294,8 @@ TEST(SoftwareImageDecodeCacheTest, LowQualityScaledSubrectIsHandled) { SkFilterQuality quality = kLow_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeXYWH(10, 10, 80, 80), quality, + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeXYWH(10, 10, 80, 80), quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); @@ -1219,7 +1314,10 @@ TEST(SoftwareImageDecodeCacheTest, LowQualityScaledSubrectIsHandled) { // SkImage object. EXPECT_TRUE(decoded_draw_image.image() != image); EXPECT_EQ(kLow_SkFilterQuality, decoded_draw_image.filter_quality()); - EXPECT_TRUE(decoded_draw_image.is_scale_adjustment_identity()); + // Low quality will be upgraded to medium and mip-mapped. + EXPECT_FALSE(decoded_draw_image.is_scale_adjustment_identity()); + EXPECT_EQ(0.5f, decoded_draw_image.scale_adjustment().width()); + EXPECT_EQ(0.5f, decoded_draw_image.scale_adjustment().height()); cache.DrawWithImageFinished(draw_image, decoded_draw_image); cache.UnrefImage(draw_image); @@ -1231,7 +1329,8 @@ TEST(SoftwareImageDecodeCacheTest, NoneQualityScaledSubrectIsHandled) { SkFilterQuality quality = kNone_SkFilterQuality; sk_sp<SkImage> image = CreateImage(100, 100); - DrawImage draw_image(image, SkIRect::MakeXYWH(10, 10, 80, 80), quality, + DrawImage draw_image(CreatePaintImage(image), + SkIRect::MakeXYWH(10, 10, 80, 80), quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); @@ -1262,10 +1361,10 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt01_5ScaleIsHandled) { SkFilterQuality quality = kMedium_SkFilterQuality; sk_sp<SkImage> image = CreateImage(500, 200); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.5f, 1.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1295,10 +1394,10 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt1_0ScaleIsHandled) { SkFilterQuality quality = kMedium_SkFilterQuality; sk_sp<SkImage> image = CreateImage(500, 200); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(1.f, 1.f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1329,8 +1428,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_75ScaleIsHandled) { sk_sp<SkImage> image = CreateImage(500, 200); DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.75f, 0.75f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.75f, 0.75f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1361,10 +1460,10 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_5ScaleIsHandled) { SkFilterQuality quality = kMedium_SkFilterQuality; sk_sp<SkImage> image = CreateImage(500, 200); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1395,8 +1494,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_49ScaleIsHandled) { sk_sp<SkImage> image = CreateImage(500, 200); DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1427,10 +1526,10 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_1ScaleIsHandled) { SkFilterQuality quality = kMedium_SkFilterQuality; sk_sp<SkImage> image = CreateImage(500, 200); - DrawImage draw_image(image, SkIRect::MakeWH(image->width(), image->height()), - quality, - CreateMatrix(SkSize::Make(0.1f, 0.1f), is_decomposable), - DefaultColorSpace()); + DrawImage draw_image( + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.1f, 0.1f), is_decomposable), + DefaultColorSpace()); scoped_refptr<TileTask> task; bool need_unref = cache.GetTaskForImageAndRef( @@ -1461,8 +1560,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_01ScaleIsHandled) { sk_sp<SkImage> image = CreateImage(500, 200); DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.01f, 0.01f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.01f, 0.01f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1494,8 +1593,8 @@ TEST(SoftwareImageDecodeCacheTest, MediumQualityAt0_001ScaleIsHandled) { sk_sp<SkImage> image = CreateImage(500, 200); DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.001f, 0.001f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.001f, 0.001f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; @@ -1519,12 +1618,12 @@ TEST(SoftwareImageDecodeCacheTest, sk_sp<SkImage> image = CreateImage(500, 200); DrawImage draw_image_50( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.5f, 0.5f), is_decomposable), DefaultColorSpace()); DrawImage draw_image_49( - image, SkIRect::MakeWH(image->width(), image->height()), quality, - CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable), + CreatePaintImage(image), SkIRect::MakeWH(image->width(), image->height()), + quality, CreateMatrix(SkSize::Make(0.49f, 0.49f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task_50; @@ -1573,7 +1672,8 @@ TEST(SoftwareImageDecodeCacheTest, ClearCache) { for (int i = 0; i < 10; ++i) { sk_sp<SkImage> image = CreateImage(100, 100); DrawImage draw_image( - image, SkIRect::MakeWH(image->width(), image->height()), quality, + CreatePaintImage(image), + SkIRect::MakeWH(image->width(), image->height()), quality, CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable), DefaultColorSpace()); scoped_refptr<TileTask> task; diff --git a/chromium/cc/tiles/tile.h b/chromium/cc/tiles/tile.h index 435d5f0552e..66dfadae0a6 100644 --- a/chromium/cc/tiles/tile.h +++ b/chromium/cc/tiles/tile.h @@ -10,6 +10,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "cc/paint/draw_image.h" #include "cc/raster/tile_task.h" #include "cc/tiles/tile_draw_info.h" #include "ui/gfx/geometry/axis_transform2d.h" @@ -117,6 +118,15 @@ class CC_EXPORT Tile { return is_solid_color_analysis_performed_; } + bool set_raster_task_scheduled_with_checker_images(bool has_checker_images) { + bool previous_value = raster_task_scheduled_with_checker_images_; + raster_task_scheduled_with_checker_images_ = has_checker_images; + return previous_value; + } + bool raster_task_scheduled_with_checker_images() const { + return raster_task_scheduled_with_checker_images_; + } + const PictureLayerTiling* tiling() const { return tiling_; } void set_tiling(const PictureLayerTiling* tiling) { tiling_ = tiling; } @@ -158,6 +168,10 @@ class CC_EXPORT Tile { Id invalidated_id_; unsigned scheduled_priority_; + + // Set to true if there is a raster task scheduled for this tile that will + // rasterize a resource with checker images. + bool raster_task_scheduled_with_checker_images_ = false; scoped_refptr<TileTask> raster_task_; DISALLOW_COPY_AND_ASSIGN(Tile); diff --git a/chromium/cc/tiles/tile_draw_info.cc b/chromium/cc/tiles/tile_draw_info.cc index 2e4eebb726c..d27059f5949 100644 --- a/chromium/cc/tiles/tile_draw_info.cc +++ b/chromium/cc/tiles/tile_draw_info.cc @@ -22,7 +22,7 @@ void TileDrawInfo::AsValueInto(base::trace_event::TracedValue* state) const { Resource* TileDrawInfo::TakeResource() { Resource* resource = resource_; - set_resource(nullptr); + set_resource(nullptr, false); return resource; } diff --git a/chromium/cc/tiles/tile_draw_info.h b/chromium/cc/tiles/tile_draw_info.h index 8ba08857dd2..dc1a8855506 100644 --- a/chromium/cc/tiles/tile_draw_info.h +++ b/chromium/cc/tiles/tile_draw_info.h @@ -77,6 +77,11 @@ class CC_EXPORT TileDrawInfo { return resource_ ? IsResourceFormatCompressed(resource_->format()) : false; } + bool is_checker_imaged() const { + DCHECK(!resource_is_checker_imaged_ || resource_); + return resource_is_checker_imaged_; + } + void SetSolidColorForTesting(SkColor color) { set_solid_color(color); } void AsValueInto(base::trace_event::TracedValue* state) const; @@ -87,9 +92,13 @@ class CC_EXPORT TileDrawInfo { const Resource* resource() const { return resource_; } - void set_resource(Resource* resource) { + void set_resource(Resource* resource, bool resource_is_checker_imaged) { + DCHECK(!resource_is_checker_imaged || resource) + << "Need to have a resource for it to be checker-imaged"; + mode_ = RESOURCE_MODE; is_resource_ready_to_draw_ = false; + resource_is_checker_imaged_ = resource_is_checker_imaged; resource_ = resource; } @@ -111,6 +120,10 @@ class CC_EXPORT TileDrawInfo { Resource* resource_ = nullptr; bool contents_swizzled_ = false; bool is_resource_ready_to_draw_ = false; + + // Set to true if |resource_| was rasterized with checker-imaged content. The + // flag can only be true iff we have a valid |resource_|. + bool resource_is_checker_imaged_ = false; }; } // namespace cc diff --git a/chromium/cc/tiles/tile_manager.cc b/chromium/cc/tiles/tile_manager.cc index 5639661f713..b34cbb8f2e4 100644 --- a/chromium/cc/tiles/tile_manager.cc +++ b/chromium/cc/tiles/tile_manager.cc @@ -425,6 +425,10 @@ void TileManager::SetResources(ResourcePool* resource_pool, } void TileManager::Release(Tile* tile) { + if (tile->raster_task_scheduled_with_checker_images()) + num_of_tiles_with_checker_images_--; + DCHECK_GE(num_of_tiles_with_checker_images_, 0); + FreeResourcesForTile(tile); tiles_.erase(tile->id()); } @@ -522,6 +526,10 @@ void TileManager::Flush() { tile_task_manager_->CheckForCompletedTasks(); did_check_for_completed_tasks_since_last_schedule_tasks_ = true; + + // Actually flush. + raster_buffer_provider_->Flush(); + CheckPendingGpuWorkTiles(true /* issue_signals */); TRACE_EVENT_INSTANT1("cc", "DidFlush", TRACE_EVENT_SCOPE_THREAD, "stats", @@ -639,6 +647,8 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() { MemoryUsage memory_usage(resource_pool_->memory_usage_bytes(), resource_pool_->resource_count()); + gfx::ColorSpace raster_color_space = client_->GetRasterColorSpace(); + std::unique_ptr<RasterTilePriorityQueue> raster_priority_queue( client_->BuildRasterQueue(global_state_.tree_priority, RasterTilePriorityQueue::Type::ALL)); @@ -684,6 +694,21 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() { continue; } + // Tiles in the raster queue should either require raster or decode for + // checker-images. If this tile does not need raster, process it only to + // build the decode queue for checkered images. + // Note that performing this check after the solid color analysis is not + // necessary for correctness. + if (!tile->draw_info().NeedsRaster()) { + DCHECK(tile->draw_info().is_checker_imaged()); + DCHECK(prioritized_tile.should_decode_checkered_images_for_tile()); + + AddCheckeredImagesToDecodeQueue( + prioritized_tile, raster_color_space, + &work_to_schedule.checker_image_decode_queue); + continue; + } + // We won't be able to schedule this tile, so break out early. if (work_to_schedule.tiles_to_raster.size() >= scheduled_raster_task_limit_) { @@ -729,6 +754,25 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() { break; } + // If the tile has a scheduled task that will rasterize a resource with + // checker-imaged content, add those images to the decode queue. Note that + // we add all images as we process the raster priority queue to ensure that + // images are added to the decode queue in raster priority order. + if (tile->HasRasterTask()) { + if (tile->raster_task_scheduled_with_checker_images() && + prioritized_tile.should_decode_checkered_images_for_tile()) { + AddCheckeredImagesToDecodeQueue( + prioritized_tile, raster_color_space, + &work_to_schedule.checker_image_decode_queue); + } + } else { + // Creating the raster task here will acquire resources, but + // this resource usage has already been accounted for above. + tile->raster_task_ = + CreateRasterTask(prioritized_tile, client_->GetRasterColorSpace(), + &work_to_schedule.checker_image_decode_queue); + } + memory_usage += memory_required_by_tile_to_be_scheduled; work_to_schedule.tiles_to_raster.push_back(prioritized_tile); } @@ -739,6 +783,33 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() { eviction_priority_queue = FreeTileResourcesUntilUsageIsWithinLimit( std::move(eviction_priority_queue), hard_memory_limit, &memory_usage); + // At this point, if we ran out of memory when allocating resources and we + // couldn't go past even the NOW bin, this means we have evicted resources + // from all tiles with a lower priority while we still might have resources + // holding checker-imaged content. The invalidations for these resources will + // be generated only if the skipped images are decoded. So we must schedule + // decodes for these tiles to update their content. + if (!had_enough_memory_to_schedule_tiles_needed_now && + num_of_tiles_with_checker_images_ > 0) { + for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) { + const PrioritizedTile& prioritized_tile = raster_priority_queue->Top(); + + if (prioritized_tile.priority().priority_bin > TilePriority::NOW) + break; + + if (!prioritized_tile.should_decode_checkered_images_for_tile()) + continue; + + Tile* tile = prioritized_tile.tile(); + if (tile->draw_info().is_checker_imaged() || + tile->raster_task_scheduled_with_checker_images()) { + AddCheckeredImagesToDecodeQueue( + prioritized_tile, raster_color_space, + &work_to_schedule.checker_image_decode_queue); + } + } + } + UMA_HISTOGRAM_BOOLEAN("TileManager.ExceededMemoryBudget", !had_enough_memory_to_schedule_tiles_needed_now); did_oom_on_last_assign_ = !had_enough_memory_to_schedule_tiles_needed_now; @@ -760,6 +831,11 @@ TileManager::PrioritizedWorkToSchedule TileManager::AssignGpuMemoryToTiles() { void TileManager::FreeResourcesForTile(Tile* tile) { TileDrawInfo& draw_info = tile->draw_info(); + + if (draw_info.is_checker_imaged()) + num_of_tiles_with_checker_images_--; + DCHECK_GE(num_of_tiles_with_checker_images_, 0); + Resource* resource = draw_info.TakeResource(); if (resource) { resource_pool_->ReleaseResource(resource); @@ -775,6 +851,45 @@ void TileManager::FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw( client_->NotifyTileStateChanged(tile); } +void TileManager::PartitionImagesForCheckering( + const PrioritizedTile& prioritized_tile, + const gfx::ColorSpace& raster_color_space, + std::vector<DrawImage>* sync_decoded_images, + std::vector<PaintImage>* checkered_images) { + Tile* tile = prioritized_tile.tile(); + std::vector<DrawImage> images_in_tile; + prioritized_tile.raster_source()->GetDiscardableImagesInRect( + tile->enclosing_layer_rect(), tile->raster_transform().scale(), + raster_color_space, &images_in_tile); + WhichTree tree = tile->tiling()->tree(); + + for (auto& draw_image : images_in_tile) { + if (checker_image_tracker_.ShouldCheckerImage(draw_image.paint_image(), + tree)) + checkered_images->push_back(draw_image.paint_image()); + else + sync_decoded_images->push_back(draw_image); + } +} + +void TileManager::AddCheckeredImagesToDecodeQueue( + const PrioritizedTile& prioritized_tile, + const gfx::ColorSpace& raster_color_space, + CheckerImageTracker::ImageDecodeQueue* image_decode_queue) { + Tile* tile = prioritized_tile.tile(); + std::vector<DrawImage> images_in_tile; + prioritized_tile.raster_source()->GetDiscardableImagesInRect( + tile->enclosing_layer_rect(), tile->raster_transform().scale(), + raster_color_space, &images_in_tile); + WhichTree tree = tile->tiling()->tree(); + + for (auto& draw_image : images_in_tile) { + if (checker_image_tracker_.ShouldCheckerImage(draw_image.paint_image(), + tree)) + image_decode_queue->push_back(draw_image.paint_image()); + } +} + void TileManager::ScheduleTasks( const PrioritizedWorkToSchedule& work_to_schedule) { const std::vector<PrioritizedTile>& tiles_that_need_to_be_rasterized = @@ -823,11 +938,7 @@ void TileManager::ScheduleTasks( DCHECK(tile->draw_info().requires_resource()); DCHECK(!tile->draw_info().resource()); - - if (!tile->raster_task_) { - tile->raster_task_ = - CreateRasterTask(prioritized_tile, raster_color_space); - } + DCHECK(tile->HasRasterTask()); TileTask* task = tile->raster_task_.get(); @@ -880,7 +991,6 @@ void TileManager::ScheduleTasks( std::vector<scoped_refptr<TileTask>> new_locked_image_tasks = image_controller_.SetPredecodeImages(std::move(new_locked_images), tracing_info); - for (auto& task : new_locked_image_tasks) { auto decode_it = std::find_if(graph_.nodes.begin(), graph_.nodes.end(), [&task](const TaskGraph::Node& node) { @@ -929,6 +1039,12 @@ void TileManager::ScheduleTasks( // in |raster_queue_|. tile_task_manager_->ScheduleTasks(&graph_); + // Schedule running of the checker-image decode queue. This replaces the + // previously scheduled queue and effectively cancels image decodes from the + // previous queue, if not already started. + checker_image_tracker_.ScheduleImageDecodeQueue( + std::move(work_to_schedule.checker_image_decode_queue)); + did_check_for_completed_tasks_since_last_schedule_tasks_ = false; TRACE_EVENT_ASYNC_STEP_INTO1("cc", "ScheduledTasks", this, "running", "state", @@ -937,8 +1053,13 @@ void TileManager::ScheduleTasks( scoped_refptr<TileTask> TileManager::CreateRasterTask( const PrioritizedTile& prioritized_tile, - const gfx::ColorSpace& color_space) { + const gfx::ColorSpace& color_space, + CheckerImageTracker::ImageDecodeQueue* checker_image_decode_queue) { + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), + "TileManager::CreateRasterTask"); Tile* tile = prioritized_tile.tile(); + TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), + "TileManager::CreateRasterTask", "Tile", tile->id()); // Get the resource. uint64_t resource_content_id = 0; @@ -964,29 +1085,44 @@ scoped_refptr<TileTask> TileManager::CreateRasterTask( playback_settings.skip_images = prioritized_tile.priority().resolution == LOW_RESOLUTION; - // Create and queue all image decode tasks that this tile depends on. + // Create and queue all image decode tasks that this tile depends on. Note + // that we need to store the images for decode tasks in + // |scheduled_draw_images_| since the tile might have been destroyed by the + // time the raster task finishes. TileTask::Vector decode_tasks; - std::vector<DrawImage>& images = scheduled_draw_images_[tile->id()]; - ImageIdFlatSet images_to_skip; - images.clear(); + std::vector<DrawImage>& sync_decoded_images = + scheduled_draw_images_[tile->id()]; + sync_decoded_images.clear(); if (!playback_settings.skip_images) { - prioritized_tile.raster_source()->GetDiscardableImagesInRect( - tile->enclosing_layer_rect(), tile->raster_transform().scale(), - color_space, &images); - checker_image_tracker_.FilterImagesForCheckeringForTile( - &images, &images_to_skip, prioritized_tile.tile()->tiling()->tree()); + std::vector<PaintImage> checkered_images; + PartitionImagesForCheckering(prioritized_tile, color_space, + &sync_decoded_images, &checkered_images); + for (const auto& image : checkered_images) { + playback_settings.images_to_skip.insert(image.sk_image()->uniqueID()); + + // This can be the case for tiles on the active tree that will be replaced + // or are occluded on the pending tree. While we still need to continue + // skipping images for these tiles, we don't need to decode them since + // they will not be required on the next active tree. + if (prioritized_tile.should_decode_checkered_images_for_tile()) + checker_image_decode_queue->push_back(image); + } } // We can skip the image hijack canvas if we have no images, or no images to // skip during raster. playback_settings.use_image_hijack_canvas = - !images.empty() || !images_to_skip.empty(); - playback_settings.images_to_skip = std::move(images_to_skip); + !sync_decoded_images.empty() || !playback_settings.images_to_skip.empty(); + + bool has_checker_images = !playback_settings.images_to_skip.empty(); + tile->set_raster_task_scheduled_with_checker_images(has_checker_images); + if (has_checker_images) + num_of_tiles_with_checker_images_++; // Get the tasks for the required images. ImageDecodeCache::TracingInfo tracing_info( prepare_tiles_count_, prioritized_tile.priority().priority_bin); - image_controller_.GetTasksForImagesAndRef(&images, &decode_tasks, + image_controller_.GetTasksForImagesAndRef(&sync_decoded_images, &decode_tasks, tracing_info); std::unique_ptr<RasterBuffer> raster_buffer = @@ -1012,10 +1148,15 @@ void TileManager::OnRasterTaskCompleted( auto found = tiles_.find(tile_id); Tile* tile = nullptr; + bool raster_task_was_scheduled_with_checker_images = false; if (found != tiles_.end()) { tile = found->second; DCHECK(tile->raster_task_.get()); tile->raster_task_ = nullptr; + raster_task_was_scheduled_with_checker_images = + tile->set_raster_task_scheduled_with_checker_images(false); + if (raster_task_was_scheduled_with_checker_images) + num_of_tiles_with_checker_images_--; } // Unref all the images. @@ -1038,8 +1179,11 @@ void TileManager::OnRasterTaskCompleted( } TileDrawInfo& draw_info = tile->draw_info(); - draw_info.set_resource(resource); + draw_info.set_resource(resource, + raster_task_was_scheduled_with_checker_images); draw_info.contents_swizzled_ = DetermineResourceRequiresSwizzle(tile); + if (raster_task_was_scheduled_with_checker_images) + num_of_tiles_with_checker_images_++; // In SMOOTHNESS_TAKES_PRIORITY mode, we wait for GPU work to complete for a // tile before setting it as ready to draw. @@ -1248,7 +1392,7 @@ void TileManager::MarkTilesOutOfMemory( } } -const ImageIdFlatSet& TileManager::TakeImagesToInvalidateOnSyncTree() { +const PaintImageIdFlatSet& TileManager::TakeImagesToInvalidateOnSyncTree() { return checker_image_tracker_.TakeImagesToInvalidateOnSyncTree(); } @@ -1256,6 +1400,11 @@ void TileManager::DidActivateSyncTree() { checker_image_tracker_.DidActivateSyncTree(); } +void TileManager::ClearCheckerImageTracking( + bool can_clear_decode_policy_tracking) { + checker_image_tracker_.ClearTracker(can_clear_decode_policy_tracking); +} + void TileManager::NeedsInvalidationForCheckerImagedTiles() { client_->RequestImplSideInvalidation(); } @@ -1287,6 +1436,11 @@ bool TileManager::UsePartialRaster() const { } void TileManager::CheckPendingGpuWorkTiles(bool issue_signals) { + TRACE_EVENT2("cc", "TileManager::CheckPendingGpuWorkTiles", + "pending_gpu_work_tiles", pending_gpu_work_tiles_.size(), + "tree_priority", + TreePriorityToString(global_state_.tree_priority)); + ResourceProvider::ResourceIdArray required_for_activation_ids; ResourceProvider::ResourceIdArray required_for_draw_ids; @@ -1364,6 +1518,12 @@ scoped_refptr<TileTask> TileManager::CreateTaskSetFinishedTask( std::unique_ptr<base::trace_event::ConvertableToTraceFormat> TileManager::ActivationStateAsValue() { auto state = base::MakeUnique<base::trace_event::TracedValue>(); + ActivationStateAsValueInto(state.get()); + return std::move(state); +} + +void TileManager::ActivationStateAsValueInto( + base::trace_event::TracedValue* state) { state->SetString("tree_priority", TreePriorityToString(global_state_.tree_priority)); state->SetInteger("soft_memory_limit", @@ -1402,7 +1562,7 @@ TileManager::ActivationStateAsValue() { state->BeginArray("raster_tiles"); for (; !raster_priority_queue->IsEmpty(); raster_priority_queue->Pop()) { state->BeginDictionary(); - tile_as_value(raster_priority_queue->Top(), state.get()); + tile_as_value(raster_priority_queue->Top(), state); state->EndDictionary(); } state->EndArray(); @@ -1414,12 +1574,10 @@ TileManager::ActivationStateAsValue() { state->BeginArray("activation_tiles"); for (; !required_priority_queue->IsEmpty(); required_priority_queue->Pop()) { state->BeginDictionary(); - tile_as_value(required_priority_queue->Top(), state.get()); + tile_as_value(required_priority_queue->Top(), state); state->EndDictionary(); } state->EndArray(); - - return std::move(state); } TileManager::MemoryUsage::MemoryUsage() diff --git a/chromium/cc/tiles/tile_manager.h b/chromium/cc/tiles/tile_manager.h index 3327cf1bf99..9b503e6af37 100644 --- a/chromium/cc/tiles/tile_manager.h +++ b/chromium/cc/tiles/tile_manager.h @@ -15,6 +15,7 @@ #include <vector> #include "base/macros.h" +#include "base/sequenced_task_runner.h" #include "base/values.h" #include "cc/base/unique_notifier.h" #include "cc/raster/raster_buffer_provider.h" @@ -150,8 +151,9 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { bool IsReadyToActivate() const; bool IsReadyToDraw() const; - const ImageIdFlatSet& TakeImagesToInvalidateOnSyncTree(); + const PaintImageIdFlatSet& TakeImagesToInvalidateOnSyncTree(); void DidActivateSyncTree(); + void ClearCheckerImageTracking(bool can_clear_decode_policy_tracking); std::unique_ptr<base::trace_event::ConvertableToTraceFormat> BasicStateAsValue() const; @@ -164,10 +166,12 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { void InitializeTilesWithResourcesForTesting(const std::vector<Tile*>& tiles) { for (size_t i = 0; i < tiles.size(); ++i) { TileDrawInfo& draw_info = tiles[i]->draw_info(); - draw_info.set_resource(resource_pool_->AcquireResource( - tiles[i]->desired_texture_size(), - raster_buffer_provider_->GetResourceFormat(false), - client_->GetRasterColorSpace())); + draw_info.set_resource( + resource_pool_->AcquireResource( + tiles[i]->desired_texture_size(), + raster_buffer_provider_->GetResourceFormat(false), + client_->GetRasterColorSpace()), + false); draw_info.set_resource_ready_for_draw(); } } @@ -234,6 +238,11 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { std::unique_ptr<base::trace_event::ConvertableToTraceFormat> ActivationStateAsValue(); + void ActivationStateAsValueInto(base::trace_event::TracedValue* state); + int num_of_tiles_with_checker_images() const { + return num_of_tiles_with_checker_images_; + } + protected: friend class Tile; // Must be called by tile during destruction. @@ -281,13 +290,15 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { std::vector<PrioritizedTile> tiles_to_raster; std::vector<PrioritizedTile> tiles_to_process_for_images; + CheckerImageTracker::ImageDecodeQueue checker_image_decode_queue; }; void FreeResourcesForTile(Tile* tile); void FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(Tile* tile); scoped_refptr<TileTask> CreateRasterTask( const PrioritizedTile& prioritized_tile, - const gfx::ColorSpace& color_space); + const gfx::ColorSpace& color_space, + CheckerImageTracker::ImageDecodeQueue* checker_image_decode_queue); std::unique_ptr<EvictionTilePriorityQueue> FreeTileResourcesUntilUsageIsWithinLimit( @@ -319,6 +330,15 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { PrioritizedWorkToSchedule AssignGpuMemoryToTiles(); void ScheduleTasks(const PrioritizedWorkToSchedule& work_to_schedule); + void PartitionImagesForCheckering(const PrioritizedTile& prioritized_tile, + const gfx::ColorSpace& raster_color_space, + std::vector<DrawImage>* sync_decoded_images, + std::vector<PaintImage>* checkered_images); + void AddCheckeredImagesToDecodeQueue( + const PrioritizedTile& prioritized_tile, + const gfx::ColorSpace& raster_color_space, + CheckerImageTracker::ImageDecodeQueue* image_decode_queue); + std::unique_ptr<base::trace_event::ConvertableToTraceFormat> ScheduledTasksStateAsValue() const; @@ -373,6 +393,10 @@ class CC_EXPORT TileManager : CheckerImageTrackerClient { std::unordered_map<Tile::Id, std::vector<DrawImage>> scheduled_draw_images_; std::vector<scoped_refptr<TileTask>> locked_image_tasks_; + // Number of tiles with a checker-imaged resource or active raster tasks which + // will create a checker-imaged resource. + int num_of_tiles_with_checker_images_ = 0; + // We need two WeakPtrFactory objects as the invalidation pattern of each is // different. The |task_set_finished_weak_ptr_factory_| is invalidated any // time new tasks are scheduled, preventing a race when the callback has diff --git a/chromium/cc/tiles/tile_manager_unittest.cc b/chromium/cc/tiles/tile_manager_unittest.cc index b410cc006af..4cfe0123e32 100644 --- a/chromium/cc/tiles/tile_manager_unittest.cc +++ b/chromium/cc/tiles/tile_manager_unittest.cc @@ -29,6 +29,7 @@ #include "cc/test/fake_recording_source.h" #include "cc/test/fake_tile_manager.h" #include "cc/test/fake_tile_task_manager.h" +#include "cc/test/skia_common.h" #include "cc/test/test_layer_tree_host_base.h" #include "cc/test/test_task_graph_runner.h" #include "cc/test/test_tile_priorities.h" @@ -1493,7 +1494,7 @@ TEST_F(TileManagerTilePriorityQueueTest, NoRasterTasksforSolidColorTiles) { std::unique_ptr<PictureLayerImpl> layer_impl = PictureLayerImpl::Create( host_impl()->active_tree(), 1, Layer::LayerMaskType::NOT_MASK); - layer_impl->set_is_drawn_render_surface_layer_list_member(true); + layer_impl->set_contributes_to_drawn_render_surface(true); PictureLayerTilingSet* tiling_set = layer_impl->picture_layer_tiling_set(); PictureLayerTiling* tiling = @@ -1698,7 +1699,7 @@ TEST_F(TileManagerTest, LowResHasNoImage) { std::unique_ptr<PictureLayerImpl> layer = PictureLayerImpl::Create( host_impl()->active_tree(), 1, Layer::LayerMaskType::NOT_MASK); PictureLayerTilingSet* tiling_set = layer->picture_layer_tiling_set(); - layer->set_is_drawn_render_surface_layer_list_member(true); + layer->set_contributes_to_drawn_render_surface(true); auto* tiling = tiling_set->AddTiling(gfx::AxisTransform2d(), raster); tiling->set_resolution(resolutions[i]); @@ -2320,14 +2321,14 @@ class CheckerImagingTileManagerTest : public TestLayerTreeHostBase { SkImageInfo::MakeN32Premul(size.width(), size.height())) {} protected: - MOCK_METHOD5(onGetPixels, - bool(const SkImageInfo&, void*, size_t, SkPMColor[], int*)); + MOCK_METHOD4(onGetPixels, + bool(const SkImageInfo&, void*, size_t, const Options&)); }; void TearDown() override { // Allow all tasks on the image worker to run now. Any scheduled decodes // will be aborted. - image_worker_task_runner()->set_run_tasks_synchronously(true); + task_runner_->set_run_tasks_synchronously(true); } LayerTreeSettings CreateSettings() override { @@ -2351,8 +2352,11 @@ class CheckerImagingTileManagerTest : public TestLayerTreeHostBase { return base::MakeUnique<SynchronousTaskGraphRunner>(); } - SynchronousSimpleTaskRunner* image_worker_task_runner() const { - return task_runner_.get(); + void FlushDecodeTasks() { + while (task_runner_->HasPendingTask()) { + task_runner_->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); + } } private: @@ -2366,7 +2370,7 @@ TEST_F(CheckerImagingTileManagerTest, std::unique_ptr<FakeRecordingSource> recording_source = FakeRecordingSource::CreateFilledRecordingSource(layer_bounds); - recording_source->SetGenerateDiscardableImagesMetadata(true); + recording_source->set_fill_with_nonsolid_color(true); sk_sp<SkImage> image = SkImage::MakeFromGenerator( base::MakeUnique<testing::StrictMock<MockImageGenerator>>( @@ -2379,7 +2383,7 @@ TEST_F(CheckerImagingTileManagerTest, std::unique_ptr<PictureLayerImpl> layer_impl = PictureLayerImpl::Create( host_impl()->active_tree(), 1, Layer::LayerMaskType::NOT_MASK); - layer_impl->set_is_drawn_render_surface_layer_list_member(true); + layer_impl->set_contributes_to_drawn_render_surface(true); PictureLayerTilingSet* tiling_set = layer_impl->picture_layer_tiling_set(); PictureLayerTiling* tiling = @@ -2402,5 +2406,252 @@ TEST_F(CheckerImagingTileManagerTest, EXPECT_FALSE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting()); } +TEST_F(CheckerImagingTileManagerTest, BuildsImageDecodeQueueAsExpected) { + const gfx::Size layer_bounds(900, 900); + + std::unique_ptr<FakeRecordingSource> recording_source = + FakeRecordingSource::CreateFilledRecordingSource(layer_bounds); + recording_source->set_fill_with_nonsolid_color(true); + + int dimension = 450; + sk_sp<SkImage> image1 = + CreateDiscardableImage(gfx::Size(dimension, dimension)); + sk_sp<SkImage> image2 = + CreateDiscardableImage(gfx::Size(dimension, dimension)); + sk_sp<SkImage> image3 = + CreateDiscardableImage(gfx::Size(dimension, dimension)); + recording_source->add_draw_image(image1, gfx::Point(0, 0)); + recording_source->add_draw_image(image2, gfx::Point(600, 0)); + recording_source->add_draw_image(image3, gfx::Point(0, 600)); + + recording_source->Rerecord(); + scoped_refptr<RasterSource> raster_source = + RasterSource::CreateFromRecordingSource(recording_source.get(), false); + + gfx::Size tile_size(500, 500); + Region invalidation((gfx::Rect(layer_bounds))); + SetupPendingTree(raster_source, tile_size, invalidation); + + PictureLayerTilingSet* tiling_set = + pending_layer()->picture_layer_tiling_set(); + PictureLayerTiling* pending_tiling = tiling_set->tiling_at(0); + pending_tiling->set_resolution(HIGH_RESOLUTION); + pending_tiling->CreateAllTilesForTesting(); + pending_tiling->SetTilePriorityRectsForTesting( + gfx::Rect(layer_bounds), // Visible rect. + gfx::Rect(layer_bounds), // Skewport rect. + gfx::Rect(layer_bounds), // Soon rect. + gfx::Rect(layer_bounds)); // Eventually rect. + + // PrepareTiles and make sure we account correctly for tiles that have been + // scheduled with checkered images. + host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state()); + EXPECT_TRUE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting()); + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + const Tile* tile = pending_tiling->TileAt(i, j); + EXPECT_TRUE(tile->HasRasterTask()); + if (i == 1 && j == 1) + EXPECT_FALSE(tile->raster_task_scheduled_with_checker_images()); + else + EXPECT_TRUE(tile->raster_task_scheduled_with_checker_images()); + } + } + EXPECT_EQ(host_impl()->tile_manager()->num_of_tiles_with_checker_images(), 3); + + // Now raster all the tiles and make sure these tiles are still accounted for + // with checkered images. + static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting()); + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + const Tile* tile = pending_tiling->TileAt(i, j); + EXPECT_FALSE(tile->HasRasterTask()); + EXPECT_FALSE(tile->raster_task_scheduled_with_checker_images()); + EXPECT_TRUE(tile->draw_info().has_resource()); + if (i == 1 && j == 1) + EXPECT_FALSE(tile->draw_info().is_checker_imaged()); + else + EXPECT_TRUE(tile->draw_info().is_checker_imaged()); + } + } + EXPECT_EQ(host_impl()->tile_manager()->num_of_tiles_with_checker_images(), 3); + + // Activate the pending tree. + ActivateTree(); + + // Set empty tile priority rects so an empty image decode queue is used. + gfx::Rect empty_rect; + PictureLayerTiling* active_tiling = + active_layer()->picture_layer_tiling_set()->tiling_at(0); + active_tiling->SetTilePriorityRectsForTesting( + gfx::Rect(empty_rect), // Visible rect. + gfx::Rect(empty_rect), // Skewport rect. + gfx::Rect(empty_rect), // Soon rect. + gfx::Rect(empty_rect)); // Eventually rect. + host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state()); + + // Run the decode tasks. Since the first decode is always scheduled, the + // completion for it should be triggered. + FlushDecodeTasks(); + + // Create a new pending tree to invalidate tiles for decoded images and verify + // that only tiles for |image1| are invalidated. + EXPECT_TRUE(host_impl()->client()->did_request_impl_side_invalidation()); + PerformImplSideInvalidation(); + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + const Tile* tile = pending_tiling->TileAt(i, j); + if (i == 0 && j == 0) + EXPECT_TRUE(tile); + else + EXPECT_FALSE(tile); + } + } + host_impl()->client()->reset_did_request_impl_side_invalidation(); + + // Activating the tree replaces the checker-imaged tile. + EXPECT_EQ(host_impl()->tile_manager()->num_of_tiles_with_checker_images(), 3); + ActivateTree(); + EXPECT_EQ(host_impl()->tile_manager()->num_of_tiles_with_checker_images(), 2); + + // Set the tile priority rects such that only the tile with the second image + // is scheduled for decodes, since it is checker-imaged. + gfx::Rect rect_to_raster(600, 0, 300, 900); + active_tiling->SetTilePriorityRectsForTesting( + gfx::Rect(rect_to_raster), // Visible rect. + gfx::Rect(rect_to_raster), // Skewport rect. + gfx::Rect(rect_to_raster), // Soon rect. + gfx::Rect(rect_to_raster)); // Eventually rect. + host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state()); + + // Run decode tasks to trigger completion of any pending decodes. + FlushDecodeTasks(); + + // Create a new pending tree to invalidate tiles for decoded images and verify + // that only tiles for |image2| are invalidated. + EXPECT_TRUE(host_impl()->client()->did_request_impl_side_invalidation()); + PerformImplSideInvalidation(); + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + const Tile* tile = pending_tiling->TileAt(i, j); + if (i == 1 && j == 0) + EXPECT_TRUE(tile); + else + EXPECT_FALSE(tile); + } + } + host_impl()->client()->reset_did_request_impl_side_invalidation(); + + // Activating the tree replaces the checker-imaged tile. + EXPECT_EQ(host_impl()->tile_manager()->num_of_tiles_with_checker_images(), 2); + ActivateTree(); + EXPECT_EQ(host_impl()->tile_manager()->num_of_tiles_with_checker_images(), 1); + + // Set the tile priority rects to cover the complete tiling and change the + // visibility. While |image3| has not yet been decoded, since we are + // invisible no decodes should have been scheduled. + active_tiling->SetTilePriorityRectsForTesting( + gfx::Rect(layer_bounds), // Visible rect. + gfx::Rect(layer_bounds), // Skewport rect. + gfx::Rect(layer_bounds), // Soon rect. + gfx::Rect(layer_bounds)); // Eventually rect. + host_impl()->SetVisible(false); + host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state()); + FlushDecodeTasks(); + EXPECT_FALSE(host_impl()->client()->did_request_impl_side_invalidation()); +} + +class CheckerImagingTileManagerMemoryTest + : public CheckerImagingTileManagerTest { + public: + std::unique_ptr<FakeLayerTreeHostImpl> CreateHostImpl( + const LayerTreeSettings& settings, + TaskRunnerProvider* task_runner_provider, + TaskGraphRunner* task_graph_runner) override { + LayerTreeSettings new_settings = settings; + new_settings.gpu_memory_policy.num_resources_limit = 4; + return CheckerImagingTileManagerTest::CreateHostImpl( + new_settings, task_runner_provider, task_graph_runner); + } +}; + +TEST_F(CheckerImagingTileManagerMemoryTest, AddsAllNowTilesToImageDecodeQueue) { + const gfx::Size layer_bounds(900, 1400); + + std::unique_ptr<FakeRecordingSource> recording_source = + FakeRecordingSource::CreateFilledRecordingSource(layer_bounds); + recording_source->set_fill_with_nonsolid_color(true); + + int dimension = 450; + sk_sp<SkImage> image1 = + CreateDiscardableImage(gfx::Size(dimension, dimension)); + sk_sp<SkImage> image2 = + CreateDiscardableImage(gfx::Size(dimension, dimension)); + recording_source->add_draw_image(image1, gfx::Point(0, 515)); + recording_source->add_draw_image(image2, gfx::Point(515, 515)); + + recording_source->Rerecord(); + scoped_refptr<RasterSource> raster_source = + RasterSource::CreateFromRecordingSource(recording_source.get(), false); + + gfx::Size tile_size(500, 500); + Region invalidation((gfx::Rect(layer_bounds))); + SetupPendingTree(raster_source, tile_size, invalidation); + + PictureLayerTilingSet* tiling_set = + pending_layer()->picture_layer_tiling_set(); + PictureLayerTiling* pending_tiling = tiling_set->tiling_at(0); + pending_tiling->set_resolution(HIGH_RESOLUTION); + pending_tiling->CreateAllTilesForTesting(); + + // Use a rect that only rasterizes the bottom 2 rows of tiles. + gfx::Rect rect_to_raster(0, 500, 900, 900); + pending_tiling->SetTilePriorityRectsForTesting( + rect_to_raster, // Visible rect. + rect_to_raster, // Skewport rect. + rect_to_raster, // Soon rect. + rect_to_raster); // Eventually rect. + + // PrepareTiles, rasterize all scheduled tiles and activate while no images + // have been decoded. + host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state()); + static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())->RunUntilIdle(); + base::RunLoop().RunUntilIdle(); + ActivateTree(); + + // Expand the visible rect to include the complete tiling. The tile iteration + // will not go beyond the first tile since there are no resources with a lower + // priority that can be evicted. But we should still see image decodes + // scheduled for all visible tiles. + gfx::Rect complete_tiling_rect(layer_bounds); + PictureLayerTiling* active_tiling = + active_layer()->picture_layer_tiling_set()->tiling_at(0); + active_tiling->SetTilePriorityRectsForTesting( + complete_tiling_rect, // Visible rect. + complete_tiling_rect, // Skewport rect. + complete_tiling_rect, // Soon rect. + complete_tiling_rect); // Eventually rect. + host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state()); + + // Flush all decode tasks. The tiles with checkered images should be + // invalidated. + FlushDecodeTasks(); + EXPECT_TRUE(host_impl()->client()->did_request_impl_side_invalidation()); + PerformImplSideInvalidation(); + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 3; j++) { + const Tile* tile = pending_tiling->TileAt(i, j); + if (j == 1) + EXPECT_TRUE(tile); + else + EXPECT_FALSE(tile); + } + } + host_impl()->client()->reset_did_request_impl_side_invalidation(); +} + } // namespace } // namespace cc diff --git a/chromium/cc/tiles/tiling_set_raster_queue_all.cc b/chromium/cc/tiles/tiling_set_raster_queue_all.cc index 74bf1b2f144..adae8a8dfbe 100644 --- a/chromium/cc/tiles/tiling_set_raster_queue_all.cc +++ b/chromium/cc/tiles/tiling_set_raster_queue_all.cc @@ -200,8 +200,23 @@ bool TilingSetRasterQueueAll::OnePriorityRectIterator:: bool TilingSetRasterQueueAll::OnePriorityRectIterator::IsTileValid( const Tile* tile) const { - if (!tile || !TileNeedsRaster(tile)) + if (!tile) return false; + + // A tile is valid for raster if it needs raster and is unoccluded. + bool tile_is_valid_for_raster = + tile->draw_info().NeedsRaster() && !tiling_->IsTileOccluded(tile); + + // A tile is not valid for the raster queue if it is not valid for raster or + // processing for checker-images. + if (!tile_is_valid_for_raster) { + bool tile_is_valid_for_checker_images = + tile->draw_info().is_checker_imaged() && + tiling_->ShouldDecodeCheckeredImagesForTile(tile); + if (!tile_is_valid_for_checker_images) + return false; + } + // After the pending visible rect has been processed, we must return false // for pending visible rect tiles as tiling iterators do not ignore those // tiles. diff --git a/chromium/cc/tiles/tiling_set_raster_queue_all.h b/chromium/cc/tiles/tiling_set_raster_queue_all.h index 318156380a1..73bb4c9e17b 100644 --- a/chromium/cc/tiles/tiling_set_raster_queue_all.h +++ b/chromium/cc/tiles/tiling_set_raster_queue_all.h @@ -44,9 +44,6 @@ class CC_EXPORT TilingSetRasterQueueAll { protected: ~OnePriorityRectIterator() = default; - bool TileNeedsRaster(const Tile* tile) const { - return tile->draw_info().NeedsRaster() && !tiling_->IsTileOccluded(tile); - } template <typename TilingIteratorType> void AdvanceToNextTile(TilingIteratorType* iterator); |