[go: nahoru, domu]

blob: 6c9959d9e6fbcb74def7421d4b8f2cc88ece2568 [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include <utility>
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/test/test_simple_task_runner.h"
#include "base/time/time_override.h"
#include "base/trace_event/process_memory_dump.h"
#include "cc/base/features.h"
#include "cc/layers/recording_source.h"
#include "cc/raster/raster_buffer.h"
#include "cc/raster/raster_source.h"
#include "cc/raster/synchronous_task_graph_runner.h"
#include "cc/resources/resource_pool.h"
#include "cc/test/fake_impl_task_runner_provider.h"
#include "cc/test/fake_layer_tree_frame_sink.h"
#include "cc/test/fake_layer_tree_frame_sink_client.h"
#include "cc/test/fake_layer_tree_host_impl.h"
#include "cc/test/fake_paint_image_generator.h"
#include "cc/test/fake_picture_layer_impl.h"
#include "cc/test/fake_picture_layer_tiling_client.h"
#include "cc/test/fake_raster_query_queue.h"
#include "cc/test/fake_raster_source.h"
#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/layer_test_common.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"
#include "cc/tiles/eviction_tile_priority_queue.h"
#include "cc/tiles/raster_tile_priority_queue.h"
#include "cc/tiles/tile.h"
#include "cc/tiles/tile_priority.h"
#include "cc/tiles/tiling_set_raster_queue_all.h"
#include "cc/trees/layer_tree_impl.h"
#include "components/viz/common/resources/resource_sizes.h"
#include "components/viz/common/resources/shared_image_format_utils.h"
#include "components/viz/test/begin_frame_args_test.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkImageGenerator.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkSurface.h"
using testing::_;
using testing::Invoke;
using testing::Return;
using testing::StrictMock;
namespace cc {
namespace {
// A version of simple task runner that lets the user control if all tasks
// posted should run synchronously.
class SynchronousSimpleTaskRunner : public base::TestSimpleTaskRunner {
public:
bool PostDelayedTask(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
TestSimpleTaskRunner::PostDelayedTask(from_here, std::move(task), delay);
if (run_tasks_synchronously_)
RunUntilIdle();
return true;
}
bool PostNonNestableDelayedTask(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
return PostDelayedTask(from_here, std::move(task), delay);
}
void set_run_tasks_synchronously(bool run_tasks_synchronously) {
run_tasks_synchronously_ = run_tasks_synchronously;
}
protected:
~SynchronousSimpleTaskRunner() override = default;
bool run_tasks_synchronously_ = false;
};
class FakeRasterBuffer : public RasterBuffer {
public:
FakeRasterBuffer() = default;
explicit FakeRasterBuffer(float expected_hdr_headroom)
: expected_hdr_headroom_(expected_hdr_headroom) {}
void Playback(const RasterSource* raster_source,
const gfx::Rect& raster_full_rect,
const gfx::Rect& raster_dirty_rect,
uint64_t new_content_id,
const gfx::AxisTransform2d& transform,
const RasterSource::PlaybackSettings& playback_settings,
const GURL& url) override {
EXPECT_EQ(expected_hdr_headroom_, playback_settings.hdr_headroom);
}
bool SupportsBackgroundThreadPriority() const override { return true; }
private:
const float expected_hdr_headroom_ = 1.f;
};
class TileManagerTilePriorityQueueTest : public TestLayerTreeHostBase {
public:
LayerTreeSettings CreateSettings() override {
auto settings = TestLayerTreeHostBase::CreateSettings();
settings.create_low_res_tiling = true;
return settings;
}
TileManager* tile_manager() { return host_impl()->tile_manager(); }
class StubGpuBacking : public ResourcePool::GpuBacking {
public:
void OnMemoryDump(
base::trace_event::ProcessMemoryDump* pmd,
const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid,
uint64_t tracing_process_id,
int importance) const override {}
};
};
TEST_F(TileManagerTilePriorityQueueTest, RasterTilePriorityQueue) {
const gfx::Size layer_bounds(1000, 1000);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
SetupDefaultTrees(layer_bounds);
std::unique_ptr<RasterTilePriorityQueue> queue(host_impl()->BuildRasterQueue(
SAME_PRIORITY_FOR_BOTH_TREES, RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(queue->IsEmpty());
size_t tile_count = 0;
std::set<Tile*> all_tiles;
while (!queue->IsEmpty()) {
EXPECT_TRUE(queue->Top().tile());
all_tiles.insert(queue->Top().tile());
++tile_count;
queue->Pop();
}
EXPECT_EQ(tile_count, all_tiles.size());
EXPECT_EQ(16u, tile_count);
// Sanity check, all tiles should be visible.
std::set<Tile*> smoothness_tiles;
queue = host_impl()->BuildRasterQueue(SMOOTHNESS_TAKES_PRIORITY,
RasterTilePriorityQueue::Type::ALL);
bool had_low_res = false;
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
EXPECT_TRUE(prioritized_tile.tile());
EXPECT_EQ(TilePriority::NOW, prioritized_tile.priority().priority_bin);
if (prioritized_tile.priority().resolution == LOW_RESOLUTION)
had_low_res = true;
else
smoothness_tiles.insert(prioritized_tile.tile());
queue->Pop();
}
EXPECT_EQ(all_tiles, smoothness_tiles);
EXPECT_TRUE(had_low_res);
// Check that everything is required for activation.
queue = host_impl()->BuildRasterQueue(
SMOOTHNESS_TAKES_PRIORITY,
RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION);
std::set<Tile*> required_for_activation_tiles;
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
EXPECT_TRUE(prioritized_tile.tile()->required_for_activation());
required_for_activation_tiles.insert(prioritized_tile.tile());
queue->Pop();
}
EXPECT_EQ(all_tiles, required_for_activation_tiles);
// Check that everything is required for draw.
queue = host_impl()->BuildRasterQueue(
SMOOTHNESS_TAKES_PRIORITY,
RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW);
std::set<Tile*> required_for_draw_tiles;
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
EXPECT_TRUE(prioritized_tile.tile()->required_for_draw());
required_for_draw_tiles.insert(prioritized_tile.tile());
queue->Pop();
}
EXPECT_EQ(all_tiles, required_for_draw_tiles);
Region invalidation(gfx::Rect(0, 0, 500, 500));
// Invalidate the pending tree.
pending_layer()->set_invalidation(invalidation);
pending_layer()->HighResTiling()->Invalidate(invalidation);
// Renew all of the tile priorities.
gfx::Rect viewport(50, 50, 100, 100);
pending_layer()->picture_layer_tiling_set()->UpdateTilePriorities(
viewport, 1.0f, 1.0, Occlusion(), true);
active_layer()->picture_layer_tiling_set()->UpdateTilePriorities(
viewport, 1.0f, 1.0, Occlusion(), true);
// Populate all tiles directly from the tilings.
all_tiles.clear();
std::set<Tile*> high_res_tiles;
std::vector<Tile*> pending_high_res_tiles =
pending_layer()->HighResTiling()->AllTilesForTesting();
for (size_t i = 0; i < pending_high_res_tiles.size(); ++i) {
all_tiles.insert(pending_high_res_tiles[i]);
high_res_tiles.insert(pending_high_res_tiles[i]);
}
std::vector<Tile*> active_high_res_tiles =
active_layer()->HighResTiling()->AllTilesForTesting();
for (size_t i = 0; i < active_high_res_tiles.size(); ++i) {
all_tiles.insert(active_high_res_tiles[i]);
high_res_tiles.insert(active_high_res_tiles[i]);
}
std::vector<Tile*> active_low_res_tiles =
active_layer()->LowResTiling()->AllTilesForTesting();
for (size_t i = 0; i < active_low_res_tiles.size(); ++i)
all_tiles.insert(active_low_res_tiles[i]);
PrioritizedTile last_tile;
smoothness_tiles.clear();
tile_count = 0;
size_t correct_order_tiles = 0u;
// Here we expect to get increasing ACTIVE_TREE priority_bin.
queue = host_impl()->BuildRasterQueue(SMOOTHNESS_TAKES_PRIORITY,
RasterTilePriorityQueue::Type::ALL);
std::set<Tile*> expected_required_for_draw_tiles;
std::set<Tile*> expected_required_for_activation_tiles;
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
EXPECT_TRUE(prioritized_tile.tile());
if (!last_tile.tile())
last_tile = prioritized_tile;
EXPECT_LE(last_tile.priority().priority_bin,
prioritized_tile.priority().priority_bin);
bool skip_updating_last_tile = false;
if (last_tile.priority().priority_bin ==
prioritized_tile.priority().priority_bin) {
correct_order_tiles += last_tile.priority().distance_to_visible <=
prioritized_tile.priority().distance_to_visible;
} else if (prioritized_tile.priority().priority_bin == TilePriority::NOW) {
// Since we'd return pending tree now tiles before the eventually tiles on
// the active tree, update the value.
++correct_order_tiles;
skip_updating_last_tile = true;
}
if (prioritized_tile.priority().priority_bin == TilePriority::NOW &&
last_tile.priority().resolution !=
prioritized_tile.priority().resolution) {
// Low resolution should come first.
EXPECT_EQ(LOW_RESOLUTION, last_tile.priority().resolution);
}
if (!skip_updating_last_tile)
last_tile = prioritized_tile;
++tile_count;
smoothness_tiles.insert(prioritized_tile.tile());
if (prioritized_tile.tile()->required_for_draw())
expected_required_for_draw_tiles.insert(prioritized_tile.tile());
if (prioritized_tile.tile()->required_for_activation())
expected_required_for_activation_tiles.insert(prioritized_tile.tile());
queue->Pop();
}
EXPECT_EQ(tile_count, smoothness_tiles.size());
EXPECT_EQ(all_tiles, smoothness_tiles);
// Since we don't guarantee increasing distance due to spiral iterator, we
// should check that we're _mostly_ right.
EXPECT_GT(correct_order_tiles, 3 * tile_count / 4);
// Check that we have consistent required_for_activation tiles.
queue = host_impl()->BuildRasterQueue(
SMOOTHNESS_TAKES_PRIORITY,
RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION);
required_for_activation_tiles.clear();
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
EXPECT_TRUE(prioritized_tile.tile()->required_for_activation());
required_for_activation_tiles.insert(prioritized_tile.tile());
queue->Pop();
}
EXPECT_EQ(expected_required_for_activation_tiles,
required_for_activation_tiles);
EXPECT_NE(all_tiles, required_for_activation_tiles);
// Check that we have consistent required_for_draw tiles.
queue = host_impl()->BuildRasterQueue(
SMOOTHNESS_TAKES_PRIORITY,
RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW);
required_for_draw_tiles.clear();
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
EXPECT_TRUE(prioritized_tile.tile()->required_for_draw());
required_for_draw_tiles.insert(prioritized_tile.tile());
queue->Pop();
}
EXPECT_EQ(expected_required_for_draw_tiles, required_for_draw_tiles);
EXPECT_NE(all_tiles, required_for_draw_tiles);
std::set<Tile*> new_content_tiles;
last_tile = PrioritizedTile();
size_t increasing_distance_tiles = 0u;
// Here we expect to get increasing PENDING_TREE priority_bin.
queue = host_impl()->BuildRasterQueue(NEW_CONTENT_TAKES_PRIORITY,
RasterTilePriorityQueue::Type::ALL);
tile_count = 0;
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
EXPECT_TRUE(prioritized_tile.tile());
if (!last_tile.tile())
last_tile = prioritized_tile;
EXPECT_LE(last_tile.priority().priority_bin,
prioritized_tile.priority().priority_bin);
if (last_tile.priority().priority_bin ==
prioritized_tile.priority().priority_bin) {
increasing_distance_tiles +=
last_tile.priority().distance_to_visible <=
prioritized_tile.priority().distance_to_visible;
}
if (prioritized_tile.priority().priority_bin == TilePriority::NOW &&
last_tile.priority().resolution !=
prioritized_tile.priority().resolution) {
// High resolution should come first.
EXPECT_EQ(HIGH_RESOLUTION, last_tile.priority().resolution);
}
last_tile = prioritized_tile;
new_content_tiles.insert(prioritized_tile.tile());
++tile_count;
queue->Pop();
}
EXPECT_EQ(tile_count, new_content_tiles.size());
EXPECT_EQ(high_res_tiles, new_content_tiles);
// Since we don't guarantee increasing distance due to spiral iterator, we
// should check that we're _mostly_ right.
EXPECT_GE(increasing_distance_tiles, 3 * tile_count / 4);
// Check that we have consistent required_for_activation tiles.
queue = host_impl()->BuildRasterQueue(
NEW_CONTENT_TAKES_PRIORITY,
RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION);
required_for_activation_tiles.clear();
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
EXPECT_TRUE(prioritized_tile.tile()->required_for_activation());
required_for_activation_tiles.insert(prioritized_tile.tile());
queue->Pop();
}
EXPECT_EQ(expected_required_for_activation_tiles,
required_for_activation_tiles);
EXPECT_NE(new_content_tiles, required_for_activation_tiles);
// Check that we have consistent required_for_draw tiles.
queue = host_impl()->BuildRasterQueue(
NEW_CONTENT_TAKES_PRIORITY,
RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW);
required_for_draw_tiles.clear();
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
EXPECT_TRUE(prioritized_tile.tile()->required_for_draw());
required_for_draw_tiles.insert(prioritized_tile.tile());
queue->Pop();
}
EXPECT_EQ(expected_required_for_draw_tiles, required_for_draw_tiles);
EXPECT_NE(new_content_tiles, required_for_draw_tiles);
}
TEST_F(TileManagerTilePriorityQueueTest,
RasterTilePriorityQueueAll_GetNextQueues) {
host_impl()->AdvanceToNextFrame(base::Milliseconds(1));
gfx::Size layer_bounds(1000, 1000);
SetupDefaultTrees(layer_bounds);
// Create a pending child layer.
scoped_refptr<FakeRasterSource> pending_raster_source =
FakeRasterSource::CreateFilled(layer_bounds);
auto* pending_child = AddLayer<FakePictureLayerImpl>(
host_impl()->pending_tree(), pending_raster_source);
pending_child->SetDrawsContent(true);
CopyProperties(pending_layer(), pending_child);
// Set a small viewport, so we have soon and eventually tiles.
host_impl()->active_tree()->SetDeviceViewportRect(
gfx::Rect(100, 100, 200, 200));
host_impl()->AdvanceToNextFrame(base::Milliseconds(1));
UpdateDrawProperties(host_impl()->active_tree());
UpdateDrawProperties(host_impl()->pending_tree());
host_impl()->SetRequiresHighResToDraw();
// (1) SMOOTHNESS_TAKES_PRIORITY
{
std::unique_ptr<RasterTilePriorityQueue> queue(
host_impl()->BuildRasterQueue(SMOOTHNESS_TAKES_PRIORITY,
RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(queue->IsEmpty());
TilePriority::PriorityBin last_bin = TilePriority::NOW;
WhichTree las_tree = WhichTree::ACTIVE_TREE;
float last_distance = 0.f;
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
WhichTree tree = prioritized_tile.source_tiling()->tree();
TilePriority::PriorityBin priority_bin =
prioritized_tile.priority().priority_bin;
float distance_to_visible =
prioritized_tile.priority().distance_to_visible;
// Higher priority bin should come before lower priority bin regardless
// of the tree.
EXPECT_LE(last_bin, priority_bin);
// If SMOOTHNESS_TAKES_PRIORITY, ACTIVE_TREE comes before PENDING_TREE for
// NOW bin. The rest bins use the IsHigherPriorityThan condition.
if (priority_bin == TilePriority::NOW) {
EXPECT_LE(las_tree, tree);
} else {
// Reset the distance if it's in a different bin.
if (last_bin != priority_bin)
last_distance = 0;
if (las_tree != tree)
EXPECT_LE(last_distance, distance_to_visible);
}
last_bin = priority_bin;
las_tree = tree;
last_distance = distance_to_visible;
queue->Pop();
}
}
// (2) NEW_CONTENT_TAKES_PRIORITY
{
std::unique_ptr<RasterTilePriorityQueue> queue(
host_impl()->BuildRasterQueue(NEW_CONTENT_TAKES_PRIORITY,
RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(queue->IsEmpty());
TilePriority::PriorityBin last_bin = TilePriority::NOW;
WhichTree las_tree = WhichTree::PENDING_TREE;
float last_distance = 0.f;
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
WhichTree tree = prioritized_tile.source_tiling()->tree();
TilePriority::PriorityBin priority_bin =
prioritized_tile.priority().priority_bin;
float distance_to_visible =
prioritized_tile.priority().distance_to_visible;
// Higher priority bin should come before lower priority bin regardless
// of the tree.
EXPECT_LE(last_bin, priority_bin);
// If SMOOTHNESS_TAKES_PRIORITY, PENDING_TREE comes before ACTIVE_TREE for
// NOW bin. The rest bins use the IsHigherPriorityThan condition.
if (priority_bin == TilePriority::NOW) {
EXPECT_GE(las_tree, tree);
} else {
// Reset the distance if it's in a different bin.
if (last_bin != priority_bin)
last_distance = 0;
if (las_tree != tree)
EXPECT_LE(last_distance, distance_to_visible);
}
last_bin = priority_bin;
las_tree = tree;
last_distance = distance_to_visible;
queue->Pop();
}
}
// (3) SAME_PRIORITY_FOR_BOTH_TREES
{
std::unique_ptr<RasterTilePriorityQueue> queue(
host_impl()->BuildRasterQueue(SAME_PRIORITY_FOR_BOTH_TREES,
RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(queue->IsEmpty());
TilePriority::PriorityBin last_bin = TilePriority::NOW;
WhichTree las_tree = WhichTree::PENDING_TREE;
float last_distance = 0.f;
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
WhichTree tree = prioritized_tile.source_tiling()->tree();
TilePriority::PriorityBin priority_bin =
prioritized_tile.priority().priority_bin;
float distance_to_visible =
prioritized_tile.priority().distance_to_visible;
// Higher priority bin should come before lower priority bin regardless
// of the tree.
EXPECT_LE(last_bin, priority_bin);
// Reset the distance if it's in a different bin.
if (last_bin != priority_bin)
last_distance = 0;
// Use the IsHigherPriorityThan() condition.
if (las_tree != tree)
EXPECT_LE(last_distance, distance_to_visible);
last_bin = priority_bin;
las_tree = tree;
last_distance = distance_to_visible;
queue->Pop();
}
}
}
TEST_F(TileManagerTilePriorityQueueTest,
RasterTilePriorityQueueHighNonIdealTilings) {
const gfx::Size layer_bounds(1000, 1000);
const gfx::Size viewport(800, 800);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(viewport));
SetupDefaultTrees(layer_bounds);
pending_layer()->tilings()->AddTiling(
gfx::AxisTransform2d(1.5f, gfx::Vector2dF()),
pending_layer()->raster_source());
active_layer()->tilings()->AddTiling(
gfx::AxisTransform2d(1.5f, gfx::Vector2dF()),
active_layer()->raster_source());
pending_layer()->tilings()->AddTiling(
gfx::AxisTransform2d(1.7f, gfx::Vector2dF()),
pending_layer()->raster_source());
active_layer()->tilings()->AddTiling(
gfx::AxisTransform2d(1.7f, gfx::Vector2dF()),
active_layer()->raster_source());
pending_layer()->tilings()->UpdateTilePriorities(gfx::Rect(viewport), 1.f,
5.0, Occlusion(), true);
active_layer()->tilings()->UpdateTilePriorities(gfx::Rect(viewport), 1.f, 5.0,
Occlusion(), true);
std::set<Tile*> all_expected_tiles;
for (size_t i = 0; i < pending_layer()->num_tilings(); ++i) {
PictureLayerTiling* tiling = pending_layer()->tilings()->tiling_at(i);
if (tiling->contents_scale_key() == 1.f) {
tiling->set_resolution(HIGH_RESOLUTION);
const auto& all_tiles = tiling->AllTilesForTesting();
all_expected_tiles.insert(all_tiles.begin(), all_tiles.end());
} else {
tiling->set_resolution(NON_IDEAL_RESOLUTION);
}
}
for (size_t i = 0; i < active_layer()->num_tilings(); ++i) {
PictureLayerTiling* tiling = active_layer()->tilings()->tiling_at(i);
if (tiling->contents_scale_key() == 1.5f) {
tiling->set_resolution(HIGH_RESOLUTION);
const auto& all_tiles = tiling->AllTilesForTesting();
all_expected_tiles.insert(all_tiles.begin(), all_tiles.end());
} else {
tiling->set_resolution(NON_IDEAL_RESOLUTION);
// Non ideal tilings with a high res pending twin have to be processed
// because of possible activation tiles.
if (tiling->contents_scale_key() == 1.f) {
tiling->UpdateAndGetAllPrioritizedTilesForTesting();
const auto& all_tiles = tiling->AllTilesForTesting();
for (auto* tile : all_tiles)
EXPECT_TRUE(tile->required_for_activation());
all_expected_tiles.insert(all_tiles.begin(), all_tiles.end());
}
}
}
std::unique_ptr<RasterTilePriorityQueue> queue(host_impl()->BuildRasterQueue(
SMOOTHNESS_TAKES_PRIORITY, RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(queue->IsEmpty());
size_t tile_count = 0;
std::set<Tile*> all_actual_tiles;
while (!queue->IsEmpty()) {
EXPECT_TRUE(queue->Top().tile());
all_actual_tiles.insert(queue->Top().tile());
++tile_count;
queue->Pop();
}
EXPECT_EQ(tile_count, all_actual_tiles.size());
EXPECT_EQ(all_expected_tiles.size(), all_actual_tiles.size());
EXPECT_EQ(all_expected_tiles, all_actual_tiles);
}
TEST_F(TileManagerTilePriorityQueueTest,
RasterTilePriorityQueueHighLowTilings) {
const gfx::Size layer_bounds(1000, 1000);
const gfx::Size viewport(800, 800);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(viewport));
SetupDefaultTrees(layer_bounds);
pending_layer()->tilings()->AddTiling(
gfx::AxisTransform2d(1.5f, gfx::Vector2dF()),
pending_layer()->raster_source());
active_layer()->tilings()->AddTiling(
gfx::AxisTransform2d(1.5f, gfx::Vector2dF()),
active_layer()->raster_source());
pending_layer()->tilings()->AddTiling(
gfx::AxisTransform2d(1.7f, gfx::Vector2dF()),
pending_layer()->raster_source());
active_layer()->tilings()->AddTiling(
gfx::AxisTransform2d(1.7f, gfx::Vector2dF()),
active_layer()->raster_source());
pending_layer()->tilings()->UpdateTilePriorities(gfx::Rect(viewport), 1.f,
5.0, Occlusion(), true);
active_layer()->tilings()->UpdateTilePriorities(gfx::Rect(viewport), 1.f, 5.0,
Occlusion(), true);
std::set<Tile*> all_expected_tiles;
for (size_t i = 0; i < pending_layer()->num_tilings(); ++i) {
PictureLayerTiling* tiling = pending_layer()->tilings()->tiling_at(i);
if (tiling->contents_scale_key() == 1.f) {
tiling->set_resolution(HIGH_RESOLUTION);
const auto& all_tiles = tiling->AllTilesForTesting();
all_expected_tiles.insert(all_tiles.begin(), all_tiles.end());
} else {
tiling->set_resolution(NON_IDEAL_RESOLUTION);
}
}
for (size_t i = 0; i < active_layer()->num_tilings(); ++i) {
PictureLayerTiling* tiling = active_layer()->tilings()->tiling_at(i);
if (tiling->contents_scale_key() == 1.5f) {
tiling->set_resolution(HIGH_RESOLUTION);
const auto& all_tiles = tiling->AllTilesForTesting();
all_expected_tiles.insert(all_tiles.begin(), all_tiles.end());
} else {
tiling->set_resolution(LOW_RESOLUTION);
// Low res tilings with a high res pending twin have to be processed
// because of possible activation tiles.
if (tiling->contents_scale_key() == 1.f) {
tiling->UpdateAndGetAllPrioritizedTilesForTesting();
const auto& all_tiles = tiling->AllTilesForTesting();
for (auto* tile : all_tiles)
EXPECT_TRUE(tile->required_for_activation());
all_expected_tiles.insert(all_tiles.begin(), all_tiles.end());
}
}
}
std::unique_ptr<RasterTilePriorityQueue> queue(host_impl()->BuildRasterQueue(
SAME_PRIORITY_FOR_BOTH_TREES, RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(queue->IsEmpty());
size_t tile_count = 0;
std::set<Tile*> all_actual_tiles;
while (!queue->IsEmpty()) {
EXPECT_TRUE(queue->Top().tile());
all_actual_tiles.insert(queue->Top().tile());
++tile_count;
queue->Pop();
}
EXPECT_EQ(tile_count, all_actual_tiles.size());
EXPECT_EQ(all_expected_tiles.size(), all_actual_tiles.size());
EXPECT_EQ(all_expected_tiles, all_actual_tiles);
}
TEST_F(TileManagerTilePriorityQueueTest, RasterTilePriorityQueueInvalidation) {
const gfx::Size layer_bounds(1000, 1000);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(500, 500));
SetupDefaultTrees(layer_bounds);
// Use a tile's content rect as an invalidation. We should inset it a bit to
// ensure that border math doesn't invalidate neighbouring tiles.
gfx::Rect invalidation =
active_layer()->HighResTiling()->TileAt(1, 0)->content_rect();
invalidation.Inset(2);
pending_layer()->set_invalidation(invalidation);
pending_layer()->HighResTiling()->Invalidate(invalidation);
pending_layer()->HighResTiling()->CreateMissingTilesInLiveTilesRect();
// Sanity checks: Tile at 0, 0 not exist on the pending tree (it's not
// invalidated). Tile 1, 0 should exist on both.
EXPECT_FALSE(pending_layer()->HighResTiling()->TileAt(0, 0));
EXPECT_TRUE(active_layer()->HighResTiling()->TileAt(0, 0));
EXPECT_TRUE(pending_layer()->HighResTiling()->TileAt(1, 0));
EXPECT_TRUE(active_layer()->HighResTiling()->TileAt(1, 0));
EXPECT_NE(pending_layer()->HighResTiling()->TileAt(1, 0),
active_layer()->HighResTiling()->TileAt(1, 0));
std::set<Tile*> expected_now_tiles;
std::set<Tile*> expected_required_for_draw_tiles;
std::set<Tile*> expected_required_for_activation_tiles;
for (int i = 0; i <= 1; ++i) {
for (int j = 0; j <= 1; ++j) {
bool have_pending_tile = false;
if (pending_layer()->HighResTiling()->TileAt(i, j)) {
expected_now_tiles.insert(
pending_layer()->HighResTiling()->TileAt(i, j));
expected_required_for_activation_tiles.insert(
pending_layer()->HighResTiling()->TileAt(i, j));
have_pending_tile = true;
}
Tile* active_tile = active_layer()->HighResTiling()->TileAt(i, j);
EXPECT_TRUE(active_tile);
expected_now_tiles.insert(active_tile);
expected_required_for_draw_tiles.insert(active_tile);
if (!have_pending_tile)
expected_required_for_activation_tiles.insert(active_tile);
}
}
// Expect 3 shared tiles and 1 unshared tile in total.
EXPECT_EQ(5u, expected_now_tiles.size());
// Expect 4 tiles for each draw and activation, but not all the same.
EXPECT_EQ(4u, expected_required_for_activation_tiles.size());
EXPECT_EQ(4u, expected_required_for_draw_tiles.size());
EXPECT_NE(expected_required_for_draw_tiles,
expected_required_for_activation_tiles);
std::set<Tile*> expected_all_tiles;
for (int i = 0; i <= 3; ++i) {
for (int j = 0; j <= 3; ++j) {
if (pending_layer()->HighResTiling()->TileAt(i, j))
expected_all_tiles.insert(
pending_layer()->HighResTiling()->TileAt(i, j));
EXPECT_TRUE(active_layer()->HighResTiling()->TileAt(i, j));
expected_all_tiles.insert(active_layer()->HighResTiling()->TileAt(i, j));
}
}
// Expect 15 shared tiles and 1 unshared tile.
EXPECT_EQ(17u, expected_all_tiles.size());
// The actual test will now build different queues and verify that the queues
// return the same information as computed manually above.
std::unique_ptr<RasterTilePriorityQueue> queue(host_impl()->BuildRasterQueue(
SAME_PRIORITY_FOR_BOTH_TREES, RasterTilePriorityQueue::Type::ALL));
std::set<Tile*> actual_now_tiles;
std::set<Tile*> actual_all_tiles;
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
queue->Pop();
if (prioritized_tile.priority().priority_bin == TilePriority::NOW)
actual_now_tiles.insert(prioritized_tile.tile());
actual_all_tiles.insert(prioritized_tile.tile());
}
EXPECT_EQ(expected_now_tiles, actual_now_tiles);
EXPECT_EQ(expected_all_tiles, actual_all_tiles);
queue = host_impl()->BuildRasterQueue(
SAME_PRIORITY_FOR_BOTH_TREES,
RasterTilePriorityQueue::Type::REQUIRED_FOR_DRAW);
std::set<Tile*> actual_required_for_draw_tiles;
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
queue->Pop();
actual_required_for_draw_tiles.insert(prioritized_tile.tile());
}
EXPECT_EQ(expected_required_for_draw_tiles, actual_required_for_draw_tiles);
queue = host_impl()->BuildRasterQueue(
SAME_PRIORITY_FOR_BOTH_TREES,
RasterTilePriorityQueue::Type::REQUIRED_FOR_ACTIVATION);
std::set<Tile*> actual_required_for_activation_tiles;
while (!queue->IsEmpty()) {
Tile* tile = queue->Top().tile();
queue->Pop();
actual_required_for_activation_tiles.insert(tile);
}
EXPECT_EQ(expected_required_for_activation_tiles,
actual_required_for_activation_tiles);
}
TEST_F(TileManagerTilePriorityQueueTest, ActivationComesBeforeSoon) {
host_impl()->AdvanceToNextFrame(base::Milliseconds(1));
gfx::Size layer_bounds(1000, 1000);
SetupDefaultTrees(layer_bounds);
// Create a pending child layer.
scoped_refptr<FakeRasterSource> pending_raster_source =
FakeRasterSource::CreateFilled(layer_bounds);
auto* pending_child = AddLayer<FakePictureLayerImpl>(
host_impl()->pending_tree(), pending_raster_source);
pending_child->SetDrawsContent(true);
CopyProperties(pending_layer(), pending_child);
// Set a small viewport, so we have soon and eventually tiles.
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(200, 200));
host_impl()->AdvanceToNextFrame(base::Milliseconds(1));
UpdateDrawProperties(host_impl()->pending_tree());
host_impl()->SetRequiresHighResToDraw();
std::unique_ptr<RasterTilePriorityQueue> queue(host_impl()->BuildRasterQueue(
SMOOTHNESS_TAKES_PRIORITY, RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(queue->IsEmpty());
// Get all the tiles that are NOW and make sure they are ready to draw.
std::vector<Tile*> all_tiles;
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
if (prioritized_tile.priority().priority_bin >= TilePriority::SOON)
break;
all_tiles.push_back(prioritized_tile.tile());
queue->Pop();
}
tile_manager()->InitializeTilesWithResourcesForTesting(
std::vector<Tile*>(all_tiles.begin(), all_tiles.end()));
// Ensure we can activate.
EXPECT_TRUE(tile_manager()->IsReadyToActivate());
}
TEST_F(TileManagerTilePriorityQueueTest, EvictionTilePriorityQueue) {
const gfx::Size layer_bounds(1000, 1000);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
SetupDefaultTrees(layer_bounds);
ASSERT_TRUE(active_layer()->HighResTiling());
ASSERT_TRUE(active_layer()->LowResTiling());
ASSERT_TRUE(pending_layer()->HighResTiling());
EXPECT_FALSE(pending_layer()->LowResTiling());
std::unique_ptr<EvictionTilePriorityQueue> empty_queue(
host_impl()->BuildEvictionQueue(SAME_PRIORITY_FOR_BOTH_TREES));
EXPECT_TRUE(empty_queue->IsEmpty());
std::set<Tile*> all_tiles;
size_t tile_count = 0;
std::unique_ptr<RasterTilePriorityQueue> raster_queue(
host_impl()->BuildRasterQueue(SAME_PRIORITY_FOR_BOTH_TREES,
RasterTilePriorityQueue::Type::ALL));
while (!raster_queue->IsEmpty()) {
++tile_count;
EXPECT_TRUE(raster_queue->Top().tile());
all_tiles.insert(raster_queue->Top().tile());
raster_queue->Pop();
}
EXPECT_EQ(tile_count, all_tiles.size());
EXPECT_EQ(16u, tile_count);
tile_manager()->InitializeTilesWithResourcesForTesting(
std::vector<Tile*>(all_tiles.begin(), all_tiles.end()));
std::unique_ptr<EvictionTilePriorityQueue> queue(
host_impl()->BuildEvictionQueue(SMOOTHNESS_TAKES_PRIORITY));
EXPECT_FALSE(queue->IsEmpty());
// Sanity check, all tiles should be visible.
std::set<Tile*> smoothness_tiles;
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
EXPECT_TRUE(prioritized_tile.tile());
EXPECT_EQ(TilePriority::NOW, prioritized_tile.priority().priority_bin);
EXPECT_TRUE(prioritized_tile.tile()->draw_info().has_resource());
smoothness_tiles.insert(prioritized_tile.tile());
queue->Pop();
}
EXPECT_EQ(all_tiles, smoothness_tiles);
tile_manager()->ReleaseTileResourcesForTesting(
std::vector<Tile*>(all_tiles.begin(), all_tiles.end()));
Region invalidation(gfx::Rect(0, 0, 500, 500));
// Invalidate the pending tree.
pending_layer()->set_invalidation(invalidation);
pending_layer()->HighResTiling()->Invalidate(invalidation);
pending_layer()->HighResTiling()->CreateMissingTilesInLiveTilesRect();
EXPECT_FALSE(pending_layer()->LowResTiling());
// Renew all of the tile priorities.
gfx::Rect viewport(50, 50, 100, 100);
pending_layer()->picture_layer_tiling_set()->UpdateTilePriorities(
viewport, 1.0f, 1.0, Occlusion(), true);
active_layer()->picture_layer_tiling_set()->UpdateTilePriorities(
viewport, 1.0f, 1.0, Occlusion(), true);
// Populate all tiles directly from the tilings.
all_tiles.clear();
std::vector<Tile*> pending_high_res_tiles =
pending_layer()->HighResTiling()->AllTilesForTesting();
for (size_t i = 0; i < pending_high_res_tiles.size(); ++i)
all_tiles.insert(pending_high_res_tiles[i]);
std::vector<Tile*> active_high_res_tiles =
active_layer()->HighResTiling()->AllTilesForTesting();
for (size_t i = 0; i < active_high_res_tiles.size(); ++i)
all_tiles.insert(active_high_res_tiles[i]);
std::vector<Tile*> active_low_res_tiles =
active_layer()->LowResTiling()->AllTilesForTesting();
for (size_t i = 0; i < active_low_res_tiles.size(); ++i)
all_tiles.insert(active_low_res_tiles[i]);
tile_manager()->InitializeTilesWithResourcesForTesting(
std::vector<Tile*>(all_tiles.begin(), all_tiles.end()));
PrioritizedTile last_tile;
smoothness_tiles.clear();
tile_count = 0;
// Here we expect to get increasing combined priority_bin.
queue = host_impl()->BuildEvictionQueue(SMOOTHNESS_TAKES_PRIORITY);
int distance_increasing = 0;
int distance_decreasing = 0;
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
Tile* tile = prioritized_tile.tile();
EXPECT_TRUE(tile);
EXPECT_TRUE(tile->draw_info().has_resource());
if (!last_tile.tile())
last_tile = prioritized_tile;
const TilePriority& last_priority = last_tile.priority();
const TilePriority& priority = prioritized_tile.priority();
EXPECT_GE(last_priority.priority_bin, priority.priority_bin);
if (last_priority.priority_bin == priority.priority_bin) {
EXPECT_LE(last_tile.tile()->required_for_activation(),
tile->required_for_activation());
if (last_tile.tile()->required_for_activation() ==
tile->required_for_activation()) {
if (last_priority.distance_to_visible >= priority.distance_to_visible)
++distance_decreasing;
else
++distance_increasing;
}
}
last_tile = prioritized_tile;
++tile_count;
smoothness_tiles.insert(tile);
queue->Pop();
}
// Ensure that the distance is decreasing many more times than increasing.
EXPECT_EQ(3, distance_increasing);
EXPECT_EQ(16, distance_decreasing);
EXPECT_EQ(tile_count, smoothness_tiles.size());
EXPECT_EQ(all_tiles, smoothness_tiles);
std::set<Tile*> new_content_tiles;
last_tile = PrioritizedTile();
// Again, we expect to get increasing combined priority_bin.
queue = host_impl()->BuildEvictionQueue(NEW_CONTENT_TAKES_PRIORITY);
distance_decreasing = 0;
distance_increasing = 0;
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
Tile* tile = prioritized_tile.tile();
EXPECT_TRUE(tile);
if (!last_tile.tile())
last_tile = prioritized_tile;
const TilePriority& last_priority = last_tile.priority();
const TilePriority& priority = prioritized_tile.priority();
EXPECT_GE(last_priority.priority_bin, priority.priority_bin);
if (last_priority.priority_bin == priority.priority_bin) {
EXPECT_LE(last_tile.tile()->required_for_activation(),
tile->required_for_activation());
if (last_tile.tile()->required_for_activation() ==
tile->required_for_activation()) {
if (last_priority.distance_to_visible >= priority.distance_to_visible)
++distance_decreasing;
else
++distance_increasing;
}
}
last_tile = prioritized_tile;
new_content_tiles.insert(tile);
queue->Pop();
}
// Ensure that the distance is decreasing many more times than increasing.
EXPECT_EQ(3, distance_increasing);
EXPECT_EQ(16, distance_decreasing);
EXPECT_EQ(tile_count, new_content_tiles.size());
EXPECT_EQ(all_tiles, new_content_tiles);
}
// Verifies LayerDebugInfo::name ends up memory dumps.
TEST_F(TileManagerTilePriorityQueueTest, DebugNameAppearsInMemoryDump) {
host_impl()->AdvanceToNextFrame(base::Milliseconds(1));
gfx::Size layer_bounds(1000, 1000);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
scoped_refptr<FakeRasterSource> pending_raster_source =
FakeRasterSource::CreateFilledWithText(layer_bounds);
SetupPendingTree(pending_raster_source);
auto* pending_child_layer = AddLayer<FakePictureLayerImpl>(
host_impl()->pending_tree(), pending_raster_source);
LayerDebugInfo debug_info;
debug_info.name = "debug-name";
pending_child_layer->UpdateDebugInfo(&debug_info);
pending_child_layer->SetDrawsContent(true);
CopyProperties(pending_layer(), pending_child_layer);
ActivateTree();
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
base::trace_event::MemoryDumpArgs dump_args = {
base::trace_event::MemoryDumpLevelOfDetail::kDetailed};
base::trace_event::ProcessMemoryDump memory_dump(dump_args);
host_impl()->resource_pool()->OnMemoryDump(dump_args, &memory_dump);
bool found_debug_name = false;
for (const auto& allocator_map_pair : memory_dump.allocator_dumps()) {
if (base::Contains(allocator_map_pair.first, "debug-name")) {
found_debug_name = true;
break;
}
}
EXPECT_TRUE(found_debug_name);
}
TEST_F(TileManagerTilePriorityQueueTest,
EvictionTilePriorityQueueWithOcclusion) {
host_impl()->AdvanceToNextFrame(base::Milliseconds(1));
gfx::Size layer_bounds(1000, 1000);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
scoped_refptr<FakeRasterSource> pending_raster_source =
FakeRasterSource::CreateFilledWithText(layer_bounds);
SetupPendingTree(pending_raster_source);
auto* pending_child_layer = AddLayer<FakePictureLayerImpl>(
host_impl()->pending_tree(), pending_raster_source);
int child_id = pending_child_layer->id();
pending_child_layer->SetDrawsContent(true);
CopyProperties(pending_layer(), pending_child_layer);
host_impl()->AdvanceToNextFrame(base::Milliseconds(1));
UpdateDrawProperties(host_impl()->pending_tree());
ActivateTree();
SetupPendingTree(pending_raster_source);
FakePictureLayerImpl* active_child_layer = static_cast<FakePictureLayerImpl*>(
host_impl()->active_tree()->LayerById(child_id));
std::set<Tile*> all_tiles;
size_t tile_count = 0;
std::unique_ptr<RasterTilePriorityQueue> raster_queue(
host_impl()->BuildRasterQueue(SAME_PRIORITY_FOR_BOTH_TREES,
RasterTilePriorityQueue::Type::ALL));
while (!raster_queue->IsEmpty()) {
++tile_count;
EXPECT_TRUE(raster_queue->Top().tile());
all_tiles.insert(raster_queue->Top().tile());
raster_queue->Pop();
}
EXPECT_EQ(tile_count, all_tiles.size());
EXPECT_EQ(32u, tile_count);
// Renew all of the tile priorities.
gfx::Rect viewport(layer_bounds);
pending_layer()->picture_layer_tiling_set()->UpdateTilePriorities(
viewport, 1.0f, 1.0, Occlusion(), true);
pending_child_layer->picture_layer_tiling_set()->UpdateTilePriorities(
viewport, 1.0f, 1.0, Occlusion(), true);
active_layer()->picture_layer_tiling_set()->UpdateTilePriorities(
viewport, 1.0f, 1.0, Occlusion(), true);
active_child_layer->picture_layer_tiling_set()->UpdateTilePriorities(
viewport, 1.0f, 1.0, Occlusion(), true);
// Populate all tiles directly from the tilings.
all_tiles.clear();
std::vector<Tile*> pending_high_res_tiles =
pending_layer()->HighResTiling()->AllTilesForTesting();
all_tiles.insert(pending_high_res_tiles.begin(),
pending_high_res_tiles.end());
// Set all tiles on the pending_child_layer as occluded on the pending tree.
std::vector<Tile*> pending_child_high_res_tiles =
pending_child_layer->HighResTiling()->AllTilesForTesting();
pending_child_layer->HighResTiling()->SetAllTilesOccludedForTesting();
active_child_layer->HighResTiling()->SetAllTilesOccludedForTesting();
active_child_layer->LowResTiling()->SetAllTilesOccludedForTesting();
tile_manager()->InitializeTilesWithResourcesForTesting(
std::vector<Tile*>(all_tiles.begin(), all_tiles.end()));
// Verify occlusion is considered by EvictionTilePriorityQueue.
TreePriority tree_priority = NEW_CONTENT_TAKES_PRIORITY;
size_t occluded_count = 0u;
PrioritizedTile last_tile;
std::unique_ptr<EvictionTilePriorityQueue> queue(
host_impl()->BuildEvictionQueue(tree_priority));
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
if (!last_tile.tile())
last_tile = prioritized_tile;
bool tile_is_occluded = prioritized_tile.is_occluded();
// The only way we will encounter an occluded tile after an unoccluded
// tile is if the priorty bin decreased, the tile is required for
// activation, or the scale changed.
if (tile_is_occluded) {
occluded_count++;
bool last_tile_is_occluded = last_tile.is_occluded();
if (!last_tile_is_occluded) {
TilePriority::PriorityBin tile_priority_bin =
prioritized_tile.priority().priority_bin;
TilePriority::PriorityBin last_tile_priority_bin =
last_tile.priority().priority_bin;
EXPECT_TRUE((tile_priority_bin < last_tile_priority_bin) ||
prioritized_tile.tile()->required_for_activation() ||
(prioritized_tile.tile()->raster_transform() !=
last_tile.tile()->raster_transform()));
}
}
last_tile = prioritized_tile;
queue->Pop();
}
size_t expected_occluded_count = pending_child_high_res_tiles.size();
EXPECT_EQ(expected_occluded_count, occluded_count);
}
TEST_F(TileManagerTilePriorityQueueTest,
EvictionTilePriorityQueueWithTransparentLayer) {
host_impl()->AdvanceToNextFrame(base::Milliseconds(1));
gfx::Size layer_bounds(1000, 1000);
scoped_refptr<FakeRasterSource> pending_raster_source =
FakeRasterSource::CreateFilled(layer_bounds);
SetupPendingTree(pending_raster_source);
auto* pending_child_layer = AddLayer<FakePictureLayerImpl>(
host_impl()->pending_tree(), pending_raster_source);
pending_child_layer->SetElementId(
LayerIdToElementIdForTesting(pending_child_layer->id()));
CopyProperties(pending_layer(), pending_child_layer);
// Create a fully transparent child layer so that its tile priorities are not
// considered to be valid.
pending_child_layer->SetDrawsContent(true);
CreateEffectNode(pending_child_layer).render_surface_reason =
RenderSurfaceReason::kTest;
host_impl()->AdvanceToNextFrame(base::Milliseconds(1));
UpdateDrawProperties(host_impl()->pending_tree());
host_impl()->pending_tree()->SetOpacityMutated(
pending_child_layer->element_id(), 0.0f);
host_impl()->AdvanceToNextFrame(base::Milliseconds(1));
host_impl()->pending_tree()->UpdateDrawProperties();
// Renew all of the tile priorities.
gfx::Rect viewport(layer_bounds);
pending_layer()->picture_layer_tiling_set()->UpdateTilePriorities(
viewport, 1.0f, 1.0, Occlusion(), true);
pending_child_layer->picture_layer_tiling_set()->UpdateTilePriorities(
viewport, 1.0f, 1.0, Occlusion(), true);
// Populate all tiles directly from the tilings.
std::set<Tile*> all_pending_tiles;
std::vector<Tile*> pending_high_res_tiles =
pending_layer()->HighResTiling()->AllTilesForTesting();
all_pending_tiles.insert(pending_high_res_tiles.begin(),
pending_high_res_tiles.end());
EXPECT_EQ(16u, pending_high_res_tiles.size());
std::set<Tile*> all_pending_child_tiles;
std::vector<Tile*> pending_child_high_res_tiles =
pending_child_layer->HighResTiling()->AllTilesForTesting();
all_pending_child_tiles.insert(pending_child_high_res_tiles.begin(),
pending_child_high_res_tiles.end());
EXPECT_EQ(16u, pending_child_high_res_tiles.size());
std::set<Tile*> all_tiles = all_pending_tiles;
all_tiles.insert(all_pending_child_tiles.begin(),
all_pending_child_tiles.end());
tile_manager()->InitializeTilesWithResourcesForTesting(
std::vector<Tile*>(all_tiles.begin(), all_tiles.end()));
EXPECT_TRUE(pending_layer()->HasValidTilePriorities());
EXPECT_FALSE(pending_child_layer->HasValidTilePriorities());
// Verify that eviction queue returns tiles also from layers without valid
// tile priorities and that the tile priority bin of those tiles is (at most)
// EVENTUALLY.
TreePriority tree_priority = NEW_CONTENT_TAKES_PRIORITY;
std::set<Tile*> new_content_tiles;
size_t tile_count = 0;
std::unique_ptr<EvictionTilePriorityQueue> queue(
host_impl()->BuildEvictionQueue(tree_priority));
while (!queue->IsEmpty()) {
PrioritizedTile prioritized_tile = queue->Top();
Tile* tile = prioritized_tile.tile();
const TilePriority& pending_priority = prioritized_tile.priority();
EXPECT_NE(std::numeric_limits<float>::infinity(),
pending_priority.distance_to_visible);
if (base::Contains(all_pending_child_tiles, tile)) {
EXPECT_EQ(TilePriority::EVENTUALLY, pending_priority.priority_bin);
} else {
EXPECT_EQ(TilePriority::NOW, pending_priority.priority_bin);
}
new_content_tiles.insert(tile);
++tile_count;
queue->Pop();
}
EXPECT_EQ(tile_count, new_content_tiles.size());
EXPECT_EQ(all_tiles, new_content_tiles);
}
TEST_F(TileManagerTilePriorityQueueTest, RasterTilePriorityQueueEmptyLayers) {
const gfx::Size layer_bounds(1000, 1000);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
SetupDefaultTrees(layer_bounds);
std::unique_ptr<RasterTilePriorityQueue> queue(host_impl()->BuildRasterQueue(
SAME_PRIORITY_FOR_BOTH_TREES, RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(queue->IsEmpty());
size_t tile_count = 0;
std::set<Tile*> all_tiles;
while (!queue->IsEmpty()) {
EXPECT_TRUE(queue->Top().tile());
all_tiles.insert(queue->Top().tile());
++tile_count;
queue->Pop();
}
EXPECT_EQ(tile_count, all_tiles.size());
EXPECT_EQ(16u, tile_count);
for (int i = 1; i < 10; ++i) {
auto* pending_child_layer =
AddLayer<FakePictureLayerImpl>(host_impl()->pending_tree());
pending_child_layer->SetDrawsContent(true);
pending_child_layer->set_has_valid_tile_priorities(true);
CopyProperties(pending_layer(), pending_child_layer);
}
queue = host_impl()->BuildRasterQueue(SAME_PRIORITY_FOR_BOTH_TREES,
RasterTilePriorityQueue::Type::ALL);
EXPECT_FALSE(queue->IsEmpty());
tile_count = 0;
all_tiles.clear();
while (!queue->IsEmpty()) {
EXPECT_TRUE(queue->Top().tile());
all_tiles.insert(queue->Top().tile());
++tile_count;
queue->Pop();
}
EXPECT_EQ(tile_count, all_tiles.size());
EXPECT_EQ(16u, tile_count);
}
TEST_F(TileManagerTilePriorityQueueTest, EvictionTilePriorityQueueEmptyLayers) {
const gfx::Size layer_bounds(1000, 1000);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
SetupDefaultTrees(layer_bounds);
std::unique_ptr<RasterTilePriorityQueue> raster_queue(
host_impl()->BuildRasterQueue(SAME_PRIORITY_FOR_BOTH_TREES,
RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(raster_queue->IsEmpty());
size_t tile_count = 0;
std::set<Tile*> all_tiles;
while (!raster_queue->IsEmpty()) {
EXPECT_TRUE(raster_queue->Top().tile());
all_tiles.insert(raster_queue->Top().tile());
++tile_count;
raster_queue->Pop();
}
EXPECT_EQ(tile_count, all_tiles.size());
EXPECT_EQ(16u, tile_count);
std::vector<Tile*> tiles(all_tiles.begin(), all_tiles.end());
host_impl()->tile_manager()->InitializeTilesWithResourcesForTesting(tiles);
for (int i = 1; i < 10; ++i) {
auto* pending_child_layer =
AddLayer<FakePictureLayerImpl>(host_impl()->pending_tree());
pending_child_layer->SetDrawsContent(true);
pending_child_layer->set_has_valid_tile_priorities(true);
CopyProperties(pending_layer(), pending_child_layer);
}
std::unique_ptr<EvictionTilePriorityQueue> queue(
host_impl()->BuildEvictionQueue(SAME_PRIORITY_FOR_BOTH_TREES));
EXPECT_FALSE(queue->IsEmpty());
tile_count = 0;
all_tiles.clear();
while (!queue->IsEmpty()) {
EXPECT_TRUE(queue->Top().tile());
all_tiles.insert(queue->Top().tile());
++tile_count;
queue->Pop();
}
EXPECT_EQ(tile_count, all_tiles.size());
EXPECT_EQ(16u, tile_count);
}
TEST_F(TileManagerTilePriorityQueueTest,
RasterTilePriorityQueueStaticViewport) {
FakePictureLayerTilingClient client;
gfx::Rect viewport(50, 50, 500, 500);
gfx::Size layer_bounds(1600, 1600);
const int soon_border_outset = 312;
gfx::Rect soon_rect = viewport;
soon_rect.Inset(-soon_border_outset);
client.SetTileSize(gfx::Size(30, 30));
LayerTreeSettings settings;
std::unique_ptr<PictureLayerTilingSet> tiling_set =
PictureLayerTilingSet::Create(
ACTIVE_TREE, &client, settings.tiling_interest_area_padding,
settings.skewport_target_time_in_seconds,
settings.skewport_extrapolation_limit_in_screen_pixels,
settings.max_preraster_distance_in_screen_pixels);
scoped_refptr<FakeRasterSource> raster_source =
FakeRasterSource::CreateFilled(layer_bounds);
PictureLayerTiling* tiling =
tiling_set->AddTiling(gfx::AxisTransform2d(), raster_source);
tiling->set_resolution(HIGH_RESOLUTION);
tiling_set->UpdateTilePriorities(viewport, 1.0f, 1.0, Occlusion(), true);
std::vector<Tile*> all_tiles = tiling->AllTilesForTesting();
// Sanity check.
EXPECT_EQ(3364u, all_tiles.size());
// The explanation of each iteration is as follows:
// 1. First iteration tests that we can get all of the tiles correctly.
// 2. Second iteration ensures that we can get all of the tiles again (first
// iteration didn't change any tiles), as well set all tiles to be ready to
// draw.
// 3. Third iteration ensures that no tiles are returned, since they were all
// marked as ready to draw.
for (int i = 0; i < 3; ++i) {
std::unique_ptr<TilingSetRasterQueueAll> queue(
new TilingSetRasterQueueAll(tiling_set.get(), false, false));
// There are 3 bins in TilePriority.
bool have_tiles[3] = {};
// On the third iteration, we should get no tiles since everything was
// marked as ready to draw.
if (i == 2) {
EXPECT_TRUE(queue->IsEmpty());
continue;
}
EXPECT_FALSE(queue->IsEmpty());
std::set<Tile*> unique_tiles;
unique_tiles.insert(queue->Top().tile());
PrioritizedTile last_tile = queue->Top();
have_tiles[last_tile.priority().priority_bin] = true;
// On the second iteration, mark everything as ready to draw (solid color).
if (i == 1) {
TileDrawInfo& draw_info = last_tile.tile()->draw_info();
draw_info.SetSolidColorForTesting(SkColors::kRed);
}
queue->Pop();
int eventually_bin_order_correct_count = 0;
int eventually_bin_order_incorrect_count = 0;
while (!queue->IsEmpty()) {
PrioritizedTile new_tile = queue->Top();
queue->Pop();
unique_tiles.insert(new_tile.tile());
TilePriority last_priority = last_tile.priority();
TilePriority new_priority = new_tile.priority();
EXPECT_LE(last_priority.priority_bin, new_priority.priority_bin);
if (last_priority.priority_bin == new_priority.priority_bin) {
if (last_priority.priority_bin == TilePriority::EVENTUALLY) {
bool order_correct = last_priority.distance_to_visible <=
new_priority.distance_to_visible;
eventually_bin_order_correct_count += order_correct;
eventually_bin_order_incorrect_count += !order_correct;
} else if (!soon_rect.Intersects(new_tile.tile()->content_rect()) &&
!soon_rect.Intersects(last_tile.tile()->content_rect())) {
EXPECT_LE(last_priority.distance_to_visible,
new_priority.distance_to_visible);
EXPECT_EQ(TilePriority::NOW, new_priority.priority_bin);
} else if (new_priority.distance_to_visible > 0.f) {
EXPECT_EQ(TilePriority::SOON, new_priority.priority_bin);
}
}
have_tiles[new_priority.priority_bin] = true;
last_tile = new_tile;
// On the second iteration, mark everything as ready to draw (solid
// color).
if (i == 1) {
TileDrawInfo& draw_info = last_tile.tile()->draw_info();
draw_info.SetSolidColorForTesting(SkColors::kRed);
}
}
EXPECT_GT(eventually_bin_order_correct_count,
eventually_bin_order_incorrect_count);
// We should have now and eventually tiles, as well as soon tiles from
// the border region.
EXPECT_TRUE(have_tiles[TilePriority::NOW]);
EXPECT_TRUE(have_tiles[TilePriority::SOON]);
EXPECT_TRUE(have_tiles[TilePriority::EVENTUALLY]);
EXPECT_EQ(unique_tiles.size(), all_tiles.size());
}
}
TEST_F(TileManagerTilePriorityQueueTest,
RasterTilePriorityQueueMovingViewport) {
FakePictureLayerTilingClient client;
gfx::Rect viewport(50, 0, 100, 100);
gfx::Rect moved_viewport(50, 0, 100, 500);
gfx::Size layer_bounds(1000, 1000);
client.SetTileSize(gfx::Size(30, 30));
LayerTreeSettings settings;
std::unique_ptr<PictureLayerTilingSet> tiling_set =
PictureLayerTilingSet::Create(
ACTIVE_TREE, &client, settings.tiling_interest_area_padding,
settings.skewport_target_time_in_seconds,
settings.skewport_extrapolation_limit_in_screen_pixels,
settings.max_preraster_distance_in_screen_pixels);
scoped_refptr<FakeRasterSource> raster_source =
FakeRasterSource::CreateFilled(layer_bounds);
PictureLayerTiling* tiling =
tiling_set->AddTiling(gfx::AxisTransform2d(), raster_source);
tiling->set_resolution(HIGH_RESOLUTION);
tiling_set->UpdateTilePriorities(viewport, 1.0f, 1.0, Occlusion(), true);
tiling_set->UpdateTilePriorities(moved_viewport, 1.0f, 2.0, Occlusion(),
true);
const int soon_border_outset = 312;
gfx::Rect soon_rect = moved_viewport;
soon_rect.Inset(-soon_border_outset);
// There are 3 bins in TilePriority.
bool have_tiles[3] = {};
PrioritizedTile last_tile;
int eventually_bin_order_correct_count = 0;
int eventually_bin_order_incorrect_count = 0;
std::unique_ptr<TilingSetRasterQueueAll> queue(
new TilingSetRasterQueueAll(tiling_set.get(), false, false));
for (; !queue->IsEmpty(); queue->Pop()) {
if (!last_tile.tile())
last_tile = queue->Top();
const PrioritizedTile& new_tile = queue->Top();
TilePriority last_priority = last_tile.priority();
TilePriority new_priority = new_tile.priority();
have_tiles[new_priority.priority_bin] = true;
EXPECT_LE(last_priority.priority_bin, new_priority.priority_bin);
if (last_priority.priority_bin == new_priority.priority_bin) {
if (last_priority.priority_bin == TilePriority::EVENTUALLY) {
bool order_correct = last_priority.distance_to_visible <=
new_priority.distance_to_visible;
eventually_bin_order_correct_count += order_correct;
eventually_bin_order_incorrect_count += !order_correct;
} else if (!soon_rect.Intersects(new_tile.tile()->content_rect()) &&
!soon_rect.Intersects(last_tile.tile()->content_rect())) {
EXPECT_LE(last_priority.distance_to_visible,
new_priority.distance_to_visible);
} else if (new_priority.distance_to_visible > 0.f) {
EXPECT_EQ(TilePriority::SOON, new_priority.priority_bin);
}
}
last_tile = new_tile;
}
EXPECT_GT(eventually_bin_order_correct_count,
eventually_bin_order_incorrect_count);
EXPECT_TRUE(have_tiles[TilePriority::NOW]);
EXPECT_TRUE(have_tiles[TilePriority::SOON]);
EXPECT_TRUE(have_tiles[TilePriority::EVENTUALLY]);
}
TEST_F(TileManagerTilePriorityQueueTest, SetIsLikelyToRequireADraw) {
const gfx::Size layer_bounds(1000, 1000);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
SetupDefaultTrees(layer_bounds);
// Verify that the queue has a required for draw tile at Top.
std::unique_ptr<RasterTilePriorityQueue> queue(host_impl()->BuildRasterQueue(
SAME_PRIORITY_FOR_BOTH_TREES, RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(queue->IsEmpty());
EXPECT_TRUE(queue->Top().tile()->required_for_draw());
EXPECT_FALSE(host_impl()->is_likely_to_require_a_draw());
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_TRUE(host_impl()->is_likely_to_require_a_draw());
}
TEST_F(TileManagerTilePriorityQueueTest,
SetIsLikelyToRequireADrawOnZeroMemoryBudget) {
const gfx::Size layer_bounds(1000, 1000);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
SetupDefaultTrees(layer_bounds);
// Verify that the queue has a required for draw tile at Top.
std::unique_ptr<RasterTilePriorityQueue> queue(host_impl()->BuildRasterQueue(
SAME_PRIORITY_FOR_BOTH_TREES, RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(queue->IsEmpty());
EXPECT_TRUE(queue->Top().tile()->required_for_draw());
ManagedMemoryPolicy policy = host_impl()->ActualManagedMemoryPolicy();
policy.bytes_limit_when_visible = 0;
host_impl()->SetMemoryPolicy(policy);
EXPECT_FALSE(host_impl()->is_likely_to_require_a_draw());
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_FALSE(host_impl()->is_likely_to_require_a_draw());
}
TEST_F(TileManagerTilePriorityQueueTest,
SetIsLikelyToRequireADrawOnLimitedMemoryBudget) {
const gfx::Size layer_bounds(1000, 1000);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
SetupDefaultTrees(layer_bounds);
// Verify that the queue has a required for draw tile at Top.
std::unique_ptr<RasterTilePriorityQueue> queue(host_impl()->BuildRasterQueue(
SAME_PRIORITY_FOR_BOTH_TREES, RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(queue->IsEmpty());
EXPECT_TRUE(queue->Top().tile()->required_for_draw());
EXPECT_EQ(gfx::Size(256, 256), queue->Top().tile()->desired_texture_size());
ManagedMemoryPolicy policy = host_impl()->ActualManagedMemoryPolicy();
policy.bytes_limit_when_visible =
viz::SinglePlaneFormat::kRGBA_8888.EstimatedSizeInBytes(
gfx::Size(256, 256));
host_impl()->SetMemoryPolicy(policy);
EXPECT_FALSE(host_impl()->is_likely_to_require_a_draw());
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_TRUE(host_impl()->is_likely_to_require_a_draw());
ResourcePool::InUsePoolResource resource =
host_impl()->resource_pool()->AcquireResource(
gfx::Size(256, 256), viz::SinglePlaneFormat::kRGBA_8888,
gfx::ColorSpace());
resource.set_gpu_backing(std::make_unique<StubGpuBacking>());
host_impl()->tile_manager()->CheckIfMoreTilesNeedToBePreparedForTesting();
EXPECT_FALSE(host_impl()->is_likely_to_require_a_draw());
host_impl()->resource_pool()->ReleaseResource(std::move(resource));
}
TEST_F(TileManagerTilePriorityQueueTest, DefaultMemoryPolicy) {
const gfx::Size layer_bounds(1000, 1000);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
SetupDefaultTrees(layer_bounds);
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
// 64MB is the default mem limit.
EXPECT_EQ(67108864u,
host_impl()->global_tile_state().hard_memory_limit_in_bytes);
EXPECT_EQ(TileMemoryLimitPolicy::ALLOW_ANYTHING,
host_impl()->global_tile_state().memory_limit_policy);
EXPECT_EQ(ManagedMemoryPolicy::kDefaultNumResourcesLimit,
host_impl()->global_tile_state().num_resources_limit);
}
TEST_F(TileManagerTilePriorityQueueTest, RasterQueueAllUsesCorrectTileBounds) {
// Verify that we use the real tile bounds when advancing phases during the
// tile iteration.
gfx::Size layer_bounds(1, 1);
scoped_refptr<FakeRasterSource> raster_source =
FakeRasterSource::CreateFilled(layer_bounds);
FakePictureLayerTilingClient pending_client;
pending_client.SetTileSize(gfx::Size(64, 64));
std::unique_ptr<PictureLayerTilingSet> tiling_set =
PictureLayerTilingSet::Create(WhichTree::ACTIVE_TREE, &pending_client,
1.0f, 1.0f, 1000, 1000.f);
pending_client.set_twin_tiling_set(tiling_set.get());
auto* tiling = tiling_set->AddTiling(gfx::AxisTransform2d(), raster_source);
tiling->set_resolution(HIGH_RESOLUTION);
tiling->CreateAllTilesForTesting();
// The tile is (0, 0, 1, 1), create an intersecting and non-intersecting
// rectangle to test the advance phase with. The tile size is (64, 64), so
// both rectangles intersect the tile content size, but only one should
// intersect the actual size.
gfx::Rect non_intersecting_rect(2, 2, 10, 10);
gfx::Rect intersecting_rect(0, 0, 10, 10);
{
tiling->SetTilePriorityRectsForTesting(
non_intersecting_rect, // Visible rect.
intersecting_rect, // Skewport rect.
intersecting_rect, // Soon rect.
intersecting_rect); // Eventually rect.
std::unique_ptr<TilingSetRasterQueueAll> queue(
new TilingSetRasterQueueAll(tiling_set.get(), false, false));
EXPECT_FALSE(queue->IsEmpty());
}
{
tiling->SetTilePriorityRectsForTesting(
non_intersecting_rect, // Visible rect.
non_intersecting_rect, // Skewport rect.
intersecting_rect, // Soon rect.
intersecting_rect); // Eventually rect.
std::unique_ptr<TilingSetRasterQueueAll> queue(
new TilingSetRasterQueueAll(tiling_set.get(), false, false));
EXPECT_FALSE(queue->IsEmpty());
}
{
tiling->SetTilePriorityRectsForTesting(
non_intersecting_rect, // Visible rect.
non_intersecting_rect, // Skewport rect.
non_intersecting_rect, // Soon rect.
intersecting_rect); // Eventually rect.
std::unique_ptr<TilingSetRasterQueueAll> queue(
new TilingSetRasterQueueAll(tiling_set.get(), false, false));
EXPECT_FALSE(queue->IsEmpty());
}
}
TEST_F(TileManagerTilePriorityQueueTest, NoRasterTasksforSolidColorTiles) {
gfx::Size size(10, 10);
const gfx::Size layer_bounds(1000, 1000);
std::unique_ptr<FakeRecordingSource> recording_source =
FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
PaintFlags solid_flags;
SkColor4f solid_color{0.1f, 0.2f, 0.3f, 1.0f};
solid_flags.setColor(solid_color);
recording_source->add_draw_rect_with_flags(gfx::Rect(layer_bounds),
solid_flags);
// Create non solid tile as well, otherwise tilings wouldnt be created.
SkColor4f non_solid_color{0.2f, 0.3f, 0.4f, 0.5f};
PaintFlags non_solid_flags;
non_solid_flags.setColor(non_solid_color);
recording_source->add_draw_rect_with_flags(gfx::Rect(0, 0, 10, 10),
non_solid_flags);
recording_source->Rerecord();
scoped_refptr<RasterSource> raster_source =
recording_source->CreateRasterSource();
FakePictureLayerTilingClient tiling_client;
tiling_client.SetTileSize(size);
std::unique_ptr<PictureLayerImpl> layer_impl =
PictureLayerImpl::Create(host_impl()->active_tree(), 1);
layer_impl->set_contributes_to_drawn_render_surface(true);
PictureLayerTilingSet* tiling_set = layer_impl->picture_layer_tiling_set();
PictureLayerTiling* tiling =
tiling_set->AddTiling(gfx::AxisTransform2d(), raster_source);
tiling->set_resolution(HIGH_RESOLUTION);
tiling->CreateAllTilesForTesting();
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()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
std::vector<Tile*> tiles = tiling->AllTilesForTesting();
for (size_t tile_idx = 0; tile_idx < tiles.size(); ++tile_idx) {
Tile* tile = tiles[tile_idx];
if (tile->id() == 1) {
// Non-solid tile.
EXPECT_TRUE(tile->HasRasterTask());
EXPECT_EQ(TileDrawInfo::RESOURCE_MODE, tile->draw_info().mode());
} else {
EXPECT_FALSE(tile->HasRasterTask());
EXPECT_EQ(TileDrawInfo::SOLID_COLOR_MODE, tile->draw_info().mode());
EXPECT_EQ(solid_color, tile->draw_info().solid_color());
}
}
}
class TestSoftwareBacking : public ResourcePool::SoftwareBacking {
public:
// No tracing is done during these tests.
void OnMemoryDump(
base::trace_event::ProcessMemoryDump* pmd,
const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid,
uint64_t tracing_process_id,
int importance) const override {}
std::unique_ptr<uint32_t[]> pixels;
};
// A RasterBufferProvider that allocates software backings with a standard
// array as the backing. Overrides Playback() on the RasterBuffer to raster
// into the pixels in the array.
class TestSoftwareRasterBufferProvider : public FakeRasterBufferProviderImpl {
public:
static constexpr bool kIsGpuCompositing = true;
static constexpr viz::SharedImageFormat kSharedImageFormat =
viz::SinglePlaneFormat::kRGBA_8888;
std::unique_ptr<RasterBuffer> AcquireBufferForRaster(
const ResourcePool::InUsePoolResource& resource,
uint64_t resource_content_id,
uint64_t previous_content_id,
bool depends_on_at_raster_decodes,
bool depends_on_hardware_accelerated_jpeg_candidates,
bool depends_on_hardware_accelerated_webp_candidates) override {
if (!resource.software_backing()) {
auto backing = std::make_unique<TestSoftwareBacking>();
backing->shared_bitmap_id = viz::SharedBitmap::GenerateId();
backing->pixels = std::make_unique<uint32_t[]>(
viz::ResourceSizes::CheckedSizeInBytes<size_t>(resource.size(),
kSharedImageFormat));
resource.set_software_backing(std::move(backing));
}
auto* backing =
static_cast<TestSoftwareBacking*>(resource.software_backing());
return std::make_unique<TestRasterBuffer>(resource.size(),
backing->pixels.get());
}
private:
class TestRasterBuffer : public RasterBuffer {
public:
TestRasterBuffer(const gfx::Size& size, void* pixels)
: size_(size), pixels_(pixels) {}
void Playback(const RasterSource* raster_source,
const gfx::Rect& raster_full_rect,
const gfx::Rect& raster_dirty_rect,
uint64_t new_content_id,
const gfx::AxisTransform2d& transform,
const RasterSource::PlaybackSettings& playback_settings,
const GURL& url) override {
RasterBufferProvider::PlaybackToMemory(
pixels_, kSharedImageFormat, size_, /*stride=*/0, raster_source,
raster_full_rect, /*canvas_playback_rect=*/raster_full_rect,
transform, gfx::ColorSpace(), kIsGpuCompositing, playback_settings);
}
bool SupportsBackgroundThreadPriority() const override { return true; }
private:
gfx::Size size_;
raw_ptr<void> pixels_;
};
};
class TileManagerTest : public TestLayerTreeHostBase {
public:
// MockLayerTreeHostImpl allows us to intercept tile manager callbacks.
class MockLayerTreeHostImpl : public FakeLayerTreeHostImpl {
public:
MockLayerTreeHostImpl(const LayerTreeSettings& settings,
TaskRunnerProvider* task_runner_provider,
TaskGraphRunner* task_graph_runner)
: FakeLayerTreeHostImpl(settings,
task_runner_provider,
task_graph_runner) {}
MOCK_METHOD0(NotifyReadyToActivate, void());
MOCK_METHOD0(NotifyReadyToDraw, void());
MOCK_METHOD0(NotifyAllTileTasksCompleted, void());
};
std::unique_ptr<FakeLayerTreeHostImpl> CreateHostImpl(
const LayerTreeSettings& settings,
TaskRunnerProvider* task_runner_provider,
TaskGraphRunner* task_graph_runner) override {
return std::make_unique<testing::NiceMock<MockLayerTreeHostImpl>>(
settings, task_runner_provider, task_graph_runner);
}
// By default use software compositing (no context provider).
std::unique_ptr<LayerTreeFrameSink> CreateLayerTreeFrameSink() override {
return FakeLayerTreeFrameSink::CreateSoftware();
}
MockLayerTreeHostImpl& MockHostImpl() {
return *static_cast<MockLayerTreeHostImpl*>(host_impl());
}
};
// Test to ensure that we call NotifyAllTileTasksCompleted when PrepareTiles is
// called.
TEST_F(TileManagerTest, AllWorkFinished) {
// Check with no tile work enqueued.
{
base::RunLoop run_loop;
EXPECT_FALSE(
host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
EXPECT_CALL(MockHostImpl(), NotifyReadyToDraw());
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_TRUE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
run_loop.Run();
}
// Check that the "schedule more work" path also triggers the expected
// callback.
{
base::RunLoop run_loop;
EXPECT_FALSE(
host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
EXPECT_CALL(MockHostImpl(), NotifyReadyToDraw());
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
host_impl()->tile_manager()->SetMoreTilesNeedToBeRasterizedForTesting();
EXPECT_TRUE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
run_loop.Run();
}
// Check that if callbacks are called by CheckIfMoreTilesNeedToBePrepared if
// they haven't been called already.
{
base::RunLoop run_loop;
EXPECT_FALSE(
host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
EXPECT_CALL(MockHostImpl(), NotifyReadyToDraw());
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
host_impl()->tile_manager()->ResetSignalsForTesting();
host_impl()->tile_manager()->SetMoreTilesNeedToBeRasterizedForTesting();
host_impl()->tile_manager()->CheckIfMoreTilesNeedToBePreparedForTesting();
run_loop.Run();
}
// Same test as above but with SMOOTHNESS_TAKES_PRIORITY.
{
base::RunLoop run_loop;
EXPECT_FALSE(
host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
EXPECT_CALL(MockHostImpl(), NotifyReadyToDraw());
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
host_impl()->tile_manager()->ResetSignalsForTesting();
auto global_state = host_impl()->global_tile_state();
global_state.tree_priority = SMOOTHNESS_TAKES_PRIORITY;
host_impl()->tile_manager()->SetGlobalStateForTesting(global_state);
host_impl()->tile_manager()->SetMoreTilesNeedToBeRasterizedForTesting();
host_impl()->tile_manager()->CheckIfMoreTilesNeedToBePreparedForTesting();
run_loop.Run();
}
}
TEST_F(TileManagerTest, ActivateAndDrawWhenOOM) {
SetupDefaultTrees(gfx::Size(1000, 1000));
auto global_state = host_impl()->global_tile_state();
global_state.hard_memory_limit_in_bytes = 1u;
global_state.soft_memory_limit_in_bytes = 1u;
{
base::RunLoop run_loop;
EXPECT_FALSE(
host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
EXPECT_CALL(MockHostImpl(), NotifyReadyToActivate());
EXPECT_CALL(MockHostImpl(), NotifyReadyToDraw());
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
host_impl()->tile_manager()->PrepareTiles(global_state);
EXPECT_TRUE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
run_loop.Run();
}
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToDraw());
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToActivate());
EXPECT_TRUE(host_impl()->notify_tile_state_changed_called());
// Next PrepareTiles should skip NotifyTileStateChanged since all tiles
// are marked oom already.
{
base::RunLoop run_loop;
host_impl()->set_notify_tile_state_changed_called(false);
EXPECT_CALL(MockHostImpl(), NotifyReadyToActivate());
EXPECT_CALL(MockHostImpl(), NotifyReadyToDraw());
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
host_impl()->tile_manager()->PrepareTiles(global_state);
run_loop.Run();
EXPECT_FALSE(host_impl()->notify_tile_state_changed_called());
}
}
class TileManagerOcclusionTest : public TileManagerTest {
public:
LayerTreeSettings CreateSettings() override {
auto settings = TestLayerTreeHostBase::CreateSettings();
settings.create_low_res_tiling = true;
settings.use_occlusion_for_tile_prioritization = true;
return settings;
}
void PrepareTilesAndWaitUntilDone(
const GlobalStateThatImpactsTilePriority& state) {
base::RunLoop run_loop;
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
tile_manager()->PrepareTiles(state);
run_loop.Run();
tile_manager()->PrepareToDraw();
}
TileManager* tile_manager() { return host_impl()->tile_manager(); }
};
TEST_F(TileManagerOcclusionTest, OccludedTileEvictedForVisibleTile) {
gfx::Size layer_bounds(256, 256);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
SetupPendingTree(FakeRasterSource::CreateFilledWithText(layer_bounds));
const int initial_picture_id = pending_layer()->id();
ActivateTree();
GlobalStateThatImpactsTilePriority global_state =
host_impl()->global_tile_state();
const LayerTreeSettings settings = CreateSettings();
global_state.hard_memory_limit_in_bytes =
global_state.soft_memory_limit_in_bytes =
settings.default_tile_size.GetArea() * 4 + 1;
// Call PrepareTiles and wait for it to complete. It's necessary to wait for
// completion so that the resource is pushed to the tile, which is used
// in calculating eviction.
PrepareTilesAndWaitUntilDone(global_state);
// At this point the initial PictureLayerImpl should have a Tile with a
// resource.
{
PictureLayerImpl* initial_layer = static_cast<PictureLayerImpl*>(
host_impl()->active_tree()->LayerById(initial_picture_id));
ASSERT_NE(initial_layer, nullptr);
ASSERT_EQ(1u, initial_layer->picture_layer_tiling_set()->num_tilings());
std::vector<Tile*> initial_layer_tiles =
initial_layer->picture_layer_tiling_set()
->tiling_at(0)
->AllTilesForTesting();
ASSERT_EQ(1u, initial_layer_tiles.size());
EXPECT_TRUE(initial_layer_tiles[0]->draw_info().has_resource());
}
// Add another layer on top. As there is only enough memory for one tile,
// the top most tile should get a raster task and the bottom layer should
// not.
SetupPendingTree(FakeRasterSource::CreateFilledWithText(layer_bounds));
// Advance the frame to ensure PictureLayerTilingSet applies the occlusion to
// the PictureLayerTiling. The amount of time advanced doesn't matter.
host_impl()->AdvanceToNextFrame(base::Seconds(2));
auto* picture_layer = AddLayer<FakePictureLayerImpl>(
host_impl()->pending_tree(),
FakeRasterSource::CreateFilledWithText(layer_bounds));
const int top_most_layer_id = picture_layer->id();
picture_layer->SetDrawsContent(true);
picture_layer->SetContentsOpaque(true);
CopyProperties(pending_layer(), picture_layer);
ActivateTree();
PrepareTilesAndWaitUntilDone(global_state);
{
PictureLayerImpl* initial_layer = static_cast<PictureLayerImpl*>(
host_impl()->active_tree()->LayerById(initial_picture_id));
ASSERT_NE(initial_layer, nullptr);
ASSERT_EQ(1u, initial_layer->picture_layer_tiling_set()->num_tilings());
std::vector<Tile*> initial_layer_tiles =
initial_layer->picture_layer_tiling_set()
->tiling_at(0)
->AllTilesForTesting();
ASSERT_EQ(1u, initial_layer_tiles.size());
EXPECT_FALSE(initial_layer_tiles[0]->draw_info().has_resource());
PictureLayerImpl* top_most_layer = static_cast<PictureLayerImpl*>(
host_impl()->active_tree()->LayerById(top_most_layer_id));
ASSERT_NE(top_most_layer, nullptr);
ASSERT_EQ(1u, top_most_layer->picture_layer_tiling_set()->num_tilings());
std::vector<Tile*> top_most_layer_tiles =
top_most_layer->picture_layer_tiling_set()
->tiling_at(0)
->AllTilesForTesting();
ASSERT_EQ(1u, top_most_layer_tiles.size());
EXPECT_TRUE(top_most_layer_tiles[0]->draw_info().has_resource());
}
}
class PixelInspectTileManagerTest : public TileManagerTest {
public:
~PixelInspectTileManagerTest() override {
// Ensure that the host impl doesn't outlive |raster_buffer_provider_|.
TakeHostImpl();
}
void SetUp() override {
TileManagerTest::SetUp();
// Use a RasterBufferProvider that will let us inspect pixels.
host_impl()->tile_manager()->SetRasterBufferProviderForTesting(
&raster_buffer_provider_);
}
private:
TestSoftwareRasterBufferProvider raster_buffer_provider_;
};
TEST_F(PixelInspectTileManagerTest, LowResHasNoImage) {
gfx::Size size(10, 12);
TileResolution resolutions[] = {HIGH_RESOLUTION, LOW_RESOLUTION};
for (size_t i = 0; i < std::size(resolutions); ++i) {
SCOPED_TRACE(resolutions[i]);
// Make a RasterSource that will draw a blue bitmap image.
sk_sp<SkSurface> surface = SkSurfaces::Raster(
SkImageInfo::MakeN32Premul(size.width(), size.height()));
ASSERT_NE(surface, nullptr);
surface->getCanvas()->clear(SK_ColorBLUE);
sk_sp<SkImage> blue_image = surface->makeImageSnapshot();
std::unique_ptr<FakeRecordingSource> recording_source =
FakeRecordingSource::CreateFilledRecordingSource(size);
recording_source->SetBackgroundColor(SkColors::kTransparent);
recording_source->SetRequiresClear(true);
PaintFlags flags;
flags.setColor(SK_ColorGREEN);
recording_source->add_draw_rect_with_flags(gfx::Rect(size), flags);
recording_source->add_draw_image(std::move(blue_image), gfx::Point());
recording_source->Rerecord();
scoped_refptr<RasterSource> raster = recording_source->CreateRasterSource();
FakePictureLayerTilingClient tiling_client;
tiling_client.SetTileSize(size);
std::unique_ptr<PictureLayerImpl> layer =
PictureLayerImpl::Create(host_impl()->active_tree(), 1);
PictureLayerTilingSet* tiling_set = layer->picture_layer_tiling_set();
layer->set_contributes_to_drawn_render_surface(true);
auto* tiling = tiling_set->AddTiling(gfx::AxisTransform2d(), raster);
tiling->set_resolution(resolutions[i]);
tiling->CreateAllTilesForTesting();
tiling->SetTilePriorityRectsForTesting(
gfx::Rect(size), // Visible rect.
gfx::Rect(size), // Skewport rect.
gfx::Rect(size), // Soon rect.
gfx::Rect(size)); // Eventually rect.
// SMOOTHNESS_TAKES_PRIORITY ensures that we will actually raster
// LOW_RESOLUTION tiles, otherwise they are skipped.
host_impl()->SetTreePriority(SMOOTHNESS_TAKES_PRIORITY);
// Call PrepareTiles and wait for it to complete.
auto* tile_manager = host_impl()->tile_manager();
base::RunLoop run_loop;
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
tile_manager->PrepareTiles(host_impl()->global_tile_state());
run_loop.Run();
tile_manager->PrepareToDraw();
Tile* tile = tiling->TileAt(0, 0);
// The tile in the tiling was rastered.
EXPECT_EQ(TileDrawInfo::RESOURCE_MODE, tile->draw_info().mode());
EXPECT_TRUE(tile->draw_info().IsReadyToDraw());
gfx::Size resource_size = tile->draw_info().resource_size();
SkColorType ct = ToClosestSkColorType(
TestSoftwareRasterBufferProvider::kIsGpuCompositing,
TestSoftwareRasterBufferProvider::kSharedImageFormat);
auto info = SkImageInfo::Make(resource_size.width(), resource_size.height(),
ct, kPremul_SkAlphaType);
// CreateLayerTreeFrameSink() sets up a software compositing, so the
// tile resource will be a bitmap.
auto* backing = static_cast<TestSoftwareBacking*>(
tile->draw_info().GetResource().software_backing());
SkBitmap bitmap;
bitmap.installPixels(info, backing->pixels.get(), info.minRowBytes());
for (int x = 0; x < size.width(); ++x) {
for (int y = 0; y < size.height(); ++y) {
SCOPED_TRACE(y);
SCOPED_TRACE(x);
if (resolutions[i] == LOW_RESOLUTION) {
// Since it's low res, the bitmap was not drawn, and the background
// (green) is visible instead.
ASSERT_EQ(SK_ColorGREEN, bitmap.getColor(x, y));
} else {
EXPECT_EQ(HIGH_RESOLUTION, resolutions[i]);
// Since it's high res, the bitmap (blue) was drawn, and the
// background is not visible.
ASSERT_EQ(SK_ColorBLUE, bitmap.getColor(x, y));
}
}
}
}
}
class ActivationTasksDoNotBlockReadyToDrawTest : public TileManagerTest {
protected:
std::unique_ptr<TaskGraphRunner> CreateTaskGraphRunner() override {
return std::make_unique<SynchronousTaskGraphRunner>();
}
std::unique_ptr<LayerTreeFrameSink> CreateLayerTreeFrameSink() override {
return FakeLayerTreeFrameSink::Create3dForGpuRasterization();
}
};
TEST_F(ActivationTasksDoNotBlockReadyToDrawTest,
ActivationTasksDoNotBlockReadyToDraw) {
const gfx::Size layer_bounds(1000, 1000);
EXPECT_TRUE(host_impl()->use_gpu_rasterization());
// Active tree has no non-solid tiles, so it will generate no tile tasks.
std::unique_ptr<FakeRecordingSource> active_tree_recording_source =
FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
PaintFlags solid_flags;
SkColor solid_color = SkColorSetARGB(255, 12, 23, 34);
solid_flags.setColor(solid_color);
active_tree_recording_source->add_draw_rect_with_flags(
gfx::Rect(layer_bounds), solid_flags);
active_tree_recording_source->Rerecord();
// Pending tree has non-solid tiles, so it will generate tile tasks.
std::unique_ptr<FakeRecordingSource> pending_tree_recording_source =
FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
SkColor non_solid_color = SkColorSetARGB(128, 45, 56, 67);
PaintFlags non_solid_flags;
non_solid_flags.setColor(non_solid_color);
pending_tree_recording_source->add_draw_rect_with_flags(
gfx::Rect(5, 5, 10, 10), non_solid_flags);
pending_tree_recording_source->Rerecord();
scoped_refptr<RasterSource> active_tree_raster_source =
active_tree_recording_source->CreateRasterSource();
scoped_refptr<RasterSource> pending_tree_raster_source =
pending_tree_recording_source->CreateRasterSource();
SetupTrees(pending_tree_raster_source, active_tree_raster_source);
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
// The first task to run should be ReadyToDraw (this should not be blocked by
// the tasks required for activation).
base::RunLoop run_loop;
EXPECT_CALL(MockHostImpl(), NotifyReadyToDraw())
.WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())
->RunSingleTaskForTesting();
run_loop.Run();
}
class PartialRasterTileManagerTest : public TileManagerTest {
public:
LayerTreeSettings CreateSettings() override {
auto settings = TileManagerTest::CreateSettings();
settings.use_partial_raster = true;
return settings;
}
};
// Ensures that if a raster task is cancelled, it gets returned to the resource
// pool with an invalid content ID, not with its invalidated content ID.
TEST_F(PartialRasterTileManagerTest, CancelledTasksHaveNoContentId) {
// Create a FakeTileTaskManagerImpl and set it on the tile manager so that all
// scheduled work is immediately cancelled.
host_impl()->tile_manager()->SetTileTaskManagerForTesting(
std::make_unique<FakeTileTaskManagerImpl>());
// Pick arbitrary IDs - they don't really matter as long as they're constant.
const int kLayerId = 7;
const uint64_t kInvalidatedId = 43;
const gfx::Size kTileSize(128, 128);
scoped_refptr<FakeRasterSource> pending_raster_source =
FakeRasterSource::CreateFilled(kTileSize);
host_impl()->CreatePendingTree();
LayerTreeImpl* pending_tree = host_impl()->pending_tree();
pending_tree->SetDeviceViewportRect(
host_impl()->active_tree()->GetDeviceViewport());
// Steal from the recycled tree.
std::unique_ptr<FakePictureLayerImpl> pending_layer =
FakePictureLayerImpl::Create(pending_tree, kLayerId,
pending_raster_source);
pending_layer->SetDrawsContent(true);
// The bounds() just mirror the raster source size.
pending_layer->SetBounds(pending_layer->raster_source()->size());
SetupRootProperties(pending_layer.get());
pending_tree->SetRootLayerForTesting(std::move(pending_layer));
// Add tilings/tiles for the layer.
UpdateDrawProperties(host_impl()->pending_tree());
// Build the raster queue and invalidate the top tile.
std::unique_ptr<RasterTilePriorityQueue> queue(host_impl()->BuildRasterQueue(
SAME_PRIORITY_FOR_BOTH_TREES, RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(queue->IsEmpty());
queue->Top().tile()->SetInvalidated(gfx::Rect(), kInvalidatedId);
// PrepareTiles to schedule tasks. Due to the FakeTileTaskManagerImpl,
// these tasks will immediately be canceled.
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
// Make sure that the tile we invalidated above was not returned to the pool
// with its invalidated resource ID.
gfx::Rect total_invalidated_rect;
EXPECT_FALSE(host_impl()->resource_pool()->TryAcquireResourceForPartialRaster(
kInvalidatedId + 1, gfx::Rect(), kInvalidatedId, &total_invalidated_rect,
gfx::ColorSpace::CreateSRGB()));
EXPECT_EQ(gfx::Rect(), total_invalidated_rect);
// Free our host_impl_ before the tile_task_manager we passed it, as it
// will use that class in clean up.
TakeHostImpl();
}
// FakeRasterBufferProviderImpl that verifies the resource content ID of raster
// tasks.
class VerifyResourceContentIdRasterBufferProvider
: public FakeRasterBufferProviderImpl {
public:
explicit VerifyResourceContentIdRasterBufferProvider(
uint64_t expected_content_id)
: expected_content_id_(expected_content_id) {}
~VerifyResourceContentIdRasterBufferProvider() override = default;
// RasterBufferProvider methods.
std::unique_ptr<RasterBuffer> AcquireBufferForRaster(
const ResourcePool::InUsePoolResource& resource,
uint64_t resource_content_id,
uint64_t previous_content_id,
bool depends_on_at_raster_decodes,
bool depends_on_hardware_accelerated_jpeg_candidates,
bool depends_on_hardware_accelerated_webp_candidates) override {
EXPECT_EQ(expected_content_id_, resource_content_id);
return nullptr;
}
private:
uint64_t expected_content_id_;
};
// Runs a test to ensure that partial raster is either enabled or disabled,
// depending on |partial_raster_enabled|'s value. Takes ownership of host_impl
// so that cleanup order can be controlled.
void RunPartialRasterCheck(std::unique_ptr<LayerTreeHostImpl> host_impl,
bool partial_raster_enabled) {
// Pick arbitrary IDs - they don't really matter as long as they're constant.
const int kLayerId = 7;
const uint64_t kInvalidatedId = 43;
const uint64_t kExpectedId = partial_raster_enabled ? kInvalidatedId : 0u;
const gfx::Size kTileSize(128, 128);
// Create a VerifyResourceContentIdTileTaskManager to ensure that the
// raster task we see is created with |kExpectedId|.
host_impl->tile_manager()->SetTileTaskManagerForTesting(
std::make_unique<FakeTileTaskManagerImpl>());
VerifyResourceContentIdRasterBufferProvider raster_buffer_provider(
kExpectedId);
host_impl->tile_manager()->SetRasterBufferProviderForTesting(
&raster_buffer_provider);
// Ensure there's a resource with our |kInvalidatedId| in the resource pool.
ResourcePool::InUsePoolResource resource =
host_impl->resource_pool()->AcquireResource(
kTileSize, viz::SinglePlaneFormat::kRGBA_8888,
gfx::ColorSpace::CreateSRGB());
resource.set_software_backing(std::make_unique<TestSoftwareBacking>());
host_impl->resource_pool()->PrepareForExport(
resource, viz::TransferableResource::ResourceSource::kTest);
host_impl->resource_pool()->OnContentReplaced(resource, kInvalidatedId);
host_impl->resource_pool()->ReleaseResource(std::move(resource));
scoped_refptr<FakeRasterSource> pending_raster_source =
FakeRasterSource::CreateFilled(kTileSize);
host_impl->CreatePendingTree();
LayerTreeImpl* pending_tree = host_impl->pending_tree();
pending_tree->SetDeviceViewportRect(
host_impl->active_tree()->GetDeviceViewport());
std::unique_ptr<FakePictureLayerImpl> pending_layer =
FakePictureLayerImpl::Create(pending_tree, kLayerId,
pending_raster_source);
pending_layer->SetDrawsContent(true);
// The bounds() just mirror the raster source size.
pending_layer->SetBounds(pending_layer->raster_source()->size());
SetupRootProperties(pending_layer.get());
pending_tree->SetRootLayerForTesting(std::move(pending_layer));
// Add tilings/tiles for the layer.
UpdateDrawProperties(pending_tree);
// Build the raster queue and invalidate the top tile.
std::unique_ptr<RasterTilePriorityQueue> queue(host_impl->BuildRasterQueue(
SAME_PRIORITY_FOR_BOTH_TREES, RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(queue->IsEmpty());
queue->Top().tile()->SetInvalidated(gfx::Rect(), kInvalidatedId);
// PrepareTiles to schedule tasks. Due to the
// VerifyPreviousContentRasterBufferProvider, these tasks will verified and
// cancelled.
host_impl->tile_manager()->PrepareTiles(host_impl->global_tile_state());
// Free our host_impl before the verifying_task_manager we passed it, as it
// will use that class in clean up.
host_impl = nullptr;
}
void RunPartialTileDecodeCheck(std::unique_ptr<LayerTreeHostImpl> host_impl,
bool partial_raster_enabled) {
// Pick arbitrary IDs - they don't really matter as long as they're constant.
const int kLayerId = 7;
const uint64_t kInvalidatedId = 43;
const uint64_t kExpectedId = partial_raster_enabled ? kInvalidatedId : 0u;
const gfx::Size kTileSize(400, 400);
host_impl->tile_manager()->SetTileTaskManagerForTesting(
std::make_unique<FakeTileTaskManagerImpl>());
// Create a VerifyResourceContentIdTileTaskManager to ensure that the
// raster task we see is created with |kExpectedId|.
VerifyResourceContentIdRasterBufferProvider raster_buffer_provider(
kExpectedId);
host_impl->tile_manager()->SetRasterBufferProviderForTesting(
&raster_buffer_provider);
// Ensure there's a resource with our |kInvalidatedId| in the resource pool.
ResourcePool::InUsePoolResource resource =
host_impl->resource_pool()->AcquireResource(
kTileSize, viz::SinglePlaneFormat::kRGBA_8888,
gfx::ColorSpace::CreateSRGB());
host_impl->resource_pool()->OnContentReplaced(resource, kInvalidatedId);
host_impl->resource_pool()->ReleaseResource(std::move(resource));
const gfx::Size layer_bounds(500, 500);
std::unique_ptr<FakeRecordingSource> recording_source =
FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
recording_source->set_fill_with_nonsolid_color(true);
int dimension = 250;
PaintImage image1 =
CreateDiscardablePaintImage(gfx::Size(dimension, dimension));
PaintImage image2 =
CreateDiscardablePaintImage(gfx::Size(dimension, dimension));
PaintImage image3 =
CreateDiscardablePaintImage(gfx::Size(dimension, dimension));
PaintImage image4 =
CreateDiscardablePaintImage(gfx::Size(dimension, dimension));
recording_source->add_draw_image(image1, gfx::Point(0, 0));
recording_source->add_draw_image(image2, gfx::Point(300, 0));
recording_source->add_draw_image(image3, gfx::Point(0, 300));
recording_source->add_draw_image(image4, gfx::Point(300, 300));
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->SetDeviceViewportRect(
host_impl->active_tree()->GetDeviceViewport());
// Steal from the recycled tree.
std::unique_ptr<FakePictureLayerImpl> pending_layer =
FakePictureLayerImpl::Create(pending_tree, kLayerId,
pending_raster_source);
pending_layer->SetDrawsContent(true);
// The bounds() just mirror the raster source size.
pending_layer->SetBounds(pending_layer->raster_source()->size());
SetupRootProperties(pending_layer.get());
pending_tree->SetRootLayerForTesting(std::move(pending_layer));
// Add tilings/tiles for the layer.
UpdateDrawProperties(pending_tree);
// 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());
Tile* tile = queue->Top().tile();
if (partial_raster_enabled)
tile->SetInvalidated(gfx::Rect(200, 200), kInvalidatedId);
// PrepareTiles to schedule tasks. Due to the
// VerifyPreviousContentRasterBufferProvider, these tasks will verified and
// cancelled.
host_impl->tile_manager()->PrepareTiles(host_impl->global_tile_state());
// Tile will have 1 dependent decode task if we decode images only in the
// invalidated rect. Otherwise it will have 4.
EXPECT_EQ(
host_impl->tile_manager()->decode_tasks_for_testing(tile->id()).size(),
partial_raster_enabled ? 1u : 4u);
// Free our host_impl before the verifying_task_manager we passed it, as it
// will use that class in clean up.
host_impl = nullptr;
}
// Ensures that the tile manager successfully reuses tiles when partial
// raster is enabled.
TEST_F(PartialRasterTileManagerTest, PartialRasterSuccessfullyEnabled) {
RunPartialRasterCheck(TakeHostImpl(), true /* partial_raster_enabled */);
}
TEST_F(PartialRasterTileManagerTest, PartialTileImageDecode) {
RunPartialTileDecodeCheck(TakeHostImpl(), true /* partial_raster_enabled */);
}
TEST_F(PartialRasterTileManagerTest, CompleteTileImageDecode) {
RunPartialTileDecodeCheck(TakeHostImpl(),
false /* partial_raster_disabled */);
}
// Ensures that the tile manager does not attempt to reuse tiles when partial
// raster is disabled.
TEST_F(TileManagerTest, PartialRasterSuccessfullyDisabled) {
RunPartialRasterCheck(TakeHostImpl(), false /* partial_raster_enabled */);
}
class InvalidResourceRasterBufferProvider
: public FakeRasterBufferProviderImpl {
public:
std::unique_ptr<RasterBuffer> AcquireBufferForRaster(
const ResourcePool::InUsePoolResource& resource,
uint64_t resource_content_id,
uint64_t previous_content_id,
bool depends_on_at_raster_decodes,
bool depends_on_hardware_accelerated_jpeg_candidates,
bool depends_on_hardware_accelerated_webp_candidates) override {
if (!resource.gpu_backing()) {
auto backing = std::make_unique<StubGpuBacking>();
// Don't set a mailbox to signal invalid resource.
backing->texture_target = 5;
resource.set_gpu_backing(std::move(backing));
}
return std::make_unique<FakeRasterBuffer>();
}
private:
class StubGpuBacking : public ResourcePool::GpuBacking {
public:
void OnMemoryDump(
base::trace_event::ProcessMemoryDump* pmd,
const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid,
uint64_t tracing_process_id,
int importance) const override {}
};
};
class InvalidResourceTileManagerTest : public TileManagerTest {
protected:
std::unique_ptr<LayerTreeFrameSink> CreateLayerTreeFrameSink() override {
return FakeLayerTreeFrameSink::Create3d();
}
};
TEST_F(InvalidResourceTileManagerTest, InvalidResource) {
auto* tile_manager = host_impl()->tile_manager();
InvalidResourceRasterBufferProvider raster_buffer_provider;
tile_manager->SetRasterBufferProviderForTesting(&raster_buffer_provider);
gfx::Size size(10, 12);
FakePictureLayerTilingClient tiling_client;
tiling_client.SetTileSize(size);
std::unique_ptr<PictureLayerImpl> layer =
PictureLayerImpl::Create(host_impl()->active_tree(), 1);
layer->set_contributes_to_drawn_render_surface(true);
auto* tiling = layer->picture_layer_tiling_set()->AddTiling(
gfx::AxisTransform2d(), FakeRasterSource::CreateFilled(size));
tiling->set_resolution(HIGH_RESOLUTION);
tiling->CreateAllTilesForTesting();
tiling->SetTilePriorityRectsForTesting(gfx::Rect(size), // Visible rect.
gfx::Rect(size), // Skewport rect.
gfx::Rect(size), // Soon rect.
gfx::Rect(size)); // Eventually rect.
base::RunLoop run_loop;
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
tile_manager->PrepareTiles(host_impl()->global_tile_state());
run_loop.Run();
tile_manager->PrepareToDraw();
Tile* tile = tiling->TileAt(0, 0);
ASSERT_TRUE(tile);
// The tile in the tiling was rastered, but didn't get a resource.
EXPECT_TRUE(tile->draw_info().IsReadyToDraw());
EXPECT_EQ(TileDrawInfo::OOM_MODE, tile->draw_info().mode());
// Ensure that the host impl doesn't outlive |raster_buffer_provider|.
layer = nullptr;
TakeHostImpl();
}
// FakeRasterBufferProviderImpl that allows us to mock ready to draw
// functionality.
class MockReadyToDrawRasterBufferProviderImpl
: public FakeRasterBufferProviderImpl {
public:
MOCK_METHOD1(IsResourceReadyToDraw,
bool(const ResourcePool::InUsePoolResource& resource));
MOCK_METHOD3(
SetReadyToDrawCallback,
uint64_t(
const std::vector<const ResourcePool::InUsePoolResource*>& resources,
base::OnceClosure callback,
uint64_t pending_callback_id));
void set_expected_hdr_headroom(float expected_hdr_headroom) {
expected_hdr_headroom_ = expected_hdr_headroom;
}
std::unique_ptr<RasterBuffer> AcquireBufferForRaster(
const ResourcePool::InUsePoolResource& resource,
uint64_t resource_content_id,
uint64_t previous_content_id,
bool depends_on_at_raster_decodes,
bool depends_on_hardware_accelerated_jpeg_candidates,
bool depends_on_hardware_accelerated_webp_candidates) override {
if (!resource.software_backing())
resource.set_software_backing(std::make_unique<TestSoftwareBacking>());
return std::make_unique<FakeRasterBuffer>(expected_hdr_headroom_);
}
private:
float expected_hdr_headroom_ = 1.f;
};
class TileManagerReadyToDrawTest : public TileManagerTest {
public:
~TileManagerReadyToDrawTest() override {
// Ensure that the host impl doesn't outlive |raster_buffer_provider_|.
TakeHostImpl();
}
void SetUp() override {
TileManagerTest::SetUp();
host_impl()->tile_manager()->SetRasterBufferProviderForTesting(
&mock_raster_buffer_provider_);
const gfx::Size layer_bounds(1000, 1000);
solid_color_recording_source_ =
FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
PaintFlags solid_flags;
SkColor solid_color = SkColorSetARGB(255, 12, 23, 34);
solid_flags.setColor(solid_color);
solid_color_recording_source_->add_draw_rect_with_flags(
gfx::Rect(layer_bounds), solid_flags);
solid_color_recording_source_->Rerecord();
recording_source_ =
FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
SkColor non_solid_color = SkColorSetARGB(128, 45, 56, 67);
PaintFlags non_solid_flags;
non_solid_flags.setColor(non_solid_color);
for (int i = 0; i < 100; ++i) {
for (int j = 0; j < 100; ++j) {
recording_source_->add_draw_rect_with_flags(
gfx::Rect(10 * i, 10 * j, 5, 5), non_solid_flags);
}
}
recording_source_->Rerecord();
}
void SetupTreesWithActiveTreeTiles() {
scoped_refptr<RasterSource> active_tree_raster_source =
recording_source_->CreateRasterSource();
scoped_refptr<RasterSource> pending_tree_raster_source =
solid_color_recording_source_->CreateRasterSource();
SetupTrees(pending_tree_raster_source, active_tree_raster_source);
}
void SetupTreesWithPendingTreeTiles() {
scoped_refptr<RasterSource> active_tree_raster_source =
solid_color_recording_source_->CreateRasterSource();
scoped_refptr<RasterSource> pending_tree_raster_source =
recording_source_->CreateRasterSource();
SetupTrees(pending_tree_raster_source, active_tree_raster_source);
}
TileManager* tile_manager() { return host_impl()->tile_manager(); }
MockReadyToDrawRasterBufferProviderImpl* mock_raster_buffer_provider() {
return &mock_raster_buffer_provider_;
}
private:
StrictMock<MockReadyToDrawRasterBufferProviderImpl>
mock_raster_buffer_provider_;
std::unique_ptr<FakeRecordingSource> recording_source_;
std::unique_ptr<FakeRecordingSource> solid_color_recording_source_;
};
TEST_F(TileManagerReadyToDrawTest, SmoothActivationWaitsOnCallback) {
host_impl()->SetTreePriority(SMOOTHNESS_TAKES_PRIORITY);
SetupTreesWithPendingTreeTiles();
base::OnceClosure callback;
{
base::RunLoop run_loop;
// Until we activate our ready to draw callback, treat all resources as not
// ready to draw.
EXPECT_CALL(*mock_raster_buffer_provider(),
IsResourceReadyToDraw(testing::_))
.WillRepeatedly(Return(false));
EXPECT_CALL(*mock_raster_buffer_provider(), SetReadyToDrawCallback(_, _, 0))
.WillOnce([&run_loop, &callback](
const std::vector<const ResourcePool::InUsePoolResource*>&
resources,
base::OnceClosure callback_in,
uint64_t pending_callback_id) {
callback = std::move(callback_in);
run_loop.Quit();
return 1;
});
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
run_loop.Run();
}
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToDraw());
EXPECT_FALSE(host_impl()->tile_manager()->IsReadyToActivate());
{
base::RunLoop run_loop;
EXPECT_CALL(MockHostImpl(), NotifyReadyToActivate())
.WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
EXPECT_CALL(*mock_raster_buffer_provider(),
IsResourceReadyToDraw(testing::_))
.WillRepeatedly(Return(true));
std::move(callback).Run();
run_loop.Run();
}
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToDraw());
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToActivate());
}
TEST_F(TileManagerReadyToDrawTest, NonSmoothActivationDoesNotWaitOnCallback) {
SetupTreesWithPendingTreeTiles();
// We're using a StrictMock on the RasterBufferProvider, so any function call
// will cause a test failure.
base::RunLoop run_loop;
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_CALL(MockHostImpl(), NotifyReadyToActivate())
.WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
run_loop.Run();
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToDraw());
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToActivate());
}
TEST_F(TileManagerReadyToDrawTest, HdrHeadroomPropagated) {
constexpr float kTestHdrHeadroom = 4.f;
TargetColorParams target_color_params;
target_color_params.hdr_max_luminance_relative = kTestHdrHeadroom;
host_impl()->set_target_color_params(target_color_params);
mock_raster_buffer_provider()->set_expected_hdr_headroom(kTestHdrHeadroom);
SetupTreesWithPendingTreeTiles();
base::RunLoop run_loop;
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_CALL(MockHostImpl(), NotifyReadyToActivate())
.WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
run_loop.Run();
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToDraw());
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToActivate());
}
TEST_F(TileManagerReadyToDrawTest, SmoothDrawWaitsOnCallback) {
host_impl()->SetTreePriority(SMOOTHNESS_TAKES_PRIORITY);
SetupTreesWithActiveTreeTiles();
base::OnceClosure callback;
{
base::RunLoop run_loop;
// Until we activate our ready to draw callback, treat all resources as not
// ready to draw.
EXPECT_CALL(*mock_raster_buffer_provider(),
IsResourceReadyToDraw(testing::_))
.WillRepeatedly(Return(false));
EXPECT_CALL(*mock_raster_buffer_provider(), SetReadyToDrawCallback(_, _, 0))
.WillOnce([&run_loop, &callback](
const std::vector<const ResourcePool::InUsePoolResource*>&
resources,
base::OnceClosure callback_in,
uint64_t pending_callback_id) {
callback = std::move(callback_in);
run_loop.Quit();
return 1;
});
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
run_loop.Run();
}
EXPECT_FALSE(host_impl()->tile_manager()->IsReadyToDraw());
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToActivate());
{
base::RunLoop run_loop;
EXPECT_CALL(MockHostImpl(), NotifyReadyToDraw())
.WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
EXPECT_CALL(*mock_raster_buffer_provider(),
IsResourceReadyToDraw(testing::_))
.WillRepeatedly(Return(true));
std::move(callback).Run();
run_loop.Run();
}
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToDraw());
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToActivate());
}
TEST_F(TileManagerReadyToDrawTest, NonSmoothDrawDoesNotWaitOnCallback) {
SetupTreesWithActiveTreeTiles();
// We're using a StrictMock on the RasterBufferProvider, so any function call
// will cause a test failure.
base::RunLoop run_loop;
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_CALL(MockHostImpl(), NotifyReadyToDraw())
.WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
run_loop.Run();
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToDraw());
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToActivate());
}
TEST_F(TileManagerReadyToDrawTest, NoCallbackWhenAlreadyReadyToDraw) {
host_impl()->SetTreePriority(SMOOTHNESS_TAKES_PRIORITY);
SetupTreesWithPendingTreeTiles();
base::RunLoop run_loop;
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_CALL(MockHostImpl(), NotifyReadyToActivate())
.WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
EXPECT_CALL(*mock_raster_buffer_provider(), IsResourceReadyToDraw(_))
.WillRepeatedly(Return(true));
run_loop.Run();
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToDraw());
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToActivate());
}
TEST_F(TileManagerReadyToDrawTest, TilePrioritiesUpdated) {
// Use smoothness as that's a mode in which we wait on resources to be
// ready instead of marking them ready immediately.
host_impl()->SetTreePriority(SMOOTHNESS_TAKES_PRIORITY);
gfx::Size very_small(1, 1);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(very_small));
gfx::Size layer_bounds(1000, 1000);
SetupDefaultTrees(layer_bounds);
// Run until all tile tasks are complete, but don't let any draw callbacks
// finish.
{
base::RunLoop run_loop;
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
// Until we activate our ready to draw callback, treat all resources as not
// ready to draw.
EXPECT_CALL(*mock_raster_buffer_provider(),
IsResourceReadyToDraw(testing::_))
.WillRepeatedly(Return(false));
EXPECT_CALL(*mock_raster_buffer_provider(), SetReadyToDrawCallback(_, _, _))
.WillRepeatedly(Return(1));
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
run_loop.Run();
}
// Inspect the current state of tiles in this world of cpu done but gpu
// not ready yet.
size_t orig_num_required = 0;
size_t orig_num_prepaint = 0;
std::vector<Tile*> prepaint_tiles;
for (auto* tile : host_impl()->tile_manager()->AllTilesForTesting()) {
if (tile->draw_info().has_resource()) {
if (tile->is_prepaint()) {
orig_num_prepaint++;
prepaint_tiles.push_back(tile);
} else {
orig_num_required++;
}
}
}
// Verify that there exist some prepaint tiles here.
EXPECT_GT(orig_num_prepaint, 0u);
EXPECT_GT(orig_num_required, 0u);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
UpdateDrawProperties(host_impl()->active_tree());
UpdateDrawProperties(host_impl()->pending_tree());
// Rerun prepare tiles.
{
base::RunLoop run_loop;
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
run_loop.Run();
}
// Make sure tiles priorities are updated.
size_t final_num_required = 0;
size_t final_num_prepaint = 0;
bool found_one_prepaint_to_required_transition = false;
for (auto* tile : host_impl()->tile_manager()->AllTilesForTesting()) {
if (tile->draw_info().has_resource()) {
if (tile->is_prepaint()) {
final_num_prepaint++;
} else {
final_num_required++;
if (base::Contains(prepaint_tiles, tile)) {
found_one_prepaint_to_required_transition = true;
}
}
}
}
// Tile priorities should be updated and we should have more required
// and fewer prepaint now that the viewport has changed.
EXPECT_GT(final_num_required, orig_num_required);
EXPECT_LT(final_num_prepaint, orig_num_prepaint);
EXPECT_TRUE(found_one_prepaint_to_required_transition);
}
TEST_F(TileManagerReadyToDrawTest, PrepaintTilesAreDroppedWhenIdle) {
auto count_prepaint_tiles = [](const std::vector<Tile*>& tiles) {
int count = 0;
for (auto* tile : tiles) {
if (tile->draw_info().has_resource() && tile->is_prepaint()) {
count++;
}
}
return count;
};
base::test::ScopedFeatureList scoped_feature_list{
features::kReclaimPrepaintTilesWhenIdle};
auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
host_impl()->tile_manager()->SetOverridesForTesting(
task_runner, task_runner->GetMockTickClock());
gfx::Size very_small(1, 1);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(very_small));
gfx::Size layer_bounds(1000, 1000);
SetupDefaultTrees(layer_bounds);
base::RunLoop run_loop;
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
run_loop.Run();
int prepaint_tiles =
count_prepaint_tiles(host_impl()->tile_manager()->AllTilesForTesting());
EXPECT_GT(prepaint_tiles, 0);
EXPECT_TRUE(task_runner->HasPendingTask());
// Too soon.
task_runner->FastForwardBy(TileManager::kDelayBeforeTimeReclaim / 2);
prepaint_tiles =
count_prepaint_tiles(host_impl()->tile_manager()->AllTilesForTesting());
EXPECT_GT(prepaint_tiles, 0);
EXPECT_TRUE(task_runner->HasPendingTask());
task_runner->FastForwardBy(TileManager::kDelayBeforeTimeReclaim / 2);
// Prepaint tiles are dropped.
prepaint_tiles =
count_prepaint_tiles(host_impl()->tile_manager()->AllTilesForTesting());
EXPECT_EQ(prepaint_tiles, 0);
// Don't repost a task.
EXPECT_FALSE(task_runner->HasPendingTask());
host_impl()->tile_manager()->SetOverridesForTesting(nullptr, nullptr);
}
TEST_F(TileManagerReadyToDrawTest, PrepaintTilesAreNotDropped) {
auto count_prepaint_tiles = [](const std::vector<Tile*>& tiles) {
int count = 0;
for (auto* tile : tiles) {
if (tile->draw_info().has_resource() && tile->is_prepaint()) {
count++;
}
}
return count;
};
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(
features::kReclaimPrepaintTilesWhenIdle);
auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
host_impl()->tile_manager()->SetOverridesForTesting(
task_runner, task_runner->GetMockTickClock());
gfx::Size very_small(1, 1);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(very_small));
gfx::Size layer_bounds(1000, 1000);
SetupDefaultTrees(layer_bounds);
base::RunLoop run_loop;
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
run_loop.Run();
int before_prepaint_tiles =
count_prepaint_tiles(host_impl()->tile_manager()->AllTilesForTesting());
ASSERT_GT(before_prepaint_tiles, 0);
// No release task.
EXPECT_FALSE(task_runner->HasPendingTask());
host_impl()->tile_manager()->SetOverridesForTesting(nullptr, nullptr);
}
TEST_F(TileManagerReadyToDrawTest, PrepaintTilesContinuousIdleTime) {
auto count_prepaint_tiles = [](const std::vector<Tile*>& tiles) {
int count = 0;
for (auto* tile : tiles) {
if (tile->draw_info().has_resource() && tile->is_prepaint()) {
count++;
}
}
return count;
};
auto prepare_tiles = [this]() {
base::RunLoop run_loop;
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
run_loop.Run();
};
base::test::ScopedFeatureList scoped_feature_list{
features::kReclaimPrepaintTilesWhenIdle};
auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
host_impl()->tile_manager()->SetOverridesForTesting(
task_runner, task_runner->GetMockTickClock());
gfx::Size very_small(1, 1);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(very_small));
gfx::Size layer_bounds(1000, 1000);
SetupDefaultTrees(layer_bounds);
prepare_tiles();
int before_prepaint_tiles =
count_prepaint_tiles(host_impl()->tile_manager()->AllTilesForTesting());
ASSERT_GT(before_prepaint_tiles, 0);
EXPECT_TRUE(task_runner->HasPendingTask());
// Not enough continuous idle time.
task_runner->FastForwardBy(TileManager::kDelayBeforeTimeReclaim / 2);
prepare_tiles();
task_runner->FastForwardBy(TileManager::kDelayBeforeTimeReclaim / 2);
// A task has been re-posted.
EXPECT_TRUE(task_runner->HasPendingTask());
int after_prepaint_tiles =
count_prepaint_tiles(host_impl()->tile_manager()->AllTilesForTesting());
EXPECT_EQ(after_prepaint_tiles, before_prepaint_tiles);
task_runner->FastForwardBy(TileManager::kDelayBeforeTimeReclaim / 2);
EXPECT_EQ(0, count_prepaint_tiles(
host_impl()->tile_manager()->AllTilesForTesting()));
// No task is posted when there is nothing to do.
EXPECT_FALSE(task_runner->HasPendingTask());
host_impl()->tile_manager()->SetOverridesForTesting(nullptr, nullptr);
}
void UpdateVisibleRect(FakePictureLayerImpl* layer,
const gfx::Rect visible_rect) {
PictureLayerTilingSet* tiling_set = layer->tilings();
for (size_t j = 0; j < tiling_set->num_tilings(); ++j) {
PictureLayerTiling* tiling = tiling_set->tiling_at(j);
tiling->SetTilePriorityRectsForTesting(
visible_rect, // Visible rect.
visible_rect, // Skewport rect.
visible_rect, // Soon rect.
gfx::Rect(0, 0, 1000, 1000)); // Eventually rect.
}
}
TEST_F(TileManagerReadyToDrawTest, ReadyToDrawRespectsRequirementChange) {
host_impl()->SetTreePriority(SMOOTHNESS_TAKES_PRIORITY);
SetupTreesWithPendingTreeTiles();
// Initially create a tiling with a visible rect of (0, 0, 100, 100) and
// a soon rect of the rest of the layer.
UpdateVisibleRect(pending_layer(), gfx::Rect(0, 0, 100, 100));
// Mark all these tiles as ready to draw.
{
base::RunLoop run_loop;
host_impl()->tile_manager()->DidModifyTilePriorities();
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_CALL(MockHostImpl(), NotifyReadyToActivate())
.WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
EXPECT_CALL(*mock_raster_buffer_provider(), IsResourceReadyToDraw(_))
.WillRepeatedly(Return(true));
run_loop.Run();
}
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToDraw());
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToActivate());
// Move the viewport to (900, 900, 100, 100), so that we need a different set
// of tilings.
UpdateVisibleRect(pending_layer(), gfx::Rect(900, 900, 100, 100));
EXPECT_CALL(*mock_raster_buffer_provider(), IsResourceReadyToDraw(testing::_))
.WillRepeatedly(Return(false));
base::OnceClosure callback;
{
base::RunLoop run_loop;
EXPECT_CALL(*mock_raster_buffer_provider(), SetReadyToDrawCallback(_, _, 0))
.WillOnce([&run_loop, &callback](
const std::vector<const ResourcePool::InUsePoolResource*>&
resources,
base::OnceClosure callback_in,
uint64_t pending_callback_id) {
callback = std::move(callback_in);
run_loop.Quit();
return 1;
});
host_impl()->tile_manager()->DidModifyTilePriorities();
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
run_loop.Run();
}
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToDraw());
EXPECT_FALSE(host_impl()->tile_manager()->IsReadyToActivate());
// Now switch back to our original tiling. We should be immediately able to
// activate, as we still have the original tile, and no longer need the
// tiles from the previous callback.
UpdateVisibleRect(pending_layer(), gfx::Rect(0, 0, 100, 100));
{
base::RunLoop run_loop;
host_impl()->tile_manager()->DidModifyTilePriorities();
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_CALL(MockHostImpl(), NotifyReadyToActivate())
.WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
run_loop.Run();
}
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToDraw());
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToActivate());
}
TEST_F(TileManagerReadyToDrawTest, SetBackIsLikelyToRequireADrawToFalse) {
const gfx::Size layer_bounds(1000, 1000);
host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
SetupDefaultTrees(layer_bounds);
// Verify that the queue has a required for draw tile
std::unique_ptr<RasterTilePriorityQueue> queue(host_impl()->BuildRasterQueue(
SAME_PRIORITY_FOR_BOTH_TREES, RasterTilePriorityQueue::Type::ALL));
EXPECT_FALSE(queue->IsEmpty());
EXPECT_TRUE(queue->Top().tile()->required_for_draw());
EXPECT_FALSE(host_impl()->is_likely_to_require_a_draw());
// Mark all these tiles as ready to draw.
{
base::RunLoop run_loop;
host_impl()->tile_manager()->DidModifyTilePriorities();
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_CALL(MockHostImpl(), NotifyReadyToActivate())
.WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
EXPECT_CALL(*mock_raster_buffer_provider(), IsResourceReadyToDraw(_))
.WillRepeatedly(Return(true));
run_loop.Run();
}
EXPECT_TRUE(host_impl()->is_likely_to_require_a_draw());
// Check is_likely_to_require_a_draw returns FALSE after PrepareToDraw.
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToDraw());
host_impl()->tile_manager()->PrepareToDraw();
EXPECT_FALSE(host_impl()->is_likely_to_require_a_draw());
}
PaintImage MakeCheckerablePaintImage(const gfx::Size& size) {
auto image = CreateDiscardablePaintImage(size);
return PaintImageBuilder::WithCopy(image)
.set_decoding_mode(PaintImage::DecodingMode::kAsync)
.TakePaintImage();
}
class CheckerImagingTileManagerTest : public TestLayerTreeHostBase {
public:
class MockImageGenerator : public FakePaintImageGenerator {
public:
explicit MockImageGenerator(const gfx::Size& size)
: FakePaintImageGenerator(
SkImageInfo::MakeN32Premul(size.width(), size.height())) {}
MOCK_METHOD4(
GetPixels,
bool(SkPixmap, size_t, PaintImage::GeneratorClientId, uint32_t));
};
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 {
auto settings = TestLayerTreeHostBase::CreateSettings();
settings.commit_to_active_tree = false;
settings.enable_checker_imaging = true;
settings.min_image_bytes_to_checker = 512 * 1024;
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();
}
}
void CleanUpTileManager() {
task_runner_->set_run_tasks_synchronously(true);
host_impl()->tile_manager()->FinishTasksAndCleanUp();
task_runner_->set_run_tasks_synchronously(false);
}
private:
scoped_refptr<SynchronousSimpleTaskRunner> task_runner_;
};
TEST_F(CheckerImagingTileManagerTest,
NoImageDecodeDependencyForCheckeredTiles) {
const gfx::Size layer_bounds(512, 512);
std::unique_ptr<FakeRecordingSource> recording_source =
FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
recording_source->set_fill_with_nonsolid_color(true);
auto generator =
sk_make_sp<testing::StrictMock<MockImageGenerator>>(gfx::Size(512, 512));
PaintImage image = PaintImageBuilder::WithDefault()
.set_id(PaintImage::GetNextId())
.set_paint_image_generator(generator)
.set_decoding_mode(PaintImage::DecodingMode::kAsync)
.TakePaintImage();
recording_source->add_draw_image(image, gfx::Point(0, 0));
recording_source->Rerecord();
scoped_refptr<RasterSource> raster_source =
recording_source->CreateRasterSource();
Region invalidation((gfx::Rect(layer_bounds)));
SetupPendingTree(raster_source, layer_bounds, invalidation);
PictureLayerTilingSet* tiling_set =
pending_layer()->picture_layer_tiling_set();
PictureLayerTiling* tiling = tiling_set->tiling_at(0);
tiling->set_resolution(HIGH_RESOLUTION);
tiling->CreateAllTilesForTesting();
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.
tiling->set_can_require_tiles_for_activation(true);
// PrepareTiles and synchronously run all tasks added to the TaskGraph. Since
// we are using a strict mock for the SkImageGenerator, if the decode runs as
// a part of raster tasks, the test should fail.
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_TRUE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())->RunUntilIdle();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
}
class EmptyCacheTileManagerTest : public TileManagerTest {
public:
LayerTreeSettings CreateSettings() override {
auto settings = TileManagerTest::CreateSettings();
settings.decoded_image_working_set_budget_bytes = 0;
return settings;
}
};
TEST_F(EmptyCacheTileManagerTest, AtRasterOnScreenTileRasterTasks) {
const gfx::Size layer_bounds(500, 500);
std::unique_ptr<FakeRecordingSource> recording_source =
FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
recording_source->set_fill_with_nonsolid_color(true);
int dimension = 500;
PaintImage image = MakeCheckerablePaintImage(gfx::Size(dimension, dimension));
recording_source->add_draw_image(image, gfx::Point(0, 0));
recording_source->Rerecord();
scoped_refptr<RasterSource> raster_source =
recording_source->CreateRasterSource();
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.
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
// There will be a tile raster task and an image decode task.
EXPECT_TRUE(pending_tiling->TileAt(0, 0)->HasRasterTask());
EXPECT_TRUE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
}
TEST_F(EmptyCacheTileManagerTest, AtRasterPrepaintTileRasterTasksSkipped) {
const gfx::Size layer_bounds(500, 500);
std::unique_ptr<FakeRecordingSource> recording_source =
FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
recording_source->set_fill_with_nonsolid_color(true);
int dimension = 500;
PaintImage image = MakeCheckerablePaintImage(gfx::Size(dimension, dimension));
recording_source->add_draw_image(image, gfx::Point(0, 0));
recording_source->Rerecord();
scoped_refptr<RasterSource> raster_source =
recording_source->CreateRasterSource();
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(), // An empty visual rect leads to the tile being pre-paint.
gfx::Rect(layer_bounds), // Skewport rect.
gfx::Rect(layer_bounds), // Soon rect.
gfx::Rect(layer_bounds)); // Eventually rect.
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
// There will be no tile raster task, but there will be an image decode task.
EXPECT_FALSE(pending_tiling->TileAt(0, 0)->HasRasterTask());
EXPECT_TRUE(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;
PaintImage image1 =
MakeCheckerablePaintImage(gfx::Size(dimension, dimension));
PaintImage image2 =
MakeCheckerablePaintImage(gfx::Size(dimension, dimension));
PaintImage image3 =
MakeCheckerablePaintImage(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 =
recording_source->CreateRasterSource();
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());
// Finish all raster and dispatch completion callback so that the decode work
// for checkered images can be scheduled.
static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())->RunUntilIdle();
base::RunLoop().RunUntilIdle();
// 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());
}
TEST_F(CheckerImagingTileManagerTest,
TileManagerCleanupClearsCheckerImagedDecodes) {
const gfx::Size layer_bounds(512, 512);
std::unique_ptr<FakeRecordingSource> recording_source =
FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
recording_source->set_fill_with_nonsolid_color(true);
PaintImage image = MakeCheckerablePaintImage(gfx::Size(512, 512));
recording_source->add_draw_image(image, gfx::Point(0, 0));
recording_source->Rerecord();
scoped_refptr<RasterSource> raster_source =
recording_source->CreateRasterSource();
SetupPendingTree(raster_source, gfx::Size(100, 100),
Region(gfx::Rect(0, 0, 500, 500)));
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
// Finish all raster and dispatch completion callback so that the decode work
// for checkered images can be scheduled.
static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())->RunUntilIdle();
base::RunLoop().RunUntilIdle();
FlushDecodeTasks();
EXPECT_TRUE(host_impl()
->tile_manager()
->checker_image_tracker()
.has_locked_decodes_for_testing());
host_impl()->pending_tree()->ReleaseTileResources();
CleanUpTileManager();
EXPECT_FALSE(host_impl()
->tile_manager()
->checker_image_tracker()
.has_locked_decodes_for_testing());
EXPECT_TRUE(
host_impl()->tile_manager()->TakeImagesToInvalidateOnSyncTree().empty());
}
TEST_F(CheckerImagingTileManagerTest,
TileManagerCorrectlyPrioritizesCheckerImagedDecodes) {
gfx::Size layer_bounds(500, 500);
std::unique_ptr<FakeRecordingSource> recording_source =
FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
recording_source->set_fill_with_nonsolid_color(true);
PaintImage image = MakeCheckerablePaintImage(gfx::Size(512, 512));
recording_source->add_draw_image(image, gfx::Point(0, 0));
recording_source->Rerecord();
scoped_refptr<RasterSource> raster_source =
recording_source->CreateRasterSource();
// Required for activation tiles block checker-imaged decodes.
SetupPendingTree(raster_source, gfx::Size(100, 100),
Region(gfx::Rect(0, 0, 500, 500)));
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_TRUE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
EXPECT_TRUE(host_impl()
->tile_manager()
->checker_image_tracker()
.no_decodes_allowed_for_testing());
while (!host_impl()->client()->ready_to_activate()) {
static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())
->RunSingleTaskForTesting();
base::RunLoop().RunUntilIdle();
}
EXPECT_EQ(host_impl()
->tile_manager()
->checker_image_tracker()
.decode_priority_allowed_for_testing(),
CheckerImageTracker::DecodeType::kRaster);
// Finishing all tasks allows pre-decodes.
static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())->RunUntilIdle();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(host_impl()
->tile_manager()
->checker_image_tracker()
.decode_priority_allowed_for_testing(),
CheckerImageTracker::DecodeType::kPreDecode);
// Required for draw tiles block checker-imaged decodes.
// Free all tile resources and perform another PrepareTiles.
ActivateTree();
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToDraw());
host_impl()->tile_manager()->PrepareTiles(
GlobalStateThatImpactsTilePriority());
EXPECT_FALSE(host_impl()->tile_manager()->IsReadyToDraw());
host_impl()->client()->reset_ready_to_draw();
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_TRUE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
EXPECT_TRUE(host_impl()
->tile_manager()
->checker_image_tracker()
.no_decodes_allowed_for_testing());
while (!host_impl()->tile_manager()->IsReadyToDraw()) {
static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())
->RunSingleTaskForTesting();
base::RunLoop().RunUntilIdle();
}
EXPECT_EQ(host_impl()
->tile_manager()
->checker_image_tracker()
.decode_priority_allowed_for_testing(),
CheckerImageTracker::DecodeType::kRaster);
}
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.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;
PaintImage image1 =
MakeCheckerablePaintImage(gfx::Size(dimension, dimension));
PaintImage image2 =
MakeCheckerablePaintImage(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 =
recording_source->CreateRasterSource();
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());
// Finish all raster work so the decode work for checkered images can be
// scheduled.
static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())->RunUntilIdle();
base::RunLoop().RunUntilIdle();
// 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();
}
class VerifyImageProviderRasterBuffer : public RasterBuffer {
public:
VerifyImageProviderRasterBuffer() = default;
~VerifyImageProviderRasterBuffer() override { EXPECT_TRUE(did_raster_); }
void Playback(const RasterSource* raster_source,
const gfx::Rect& raster_full_rect,
const gfx::Rect& raster_dirty_rect,
uint64_t new_content_id,
const gfx::AxisTransform2d& transform,
const RasterSource::PlaybackSettings& playback_settings,
const GURL& url) override {
did_raster_ = true;
EXPECT_TRUE(playback_settings.image_provider);
}
bool SupportsBackgroundThreadPriority() const override { return true; }
private:
bool did_raster_ = false;
};
class VerifyImageProviderRasterBufferProvider
: public FakeRasterBufferProviderImpl {
public:
VerifyImageProviderRasterBufferProvider() = default;
~VerifyImageProviderRasterBufferProvider() override {
EXPECT_GT(buffer_count_, 0);
}
std::unique_ptr<RasterBuffer> AcquireBufferForRaster(
const ResourcePool::InUsePoolResource& resource,
uint64_t resource_content_id,
uint64_t previous_content_id,
bool depends_on_at_raster_decodes,
bool depends_on_hardware_accelerated_jpeg_candidates,
bool depends_on_hardware_accelerated_webp_candidates) override {
buffer_count_++;
return std::make_unique<VerifyImageProviderRasterBuffer>();
}
private:
int buffer_count_ = 0;
};
class SynchronousRasterTileManagerTest : public TileManagerTest {
public:
std::unique_ptr<TaskGraphRunner> CreateTaskGraphRunner() override {
return std::make_unique<SynchronousTaskGraphRunner>();
}
};
TEST_F(SynchronousRasterTileManagerTest, AlwaysUseImageCache) {
// Tests that we always use the ImageDecodeCache during raster.
VerifyImageProviderRasterBufferProvider raster_buffer_provider;
host_impl()->tile_manager()->SetRasterBufferProviderForTesting(
&raster_buffer_provider);
gfx::Size layer_bounds(500, 500);
scoped_refptr<FakeRasterSource> raster_source =
FakeRasterSource::CreateFilled(layer_bounds);
Region invalidation((gfx::Rect(layer_bounds)));
SetupPendingTree(raster_source, layer_bounds, 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.
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())->RunUntilIdle();
pending_layer_ = old_pending_layer_ = active_layer_ = nullptr;
// Destroy the LTHI since it accesses the RasterBufferProvider during cleanup.
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 {
auto 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, TargetColorParams(), base::DoNothing());
host_impl()->tile_manager()->decoded_image_tracker().QueueImageDecode(
image2, TargetColorParams(), 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->SetDeviceViewportRect(
host_impl()->active_tree()->GetDeviceViewport());
// Steal from the recycled tree.
std::unique_ptr<FakePictureLayerImpl> pending_layer =
FakePictureLayerImpl::Create(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));
SetupRootProperties(pending_layer.get());
pending_tree->SetRootLayerForTesting(std::move(pending_layer));
// Add tilings/tiles for the layer.
UpdateDrawProperties(host_impl()->pending_tree());
// 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());
}
class HdrImageTileManagerTest : public CheckerImagingTileManagerTest {
public:
void DecodeHdrImage(const gfx::ColorSpace& output_cs) {
auto color_space = gfx::ColorSpace::CreateHDR10();
auto size = gfx::Size(250, 250);
auto info =
SkImageInfo::Make(size.width(), size.height(), kRGBA_F16_SkColorType,
kPremul_SkAlphaType, color_space.ToSkColorSpace());
SkBitmap bitmap;
bitmap.allocPixels(info);
PaintImage hdr_image = PaintImageBuilder::WithDefault()
.set_id(PaintImage::kInvalidId)
.set_is_high_bit_depth(true)
.set_image(SkImages::RasterFromBitmap(bitmap),
PaintImage::GetNextContentId())
.TakePaintImage();
// Add the image to our decoded_image_tracker.
TargetColorParams target_color_params;
target_color_params.color_space = output_cs;
host_impl()->tile_manager()->decoded_image_tracker().QueueImageDecode(
hdr_image, target_color_params, base::DoNothing());
FlushDecodeTasks();
// Add images to a fake recording source.
constexpr gfx::Size kLayerBounds(1000, 500);
auto recording_source =
FakeRecordingSource::CreateFilledRecordingSource(kLayerBounds);
recording_source->set_fill_with_nonsolid_color(true);
recording_source->add_draw_image(hdr_image, gfx::Point(0, 0));
recording_source->Rerecord();
auto raster_source = recording_source->CreateRasterSource();
constexpr gfx::Size kTileSize(500, 500);
Region invalidation((gfx::Rect(kLayerBounds)));
SetupPendingTree(raster_source, kTileSize, invalidation);
constexpr float kCustomWhiteLevel = 200.f;
auto display_cs = gfx::DisplayColorSpaces(output_cs);
if (output_cs.IsHDR())
display_cs.SetSDRMaxLuminanceNits(kCustomWhiteLevel);
pending_layer()->layer_tree_impl()->SetDisplayColorSpaces(display_cs);
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(kLayerBounds), // Visible rect.
gfx::Rect(kLayerBounds), // Skewport rect.
gfx::Rect(kLayerBounds), // Soon rect.
gfx::Rect(kLayerBounds)); // Eventually rect.
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
ASSERT_TRUE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
auto pending_tiles = pending_tiling->AllTilesForTesting();
ASSERT_FALSE(pending_tiles.empty());
const auto raster_cs = gfx::ColorSpace::CreateExtendedSRGB();
if (output_cs.IsHDR()) {
// Only the last tile will have any pending tasks.
const auto& pending_tasks =
host_impl()->tile_manager()->decode_tasks_for_testing(
pending_tiles.back()->id());
EXPECT_FALSE(pending_tasks.empty());
for (const auto& draw_info : pending_tasks) {
EXPECT_EQ(draw_info.target_color_space(), raster_cs);
EXPECT_FLOAT_EQ(draw_info.sdr_white_level(), kCustomWhiteLevel);
}
}
// Raster all tiles.
static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())
->RunUntilIdle();
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(
host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
auto expected_format = output_cs.IsHDR()
? viz::SinglePlaneFormat::kRGBA_F16
: viz::SinglePlaneFormat::kRGBA_8888;
auto all_tiles = host_impl()->tile_manager()->AllTilesForTesting();
for (const auto* tile : all_tiles)
EXPECT_EQ(expected_format,
tile->draw_info().resource_shared_image_format());
}
};
TEST_F(HdrImageTileManagerTest, DecodeHdrImagesToHdrPq) {
DecodeHdrImage(gfx::ColorSpace::CreateHDR10());
}
TEST_F(HdrImageTileManagerTest, DecodeHdrImagesToHdrHlg) {
DecodeHdrImage(gfx::ColorSpace::CreateHLG());
}
TEST_F(HdrImageTileManagerTest, DecodeHdrImagesToSdrSrgb) {
DecodeHdrImage(gfx::ColorSpace::CreateSRGB());
}
TEST_F(HdrImageTileManagerTest, DecodeHdrImagesToSdrP3) {
DecodeHdrImage(gfx::ColorSpace::CreateDisplayP3D65());
}
class TileManagerCheckRasterQueriesTest : public TileManagerTest {
public:
TileManagerCheckRasterQueriesTest()
: pending_raster_queries_(
viz::TestContextProvider::CreateWorker().get()) {}
~TileManagerCheckRasterQueriesTest() override {
// Ensure that the host impl doesn't outlive |raster_buffer_provider_|.
TakeHostImpl();
}
void SetUp() override {
TileManagerTest::SetUp();
host_impl()->tile_manager()->SetPendingRasterQueriesForTesting(
&pending_raster_queries_);
}
protected:
class MockRasterQueryQueue : public FakeRasterQueryQueue {
public:
explicit MockRasterQueryQueue(
viz::RasterContextProvider* const worker_context_provider)
: FakeRasterQueryQueue(worker_context_provider) {}
MOCK_METHOD0(CheckRasterFinishedQueries, bool());
};
MockRasterQueryQueue pending_raster_queries_;
};
TEST_F(TileManagerCheckRasterQueriesTest,
ChecksRasterQueriesInAllTilesDoneTask) {
base::RunLoop run_loop;
EXPECT_FALSE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(testing::Invoke([&run_loop]() { run_loop.Quit(); }));
EXPECT_CALL(pending_raster_queries_, CheckRasterFinishedQueries()).Times(1);
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_TRUE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
run_loop.Run();
}
class TileManagerTileReclaimTest : public TileManagerTest {
public:
~TileManagerTileReclaimTest() override { TakeHostImpl(); }
void SetUp() override {
TileManagerTest::SetUp();
task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
host_impl()->tile_manager()->SetOverridesForTesting(
task_runner_, task_runner_->GetMockTickClock());
host_impl()->AdvanceToNextFrame(base::Milliseconds(1));
gfx::Size layer_bounds(1000, 1000);
SetupDefaultTrees(layer_bounds);
// Create a pending child layer.
scoped_refptr<FakeRasterSource> pending_raster_source =
FakeRasterSource::CreateFilled(layer_bounds);
auto* pending_child = AddLayer<FakePictureLayerImpl>(
host_impl()->pending_tree(), pending_raster_source);
pending_child->SetDrawsContent(true);
CopyProperties(pending_layer(), pending_child);
}
void TearDown() override {
ASSERT_FALSE(task_runner()->HasPendingTask());
host_impl()->tile_manager()->SetOverridesForTesting(nullptr, nullptr);
TileManagerTest::TearDown();
}
scoped_refptr<base::TestMockTimeTaskRunner> task_runner() const {
return task_runner_;
}
void MakeFrame(GlobalStateThatImpactsTilePriority global_tile_state) {
host_impl()->AdvanceToNextFrame(base::Milliseconds(1));
UpdateDrawProperties(host_impl()->active_tree());
UpdateDrawProperties(host_impl()->pending_tree());
base::RunLoop run_loop;
EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
.WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
host_impl()->tile_manager()->PrepareTiles(global_tile_state);
run_loop.Run();
}
private:
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
};
TEST_F(TileManagerTileReclaimTest, ReclaimOldPrepainTilesSimple) {
base::test::ScopedFeatureList scoped_feature_list{
features::kReclaimOldPrepaintTiles};
// Set a small viewport, so we have soon and eventually tiles.
gfx::Rect viewport{100, 100, 100, 100};
GlobalStateThatImpactsTilePriority global_tile_state =
host_impl()->global_tile_state();
ASSERT_EQ(global_tile_state.memory_limit_policy,
TileMemoryLimitPolicy::ALLOW_ANYTHING);
host_impl()->active_tree()->SetDeviceViewportRect(viewport);
MakeFrame(global_tile_state);
auto eviction_queue = host_impl()->BuildEvictionQueue(
host_impl()->global_tile_state().tree_priority);
bool has_eventually_tiles = false;
size_t tiles_count_before = 0;
for (; !eviction_queue->IsEmpty(); eviction_queue->Pop()) {
const auto& tile = eviction_queue->Top();
has_eventually_tiles |=
tile.priority().priority_bin == TilePriority::EVENTUALLY &&
tile.tile()->draw_info().IsReadyToDraw();
// Tiles are initially marked as "used".
EXPECT_TRUE(tile.tile()->used());
tiles_count_before++;
}
ASSERT_TRUE(has_eventually_tiles);
task_runner()->FastForwardBy(TileManager::GetTrimPrepaintTilesDelay());
// Since the policy is still ALLOW_ANYTHING, no changes.
eviction_queue = host_impl()->BuildEvictionQueue(
host_impl()->global_tile_state().tree_priority);
size_t tiles_count_after = 0;
for (; !eviction_queue->IsEmpty(); eviction_queue->Pop()) {
const auto& tile = eviction_queue->Top();
has_eventually_tiles |=
tile.priority().priority_bin == TilePriority::EVENTUALLY &&
tile.tile()->draw_info().IsReadyToDraw();
EXPECT_TRUE(tile.tile()->used());
tiles_count_after++;
}
EXPECT_EQ(tiles_count_after, tiles_count_before);
// No progress can be made, do not repost a task.
EXPECT_FALSE(task_runner()->HasPendingTask());
// Change to a policy where EVENTUALLY tiles can be reclaimed.
global_tile_state.memory_limit_policy =
TileMemoryLimitPolicy::ALLOW_PREPAINT_ONLY;
host_impl()->active_tree()->SetDeviceViewportRect(viewport);
MakeFrame(global_tile_state);
EXPECT_TRUE(task_runner()->HasPendingTask());
task_runner()->FastForwardBy(TileManager::GetTrimPrepaintTilesDelay());
tiles_count_after = 0;
eviction_queue = host_impl()->BuildEvictionQueue(
host_impl()->global_tile_state().tree_priority);
for (; !eviction_queue->IsEmpty(); eviction_queue->Pop()) {
const auto& tile = eviction_queue->Top();
if (tile.priority().priority_bin == TilePriority::EVENTUALLY) {
EXPECT_FALSE(tile.tile()->used());
} else {
EXPECT_TRUE(tile.tile()->used());
}
tiles_count_after++;
}
EXPECT_EQ(tiles_count_after, tiles_count_before);
// A task is re-posted, since we can make progress (some tiles became "old").
EXPECT_TRUE(task_runner()->HasPendingTask());
task_runner()->FastForwardBy(TileManager::GetTrimPrepaintTilesDelay());
// All the EVENTUALLY tiles are gone.
tiles_count_after = 0;
eviction_queue = host_impl()->BuildEvictionQueue(
host_impl()->global_tile_state().tree_priority);
for (; !eviction_queue->IsEmpty(); eviction_queue->Pop()) {
const auto& tile = eviction_queue->Top();
EXPECT_NE(tile.priority().priority_bin, TilePriority::EVENTUALLY);
tiles_count_after++;
}
EXPECT_LT(tiles_count_after, tiles_count_before);
// And there is nothing left to do.
EXPECT_FALSE(task_runner()->HasPendingTask());
}
TEST_F(TileManagerTileReclaimTest, ReclaimOldPrepainTilesOldYoungTiles) {
base::test::ScopedFeatureList scoped_feature_list{
features::kReclaimOldPrepaintTiles};
// Set a small viewport, so we have soon and eventually tiles.
gfx::Rect viewport{100, 100, 100, 100};
GlobalStateThatImpactsTilePriority global_tile_state =
host_impl()->global_tile_state();
host_impl()->active_tree()->SetDeviceViewportRect(viewport);
MakeFrame(global_tile_state);
auto eviction_queue = host_impl()->BuildEvictionQueue(
host_impl()->global_tile_state().tree_priority);
bool has_eventually_tiles = false;
size_t tiles_count_before = 0;
for (; !eviction_queue->IsEmpty(); eviction_queue->Pop()) {
const auto& tile = eviction_queue->Top();
has_eventually_tiles |=
tile.priority().priority_bin == TilePriority::EVENTUALLY &&
tile.tile()->draw_info().IsReadyToDraw();
// Tiles are initially marked as "used".
EXPECT_TRUE(tile.tile()->used());
tiles_count_before++;
}
ASSERT_TRUE(has_eventually_tiles);
global_tile_state.memory_limit_policy =
TileMemoryLimitPolicy::ALLOW_PREPAINT_ONLY;
host_impl()->active_tree()->SetDeviceViewportRect(viewport);
MakeFrame(global_tile_state);
EXPECT_TRUE(task_runner()->HasPendingTask());
task_runner()->FastForwardBy(TileManager::GetTrimPrepaintTilesDelay());
size_t tiles_count_after = 0;
Tile* first_eventually_tile = nullptr;
eviction_queue = host_impl()->BuildEvictionQueue(
host_impl()->global_tile_state().tree_priority);
for (; !eviction_queue->IsEmpty(); eviction_queue->Pop()) {
const auto& tile = eviction_queue->Top();
if (tile.priority().priority_bin == TilePriority::EVENTUALLY) {
EXPECT_FALSE(tile.tile()->used());
if (!first_eventually_tile) {
first_eventually_tile = tile.tile();
}
} else {
EXPECT_TRUE(tile.tile()->used());
}
tiles_count_after++;
}
EXPECT_EQ(tiles_count_after, tiles_count_before);
// A task is re-posted, since we can make progress (some tiles became "old").
EXPECT_TRUE(task_runner()->HasPendingTask());
// Pretend that the tile was used in the meantime.
first_eventually_tile->mark_used();
task_runner()->FastForwardBy(TileManager::GetTrimPrepaintTilesDelay());
// The tile is there, it's the only remaining EVENTUALLY one.
tiles_count_after = 0;
bool has_found_tile = false;
eviction_queue = host_impl()->BuildEvictionQueue(
host_impl()->global_tile_state().tree_priority);
for (; !eviction_queue->IsEmpty(); eviction_queue->Pop()) {
const auto& tile = eviction_queue->Top();
EXPECT_TRUE(tile.priority().priority_bin != TilePriority::EVENTUALLY ||
tile.tile() == first_eventually_tile);
has_found_tile |= tile.tile() == first_eventually_tile;
tiles_count_after++;
}
EXPECT_LT(tiles_count_after, tiles_count_before);
EXPECT_TRUE(has_found_tile);
EXPECT_FALSE(first_eventually_tile->used());
// Progress can be made
EXPECT_TRUE(task_runner()->HasPendingTask());
task_runner()->FastForwardBy(TileManager::GetTrimPrepaintTilesDelay());
// The tile is not there anymore.
eviction_queue = host_impl()->BuildEvictionQueue(
host_impl()->global_tile_state().tree_priority);
for (; !eviction_queue->IsEmpty(); eviction_queue->Pop()) {
const auto& tile = eviction_queue->Top();
EXPECT_NE(tile.tile(), first_eventually_tile);
}
// No progress can be made, do not repost a task.
EXPECT_FALSE(task_runner()->HasPendingTask());
}
} // namespace
} // namespace cc