// 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 <algorithm>
#include <array>
#include <cmath>
#include <utility>

#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/metrics/histogram_tester.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/picture_layer_tiling_set.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/shared_image_format_utils.h"
#include "components/viz/test/begin_frame_args_test.h"
#include "gpu/command_buffer/client/test_shared_image_interface.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.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::Return;
using testing::StrictMock;

namespace cc {
namespace {

DrawImage DrawImageForDecoding(const PaintImage& paint_image,
                               const TargetColorParams& color_params) {
  return DrawImage(paint_image,
                   /*use_dark_mode=*/false,
                   SkIRect::MakeWH(paint_image.width(), paint_image.height()),
                   PaintFlags::FilterQuality::kNone, SkM44(),
                   PaintImage::kDefaultFrameIndex, color_params);
}

// 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_ = 0.f;
};

class TileManagerTilePriorityQueueTest : public TestLayerTreeHostBase {
 public:
  LayerTreeSettings CreateSettings() override {
    auto settings = TestLayerTreeHostBase::CreateSettings();
    return settings;
  }

  TileManager* tile_manager() { return host_impl()->tile_manager(); }
};

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);
  // 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]);
  }

  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 (!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, 2 * 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, 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(pending_layer()->HighResTiling());

  std::unique_ptr<EvictionTilePriorityQueue> empty_queue(
      host_impl()->BuildEvictionQueue());
  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());
  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();

  // 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]);

  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();
  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(15, 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();
  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(15, 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_1 =
      FakeRasterSource::CreateFilledWithText(layer_bounds);
  scoped_refptr<FakeRasterSource> pending_raster_source_2 =
      FakeRasterSource::CreateFilledWithText(layer_bounds);

  SetupPendingTree(pending_raster_source_1);

  auto* pending_child_layer =
      AddLayer<FakePictureLayerImpl>(host_impl()->pending_tree());
  LayerDebugInfo debug_info;
  debug_info.name = "debug-name";

  // The debug_info.name is copied to the raster source in SetRasterSource
  // so it must be set before.
  pending_child_layer->UpdateDebugInfo(&debug_info);
  pending_child_layer->SetBounds(pending_raster_source_2->size());
  pending_child_layer->SetRasterSource(pending_raster_source_2, Region());
  pending_child_layer->SetDrawsContent(true);
  CopyProperties(pending_layer(), pending_child_layer);

  ActivateTree();

  UpdateDrawProperties(host_impl()->active_tree());
  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 (allocator_map_pair.first.contains("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();

  tile_manager()->InitializeTilesWithResourcesForTesting(
      std::vector<Tile*>(all_tiles.begin(), all_tiles.end()));

  // Verify occlusion is considered by EvictionTilePriorityQueue.
  size_t occluded_count = 0u;
  PrioritizedTile last_tile;
  std::unique_ptr<EvictionTilePriorityQueue> queue(
      host_impl()->BuildEvictionQueue());
  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(
      /*update_tiles=*/true, /*update_image_animation_controller=*/true);

  // 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.
  std::set<Tile*> new_content_tiles;
  size_t tile_count = 0;
  std::unique_ptr<EvictionTilePriorityQueue> queue(
      host_impl()->BuildEvictionQueue());
  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 (all_pending_child_tiles.contains(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());
  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 =
        TilingSetRasterQueueAll::Create(tiling_set.get(), false);
    EXPECT_TRUE(queue);

    // There are 3 bins in TilePriority.
    std::array<bool, 3> have_tiles = {};

    // 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.
  std::array<bool, 3> have_tiles = {};
  PrioritizedTile last_tile;
  int eventually_bin_order_correct_count = 0;
  int eventually_bin_order_incorrect_count = 0;
  std::unique_ptr<TilingSetRasterQueueAll> queue =
      TilingSetRasterQueueAll::Create(tiling_set.get(), false);
  EXPECT_TRUE(queue);
  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_backing(std::make_unique<ResourcePool::Backing>(
      resource.size(), resource.format(), resource.color_space()));

  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 =
        TilingSetRasterQueueAll::Create(tiling_set.get(), false);
    if (features::IsCCSlimmingEnabled()) {
      EXPECT_FALSE(queue);
    } else {
      EXPECT_TRUE(queue);
    }
  }

  // This is the behavior difference between FakePictureLayerTilingClient
  // and the real PictureLayerTilingClient a.k.a. PictureLayerImpl.
  // The FakePictureLayerTilingClient doesn't clear PictureLayerTilingSet's
  // |all_tiles_done_| when AddTile() is called whereas PictureLayerImpl does.
  tiling_set->set_all_tiles_done(false);

  {
    tiling->SetTilePriorityRectsForTesting(
        non_intersecting_rect,  // Visible rect.
        intersecting_rect,      // Skewport rect.
        intersecting_rect,      // Soon rect.
        intersecting_rect);     // Eventually rect.
    std::unique_ptr<TilingSetRasterQueueAll> queue =
        TilingSetRasterQueueAll::Create(tiling_set.get(), false);
    EXPECT_TRUE(queue);
    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 =
        TilingSetRasterQueueAll::Create(tiling_set.get(), false);
    EXPECT_TRUE(queue);
    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 =
        TilingSetRasterQueueAll::Create(tiling_set.get(), false);
    EXPECT_TRUE(queue);
    EXPECT_FALSE(queue->IsEmpty());
  }

  // Clear the raw_ptr to tiling_set before it goes out of scope to prevent
  // dangling pointer when pending_client is destroyed after tiling_set.
  pending_client.set_twin_tiling_set(nullptr);
}

TEST_F(TileManagerTilePriorityQueueTest, NoRasterTasksforSolidColorTiles) {
  gfx::Size size(10, 10);
  const gfx::Size layer_bounds(1000, 1000);

  FakeRecordingSource recording_source(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);

  SetupTrees(raster_source, raster_source);
  pending_layer_->set_contributes_to_drawn_render_surface(true);
  PictureLayerTilingSet* tiling_set =
      pending_layer_->picture_layer_tiling_set();
  tiling_set->RemoveAllTilings();

  PictureLayerTiling* tiling =
      tiling_set->AddTiling(gfx::AxisTransform2d(), raster_source);
  tiling->set_resolution(HIGH_RESOLUTION);
  tiling->CreateAllTilesForTesting(gfx::Rect(layer_bounds));

  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());
    }
  }
}

