cc: adds LayerTreeHost::SetPriorityCutoffOverride

And if priority is made more restrictive, tile resources
that violate the memory policy are freed.

BUG=1297315

Change-Id: I224d608c46e436ef7b1e8dd735e5b8538d202531
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3469316
Reviewed-by: Vladimir Levin <[email protected]>
Commit-Queue: Scott Violet <[email protected]>
Cr-Commit-Position: refs/heads/main@{#972735}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 4d46ac4c..4b005d4 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -300,8 +300,6 @@
     "tiles/image_decode_cache_utils.h",
     "tiles/mipmap_util.cc",
     "tiles/mipmap_util.h",
-    "tiles/occluded_tile_iterator.cc",
-    "tiles/occluded_tile_iterator.h",
     "tiles/picture_layer_tiling.cc",
     "tiles/picture_layer_tiling.h",
     "tiles/picture_layer_tiling_set.cc",
@@ -330,6 +328,8 @@
     "tiles/tile_priority.h",
     "tiles/tile_task_manager.cc",
     "tiles/tile_task_manager.h",
+    "tiles/tiles_with_resource_iterator.cc",
+    "tiles/tiles_with_resource_iterator.h",
     "tiles/tiling_set_eviction_queue.cc",
     "tiles/tiling_set_eviction_queue.h",
     "tiles/tiling_set_raster_queue_all.cc",
diff --git a/cc/test/fake_tile_manager_client.cc b/cc/test/fake_tile_manager_client.cc
index 8d7fd34d..434b757 100644
--- a/cc/test/fake_tile_manager_client.cc
+++ b/cc/test/fake_tile_manager_client.cc
@@ -4,7 +4,7 @@
 
 #include "cc/test/fake_tile_manager_client.h"
 
-#include "cc/tiles/occluded_tile_iterator.h"
+#include "cc/tiles/tiles_with_resource_iterator.h"
 
 namespace cc {
 
@@ -23,8 +23,8 @@
   return nullptr;
 }
 
-std::unique_ptr<OccludedTileIterator>
-FakeTileManagerClient::CreateOccludedTileIterator() {
+std::unique_ptr<TilesWithResourceIterator>
+FakeTileManagerClient::CreateTilesWithResourceIterator() {
   return nullptr;
 }
 
diff --git a/cc/test/fake_tile_manager_client.h b/cc/test/fake_tile_manager_client.h
index 4f1946c..653eae4b 100644
--- a/cc/test/fake_tile_manager_client.h
+++ b/cc/test/fake_tile_manager_client.h
@@ -27,7 +27,8 @@
       RasterTilePriorityQueue::Type type) override;
   std::unique_ptr<EvictionTilePriorityQueue> BuildEvictionQueue(
       TreePriority tree_priority) override;
-  std::unique_ptr<OccludedTileIterator> CreateOccludedTileIterator() override;
+  std::unique_ptr<TilesWithResourceIterator> CreateTilesWithResourceIterator()
+      override;
   void SetIsLikelyToRequireADraw(bool is_likely_to_require_a_draw) override {}
   TargetColorParams GetTargetColorParams(
       gfx::ContentColorUsage content_color_usage) const override;
diff --git a/cc/tiles/picture_layer_tiling.h b/cc/tiles/picture_layer_tiling.h
index b2b33b8..c27c0316 100644
--- a/cc/tiles/picture_layer_tiling.h
+++ b/cc/tiles/picture_layer_tiling.h
@@ -328,12 +328,12 @@
 
  protected:
   friend class CoverageIterator;
-  friend class OccludedTileIterator;
   friend class PrioritizedTile;
   friend class TileIterator;
   friend class TilingSetRasterQueueAll;
   friend class TilingSetRasterQueueRequired;
   friend class TilingSetEvictionQueue;
+  friend class TilesWithResourceIterator;
 
   // PENDING VISIBLE RECT refers to the visible rect that will become current
   // upon activation (ie, the pending tree's visible rect). Tiles in this
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc
index 93dc213..a2b8e66 100644
--- a/cc/tiles/tile_manager.cc
+++ b/cc/tiles/tile_manager.cc
@@ -28,8 +28,8 @@
 #include "cc/raster/raster_buffer.h"
 #include "cc/raster/task_category.h"
 #include "cc/tiles/frame_viewer_instrumentation.h"
-#include "cc/tiles/occluded_tile_iterator.h"
 #include "cc/tiles/tile.h"
+#include "cc/tiles/tiles_with_resource_iterator.h"
 #include "components/viz/common/resources/resource_sizes.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/geometry/axis_transform2d.h"
@@ -560,6 +560,10 @@
     return false;
   }
 
