| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <utility> |
| |
| #include "cc/resources/tiling_set_eviction_queue.h" |
| |
| namespace cc { |
| |
| TilingSetEvictionQueue::TilingSetEvictionQueue( |
| PictureLayerTilingSet* tiling_set, |
| TreePriority tree_priority, |
| bool skip_shared_out_of_order_tiles) |
| : tiling_set_(tiling_set), |
| tree_(tiling_set->client()->GetTree()), |
| tree_priority_(tree_priority), |
| skip_all_shared_tiles_( |
| skip_shared_out_of_order_tiles && |
| tree_priority == (tree_ == ACTIVE_TREE ? NEW_CONTENT_TAKES_PRIORITY |
| : SMOOTHNESS_TAKES_PRIORITY)), |
| skip_shared_out_of_order_tiles_(skip_shared_out_of_order_tiles), |
| processing_soon_border_rect_(false), |
| processing_tiling_with_required_for_activation_tiles_(false), |
| tiling_index_with_required_for_activation_tiles_(0u), |
| current_priority_bin_(TilePriority::EVENTUALLY), |
| current_tiling_index_(0u), |
| current_tiling_range_type_(PictureLayerTilingSet::HIGHER_THAN_HIGH_RES), |
| current_eviction_tile_(nullptr) { |
| // Early out if the layer has no tilings. |
| if (!tiling_set_->num_tilings()) |
| return; |
| |
| tiling_index_with_required_for_activation_tiles_ = |
| TilingIndexWithRequiredForActivationTiles(); |
| |
| current_tiling_index_ = CurrentTilingRange().start - 1u; |
| AdvanceToNextValidTiling(); |
| } |
| |
| TilingSetEvictionQueue::~TilingSetEvictionQueue() { |
| } |
| |
| bool TilingSetEvictionQueue::IsEmpty() const { |
| return !current_eviction_tile_; |
| } |
| |
| void TilingSetEvictionQueue::Pop() { |
| DCHECK(!IsEmpty()); |
| |
| if (!AdvanceToNextEvictionTile()) |
| AdvanceToNextValidTiling(); |
| } |
| |
| Tile* TilingSetEvictionQueue::Top() { |
| DCHECK(!IsEmpty()); |
| return current_eviction_tile_; |
| } |
| |
| const Tile* TilingSetEvictionQueue::Top() const { |
| DCHECK(!IsEmpty()); |
| return current_eviction_tile_; |
| } |
| |
| bool TilingSetEvictionQueue::AdvanceToNextEvictionTile() { |
| // Advance to the next eviction tile within the current priority bin and |
| // tiling. This is done while advancing to a new tiling and while popping |
| // the current tile. |
| |
| bool required_for_activation = |
| processing_tiling_with_required_for_activation_tiles_; |
| |
| for (;;) { |
| while (spiral_iterator_) { |
| std::pair<int, int> next_index = spiral_iterator_.index(); |
| Tile* tile = current_tiling_->TileAt(next_index.first, next_index.second); |
| ++spiral_iterator_; |
| if (!tile || !tile->HasResource()) |
| continue; |
| if (skip_all_shared_tiles_ && tile->is_shared()) |
| continue; |
| current_tiling_->UpdateTileAndTwinPriority(tile); |
| if (skip_shared_out_of_order_tiles_ && IsSharedOutOfOrderTile(tile)) |
| continue; |
| if (tile->required_for_activation() != required_for_activation) |
| continue; |
| current_eviction_tile_ = tile; |
| return true; |
| } |
| if (processing_soon_border_rect_) { |
| // Advance from soon border rect to skewport rect. |
| processing_soon_border_rect_ = false; |
| if (current_tiling_->has_skewport_rect_tiles_) { |
| spiral_iterator_ = TilingData::ReverseSpiralDifferenceIterator( |
| ¤t_tiling_->tiling_data_, |
| current_tiling_->current_skewport_rect_, |
| current_tiling_->current_visible_rect_, |
| current_tiling_->current_visible_rect_); |
| continue; |
| } |
| } |
| break; |
| } |
| |
| TilePriority::PriorityBin max_tile_priority_bin = |
| current_tiling_->client_->GetMaxTilePriorityBin(); |
| while (visible_iterator_) { |
| std::pair<int, int> next_index = visible_iterator_.index(); |
| Tile* tile = current_tiling_->TileAt(next_index.first, next_index.second); |
| ++visible_iterator_; |
| if (!tile || !tile->HasResource()) |
| continue; |
| if (skip_all_shared_tiles_ && tile->is_shared()) |
| continue; |
| // If the max tile priority is not NOW, updated priorities for tiles |
| // returned by the visible iterator will not have NOW (but EVENTUALLY) |
| // priority bin and cannot therefore be required for activation tiles nor |
| // occluded NOW tiles in the current tiling. |
| if (max_tile_priority_bin <= TilePriority::NOW) { |
| // If the current tiling is a pending tree tiling, required for |
| // activation tiles can be detected without updating tile priorities. |
| if (tree_ == PENDING_TREE && |
| current_tiling_->IsTileRequiredForActivationIfVisible(tile) != |
| required_for_activation) { |
| continue; |
| } |
| // Unoccluded NOW tiles should be evicted (and thus returned) only after |
| // all occluded NOW tiles. |
| if (!current_tiling_->IsTileOccluded(tile)) { |
| unoccluded_now_tiles_.push_back(tile); |
| continue; |
| } |
| } |
| current_tiling_->UpdateTileAndTwinPriority(tile); |
| if (skip_shared_out_of_order_tiles_ && IsSharedOutOfOrderTile(tile)) |
| continue; |
| if (tile->required_for_activation() != required_for_activation) |
| continue; |
| current_eviction_tile_ = tile; |
| return true; |
| } |
| |
| while (!unoccluded_now_tiles_.empty()) { |
| // All (unoccluded) NOW tiles have the same priority bin (NOW) and the same |
| // distance to visible (0.0), so it does not matter that tiles are popped |
| // in reversed (FILO) order. |
| Tile* tile = unoccluded_now_tiles_.back(); |
| unoccluded_now_tiles_.pop_back(); |
| DCHECK(tile); |
| if (!tile->HasResource()) |
| continue; |
| current_tiling_->UpdateTileAndTwinPriority(tile); |
| if (skip_shared_out_of_order_tiles_ && IsSharedOutOfOrderTile(tile)) |
| continue; |
| if (tile->required_for_activation() != required_for_activation) |
| continue; |
| current_eviction_tile_ = tile; |
| return true; |
| } |
| |
| current_eviction_tile_ = nullptr; |
| return false; |
| } |
| |
| bool TilingSetEvictionQueue::AdvanceToNextPriorityBin() { |
| // Advance to the next priority bin. This is done only after all tiling range |
| // types (including the required for activation tiling) within the previous |
| // priority bin have been gone through. |
| DCHECK_EQ(current_tiling_range_type_, PictureLayerTilingSet::HIGH_RES); |
| |
| switch (current_priority_bin_) { |
| case TilePriority::EVENTUALLY: |
| current_priority_bin_ = TilePriority::SOON; |
| return true; |
| case TilePriority::SOON: |
| current_priority_bin_ = TilePriority::NOW; |
| return true; |
| case TilePriority::NOW: |
| return false; |
| } |
| NOTREACHED(); |
| return false; |
| } |
| |
| bool TilingSetEvictionQueue::AdvanceToNextTilingRangeType() { |
| // Advance to the next tiling range type within the current priority bin, to |
| // the required for activation tiling range type within the current priority |
| // bin or to the first tiling range type within the next priority bin. This |
| // is done only after all tilings within the previous tiling range type have |
| // been gone through. |
| DCHECK_EQ(current_tiling_index_, CurrentTilingRange().end); |
| |
| switch (current_tiling_range_type_) { |
| case PictureLayerTilingSet::HIGHER_THAN_HIGH_RES: |
| current_tiling_range_type_ = PictureLayerTilingSet::LOWER_THAN_LOW_RES; |
| return true; |
| case PictureLayerTilingSet::LOWER_THAN_LOW_RES: |
| current_tiling_range_type_ = |
| PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES; |
| return true; |
| case PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES: |
| current_tiling_range_type_ = PictureLayerTilingSet::LOW_RES; |
| return true; |
| case PictureLayerTilingSet::LOW_RES: |
| current_tiling_range_type_ = PictureLayerTilingSet::HIGH_RES; |
| return true; |
| case PictureLayerTilingSet::HIGH_RES: |
| // Process required for activation tiles (unless that has already been |
| // done for the current priority bin) if there is a tiling with required |
| // for activation tiles and that tiling may have required for activation |
| // tiles having the current priority bin (in the pending tree only NOW |
| // tiles may be required for activation). |
| if (!processing_tiling_with_required_for_activation_tiles_ && |
| tiling_index_with_required_for_activation_tiles_ < |
| tiling_set_->num_tilings() && |
| (current_priority_bin_ == TilePriority::NOW || |
| tree_ == ACTIVE_TREE)) { |
| processing_tiling_with_required_for_activation_tiles_ = true; |
| return true; |
| } |
| processing_tiling_with_required_for_activation_tiles_ = false; |
| |
| if (!AdvanceToNextPriorityBin()) |
| return false; |
| |
| current_tiling_range_type_ = PictureLayerTilingSet::HIGHER_THAN_HIGH_RES; |
| return true; |
| } |
| NOTREACHED(); |
| return false; |
| } |
| |
| bool TilingSetEvictionQueue::AdvanceToNextValidTiling() { |
| // Advance to the next tiling within current tiling range type or to |
| // the first tiling within the next tiling range type or priority bin until |
| // the next eviction tile is found. This is done only after all eviction |
| // tiles within the previous tiling within the current priority bin and |
| // tiling range type have been gone through. |
| DCHECK(!current_eviction_tile_); |
| DCHECK_NE(current_tiling_index_, CurrentTilingRange().end); |
| |
| for (;;) { |
| ++current_tiling_index_; |
| while (current_tiling_index_ == CurrentTilingRange().end) { |
| if (!AdvanceToNextTilingRangeType()) |
| return false; |
| current_tiling_index_ = CurrentTilingRange().start; |
| } |
| current_tiling_ = tiling_set_->tiling_at(CurrentTilingIndex()); |
| |
| switch (current_priority_bin_) { |
| case TilePriority::EVENTUALLY: |
| if (current_tiling_->has_eventually_rect_tiles_) { |
| spiral_iterator_ = TilingData::ReverseSpiralDifferenceIterator( |
| ¤t_tiling_->tiling_data_, |
| current_tiling_->current_eventually_rect_, |
| current_tiling_->current_skewport_rect_, |
| current_tiling_->current_soon_border_rect_); |
| if (AdvanceToNextEvictionTile()) |
| return true; |
| } |
| break; |
| case TilePriority::SOON: |
| if (current_tiling_->has_skewport_rect_tiles_ || |
| current_tiling_->has_soon_border_rect_tiles_) { |
| processing_soon_border_rect_ = true; |
| if (current_tiling_->has_soon_border_rect_tiles_) |
| spiral_iterator_ = TilingData::ReverseSpiralDifferenceIterator( |
| ¤t_tiling_->tiling_data_, |
| current_tiling_->current_soon_border_rect_, |
| current_tiling_->current_skewport_rect_, |
| current_tiling_->current_visible_rect_); |
| if (AdvanceToNextEvictionTile()) |
| return true; |
| } |
| break; |
| case TilePriority::NOW: |
| if (current_tiling_->has_visible_rect_tiles_) { |
| visible_iterator_ = |
| TilingData::Iterator(¤t_tiling_->tiling_data_, |
| current_tiling_->current_visible_rect_, |
| false /* include_borders */); |
| if (AdvanceToNextEvictionTile()) |
| return true; |
| } |
| break; |
| } |
| } |
| } |
| |
| PictureLayerTilingSet::TilingRange |
| TilingSetEvictionQueue::CurrentTilingRange() const { |
| if (processing_tiling_with_required_for_activation_tiles_) |
| return PictureLayerTilingSet::TilingRange( |
| tiling_index_with_required_for_activation_tiles_, |
| tiling_index_with_required_for_activation_tiles_ + 1); |
| return tiling_set_->GetTilingRange(current_tiling_range_type_); |
| } |
| |
| size_t TilingSetEvictionQueue::CurrentTilingIndex() const { |
| DCHECK_NE(current_tiling_index_, CurrentTilingRange().end); |
| switch (current_tiling_range_type_) { |
| case PictureLayerTilingSet::HIGHER_THAN_HIGH_RES: |
| case PictureLayerTilingSet::LOW_RES: |
| case PictureLayerTilingSet::HIGH_RES: |
| return current_tiling_index_; |
| // Tilings in the following ranges are accessed in reverse order. |
| case PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES: |
| case PictureLayerTilingSet::LOWER_THAN_LOW_RES: { |
| PictureLayerTilingSet::TilingRange tiling_range = CurrentTilingRange(); |
| size_t current_tiling_range_offset = |
| current_tiling_index_ - tiling_range.start; |
| return tiling_range.end - 1 - current_tiling_range_offset; |
| } |
| } |
| NOTREACHED(); |
| return 0; |
| } |
| |
| bool TilingSetEvictionQueue::IsSharedOutOfOrderTile(const Tile* tile) const { |
| if (!tile->is_shared()) |
| return false; |
| |
| switch (tree_priority_) { |
| case SMOOTHNESS_TAKES_PRIORITY: |
| DCHECK_EQ(ACTIVE_TREE, tree_); |
| return false; |
| case NEW_CONTENT_TAKES_PRIORITY: |
| DCHECK_EQ(PENDING_TREE, tree_); |
| return false; |
| case SAME_PRIORITY_FOR_BOTH_TREES: |
| break; |
| } |
| |
| // The priority for tile priority of a shared tile will be a combined |
| // priority thus return shared tiles from a higher priority tree as |
| // it is out of order for a lower priority tree. |
| WhichTree twin_tree = tree_ == ACTIVE_TREE ? PENDING_TREE : ACTIVE_TREE; |
| const TilePriority& priority = tile->priority(tree_); |
| const TilePriority& twin_priority = tile->priority(twin_tree); |
| if (priority.priority_bin != twin_priority.priority_bin) |
| return priority.priority_bin > twin_priority.priority_bin; |
| const bool occluded = tile->is_occluded(tree_); |
| const bool twin_occluded = tile->is_occluded(twin_tree); |
| if (occluded != twin_occluded) |
| return occluded; |
| if (priority.distance_to_visible != twin_priority.distance_to_visible) |
| return priority.distance_to_visible > twin_priority.distance_to_visible; |
| |
| // If priorities are the same, it does not matter which tree returns |
| // the tile. Let's pick the pending tree. |
| return tree_ != PENDING_TREE; |
| } |
| |
| size_t TilingSetEvictionQueue::TilingIndexWithRequiredForActivationTiles() |
| const { |
| // Returns the tiling index of the tiling with requuired for activation tiles. |
| // If no such tiling exists, returns the past-the-last index (num_tilings). |
| size_t num_tilings = tiling_set_->num_tilings(); |
| |
| if (tree_ == PENDING_TREE) { |
| // For the pending tree, the tiling with required for activation tiles is |
| // the high res one. |
| PictureLayerTilingSet::TilingRange high_res_tiling_range = |
| tiling_set_->GetTilingRange(PictureLayerTilingSet::HIGH_RES); |
| if (high_res_tiling_range.start != high_res_tiling_range.end) |
| return high_res_tiling_range.start; |
| } else { |
| DCHECK_EQ(ACTIVE_TREE, tree_); |
| // Only pending tree tiles can be required for activation. They can appear |
| // also in the active tree only if they are shared. If we skip all shared |
| // tiles, there is no need to find them as they will not be returned. |
| if (skip_all_shared_tiles_) |
| return num_tilings; |
| |
| // For the active tree, the tiling with required for activation tiles is |
| // the one whose twin tiling is the high res pending tiling. |
| for (size_t i = 0; i < num_tilings; ++i) { |
| const PictureLayerTiling* tiling = tiling_set_->tiling_at(i); |
| const PictureLayerTiling* pending_tiling = |
| tiling_set_->client()->GetPendingOrActiveTwinTiling(tiling); |
| if (pending_tiling && pending_tiling->resolution() == HIGH_RESOLUTION) |
| return i; |
| } |
| } |
| |
| return num_tilings; |
| } |
| |
| } // namespace cc |