// A RasterBufferProvider that creates an SII internally and allocates
// Backings via that SII.
class TestSoftwareRasterBufferProvider : public FakeRasterBufferProviderImpl {
 public:
  TestSoftwareRasterBufferProvider() {
    sii_ = base::MakeRefCounted<gpu::TestSharedImageInterface>();
  }

  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) override {
    if (!resource.backing()) {
      resource.InstallSoftwareBacking(sii_, "TextureLayerTest");

      resource.backing()->mailbox_sync_token = sii_->GenUnverifiedSyncToken();
    }
    return std::make_unique<TestRasterBuffer>(resource.size(),
                                              resource.backing());
  }

 private:
  class TestRasterBuffer : public RasterBuffer {
   public:
    TestRasterBuffer(const gfx::Size& size, ResourcePool::Backing* backing)
        : size_(size), backing_(backing) {}

    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 {
      auto mapping = backing_->shared_image()->Map();
      void* memory = mapping->GetMemoryForPlane(0).data();
      RasterBufferProvider::PlaybackToMemory(
          memory, kSharedImageFormat, size_, /*stride=*/0, raster_source,
          raster_full_rect, /*canvas_playback_rect=*/raster_full_rect,
          transform, gfx::ColorSpace(), playback_settings);
    }

    bool SupportsBackgroundThreadPriority() const override { return true; }

   private:
    gfx::Size size_;
    raw_ptr<ResourcePool::Backing> backing_;
  };

  scoped_refptr<gpu::TestSharedImageInterface> sii_;
};

class TileManagerTest : public TestLayerTreeHostBase {
 public:
  LayerTreeSettings CreateSettings() override {
    LayerTreeSettings settings = TestLayerTreeHostBase::CreateSettings();
    CHECK(!settings.commit_to_active_tree);
    // This is required in commit-to-pending-tree mode to call
    // NotifyReadyToDraw().
    settings.wait_for_all_pipeline_stages_before_draw = true;
    return settings;
  }

  // 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([&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([&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([&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([&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();
  }
}

// Tests the "no work" fast path.
TEST_F(TileManagerTest, FastPathWhenNoRasterWork) {
  base::HistogramTester histogram_tester;

  host_impl()->tile_manager()->DisbleMetricsSubsamplingForTesting();

  // 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([&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([&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();
  }
  histogram_tester.ExpectTotalCount(
      "Compositing.TileManager.RasterTasksDuration", 2u);
}

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([&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([&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 TileManagerDeferredRedrawTest : public TestLayerTreeHostBase {
 public:
  // Host impl that simulates a client which removes tilings synchronously when
  // notified of a tile state change with `set_needs_redraw` set. TileManager
  // must defer SetNeedsRedraw() until it has finished iterating its priority
  // queues so that it is safe for the client to do this.
  class ReentrantHostImpl : public FakeLayerTreeHostImpl {
   public:
    using FakeLayerTreeHostImpl::FakeLayerTreeHostImpl;

    void NotifyTileStateChanged(const Tile* tile,
                                bool update_damage,
                                bool set_needs_redraw) override {
      if (set_needs_redraw && tilings_to_remove_) {
        ++notify_with_redraw_count_;
        PictureLayerTilingSet* tilings = tilings_to_remove_;
        tilings_to_remove_ = nullptr;
        tilings->RemoveAllTilings();
      }
    }

    void SetNeedsRedraw(bool animation_only,
                        bool skip_if_inside_draw) override {
      ++set_needs_redraw_count_;
    }

    void set_tilings_to_remove(PictureLayerTilingSet* tilings) {
      tilings_to_remove_ = tilings;
    }
    int notify_with_redraw_count() const { return notify_with_redraw_count_; }
    int set_needs_redraw_count() const { return set_needs_redraw_count_; }

   private:
    raw_ptr<PictureLayerTilingSet> tilings_to_remove_ = nullptr;
    int notify_with_redraw_count_ = 0;
    int set_needs_redraw_count_ = 0;
  };

  std::unique_ptr<FakeLayerTreeHostImpl> CreateHostImpl(
      const LayerTreeSettings& settings,
      TaskRunnerProvider* task_runner_provider,
      TaskGraphRunner* task_graph_runner) override {
    return std::make_unique<ReentrantHostImpl>(settings, task_runner_provider,
                                               task_graph_runner);
  }

  std::unique_ptr<LayerTreeFrameSink> CreateLayerTreeFrameSink() override {
    return FakeLayerTreeFrameSink::CreateSoftware();
  }

  ReentrantHostImpl* reentrant_host_impl() {
    return static_cast<ReentrantHostImpl*>(host_impl());
  }
};

TEST_F(TileManagerDeferredRedrawTest, DeferRedrawWhileFreeingTileResources) {
  const gfx::Size layer_bounds(1000, 1000);
  host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(layer_bounds));
  SetupDefaultTrees(layer_bounds);

  std::vector<Tile*> active_tiles =
      active_layer()->HighResTiling()->AllTilesForTesting();
  ASSERT_GT(active_tiles.size(), 1u);
  host_impl()->tile_manager()->InitializeTilesWithResourcesForTesting(
      active_tiles);

  // Reduce the memory limit so that all active tiles will be evicted on the
  // next PrepareTiles().
  auto global_state = host_impl()->global_tile_state();
  global_state.hard_memory_limit_in_bytes = 0u;
  global_state.soft_memory_limit_in_bytes = 0u;
  reentrant_host_impl()->set_tilings_to_remove(active_layer()->tilings());

  // Freeing tile resources notifies the client of each tile's state change but
  // must defer SetNeedsRedraw() until the priority queues are no longer in use.
  // No tile state change notification should request a redraw while resources
  // are being freed; instead a single SetNeedsRedraw() should be issued
  // afterwards.
  host_impl()->tile_manager()->PrepareTiles(global_state);
  EXPECT_EQ(0, reentrant_host_impl()->notify_with_redraw_count());
  EXPECT_LE(1, reentrant_host_impl()->set_needs_redraw_count());
}

class TileManagerOcclusionTest : public TileManagerTest {
 public:
  LayerTreeSettings CreateSettings() override {
    auto settings = TileManagerTest::CreateSettings();
    settings.use_occlusion_for_tile_prioritization = true;
    return settings;
  }

  void PrepareTilesAndWaitUntilDone(
      const GlobalStateThatImpactsTilePriority& state) {
    base::RunLoop run_loop;
    EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
        .WillOnce([&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:
  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, ImageDrawn) {
  gfx::Size size(10, 12);
  host_impl()->CreatePendingTree();

  SCOPED_TRACE(HIGH_RESOLUTION);

  // 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();

  FakeRecordingSource recording_source(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();

  std::unique_ptr<PictureLayerImpl> layer =
      PictureLayerImpl::Create(host_impl()->pending_tree(), 1);
  layer->SetBounds(size);
  layer->SetRasterSourceForTesting(raster);
  PictureLayerTilingSet* tiling_set = layer->picture_layer_tiling_set();
  layer->set_contributes_to_drawn_render_surface(true);
  host_impl()->pending_tree()->AddLayer(std::move(layer));

  auto* tiling = tiling_set->AddTiling(gfx::AxisTransform2d(), raster);
  tiling->set_resolution(HIGH_RESOLUTION);
  tiling->CreateAllTilesForTesting(gfx::Rect(size));

  // Call PrepareTiles and wait for it to complete.
  auto* tile_manager = host_impl()->tile_manager();
  base::RunLoop run_loop;
  EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
      .WillOnce([&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::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 = tile->draw_info().GetResource().backing();
  SkBitmap bitmap;
  auto mapping = backing->shared_image()->Map();
  void* pixels = mapping->GetMemoryForPlane(0).data();
  bitmap.installPixels(info, pixels, info.minRowBytes());

  for (int x = 0; x < size.width(); ++x) {
    for (int y = 0; y < size.height(); ++y) {
      SCOPED_TRACE(y);
      SCOPED_TRACE(x);
      // 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.
  FakeRecordingSource active_tree_recording_source(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.
  FakeRecordingSource pending_tree_recording_source(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([&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. The lifetime of the
  // queue can't outlive |host_impl| or the queue will contain dangling
  // pointers so wrap it in a scope.
  {
    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.
  ClearLayersAndHost();
}

// 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) 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<ClientLayerTreeHostImpl> 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::kBGRA_8888,
          gfx::ColorSpace::CreateSRGB());

  auto backing = std::make_unique<ResourcePool::Backing>(
      resource.size(), resource.format(), resource.color_space());
  backing->CreateSharedImageForTesting();
  backing->mailbox_sync_token.Set(gpu::GPU_IO,
                                  gpu::CommandBufferId::FromUnsafeValue(1), 1);

  resource.set_backing(std::move(backing));
  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. The lifetime of the
  // queue can't outlive |host_impl| or the queue will contain dangling
  // pointers so wrap it in a scope.
  {
    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<ClientLayerTreeHostImpl> 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, host_impl->GetTileFormat(), 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);

  FakeRecordingSource recording_source(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);

  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. The lifetime of the queue can't outlive |host_impl| or the
  // queue will contain dangling pointers, so wrap it in a scope.
  {
    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:
  InvalidResourceRasterBufferProvider() = default;
  std::unique_ptr<RasterBuffer> AcquireBufferForRaster(
      const ResourcePool::InUsePoolResource& resource,
      uint64_t resource_content_id,
      uint64_t previous_content_id) override {
    if (!resource.backing()) {
      auto backing = std::make_unique<ResourcePool::Backing>(
          resource.size(), resource.format(), resource.color_space());
      // Don't set a mailbox to signal invalid resource.
      resource.set_backing(std::move(backing));
    }
    return std::make_unique<FakeRasterBuffer>();
  }
};

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);

  auto raster = FakeRasterSource::CreateFilled(size);
  SetupTrees(raster, raster);
  active_layer()->set_contributes_to_drawn_render_surface(true);

  active_layer()->picture_layer_tiling_set()->RemoveAllTilings();
  auto* tiling = active_layer()->picture_layer_tiling_set()->AddTiling(
      gfx::AxisTransform2d(), std::move(raster));
  tiling->set_resolution(HIGH_RESOLUTION);
  tiling->CreateAllTilesForTesting(gfx::Rect(size));

  base::RunLoop run_loop;
  EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
      .WillOnce([&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|.
  ClearLayersAndHost();
}

// FakeRasterBufferProviderImpl that allows us to mock ready to draw
// functionality.
class MockReadyToDrawRasterBufferProviderImpl
    : public FakeRasterBufferProviderImpl {
 public:
  MockReadyToDrawRasterBufferProviderImpl() = default;
  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) override {
    if (!resource.backing()) {
      auto backing = std::make_unique<ResourcePool::Backing>(
          resource.size(), resource.format(), resource.color_space());
      backing->CreateSharedImageForTesting();
      backing->mailbox_sync_token.Set(
          gpu::GPU_IO, gpu::CommandBufferId::FromUnsafeValue(1), 1);
      resource.set_backing(std::move(backing));
    }
    return std::make_unique<FakeRasterBuffer>(expected_hdr_headroom_);
  }

 private:
  float expected_hdr_headroom_ = 0.f;
};

class TileManagerReadyToDrawTest : public TileManagerTest {
 public:
  void SetUp() override {
    TileManagerTest::SetUp();
    host_impl()->tile_manager()->SetRasterBufferProviderForTesting(
        &mock_raster_buffer_provider_);

    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(kLayerBounds), solid_flags);

    solid_color_recording_source_.Rerecord();

    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_;
  const gfx::Size kLayerBounds{1000, 1000};
  FakeRecordingSource recording_source_{kLayerBounds};
  FakeRecordingSource solid_color_recording_source_{kLayerBounds};
};

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([&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([&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 = 2.f;
  TargetColorParams target_color_params;
  target_color_params.hdr_headroom = 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([&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([&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([&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([&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([&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([&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 (std::ranges::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([&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([&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([&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([&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([&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([&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.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);

  FakeRecordingSource recording_source(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(gfx::Rect(layer_bounds));
  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);

  FakeRecordingSource recording_source(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(gfx::Rect(layer_bounds));

  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);

  FakeRecordingSource recording_source(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);

  FakeRecordingSource recording_source(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(gfx::Rect(layer_bounds));

  // 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()->delegate()->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()->delegate()->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()->delegate()->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()->delegate()->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()->delegate()->did_request_impl_side_invalidation());
}

TEST_F(CheckerImagingTileManagerTest,
       TileManagerCleanupClearsCheckerImagedDecodes) {
  const gfx::Size layer_bounds(512, 512);

  FakeRecordingSource recording_source(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);

  FakeRecordingSource recording_source(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()->delegate()->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()->delegate()->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);

  FakeRecordingSource recording_source(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);

  // Use a rect that only rasterizes the bottom 2 rows of tiles.
  gfx::Rect rect_to_raster(0, 500, 900, 900);
  pending_tiling->CreateAllTilesForTesting(rect_to_raster);

  // 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()->delegate()->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()->delegate()->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) 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(gfx::Rect(layer_bounds));

  host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
  static_cast<SynchronousTaskGraphRunner*>(task_graph_runner())->RunUntilIdle();

  // Destroy the LTHI since it accesses the RasterBufferProvider during cleanup.
  ClearLayersAndHost();
}

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(
      DrawImageForDecoding(image1, TargetColorParams()), base::DoNothing(),
      /*speculative*/ false);
  host_impl()->tile_manager()->decoded_image_tracker().QueueImageDecode(
      DrawImageForDecoding(image2, TargetColorParams()), base::DoNothing(),
      /*speculative*/ false);
  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);
  FakeRecordingSource recording_source(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);

  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(
        DrawImageForDecoding(hdr_image, target_color_params), base::DoNothing(),
        /*speculative*/ false);
    FlushDecodeTasks();

    // Add images to a fake recording source.
    constexpr gfx::Size kLayerBounds(1000, 500);
    FakeRecordingSource recording_source(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(gfx::Rect(kLayerBounds));

    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::CreateDisplayP3D65().GetAsHDR();
    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);
      }
    }

    // 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()
      : worker_context_provider_(viz::TestContextProvider::CreateWorker()),
        pending_raster_queries_(worker_context_provider_.get()) {}

  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 holds onto a ptr to TestContextProvider, so member
  // declaration order is important to avoid a dangling pointer.
  scoped_refptr<viz::TestContextProvider> worker_context_provider_;
  MockRasterQueryQueue pending_raster_queries_;
};

TEST_F(TileManagerCheckRasterQueriesTest,
       ChecksRasterQueriesInAllTilesDoneTask) {
  base::RunLoop run_loop;
  EXPECT_FALSE(host_impl()->tile_manager()->HasScheduledTileTasksForTesting());
  EXPECT_CALL(MockHostImpl(), NotifyAllTileTasksCompleted())
      .WillOnce([&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:
  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([&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();
  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();
  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();
  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();
  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();
  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();
  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();
  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();
  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