+  const bool did_memory_policy_become_more_restrictive =
+      IsTileMemoryLimitPolicyMoreRestictive(state.memory_limit_policy,
+                                            global_state_.memory_limit_policy);
+
   signals_ = Signals();
   global_state_ = state;
 
@@ -579,6 +583,9 @@
   if (!ShouldRasterOccludedTiles())
     FreeResourcesForOccludedTiles();
 
+  if (did_memory_policy_become_more_restrictive)
+    FreeResourcesForTilesThatViolateMemoryPolicy();
+
   PrioritizedWorkToSchedule prioritized_work = AssignGpuMemoryToTiles();
 
   // Inform the client that will likely require a draw if the highest priority
@@ -918,10 +925,25 @@
 }
 
 void TileManager::FreeResourcesForOccludedTiles() {
-  std::unique_ptr<OccludedTileIterator> iterator =
-      client_->CreateOccludedTileIterator();
-  for (; !iterator->AtEnd(); iterator->Next())
-    FreeResourcesForTile(iterator->GetCurrent());
+  std::unique_ptr<TilesWithResourceIterator> iterator =
+      client_->CreateTilesWithResourceIterator();
+  for (; !iterator->AtEnd(); iterator->Next()) {
+    if (iterator->IsCurrentTileOccluded())
+      FreeResourcesForTile(iterator->GetCurrent());
+  }
+}
+
+void TileManager::FreeResourcesForTilesThatViolateMemoryPolicy() {
+  std::unique_ptr<TilesWithResourceIterator> iterator =
+      client_->CreateTilesWithResourceIterator();
+  for (; !iterator->AtEnd(); iterator->Next()) {
+    const PrioritizedTile* prioritized_tile =
+        iterator->GetCurrentAsPrioritizedTile();
+    DCHECK(prioritized_tile);
+    Tile* tile = prioritized_tile->tile();
+    if (TilePriorityViolatesMemoryPolicy(prioritized_tile->priority()))
+      FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile);
+  }
 }
 
 void TileManager::FreeResourcesForTile(Tile* tile) {
diff --git a/cc/tiles/tile_manager.h b/cc/tiles/tile_manager.h
index 34c53337..b850ebf 100644
--- a/cc/tiles/tile_manager.h
+++ b/cc/tiles/tile_manager.h
@@ -46,7 +46,7 @@
 
 namespace cc {
 class ImageDecodeCache;
-class OccludedTileIterator;
+class TilesWithResourceIterator;
 
 class CC_EXPORT TileManagerClient {
  public:
@@ -80,9 +80,9 @@
   virtual std::unique_ptr<EvictionTilePriorityQueue> BuildEvictionQueue(
       TreePriority tree_priority) = 0;
 
-  // Returns an iterator of the occluded tiles.
-  virtual std::unique_ptr<OccludedTileIterator>
-  CreateOccludedTileIterator() = 0;
+  // Returns an iterator over all the tiles that have a resource.
+  virtual std::unique_ptr<TilesWithResourceIterator>
+  CreateTilesWithResourceIterator() = 0;
 
   // Informs the client that due to the currently rasterizing (or scheduled to
   // be rasterized) tiles, we will be in a position that will likely require a
@@ -379,6 +379,10 @@
 
   // Frees the resources of all occluded tiles.
   void FreeResourcesForOccludedTiles();
+
+  // Frees the resources of tiles that violate the memory policy.
+  void FreeResourcesForTilesThatViolateMemoryPolicy();
+
   void FreeResourcesForTile(Tile* tile);
   void FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(Tile* tile);
   scoped_refptr<TileTask> CreateRasterTask(
diff --git a/cc/tiles/tile_priority.cc b/cc/tiles/tile_priority.cc
index 1bfe552..cae5550 100644
--- a/cc/tiles/tile_priority.cc
+++ b/cc/tiles/tile_priority.cc
@@ -60,6 +60,17 @@
   }
 }
 
+bool IsTileMemoryLimitPolicyMoreRestictive(TileMemoryLimitPolicy policy1,
+                                           TileMemoryLimitPolicy policy2) {
+  static_assert(
+      ALLOW_NOTHING < ALLOW_ABSOLUTE_MINIMUM &&
+          ALLOW_ABSOLUTE_MINIMUM < ALLOW_PREPAINT_ONLY &&
+          ALLOW_PREPAINT_ONLY < ALLOW_ANYTHING,
+      "TileMemoryLimitPolicy must be ordered from most restrictive to least "
+      "restrictive");
+  return policy1 < policy2;
+}
+
 std::string TreePriorityToString(TreePriority prio) {
   switch (prio) {
   case SAME_PRIORITY_FOR_BOTH_TREES:
diff --git a/cc/tiles/tile_priority.h b/cc/tiles/tile_priority.h
index bffd8d8..20bc32c 100644
--- a/cc/tiles/tile_priority.h
+++ b/cc/tiles/tile_priority.h
@@ -63,6 +63,8 @@
 
 std::string TilePriorityBinToString(TilePriority::PriorityBin bin);
 
+// It is expected the values are ordered from most restrictive to least
+// restrictive. See IsTileMemoryLimitPolicyMoreRestictive().
 enum TileMemoryLimitPolicy {
   // Nothing. This mode is used when visible is set to false.
   ALLOW_NOTHING = 0,  // Decaf.
@@ -78,6 +80,10 @@
 };
 std::string TileMemoryLimitPolicyToString(TileMemoryLimitPolicy policy);
 
+// Returns true if `policy1` is more restrictive than `policy2`.
+bool IsTileMemoryLimitPolicyMoreRestictive(TileMemoryLimitPolicy policy1,
+                                           TileMemoryLimitPolicy policy2);
+
 enum TreePriority {
   SAME_PRIORITY_FOR_BOTH_TREES,
   SMOOTHNESS_TAKES_PRIORITY,
diff --git a/cc/tiles/occluded_tile_iterator.cc b/cc/tiles/tiles_with_resource_iterator.cc
similarity index 67%
rename from cc/tiles/occluded_tile_iterator.cc
rename to cc/tiles/tiles_with_resource_iterator.cc
index 1221311..0f1bb5996 100644
--- a/cc/tiles/occluded_tile_iterator.cc
+++ b/cc/tiles/tiles_with_resource_iterator.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "cc/tiles/occluded_tile_iterator.h"
+#include "cc/tiles/tiles_with_resource_iterator.h"
 
 #include "cc/layers/picture_layer_impl.h"
 #include "cc/tiles/picture_layer_tiling_set.h"
 
 namespace cc {
 
-OccludedTileIterator::OccludedTileIterator(
+TilesWithResourceIterator::TilesWithResourceIterator(
     const std::vector<PictureLayerImpl*>* picture_layers,
     const std::vector<PictureLayerImpl*>* secondary_picture_layers)
     : picture_layers_(picture_layers),
@@ -18,19 +18,38 @@
   FindNextInPictureLayers();
 }
 
-OccludedTileIterator::~OccludedTileIterator() = default;
+TilesWithResourceIterator::~TilesWithResourceIterator() = default;
 
-bool OccludedTileIterator::AtEnd() const {
+bool TilesWithResourceIterator::AtEnd() const {
   return !tile_iterator_.has_value();
 }
 
-Tile* OccludedTileIterator::GetCurrent() {
+Tile* TilesWithResourceIterator::GetCurrent() {
   return AtEnd() ? nullptr : tile_iterator_->GetCurrent();
 }
 
-void OccludedTileIterator::Next() {
+PrioritizedTile* TilesWithResourceIterator::GetCurrentAsPrioritizedTile() {
+  if (prioritized_tile_)
+    return &*prioritized_tile_;
+  Tile* tile = GetCurrent();
+  if (!tile)
+    return nullptr;
+  PictureLayerTiling* tiling = CurrentPictureLayerTiling();
+  prioritized_tile_ = tiling->MakePrioritizedTile(
+      tile, tiling->ComputePriorityRectTypeForTile(tile),
+      tiling->IsTileOccluded(tile));
+  return &*prioritized_tile_;
+}
+
+bool TilesWithResourceIterator::IsCurrentTileOccluded() {
+  Tile* tile = GetCurrent();
+  return tile && tile->tiling()->IsTileOccluded(tile);
+}
+
+void TilesWithResourceIterator::Next() {
   if (AtEnd())
     return;
+  prioritized_tile_.reset();
   DCHECK(tile_iterator_);
   tile_iterator_->Next();
   if (FindNextInTileIterator())
@@ -45,7 +64,7 @@
   DCHECK(AtEnd());
 }
 
-bool OccludedTileIterator::FindNextInPictureLayers() {
+bool TilesWithResourceIterator::FindNextInPictureLayers() {
   if (FindNextInActiveLayers())
     return true;
   DCHECK(AtEnd());
@@ -61,7 +80,7 @@
   return FindNextInActiveLayers();
 }
 
-bool OccludedTileIterator::FindNextInActiveLayers() {
+bool TilesWithResourceIterator::FindNextInActiveLayers() {
   for (; current_picture_layer_index_ < active_layers_->size();
        ++current_picture_layer_index_) {
     current_picture_layer_tiling_index_ = 0u;
@@ -74,7 +93,7 @@
   return false;
 }
 
-bool OccludedTileIterator::FindNextInPictureLayerTilingSet() {
+bool TilesWithResourceIterator::FindNextInPictureLayerTilingSet() {
   PictureLayerTilingSet* tiling_set = CurrentPictureLayerTilingSet();
   for (; current_picture_layer_tiling_index_ < tiling_set->num_tilings();
        ++current_picture_layer_tiling_index_) {
@@ -86,24 +105,22 @@
   return false;
 }
 
-bool OccludedTileIterator::FindNextInTileIterator() {
-  PictureLayerTiling* tiling = CurrentPictureLayerTiling();
+bool TilesWithResourceIterator::FindNextInTileIterator() {
   for (; !tile_iterator_->AtEnd(); tile_iterator_->Next()) {
     Tile* tile = tile_iterator_->GetCurrent();
-    if (visited_.insert(tile).second && tile->draw_info().has_resource() &&
-        tiling->IsTileOccluded(tile_iterator_->GetCurrent())) {
+    if (visited_.insert(tile).second && tile->draw_info().has_resource())
       return true;
-    }
   }
   return false;
 }
 
-PictureLayerTilingSet* OccludedTileIterator::CurrentPictureLayerTilingSet() {
+PictureLayerTilingSet*
+TilesWithResourceIterator::CurrentPictureLayerTilingSet() {
   return (*active_layers_)[current_picture_layer_index_]
       ->picture_layer_tiling_set();
 }
 
-PictureLayerTiling* OccludedTileIterator::CurrentPictureLayerTiling() {
+PictureLayerTiling* TilesWithResourceIterator::CurrentPictureLayerTiling() {
   return CurrentPictureLayerTilingSet()->tiling_at(
       current_picture_layer_tiling_index_);
 }
diff --git a/cc/tiles/occluded_tile_iterator.h b/cc/tiles/tiles_with_resource_iterator.h
similarity index 70%
rename from cc/tiles/occluded_tile_iterator.h
rename to cc/tiles/tiles_with_resource_iterator.h
index ba27597..d19f7d1 100644
--- a/cc/tiles/occluded_tile_iterator.h
+++ b/cc/tiles/tiles_with_resource_iterator.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CC_TILES_OCCLUDED_TILE_ITERATOR_H_
-#define CC_TILES_OCCLUDED_TILE_ITERATOR_H_
+#ifndef CC_TILES_TILES_WITH_RESOURCE_ITERATOR_H_
+#define CC_TILES_TILES_WITH_RESOURCE_ITERATOR_H_
 
 #include <set>
 #include <vector>
@@ -11,6 +11,7 @@
 #include "base/memory/raw_ptr.h"
 #include "cc/cc_export.h"
 #include "cc/tiles/picture_layer_tiling.h"
+#include "cc/tiles/prioritized_tile.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace cc {
@@ -18,21 +19,28 @@
 class PictureLayerImpl;
 class PictureLayerTilingSet;
 
-// Used to iterate over occluded tiles with resources. The order of iteration
-// is not defined.
-class CC_EXPORT OccludedTileIterator {
+// Iterates over all tiles that have a resource. The order of iteration is not
+// defined.
+class CC_EXPORT TilesWithResourceIterator {
  public:
-  OccludedTileIterator(
+  TilesWithResourceIterator(
       const std::vector<PictureLayerImpl*>* picture_layers,
       const std::vector<PictureLayerImpl*>* secondary_picture_layers);
-  OccludedTileIterator(const OccludedTileIterator&) = delete;
-  OccludedTileIterator& operator=(const OccludedTileIterator&) = delete;
-  ~OccludedTileIterator();
+  TilesWithResourceIterator(const TilesWithResourceIterator&) = delete;
+  TilesWithResourceIterator& operator=(const TilesWithResourceIterator&) =
+      delete;
+  ~TilesWithResourceIterator();
 
   bool AtEnd() const;
   void Next();
   Tile* GetCurrent();
 
+  // Returns the PrioritizedTile for the current tile, null if at the end.
+  PrioritizedTile* GetCurrentAsPrioritizedTile();
+
+  // Returns true if the current tile is occluded, false if at the end.
+  bool IsCurrentTileOccluded();
+
  private:
   // The following functions start iterating at the *current* location.
   // Each function returns true if a match is found, false indicates there
@@ -70,8 +78,11 @@
   // Set of tiles that have been visited. Used to ensure the same tile isn't
   // visited more than once.
   std::set<Tile*> visited_;
+
+  // Created when GetCurrentAsPrioritizedTile() is called.
+  absl::optional<PrioritizedTile> prioritized_tile_;
 };
 
 }  // namespace cc
 
-#endif  // CC_TILES_OCCLUDED_TILE_ITERATOR_H_
+#endif  // CC_TILES_TILES_WITH_RESOURCE_ITERATOR_H_
diff --git a/cc/trees/commit_state.cc b/cc/trees/commit_state.cc
index e541fa5d..f3e8152 100644
--- a/cc/trees/commit_state.cc
+++ b/cc/trees/commit_state.cc
@@ -38,7 +38,8 @@
       overscroll_behavior(prev.overscroll_behavior),
       background_color(prev.background_color),
       viewport_property_ids(prev.viewport_property_ids),
-      local_surface_id_from_parent(prev.local_surface_id_from_parent) {
+      local_surface_id_from_parent(prev.local_surface_id_from_parent),
+      priority_cutoff(prev.priority_cutoff) {
   memcpy(event_listener_properties, prev.event_listener_properties,
          sizeof(event_listener_properties));
 }
diff --git a/cc/trees/commit_state.h b/cc/trees/commit_state.h
index b4978d8..7285d57b 100644
--- a/cc/trees/commit_state.h
+++ b/cc/trees/commit_state.h
@@ -32,6 +32,7 @@
 #include "cc/trees/swap_promise.h"
 #include "cc/trees/viewport_property_ids.h"
 #include "components/viz/common/surfaces/local_surface_id.h"
+#include "gpu/command_buffer/common/gpu_memory_allocation.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/delegated_ink_metadata.h"
 #include "ui/gfx/display_color_spaces.h"
@@ -104,6 +105,8 @@
   SkColor background_color = SK_ColorWHITE;
   ViewportPropertyIds viewport_property_ids;
   viz::LocalSurfaceId local_surface_id_from_parent;
+  gpu::MemoryAllocation::PriorityCutoff priority_cutoff =
+      gpu::MemoryAllocation::PriorityCutoff::CUTOFF_ALLOW_EVERYTHING;
 
   // -------------------------------------------------------------------------
   // Take/reset: these values are reset on the LayerTreeHost between commits.
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 1620fd9..c915906 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -158,6 +158,8 @@
       (mode == CompositorMode::THREADED);
   pending_commit_state_->needs_full_tree_sync = true;
   pending_commit_state_->debug_state = settings_.initial_debug_state;
+  pending_commit_state_->priority_cutoff =
+      settings_.memory_policy.priority_cutoff_when_visible;
 
   rendering_stats_instrumentation_->set_record_rendering_stats(
       pending_commit_state_->debug_state.RecordRenderingStats());
@@ -1652,6 +1654,18 @@
   pending_commit_state()->needs_full_tree_sync = false;
 }
 
+void LayerTreeHost::SetPriorityCutoffOverride(
+    absl::optional<gpu::MemoryAllocation::PriorityCutoff> priority_cutoff) {
+  const gpu::MemoryAllocation::PriorityCutoff actual_value =
+      priority_cutoff.has_value()
+          ? *priority_cutoff
+          : settings_.memory_policy.priority_cutoff_when_visible;
+  if (pending_commit_state()->priority_cutoff == actual_value)
+    return;
+  pending_commit_state()->priority_cutoff = actual_value;
+  SetNeedsCommit();
+}
+
 void LayerTreeHost::SetPropertyTreesNeedRebuild() {
   property_trees()->set_needs_rebuild(true);
   SetNeedsUpdateLayers();
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index ab5eb12..d37a045 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -63,6 +63,7 @@
 #include "components/viz/common/resources/resource_format.h"
 #include "components/viz/common/surfaces/local_surface_id.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/gfx/delegated_ink_metadata.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/overlay_transform.h"
@@ -594,6 +595,11 @@
     return thread_unsafe_commit_state().mutator_host;
   }
 
+  // Overrides the value specified in LayerTreeSettings. Providing an empty
+  // value results in using the value from LayerTreeSettings.
+  void SetPriorityCutoffOverride(
+      absl::optional<gpu::MemoryAllocation::PriorityCutoff> priority_cutoff);
+
   void SetPropertyTreesForTesting(const PropertyTrees* property_trees);
 
   void SetNeedsDisplayOnAllLayers();
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 61ab59e0..3824e50 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -72,10 +72,10 @@
 #include "cc/tiles/eviction_tile_priority_queue.h"
 #include "cc/tiles/frame_viewer_instrumentation.h"
 #include "cc/tiles/gpu_image_decode_cache.h"
-#include "cc/tiles/occluded_tile_iterator.h"
 #include "cc/tiles/picture_layer_tiling.h"
 #include "cc/tiles/raster_tile_priority_queue.h"
 #include "cc/tiles/software_image_decode_cache.h"
+#include "cc/tiles/tiles_with_resource_iterator.h"
 #include "cc/trees/compositor_commit_data.h"
 #include "cc/trees/damage_tracker.h"
 #include "cc/trees/debug_rect_history.h"
@@ -637,6 +637,13 @@
   set_viewport_mobile_optimized(commit_state.is_viewport_mobile_optimized);
   SetPrefersReducedMotion(commit_state.prefers_reduced_motion);
   SetMayThrottleIfUndrawnFrames(commit_state.may_throttle_if_undrawn_frames);
+  if (!was_set_memory_policy_called_ &&
+      commit_state.priority_cutoff !=
+          cached_managed_memory_policy_.priority_cutoff_when_visible) {
+    cached_managed_memory_policy_.priority_cutoff_when_visible =
+        commit_state.priority_cutoff;
+    UpdateTileManagerMemoryPolicy(ActualManagedMemoryPolicy());
+  }
 }
 
 void LayerTreeHostImpl::RecordGpuRasterizationHistogram() {
@@ -1863,9 +1870,9 @@
   return queue;
 }
 
-std::unique_ptr<OccludedTileIterator>
-LayerTreeHostImpl::CreateOccludedTileIterator() {
-  return std::make_unique<OccludedTileIterator>(
+std::unique_ptr<TilesWithResourceIterator>
+LayerTreeHostImpl::CreateTilesWithResourceIterator() {
+  return std::make_unique<TilesWithResourceIterator>(
       &active_tree_->picture_layers(),
       pending_tree_ ? &pending_tree_->picture_layers() : nullptr);
 }
@@ -2044,7 +2051,9 @@
 void LayerTreeHostImpl::SetMemoryPolicy(const ManagedMemoryPolicy& policy) {
   DCHECK(task_runner_provider_->IsImplThread());
 
-  SetManagedMemoryPolicy(policy);
+  was_set_memory_policy_called_ = true;
+
+  SetMemoryPolicyImpl(policy);
 
   // This is short term solution to synchronously drop tile resources when
   // using synchronous compositing to avoid memory usage regression.
@@ -2070,8 +2079,7 @@
   tree_activation_callback_ = std::move(callback);
 }
 
-void LayerTreeHostImpl::SetManagedMemoryPolicy(
-    const ManagedMemoryPolicy& policy) {
+void LayerTreeHostImpl::SetMemoryPolicyImpl(const ManagedMemoryPolicy& policy) {
   if (cached_managed_memory_policy_ == policy)
     return;
 
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index d8ac981..4f00370 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -517,7 +517,8 @@
   std::unique_ptr<EvictionTilePriorityQueue> BuildEvictionQueue(
       TreePriority tree_priority) override;
   void SetIsLikelyToRequireADraw(bool is_likely_to_require_a_draw) override;
-  std::unique_ptr<OccludedTileIterator> CreateOccludedTileIterator() override;
+  std::unique_ptr<TilesWithResourceIterator> CreateTilesWithResourceIterator()
+      override;
   TargetColorParams GetTargetColorParams(
       gfx::ContentColorUsage content_color_usage) const override;
   void RequestImplSideInvalidationForCheckerImagedTiles() override;
@@ -965,7 +966,6 @@
   DrawResult CalculateRenderPasses(FrameData* frame);
 
   void StartScrollbarFadeRecursive(LayerImpl* layer);
-  void SetManagedMemoryPolicy(const ManagedMemoryPolicy& policy);
 
   // Once a resource is uploaded or deleted, it is no longer an evicted id, this
   // removes it from the evicted set, and updates if we're able to draw now that
@@ -987,6 +987,7 @@
   void NotifyLatencyInfoSwapPromiseMonitors();
 
  private:
+  void SetMemoryPolicyImpl(const ManagedMemoryPolicy& policy);
   void SetContextVisibility(bool is_visible);
 
   void ShowScrollbarsForImplScroll(ElementId element_id);
@@ -1290,6 +1291,8 @@
 
   std::vector<uint32_t> finished_transition_request_sequence_ids_;
 
+  bool was_set_memory_policy_called_ = false;
+
   // Must be the last member to ensure this is destroyed first in the
   // destruction order and invalidates all weak pointers.
   base::WeakPtrFactory<LayerTreeHostImpl> weak_factory_{this};
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 26dd54a..966e6e7a 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -10146,6 +10146,87 @@
 
 SINGLE_THREAD_TEST_F(LayerTreeHostTestOccludedTileReleased);
 
+class LayerTreeHostTestPriorityCutoffOverride
+    : public LayerTreeHostTestWithHelper {
+ public:
+  void InitializeSettings(LayerTreeSettings* settings) override {
+    settings->use_occlusion_for_tile_prioritization = true;
+    settings->minimum_occlusion_tracking_size = gfx::Size(60, 60);
+    settings->default_tile_size = gfx::Size(128, 128);
+  }
+
+  void BeginTest() override {
+    layer_tree_host()->SetViewportRectAndScale(gfx::Rect(100, 100), 1.f,
+                                               viz::LocalSurfaceId());
+    layer_tree_host()->root_layer()->SetBounds(gfx::Size(100, 100));
+    picture_layer_ = CreateAndAddFakePictureLayer(gfx::Size(128, 1280));
+    PostSetNeedsCommitToMainThread();
+  }
+
+  void NotifyTileStateChangedOnThread(LayerTreeHostImpl* host_impl,
+                                      const Tile* tile) override {
+    const int tile_with_resource_count =
+        GetPictureLayerImpl(host_impl)->GetNumberOfTilesWithResources();
+    switch (phase_) {
+      case Phase::kWaitingForInitialState:
+        if (tile_with_resource_count > 1) {
+          phase_ = Phase::kRequiredOnly;
+          ScheduleChangePriorityCutoff();
+        }
+        break;
+      case Phase::kRequiredOnly:
+        if (tile_with_resource_count == 1) {
+          phase_ = Phase::kBackToAllowEverything;
+          ScheduleChangePriorityCutoff();
+        }
+        break;
+      case Phase::kBackToAllowEverything:
+        if (tile_with_resource_count > 1)
+          EndTest();
+    }
+  }
+
+ private:
+  void ScheduleChangePriorityCutoff() {
+    MainThreadTaskRunner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &LayerTreeHostTestPriorityCutoffOverride::ChangePriorityCutoff,
+            base::Unretained(this)));
+  }
+
+  void ChangePriorityCutoff() {
+    layer_tree_host()->SetPriorityCutoffOverride(
+        phase_ == Phase::kRequiredOnly
+            ? gpu::MemoryAllocation::CUTOFF_ALLOW_REQUIRED_ONLY
+            : gpu::MemoryAllocation::CUTOFF_ALLOW_EVERYTHING);
+  }
+
+  // Phases (in order) the test transitions through.
+  enum class Phase {
+    // Waits for more than one tile with a resource.
+    kWaitingForInitialState,
+
+    // Changes the cutoff to CUTOFF_ALLOW_REQUIRED_ONLY, which should trigger
+    // going back to only one tile having a resource.
+    kRequiredOnly,
+
+    // Changes the cutoff back to CUTOFF_ALLOW_EVERYTHING, which should trigger
+    // going back to more than one tile with a resource.
+    kBackToAllowEverything,
+  };
+
+  FakePictureLayerImpl* GetPictureLayerImpl(LayerTreeHostImpl* host_impl) {
+    return static_cast<FakePictureLayerImpl*>(
+        host_impl->sync_tree()->LayerById(picture_layer_->id()));
+  }
+
+  scoped_refptr<FakePictureLayer> picture_layer_;
+  Phase phase_ = Phase::kWaitingForInitialState;
+};
+
+SINGLE_THREAD_TEST_F(LayerTreeHostTestPriorityCutoffOverride);
+
 class LayerTreeHostTestNoCommitDeadlock : public LayerTreeHostTest {
   void BeginTest() override { PostSetNeedsCommitToMainThread(); }
   void WillCommit(const CommitState& commit_state) override {