Commit 520ce5d7 authored by Eric Karl's avatar Eric Karl Committed by Commit Bot

Hold pre-decoded images until use or 250ms rather than 2 frames

We currently unlock pre-decoded images after 2 frames of any type. This
doesn't always give callers a chance to use their images (if main thread
is a bit backed up). Instead we should wait for the caller to actually
use the image. Additionally, we drop images after 250ms to prevent
excessive growth if a user fails to use images in a timely manner.

See the linked bug for example traces which show the benefits.

R=khushalsagar

Bug: 869491
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: Icd94709aad5c076b1ba295495eaab24dc893ebdb
Reviewed-on: https://chromium-review.googlesource.com/1159203
Commit-Queue: Eric Karl <ericrk@chromium.org>
Reviewed-by: default avatarKhushal <khushalsagar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583171}
parent 8d5f940b
...@@ -7,17 +7,33 @@ ...@@ -7,17 +7,33 @@
namespace cc { namespace cc {
namespace { namespace {
const int kNumFramesToLock = 2; // Timeout images after 250ms, whether or not they've been used. This prevents
// unbounded cache usage.
const int64_t kTimeoutDurationMs = 250;
} // namespace } // namespace
DecodedImageTracker::DecodedImageTracker(ImageController* controller) DecodedImageTracker::ImageLock::ImageLock(
: image_controller_(controller) { DecodedImageTracker* tracker,
ImageController::ImageDecodeRequestId request_id,
base::TimeTicks lock_time)
: tracker_(tracker), request_id_(request_id), lock_time_(lock_time) {}
DecodedImageTracker::ImageLock::~ImageLock() {
tracker_->image_controller_->UnlockImageDecode(request_id_);
}
DecodedImageTracker::DecodedImageTracker(
ImageController* controller,
scoped_refptr<base::SequencedTaskRunner> task_runner)
: image_controller_(controller),
task_runner_(std::move(task_runner)),
now_fn_(base::Bind(&base::TimeTicks::Now)),
weak_ptr_factory_(this) {
DCHECK(image_controller_); DCHECK(image_controller_);
} }
DecodedImageTracker::~DecodedImageTracker() { DecodedImageTracker::~DecodedImageTracker() {
for (auto& pair : locked_images_) UnlockAllImages();
image_controller_->UnlockImageDecode(pair.first);
} }
void DecodedImageTracker::QueueImageDecode( void DecodedImageTracker::QueueImageDecode(
...@@ -35,38 +51,74 @@ void DecodedImageTracker::QueueImageDecode( ...@@ -35,38 +51,74 @@ void DecodedImageTracker::QueueImageDecode(
DrawImage draw_image(image, image_bounds, kNone_SkFilterQuality, DrawImage draw_image(image, image_bounds, kNone_SkFilterQuality,
SkMatrix::I(), frame_index, target_color_space); SkMatrix::I(), frame_index, target_color_space);
image_controller_->QueueImageDecode( image_controller_->QueueImageDecode(
draw_image, base::Bind(&DecodedImageTracker::ImageDecodeFinished, draw_image,
base::Unretained(this), callback)); base::Bind(&DecodedImageTracker::ImageDecodeFinished,
base::Unretained(this), callback, image.stable_id()));
} }
void DecodedImageTracker::NotifyFrameFinished() { void DecodedImageTracker::UnlockAllImages() {
// Go through the images and if the frame ref count goes to 0, unlock the locked_images_.clear();
// image in the controller. }
for (auto it = locked_images_.begin(); it != locked_images_.end();) {
auto id = it->first; void DecodedImageTracker::OnImagesUsedInDraw(
int& ref_count = it->second; const std::vector<DrawImage>& draw_images) {
if (--ref_count != 0) { for (const DrawImage& draw_image : draw_images)
++it; locked_images_.erase(draw_image.paint_image().stable_id());
continue;
}
image_controller_->UnlockImageDecode(id);
it = locked_images_.erase(it);
}
} }
void DecodedImageTracker::ImageDecodeFinished( void DecodedImageTracker::ImageDecodeFinished(
const base::Callback<void(bool)>& callback, const base::Callback<void(bool)>& callback,
ImageController::ImageDecodeRequestId id, PaintImage::Id image_id,
ImageController::ImageDecodeRequestId request_id,
ImageController::ImageDecodeResult result) { ImageController::ImageDecodeResult result) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"DecodedImageTracker::ImageDecodeFinished"); "DecodedImageTracker::ImageDecodeFinished");
if (result == ImageController::ImageDecodeResult::SUCCESS) if (result == ImageController::ImageDecodeResult::SUCCESS) {
locked_images_.push_back(std::make_pair(id, kNumFramesToLock)); // If this image already exists, just replace it with the new (latest)
// decode.
locked_images_.erase(image_id);
locked_images_.emplace(
image_id, std::make_unique<ImageLock>(this, request_id, now_fn_.Run()));
EnqueueTimeout();
}
bool decode_succeeded = bool decode_succeeded =
result == ImageController::ImageDecodeResult::SUCCESS || result == ImageController::ImageDecodeResult::SUCCESS ||
result == ImageController::ImageDecodeResult::DECODE_NOT_REQUIRED; result == ImageController::ImageDecodeResult::DECODE_NOT_REQUIRED;
callback.Run(decode_succeeded); callback.Run(decode_succeeded);
} }
void DecodedImageTracker::OnTimeoutImages() {
timeout_pending_ = false;
if (locked_images_.size() == 0)
return;
auto now = now_fn_.Run();
auto timeout = base::TimeDelta::FromMilliseconds(kTimeoutDurationMs);
for (auto it = locked_images_.begin(); it != locked_images_.end();) {
auto& image = it->second;
if (now - image->lock_time() < timeout) {
++it;
continue;
}
it = locked_images_.erase(it);
}
EnqueueTimeout();
}
void DecodedImageTracker::EnqueueTimeout() {
if (timeout_pending_)
return;
if (locked_images_.size() == 0)
return;
timeout_pending_ = true;
task_runner_->PostDelayedTask(
FROM_HERE,
base::Bind(&DecodedImageTracker::OnTimeoutImages,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kTimeoutDurationMs));
}
} // namespace cc } // namespace cc
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <vector> #include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/time/time.h"
#include "cc/cc_export.h" #include "cc/cc_export.h"
#include "cc/tiles/image_controller.h" #include "cc/tiles/image_controller.h"
...@@ -24,7 +25,9 @@ namespace cc { ...@@ -24,7 +25,9 @@ namespace cc {
// are silently ignored. // are silently ignored.
class CC_EXPORT DecodedImageTracker { class CC_EXPORT DecodedImageTracker {
public: public:
explicit DecodedImageTracker(ImageController* controller); explicit DecodedImageTracker(
ImageController* controller,
scoped_refptr<base::SequencedTaskRunner> task_runner);
~DecodedImageTracker(); ~DecodedImageTracker();
// Request that the given image be decoded. This issues a callback upon // Request that the given image be decoded. This issues a callback upon
...@@ -33,18 +36,57 @@ class CC_EXPORT DecodedImageTracker { ...@@ -33,18 +36,57 @@ class CC_EXPORT DecodedImageTracker {
void QueueImageDecode(const PaintImage& image, void QueueImageDecode(const PaintImage& image,
const gfx::ColorSpace& target_color_space, const gfx::ColorSpace& target_color_space,
const base::Callback<void(bool)>& callback); const base::Callback<void(bool)>& callback);
void NotifyFrameFinished();
// Unlock all locked images - used to respond to memory pressure or
// application background.
void UnlockAllImages();
// Notifies the tracker that images have been used, allowing it to
// unlock them.
void OnImagesUsedInDraw(const std::vector<DrawImage>& draw_images);
using NowFn = base::Callback<base::TimeTicks()>;
void SetNowFunctionForTesting(NowFn now_fn) { now_fn_ = now_fn; }
// Test only functions:
size_t NumLockedImagesForTesting() const { return locked_images_.size(); }
private: private:
friend class DecodedImageTrackerTest; friend class DecodedImageTrackerTest;
void ImageDecodeFinished(const base::Callback<void(bool)>& callback, void ImageDecodeFinished(const base::Callback<void(bool)>& callback,
ImageController::ImageDecodeRequestId id, PaintImage::Id image_id,
ImageController::ImageDecodeRequestId request_id,
ImageController::ImageDecodeResult result); ImageController::ImageDecodeResult result);
void OnTimeoutImages();
void EnqueueTimeout();
ImageController* image_controller_; ImageController* image_controller_;
std::vector<std::pair<ImageController::ImageDecodeRequestId, int>>
locked_images_; // Helper class tracking a locked image decode. Automatically releases the
// lock using the provided DecodedImageTracker* on destruction.
class ImageLock {
public:
ImageLock(DecodedImageTracker* tracker,
ImageController::ImageDecodeRequestId request_id,
base::TimeTicks lock_time);
~ImageLock();
base::TimeTicks lock_time() const { return lock_time_; }
private:
DecodedImageTracker* tracker_;
ImageController::ImageDecodeRequestId request_id_;
base::TimeTicks lock_time_;
DISALLOW_COPY_AND_ASSIGN(ImageLock);
};
base::flat_map<PaintImage::Id, std::unique_ptr<ImageLock>> locked_images_;
bool timeout_pending_ = false;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
// Defaults to base::TimeTicks::Now(), but overrideable for testing.
NowFn now_fn_;
base::WeakPtrFactory<DecodedImageTracker> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(DecodedImageTracker); DISALLOW_COPY_AND_ASSIGN(DecodedImageTracker);
}; };
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <vector> #include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/test/test_mock_time_task_runner.h"
#include "cc/paint/paint_image_builder.h" #include "cc/paint/paint_image_builder.h"
#include "cc/test/skia_common.h" #include "cc/test/skia_common.h"
#include "cc/tiles/decoded_image_tracker.h" #include "cc/tiles/decoded_image_tracker.h"
...@@ -63,15 +64,22 @@ class TestImageController : public ImageController { ...@@ -63,15 +64,22 @@ class TestImageController : public ImageController {
class DecodedImageTrackerTest : public testing::Test { class DecodedImageTrackerTest : public testing::Test {
public: public:
DecodedImageTrackerTest() : decoded_image_tracker_(&image_controller_) {} DecodedImageTrackerTest()
: task_runner_(new base::TestMockTimeTaskRunner()),
decoded_image_tracker_(&image_controller_, task_runner_) {
decoded_image_tracker_.SetNowFunctionForTesting(
base::Bind(&base::TestMockTimeTaskRunner::NowTicks, task_runner_));
}
TestImageController* image_controller() { return &image_controller_; } TestImageController* image_controller() { return &image_controller_; }
DecodedImageTracker* decoded_image_tracker() { DecodedImageTracker* decoded_image_tracker() {
return &decoded_image_tracker_; return &decoded_image_tracker_;
} }
base::TestMockTimeTaskRunner* task_runner() { return task_runner_.get(); }
private: private:
TestImageController image_controller_; TestImageController image_controller_;
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
DecodedImageTracker decoded_image_tracker_; DecodedImageTracker decoded_image_tracker_;
}; };
...@@ -85,7 +93,7 @@ TEST_F(DecodedImageTrackerTest, QueueImageLocksImages) { ...@@ -85,7 +93,7 @@ TEST_F(DecodedImageTrackerTest, QueueImageLocksImages) {
EXPECT_EQ(1u, image_controller()->num_locked_images()); EXPECT_EQ(1u, image_controller()->num_locked_images());
} }
TEST_F(DecodedImageTrackerTest, NotifyFrameFinishedUnlocksImages) { TEST_F(DecodedImageTrackerTest, Colorspace) {
bool locked = false; bool locked = false;
gfx::ColorSpace decoded_color_space( gfx::ColorSpace decoded_color_space(
gfx::ColorSpace::PrimaryID::XYZ_D50, gfx::ColorSpace::PrimaryID::XYZ_D50,
...@@ -96,11 +104,6 @@ TEST_F(DecodedImageTrackerTest, NotifyFrameFinishedUnlocksImages) { ...@@ -96,11 +104,6 @@ TEST_F(DecodedImageTrackerTest, NotifyFrameFinishedUnlocksImages) {
paint_image, decoded_color_space, paint_image, decoded_color_space,
base::Bind([](bool* locked, bool success) { *locked = true; }, base::Bind([](bool* locked, bool success) { *locked = true; },
base::Unretained(&locked))); base::Unretained(&locked)));
EXPECT_TRUE(locked);
EXPECT_EQ(1u, image_controller()->num_locked_images());
decoded_image_tracker()->NotifyFrameFinished();
EXPECT_EQ(1u, image_controller()->num_locked_images());
// Check that the decoded color space images are locked, but if the color // Check that the decoded color space images are locked, but if the color
// space differs then that image is not locked. Note that we use the high // space differs then that image is not locked. Note that we use the high
...@@ -114,19 +117,97 @@ TEST_F(DecodedImageTrackerTest, NotifyFrameFinishedUnlocksImages) { ...@@ -114,19 +117,97 @@ TEST_F(DecodedImageTrackerTest, NotifyFrameFinishedUnlocksImages) {
kHigh_SkFilterQuality, SkMatrix::I(), kHigh_SkFilterQuality, SkMatrix::I(),
PaintImage::kDefaultFrameIndex, srgb_color_space); PaintImage::kDefaultFrameIndex, srgb_color_space);
EXPECT_FALSE(image_controller()->IsDrawImageLocked(srgb_draw_image)); EXPECT_FALSE(image_controller()->IsDrawImageLocked(srgb_draw_image));
}
TEST_F(DecodedImageTrackerTest, ImagesTimeOut) {
// Add an image, this will start a 250ms timeout to release it.
bool locked = false;
decoded_image_tracker()->QueueImageDecode(
CreateDiscardablePaintImage(gfx::Size(1, 1)), gfx::ColorSpace(),
base::Bind([](bool* locked, bool success) { *locked = true; },
base::Unretained(&locked)));
EXPECT_TRUE(locked);
EXPECT_EQ(1u, image_controller()->num_locked_images());
// Advance by 150ms, the image should still be locked.
task_runner()->FastForwardBy(base::TimeDelta::FromMilliseconds(150));
EXPECT_EQ(1u, image_controller()->num_locked_images());
// Add an image, this will not start a new timeout, as one is pending.
decoded_image_tracker()->QueueImageDecode(
CreateDiscardablePaintImage(gfx::Size(1, 1)), gfx::ColorSpace(),
base::Bind([](bool* locked, bool success) { *locked = true; },
base::Unretained(&locked)));
EXPECT_TRUE(locked);
EXPECT_EQ(2u, image_controller()->num_locked_images());
// Advance by 100ms, we our first image should be released.
// Trigger a single commit, the first image should be unlocked.
task_runner()->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
EXPECT_EQ(1u, image_controller()->num_locked_images());
// Advance by another 250ms, our second image should release.
task_runner()->FastForwardBy(base::TimeDelta::FromMilliseconds(250));
EXPECT_EQ(0u, image_controller()->num_locked_images());
}
locked = false; TEST_F(DecodedImageTrackerTest, ImageUsedInDraw) {
// Insert two images:
bool locked = false;
auto paint_image_1 = CreateDiscardablePaintImage(gfx::Size(1, 1));
decoded_image_tracker()->QueueImageDecode( decoded_image_tracker()->QueueImageDecode(
CreateDiscardablePaintImage(gfx::Size(1, 1)), decoded_color_space, paint_image_1, gfx::ColorSpace(),
base::Bind([](bool* locked, bool success) { *locked = true; },
base::Unretained(&locked)));
EXPECT_TRUE(locked);
EXPECT_EQ(1u, image_controller()->num_locked_images());
auto paint_image_2 = CreateDiscardablePaintImage(gfx::Size(1, 1));
decoded_image_tracker()->QueueImageDecode(
paint_image_2, gfx::ColorSpace(),
base::Bind([](bool* locked, bool success) { *locked = true; }, base::Bind([](bool* locked, bool success) { *locked = true; },
base::Unretained(&locked))); base::Unretained(&locked)));
EXPECT_TRUE(locked); EXPECT_TRUE(locked);
EXPECT_EQ(2u, image_controller()->num_locked_images()); EXPECT_EQ(2u, image_controller()->num_locked_images());
decoded_image_tracker()->NotifyFrameFinished(); // Create dummy draw images for each:
DrawImage draw_image_1(paint_image_1, SkIRect::MakeWH(1, 1),
kHigh_SkFilterQuality, SkMatrix::I(), 0,
gfx::ColorSpace());
DrawImage draw_image_2(paint_image_2, SkIRect::MakeWH(1, 1),
kHigh_SkFilterQuality, SkMatrix::I(), 0,
gfx::ColorSpace());
// Both should be in the cache:
EXPECT_TRUE(image_controller()->IsDrawImageLocked(draw_image_1));
EXPECT_TRUE(image_controller()->IsDrawImageLocked(draw_image_2));
// Pretend we've drawn with image 2.
decoded_image_tracker()->OnImagesUsedInDraw({draw_image_2});
// Only image 1 should now be in the cache.
EXPECT_TRUE(image_controller()->IsDrawImageLocked(draw_image_1));
EXPECT_FALSE(image_controller()->IsDrawImageLocked(draw_image_2));
}
TEST_F(DecodedImageTrackerTest, UnlockAllImages) {
// Insert two images:
bool locked = false;
decoded_image_tracker()->QueueImageDecode(
CreateDiscardablePaintImage(gfx::Size(1, 1)), gfx::ColorSpace(),
base::Bind([](bool* locked, bool success) { *locked = true; },
base::Unretained(&locked)));
EXPECT_TRUE(locked);
EXPECT_EQ(1u, image_controller()->num_locked_images()); EXPECT_EQ(1u, image_controller()->num_locked_images());
decoded_image_tracker()->QueueImageDecode(
CreateDiscardablePaintImage(gfx::Size(1, 1)), gfx::ColorSpace(),
base::Bind([](bool* locked, bool success) { *locked = true; },
base::Unretained(&locked)));
EXPECT_TRUE(locked);
EXPECT_EQ(2u, image_controller()->num_locked_images());
decoded_image_tracker()->NotifyFrameFinished(); // Unlock all images.
decoded_image_tracker()->UnlockAllImages();
EXPECT_EQ(0u, image_controller()->num_locked_images()); EXPECT_EQ(0u, image_controller()->num_locked_images());
} }
......
...@@ -362,7 +362,7 @@ TileManager::TileManager( ...@@ -362,7 +362,7 @@ TileManager::TileManager(
did_oom_on_last_assign_(false), did_oom_on_last_assign_(false),
image_controller_(origin_task_runner, image_controller_(origin_task_runner,
std::move(image_worker_task_runner)), std::move(image_worker_task_runner)),
decoded_image_tracker_(&image_controller_), decoded_image_tracker_(&image_controller_, origin_task_runner),
checker_image_tracker_(&image_controller_, checker_image_tracker_(&image_controller_,
this, this,
tile_manager_settings_.enable_checker_imaging, tile_manager_settings_.enable_checker_imaging,
...@@ -1032,8 +1032,10 @@ void TileManager::ScheduleTasks(PrioritizedWorkToSchedule work_to_schedule) { ...@@ -1032,8 +1032,10 @@ void TileManager::ScheduleTasks(PrioritizedWorkToSchedule work_to_schedule) {
prepare_tiles_count_, TilePriority::SOON, prepare_tiles_count_, TilePriority::SOON,
ImageDecodeCache::TaskType::kInRaster); ImageDecodeCache::TaskType::kInRaster);
std::vector<scoped_refptr<TileTask>> new_locked_image_tasks = std::vector<scoped_refptr<TileTask>> new_locked_image_tasks =
image_controller_.SetPredecodeImages(std::move(new_locked_images), image_controller_.SetPredecodeImages(new_locked_images, tracing_info);
tracing_info); // Notify |decoded_image_tracker_| after |image_controller_| to ensure we've
// taken new refs on the images before releasing the predecode API refs.
decoded_image_tracker_.OnImagesUsedInDraw(new_locked_images);
work_to_schedule.extra_prepaint_images.clear(); work_to_schedule.extra_prepaint_images.clear();
for (auto& task : new_locked_image_tasks) { for (auto& task : new_locked_image_tasks) {
...@@ -1155,6 +1157,9 @@ scoped_refptr<TileTask> TileManager::CreateRasterTask( ...@@ -1155,6 +1157,9 @@ scoped_refptr<TileTask> TileManager::CreateRasterTask(
bool has_at_raster_images = false; bool has_at_raster_images = false;
image_controller_.GetTasksForImagesAndRef( image_controller_.GetTasksForImagesAndRef(
&sync_decoded_images, &decode_tasks, &has_at_raster_images, tracing_info); &sync_decoded_images, &decode_tasks, &has_at_raster_images, tracing_info);
// Notify |decoded_image_tracker_| after |image_controller_| to ensure we've
// taken new refs on the images before releasing the predecode API refs.
decoded_image_tracker_.OnImagesUsedInDraw(sync_decoded_images);
const bool has_checker_images = !checkered_images.empty(); const bool has_checker_images = !checkered_images.empty();
tile->set_raster_task_scheduled_with_checker_images(has_checker_images); tile->set_raster_task_scheduled_with_checker_images(has_checker_images);
......
...@@ -3220,5 +3220,124 @@ TEST_F(SynchronousRasterTileManagerTest, AlwaysUseImageCache) { ...@@ -3220,5 +3220,124 @@ TEST_F(SynchronousRasterTileManagerTest, AlwaysUseImageCache) {
TakeHostImpl(); TakeHostImpl();
} }
class DecodedImageTrackerTileManagerTest : public TestLayerTreeHostBase {
public:
void TearDown() override {
// Allow all tasks on the image worker to run now. Any scheduled decodes
// will be aborted.
task_runner_->set_run_tasks_synchronously(true);
}
LayerTreeSettings CreateSettings() override {
LayerTreeSettings settings = TestLayerTreeHostBase::CreateSettings();
settings.max_preraster_distance_in_screen_pixels = 100;
return settings;
}
std::unique_ptr<FakeLayerTreeHostImpl> CreateHostImpl(
const LayerTreeSettings& settings,
TaskRunnerProvider* task_runner_provider,
TaskGraphRunner* task_graph_runner) override {
task_runner_ = base::MakeRefCounted<SynchronousSimpleTaskRunner>();
return std::make_unique<FakeLayerTreeHostImpl>(
settings, task_runner_provider, task_graph_runner, task_runner_);
}
std::unique_ptr<TaskGraphRunner> CreateTaskGraphRunner() override {
return std::make_unique<SynchronousTaskGraphRunner>();
}
void FlushDecodeTasks() {
while (task_runner_->HasPendingTask()) {
task_runner_->RunUntilIdle();
base::RunLoop().RunUntilIdle();
}
}
private:
scoped_refptr<SynchronousSimpleTaskRunner> task_runner_;
};
TEST_F(DecodedImageTrackerTileManagerTest, DecodedImageTrackerDropsLocksOnUse) {
// Pick arbitrary IDs - they don't really matter as long as they're constant.
const int kLayerId = 7;
host_impl()->tile_manager()->SetTileTaskManagerForTesting(
std::make_unique<FakeTileTaskManagerImpl>());
// Create two test images, one will be positioned to be needed NOW, the other
// will be positioned to be prepaint.
int dimension = 250;
PaintImage image1 =
CreateDiscardablePaintImage(gfx::Size(dimension, dimension));
PaintImage image2 =
CreateDiscardablePaintImage(gfx::Size(dimension, dimension));
// Add the images to our decoded_image_tracker.
host_impl()->tile_manager()->decoded_image_tracker().QueueImageDecode(
image1, gfx::ColorSpace(), base::DoNothing());
host_impl()->tile_manager()->decoded_image_tracker().QueueImageDecode(
image2, gfx::ColorSpace(), base::DoNothing());
EXPECT_EQ(0u, host_impl()
->tile_manager()
->decoded_image_tracker()
.NumLockedImagesForTesting());
FlushDecodeTasks();
EXPECT_EQ(2u, host_impl()
->tile_manager()
->decoded_image_tracker()
.NumLockedImagesForTesting());
// Add images to a fake recording source.
const gfx::Size layer_bounds(1000, 500);
std::unique_ptr<FakeRecordingSource> recording_source =
FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
recording_source->set_fill_with_nonsolid_color(true);
recording_source->add_draw_image(image1, gfx::Point(0, 0));
recording_source->add_draw_image(image2, gfx::Point(700, 0));
recording_source->Rerecord();
scoped_refptr<FakeRasterSource> pending_raster_source =
FakeRasterSource::CreateFromRecordingSource(recording_source.get());
host_impl()->CreatePendingTree();
LayerTreeImpl* pending_tree = host_impl()->pending_tree();
pending_tree->SetDeviceViewportSize(
host_impl()->active_tree()->GetDeviceViewport().size());
// Steal from the recycled tree.
std::unique_ptr<FakePictureLayerImpl> pending_layer =
FakePictureLayerImpl::CreateWithRasterSource(pending_tree, kLayerId,
pending_raster_source);
pending_layer->SetDrawsContent(true);
// The bounds() are half the recording source size, allowing for prepaint
// images.
pending_layer->SetBounds(gfx::Size(500, 500));
pending_tree->SetRootLayerForTesting(std::move(pending_layer));
// Add tilings/tiles for the layer.
host_impl()->pending_tree()->BuildLayerListAndPropertyTreesForTesting();
host_impl()->pending_tree()->UpdateDrawProperties();
// Build the raster queue and invalidate the top tile if partial raster is
// enabled.
std::unique_ptr<RasterTilePriorityQueue> queue(host_impl()->BuildRasterQueue(
SAME_PRIORITY_FOR_BOTH_TREES, RasterTilePriorityQueue::Type::ALL));
ASSERT_FALSE(queue->IsEmpty());
// PrepareTiles to schedule tasks. This should cause the decoded image tracker
// to release its lock.
EXPECT_EQ(2u, host_impl()
->tile_manager()
->decoded_image_tracker()
.NumLockedImagesForTesting());
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_EQ(0u, host_impl()
->tile_manager()
->decoded_image_tracker()
.NumLockedImagesForTesting());
}
} // namespace } // namespace
} // namespace cc } // namespace cc
...@@ -2441,7 +2441,6 @@ void LayerTreeHostImpl::DidFinishImplFrame() { ...@@ -2441,7 +2441,6 @@ void LayerTreeHostImpl::DidFinishImplFrame() {
skipped_frame_tracker_.FinishFrame(); skipped_frame_tracker_.FinishFrame();
impl_thread_phase_ = ImplThreadPhase::IDLE; impl_thread_phase_ = ImplThreadPhase::IDLE;
current_begin_frame_tracker_.Finish(); current_begin_frame_tracker_.Finish();
tile_manager_.decoded_image_tracker().NotifyFrameFinished();
} }
void LayerTreeHostImpl::DidNotProduceFrame(const viz::BeginFrameAck& ack) { void LayerTreeHostImpl::DidNotProduceFrame(const viz::BeginFrameAck& ack) {
...@@ -2833,6 +2832,7 @@ void LayerTreeHostImpl::OnPurgeMemory() { ...@@ -2833,6 +2832,7 @@ void LayerTreeHostImpl::OnPurgeMemory() {
} }
if (resource_pool_) if (resource_pool_)
resource_pool_->OnPurgeMemory(); resource_pool_->OnPurgeMemory();
tile_manager_.decoded_image_tracker().UnlockAllImages();
} }
void LayerTreeHostImpl::OnMemoryPressure( void LayerTreeHostImpl::OnMemoryPressure(
...@@ -2880,6 +2880,7 @@ void LayerTreeHostImpl::SetVisible(bool visible) { ...@@ -2880,6 +2880,7 @@ void LayerTreeHostImpl::SetVisible(bool visible) {
EvictAllUIResources(); EvictAllUIResources();
// Call PrepareTiles to evict tiles when we become invisible. // Call PrepareTiles to evict tiles when we become invisible.
PrepareTiles(); PrepareTiles();
tile_manager_.decoded_image_tracker().UnlockAllImages();
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment