| // Copyright 2012 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 "cc/tiles/picture_layer_tiling_set.h" |
| |
| #include <stddef.h> |
| |
| #include <limits> |
| #include <set> |
| #include <vector> |
| |
| #include "base/memory/ptr_util.h" |
| #include "cc/playback/raster_source.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| |
| namespace cc { |
| |
| namespace { |
| |
| class LargestToSmallestScaleFunctor { |
| public: |
| bool operator()(const std::unique_ptr<PictureLayerTiling>& left, |
| const std::unique_ptr<PictureLayerTiling>& right) { |
| return left->contents_scale() > right->contents_scale(); |
| } |
| }; |
| |
| inline float LargerRatio(float float1, float float2) { |
| DCHECK_GT(float1, 0.f); |
| DCHECK_GT(float2, 0.f); |
| return float1 > float2 ? float1 / float2 : float2 / float1; |
| } |
| |
| const float kSoonBorderDistanceViewportPercentage = 0.15f; |
| const float kMaxSoonBorderDistanceInScreenPixels = 312.f; |
| |
| } // namespace |
| |
| // static |
| std::unique_ptr<PictureLayerTilingSet> PictureLayerTilingSet::Create( |
| WhichTree tree, |
| PictureLayerTilingClient* client, |
| int tiling_interest_area_padding, |
| float skewport_target_time_in_seconds, |
| int skewport_extrapolation_limit_in_screen_pixels) { |
| return base::WrapUnique( |
| new PictureLayerTilingSet(tree, client, tiling_interest_area_padding, |
| skewport_target_time_in_seconds, |
| skewport_extrapolation_limit_in_screen_pixels)); |
| } |
| |
| PictureLayerTilingSet::PictureLayerTilingSet( |
| WhichTree tree, |
| PictureLayerTilingClient* client, |
| int tiling_interest_area_padding, |
| float skewport_target_time_in_seconds, |
| int skewport_extrapolation_limit_in_screen_pixels) |
| : tiling_interest_area_padding_(tiling_interest_area_padding), |
| skewport_target_time_in_seconds_(skewport_target_time_in_seconds), |
| skewport_extrapolation_limit_in_screen_pixels_( |
| skewport_extrapolation_limit_in_screen_pixels), |
| tree_(tree), |
| client_(client) {} |
| |
| PictureLayerTilingSet::~PictureLayerTilingSet() { |
| } |
| |
| void PictureLayerTilingSet::CopyTilingsAndPropertiesFromPendingTwin( |
| const PictureLayerTilingSet* pending_twin_set, |
| scoped_refptr<RasterSource> raster_source, |
| const Region& layer_invalidation) { |
| if (pending_twin_set->tilings_.empty()) { |
| // If the twin (pending) tiling set is empty, it was not updated for the |
| // current frame. So we drop tilings from our set as well, instead of |
| // leaving behind unshared tilings that are all non-ideal. |
| RemoveAllTilings(); |
| return; |
| } |
| |
| bool tiling_sort_required = false; |
| for (const auto& pending_twin_tiling : pending_twin_set->tilings_) { |
| float contents_scale = pending_twin_tiling->contents_scale(); |
| PictureLayerTiling* this_tiling = FindTilingWithScale(contents_scale); |
| if (!this_tiling) { |
| std::unique_ptr<PictureLayerTiling> new_tiling(new PictureLayerTiling( |
| tree_, contents_scale, raster_source_, client_)); |
| tilings_.push_back(std::move(new_tiling)); |
| this_tiling = tilings_.back().get(); |
| tiling_sort_required = true; |
| state_since_last_tile_priority_update_.added_tilings = true; |
| } |
| this_tiling->TakeTilesAndPropertiesFrom(pending_twin_tiling.get(), |
| layer_invalidation); |
| } |
| |
| if (tiling_sort_required) { |
| std::sort(tilings_.begin(), tilings_.end(), |
| LargestToSmallestScaleFunctor()); |
| } |
| } |
| |
| void PictureLayerTilingSet::UpdateTilingsToCurrentRasterSourceForActivation( |
| scoped_refptr<RasterSource> raster_source, |
| const PictureLayerTilingSet* pending_twin_set, |
| const Region& layer_invalidation, |
| float minimum_contents_scale, |
| float maximum_contents_scale) { |
| RemoveTilingsBelowScale(minimum_contents_scale); |
| RemoveTilingsAboveScale(maximum_contents_scale); |
| |
| raster_source_ = raster_source; |
| |
| // Copy over tilings that are shared with the |pending_twin_set| tiling set. |
| // Also, copy all of the properties from twin tilings. |
| CopyTilingsAndPropertiesFromPendingTwin(pending_twin_set, raster_source, |
| layer_invalidation); |
| |
| // If the tiling is not shared (FindTilingWithScale returns nullptr), then |
| // invalidate tiles and update them to the new raster source. |
| for (const auto& tiling : tilings_) { |
| if (pending_twin_set->FindTilingWithScale(tiling->contents_scale())) |
| continue; |
| |
| tiling->SetRasterSourceAndResize(raster_source); |
| tiling->Invalidate(layer_invalidation); |
| state_since_last_tile_priority_update_.invalidated = true; |
| // This is needed for cases where the live tiles rect didn't change but |
| // recordings exist in the raster source that did not exist on the last |
| // raster source. |
| tiling->CreateMissingTilesInLiveTilesRect(); |
| |
| // |this| is active set and |tiling| is not in the pending set, which means |
| // it is now NON_IDEAL_RESOLUTION. The exception is for LOW_RESOLUTION |
| // tilings, which are computed and created entirely on the active tree. |
| // Since the pending tree does not have them, we should just leave them as |
| // low resolution to not lose them. |
| if (tiling->resolution() != LOW_RESOLUTION) |
| tiling->set_resolution(NON_IDEAL_RESOLUTION); |
| } |
| |
| VerifyTilings(pending_twin_set); |
| } |
| |
| void PictureLayerTilingSet::UpdateTilingsToCurrentRasterSourceForCommit( |
| scoped_refptr<RasterSource> raster_source, |
| const Region& layer_invalidation, |
| float minimum_contents_scale, |
| float maximum_contents_scale) { |
| RemoveTilingsBelowScale(minimum_contents_scale); |
| RemoveTilingsAboveScale(maximum_contents_scale); |
| |
| raster_source_ = raster_source; |
| |
| // Invalidate tiles and update them to the new raster source. |
| for (const std::unique_ptr<PictureLayerTiling>& tiling : tilings_) { |
| DCHECK(tree_ != PENDING_TREE || !tiling->has_tiles()); |
| tiling->SetRasterSourceAndResize(raster_source); |
| |
| // We can commit on either active or pending trees, but only active one can |
| // have tiles at this point. |
| if (tree_ == ACTIVE_TREE) { |
| tiling->Invalidate(layer_invalidation); |
| state_since_last_tile_priority_update_.invalidated = true; |
| } |
| |
| // This is needed for cases where the live tiles rect didn't change but |
| // recordings exist in the raster source that did not exist on the last |
| // raster source. |
| tiling->CreateMissingTilesInLiveTilesRect(); |
| } |
| VerifyTilings(nullptr /* pending_twin_set */); |
| } |
| |
| void PictureLayerTilingSet::UpdateRasterSourceDueToLCDChange( |
| scoped_refptr<RasterSource> raster_source, |
| const Region& layer_invalidation) { |
| raster_source_ = raster_source; |
| for (const auto& tiling : tilings_) { |
| tiling->SetRasterSourceAndResize(raster_source); |
| tiling->Invalidate(layer_invalidation); |
| state_since_last_tile_priority_update_.invalidated = true; |
| // Since the invalidation changed, we need to create any missing tiles in |
| // the live tiles rect again. |
| tiling->CreateMissingTilesInLiveTilesRect(); |
| } |
| } |
| |
| void PictureLayerTilingSet::VerifyTilings( |
| const PictureLayerTilingSet* pending_twin_set) const { |
| #if DCHECK_IS_ON() |
| for (const auto& tiling : tilings_) { |
| DCHECK(tiling->tile_size() == |
| client_->CalculateTileSize(tiling->tiling_size())) |
| << "tile_size: " << tiling->tile_size().ToString() |
| << " tiling_size: " << tiling->tiling_size().ToString() |
| << " CalculateTileSize: " |
| << client_->CalculateTileSize(tiling->tiling_size()).ToString(); |
| } |
| |
| if (!tilings_.empty()) { |
| DCHECK_LE(NumHighResTilings(), 1); |
| // When commiting from the main thread the high res tiling may get dropped, |
| // but when cloning to the active tree, there should always be one. |
| if (pending_twin_set) { |
| DCHECK_EQ(1, NumHighResTilings()) |
| << " num tilings on active: " << tilings_.size() |
| << " num tilings on pending: " << pending_twin_set->tilings_.size() |
| << " num high res on pending: " |
| << pending_twin_set->NumHighResTilings() |
| << " are on active tree: " << (tree_ == ACTIVE_TREE); |
| } |
| } |
| #endif |
| } |
| |
| void PictureLayerTilingSet::CleanUpTilings( |
| float min_acceptable_high_res_scale, |
| float max_acceptable_high_res_scale, |
| const std::vector<PictureLayerTiling*>& needed_tilings, |
| PictureLayerTilingSet* twin_set) { |
| std::vector<PictureLayerTiling*> to_remove; |
| for (const auto& tiling : tilings_) { |
| // Keep all tilings within the min/max scales. |
| if (tiling->contents_scale() >= min_acceptable_high_res_scale && |
| tiling->contents_scale() <= max_acceptable_high_res_scale) { |
| continue; |
| } |
| |
| // Keep low resolution tilings. |
| if (tiling->resolution() == LOW_RESOLUTION) |
| continue; |
| |
| // Don't remove tilings that are required. |
| if (std::find(needed_tilings.begin(), needed_tilings.end(), tiling.get()) != |
| needed_tilings.end()) { |
| continue; |
| } |
| |
| to_remove.push_back(tiling.get()); |
| } |
| |
| for (auto* tiling : to_remove) { |
| DCHECK_NE(HIGH_RESOLUTION, tiling->resolution()); |
| Remove(tiling); |
| } |
| } |
| |
| void PictureLayerTilingSet::RemoveNonIdealTilings() { |
| auto to_remove = |
| std::remove_if(tilings_.begin(), tilings_.end(), |
| [](const std::unique_ptr<PictureLayerTiling>& t) { |
| return t->resolution() == NON_IDEAL_RESOLUTION; |
| }); |
| tilings_.erase(to_remove, tilings_.end()); |
| } |
| |
| void PictureLayerTilingSet::MarkAllTilingsNonIdeal() { |
| for (const auto& tiling : tilings_) |
| tiling->set_resolution(NON_IDEAL_RESOLUTION); |
| } |
| |
| PictureLayerTiling* PictureLayerTilingSet::AddTiling( |
| float contents_scale, |
| scoped_refptr<RasterSource> raster_source) { |
| if (!raster_source_) |
| raster_source_ = raster_source; |
| |
| for (size_t i = 0; i < tilings_.size(); ++i) { |
| DCHECK_NE(tilings_[i]->contents_scale(), contents_scale); |
| DCHECK_EQ(tilings_[i]->raster_source(), raster_source.get()); |
| } |
| |
| tilings_.push_back(base::WrapUnique( |
| new PictureLayerTiling(tree_, contents_scale, raster_source, client_))); |
| PictureLayerTiling* appended = tilings_.back().get(); |
| state_since_last_tile_priority_update_.added_tilings = true; |
| |
| std::sort(tilings_.begin(), tilings_.end(), LargestToSmallestScaleFunctor()); |
| return appended; |
| } |
| |
| int PictureLayerTilingSet::NumHighResTilings() const { |
| return std::count_if(tilings_.begin(), tilings_.end(), |
| [](const std::unique_ptr<PictureLayerTiling>& tiling) { |
| return tiling->resolution() == HIGH_RESOLUTION; |
| }); |
| } |
| |
| PictureLayerTiling* PictureLayerTilingSet::FindTilingWithScale( |
| float scale) const { |
| for (size_t i = 0; i < tilings_.size(); ++i) { |
| if (tilings_[i]->contents_scale() == scale) |
| return tilings_[i].get(); |
| } |
| return nullptr; |
| } |
| |
| PictureLayerTiling* PictureLayerTilingSet::FindTilingWithResolution( |
| TileResolution resolution) const { |
| auto iter = std::find_if( |
| tilings_.begin(), tilings_.end(), |
| [resolution](const std::unique_ptr<PictureLayerTiling>& tiling) { |
| return tiling->resolution() == resolution; |
| }); |
| if (iter == tilings_.end()) |
| return nullptr; |
| return iter->get(); |
| } |
| |
| void PictureLayerTilingSet::RemoveTilingsBelowScale(float minimum_scale) { |
| auto to_remove = std::remove_if( |
| tilings_.begin(), tilings_.end(), |
| [minimum_scale](const std::unique_ptr<PictureLayerTiling>& tiling) { |
| return tiling->contents_scale() < minimum_scale; |
| }); |
| tilings_.erase(to_remove, tilings_.end()); |
| } |
| |
| void PictureLayerTilingSet::RemoveTilingsAboveScale(float maximum_scale) { |
| auto to_remove = std::remove_if( |
| tilings_.begin(), tilings_.end(), |
| [maximum_scale](const std::unique_ptr<PictureLayerTiling>& tiling) { |
| return tiling->contents_scale() > maximum_scale; |
| }); |
| tilings_.erase(to_remove, tilings_.end()); |
| } |
| |
| void PictureLayerTilingSet::RemoveAllTilings() { |
| tilings_.clear(); |
| } |
| |
| void PictureLayerTilingSet::Remove(PictureLayerTiling* tiling) { |
| auto iter = std::find_if( |
| tilings_.begin(), tilings_.end(), |
| [tiling](const std::unique_ptr<PictureLayerTiling>& candidate) { |
| return candidate.get() == tiling; |
| }); |
| if (iter == tilings_.end()) |
| return; |
| tilings_.erase(iter); |
| } |
| |
| void PictureLayerTilingSet::RemoveAllTiles() { |
| for (size_t i = 0; i < tilings_.size(); ++i) |
| tilings_[i]->Reset(); |
| } |
| |
| float PictureLayerTilingSet::GetSnappedContentsScale( |
| float start_scale, |
| float snap_to_existing_tiling_ratio) const { |
| // If a tiling exists within the max snapping ratio, snap to its scale. |
| float snapped_contents_scale = start_scale; |
| float snapped_ratio = snap_to_existing_tiling_ratio; |
| for (const auto& tiling : tilings_) { |
| float tiling_contents_scale = tiling->contents_scale(); |
| float ratio = LargerRatio(tiling_contents_scale, start_scale); |
| if (ratio < snapped_ratio) { |
| snapped_contents_scale = tiling_contents_scale; |
| snapped_ratio = ratio; |
| } |
| } |
| return snapped_contents_scale; |
| } |
| |
| float PictureLayerTilingSet::GetMaximumContentsScale() const { |
| if (tilings_.empty()) |
| return 0.f; |
| // The first tiling has the largest contents scale. |
| return tilings_[0]->contents_scale(); |
| } |
| |
| bool PictureLayerTilingSet::TilingsNeedUpdate( |
| const gfx::Rect& visible_rect_in_layer_space, |
| double current_frame_time_in_seconds) { |
| // If we don't have any tilings, we don't need an update. |
| if (num_tilings() == 0) |
| return false; |
| |
| // If we never updated the tiling set, then our history is empty. We should |
| // update tilings. |
| if (visible_rect_history_.empty()) |
| return true; |
| |
| // If we've added new tilings since the last update, then we have to update at |
| // least that one tiling. |
| if (state_since_last_tile_priority_update_.added_tilings) |
| return true; |
| |
| // Finally, if some state changed (either frame time or visible rect), then we |
| // need to inform the tilings of the change. |
| const auto& last_frame = visible_rect_history_.front(); |
| if (current_frame_time_in_seconds != last_frame.frame_time_in_seconds) |
| return true; |
| |
| if (visible_rect_in_layer_space != last_frame.visible_rect_in_layer_space) |
| return true; |
| return false; |
| } |
| |
| gfx::Rect PictureLayerTilingSet::ComputeSkewport( |
| const gfx::Rect& visible_rect_in_layer_space, |
| double current_frame_time_in_seconds, |
| float ideal_contents_scale) { |
| gfx::Rect skewport = visible_rect_in_layer_space; |
| if (skewport.IsEmpty() || visible_rect_history_.empty()) |
| return skewport; |
| |
| // Use the oldest recorded history to get a stable skewport. |
| const auto& historical_frame = visible_rect_history_.back(); |
| double time_delta = |
| current_frame_time_in_seconds - historical_frame.frame_time_in_seconds; |
| if (time_delta == 0.) |
| return skewport; |
| |
| double extrapolation_multiplier = |
| skewport_target_time_in_seconds_ / time_delta; |
| int old_x = historical_frame.visible_rect_in_layer_space.x(); |
| int old_y = historical_frame.visible_rect_in_layer_space.y(); |
| int old_right = historical_frame.visible_rect_in_layer_space.right(); |
| int old_bottom = historical_frame.visible_rect_in_layer_space.bottom(); |
| |
| int new_x = visible_rect_in_layer_space.x(); |
| int new_y = visible_rect_in_layer_space.y(); |
| int new_right = visible_rect_in_layer_space.right(); |
| int new_bottom = visible_rect_in_layer_space.bottom(); |
| |
| int inset_x = (new_x - old_x) * extrapolation_multiplier; |
| int inset_y = (new_y - old_y) * extrapolation_multiplier; |
| int inset_right = (old_right - new_right) * extrapolation_multiplier; |
| int inset_bottom = (old_bottom - new_bottom) * extrapolation_multiplier; |
| |
| int skewport_extrapolation_limit_in_layer_pixels = |
| skewport_extrapolation_limit_in_screen_pixels_ / ideal_contents_scale; |
| gfx::Rect max_skewport = skewport; |
| max_skewport.Inset(-skewport_extrapolation_limit_in_layer_pixels, |
| -skewport_extrapolation_limit_in_layer_pixels); |
| |
| skewport.Inset(inset_x, inset_y, inset_right, inset_bottom); |
| skewport.Union(visible_rect_in_layer_space); |
| skewport.Intersect(max_skewport); |
| |
| // Due to limits in int's representation, it is possible that the two |
| // operations above (union and intersect) result in an empty skewport. To |
| // avoid any unpleasant situations like that, union the visible rect again to |
| // ensure that skewport.Contains(visible_rect_in_layer_space) is always |
| // true. |
| skewport.Union(visible_rect_in_layer_space); |
| skewport.Intersect(eventually_rect_in_layer_space_); |
| return skewport; |
| } |
| |
| gfx::Rect PictureLayerTilingSet::ComputeSoonBorderRect( |
| const gfx::Rect& visible_rect, |
| float ideal_contents_scale) { |
| int max_dimension = std::max(visible_rect.width(), visible_rect.height()); |
| int distance = |
| std::min<int>(kMaxSoonBorderDistanceInScreenPixels * ideal_contents_scale, |
| max_dimension * kSoonBorderDistanceViewportPercentage); |
| |
| gfx::Rect soon_border_rect = visible_rect; |
| soon_border_rect.Inset(-distance, -distance); |
| soon_border_rect.Intersect(eventually_rect_in_layer_space_); |
| return soon_border_rect; |
| } |
| |
| void PictureLayerTilingSet::UpdatePriorityRects( |
| const gfx::Rect& visible_rect_in_layer_space, |
| double current_frame_time_in_seconds, |
| float ideal_contents_scale) { |
| visible_rect_in_layer_space_ = gfx::Rect(); |
| eventually_rect_in_layer_space_ = gfx::Rect(); |
| |
| // We keep things as floats in here. |
| if (!visible_rect_in_layer_space.IsEmpty()) { |
| gfx::RectF eventually_rectf(visible_rect_in_layer_space); |
| eventually_rectf.Inset( |
| -tiling_interest_area_padding_ / ideal_contents_scale, |
| -tiling_interest_area_padding_ / ideal_contents_scale); |
| if (eventually_rectf.Intersects( |
| gfx::RectF(gfx::SizeF(raster_source_->GetSize())))) { |
| visible_rect_in_layer_space_ = visible_rect_in_layer_space; |
| eventually_rect_in_layer_space_ = gfx::ToEnclosingRect(eventually_rectf); |
| } |
| } |
| |
| skewport_in_layer_space_ = |
| ComputeSkewport(visible_rect_in_layer_space_, |
| current_frame_time_in_seconds, ideal_contents_scale); |
| DCHECK(skewport_in_layer_space_.Contains(visible_rect_in_layer_space_)); |
| DCHECK(eventually_rect_in_layer_space_.Contains(skewport_in_layer_space_)); |
| |
| soon_border_rect_in_layer_space_ = |
| ComputeSoonBorderRect(visible_rect_in_layer_space_, ideal_contents_scale); |
| DCHECK( |
| soon_border_rect_in_layer_space_.Contains(visible_rect_in_layer_space_)); |
| DCHECK(eventually_rect_in_layer_space_.Contains( |
| soon_border_rect_in_layer_space_)); |
| |
| // Finally, update our visible rect history. Note that we use the original |
| // visible rect here, since we want as accurate of a history as possible for |
| // stable skewports. |
| visible_rect_history_.push_front(FrameVisibleRect( |
| visible_rect_in_layer_space_, current_frame_time_in_seconds)); |
| if (visible_rect_history_.size() > 2) |
| visible_rect_history_.pop_back(); |
| } |
| |
| bool PictureLayerTilingSet::UpdateTilePriorities( |
| const gfx::Rect& visible_rect_in_layer_space, |
| float ideal_contents_scale, |
| double current_frame_time_in_seconds, |
| const Occlusion& occlusion_in_layer_space, |
| bool can_require_tiles_for_activation) { |
| StateSinceLastTilePriorityUpdate::AutoClear auto_clear_state( |
| &state_since_last_tile_priority_update_); |
| |
| if (!TilingsNeedUpdate(visible_rect_in_layer_space, |
| current_frame_time_in_seconds)) { |
| return state_since_last_tile_priority_update_.invalidated; |
| } |
| |
| UpdatePriorityRects(visible_rect_in_layer_space, |
| current_frame_time_in_seconds, ideal_contents_scale); |
| |
| for (const auto& tiling : tilings_) { |
| tiling->set_can_require_tiles_for_activation( |
| can_require_tiles_for_activation); |
| tiling->ComputeTilePriorityRects( |
| visible_rect_in_layer_space_, skewport_in_layer_space_, |
| soon_border_rect_in_layer_space_, eventually_rect_in_layer_space_, |
| ideal_contents_scale, occlusion_in_layer_space); |
| } |
| return true; |
| } |
| |
| void PictureLayerTilingSet::GetAllPrioritizedTilesForTracing( |
| std::vector<PrioritizedTile>* prioritized_tiles) const { |
| for (const auto& tiling : tilings_) |
| tiling->GetAllPrioritizedTilesForTracing(prioritized_tiles); |
| } |
| |
| PictureLayerTilingSet::CoverageIterator::CoverageIterator( |
| const PictureLayerTilingSet* set, |
| float contents_scale, |
| const gfx::Rect& content_rect, |
| float ideal_contents_scale) |
| : set_(set), |
| contents_scale_(contents_scale), |
| ideal_contents_scale_(ideal_contents_scale), |
| current_tiling_(std::numeric_limits<size_t>::max()) { |
| missing_region_.Union(content_rect); |
| |
| size_t tilings_size = set_->tilings_.size(); |
| for (ideal_tiling_ = 0; ideal_tiling_ < tilings_size; ++ideal_tiling_) { |
| PictureLayerTiling* tiling = set_->tilings_[ideal_tiling_].get(); |
| if (tiling->contents_scale() < ideal_contents_scale_) { |
| if (ideal_tiling_ > 0) |
| ideal_tiling_--; |
| break; |
| } |
| } |
| |
| if (ideal_tiling_ == tilings_size && ideal_tiling_ > 0) |
| ideal_tiling_--; |
| |
| ++(*this); |
| } |
| |
| PictureLayerTilingSet::CoverageIterator::~CoverageIterator() { |
| } |
| |
| gfx::Rect PictureLayerTilingSet::CoverageIterator::geometry_rect() const { |
| if (!tiling_iter_) { |
| if (!region_iter_.has_rect()) |
| return gfx::Rect(); |
| return region_iter_.rect(); |
| } |
| return tiling_iter_.geometry_rect(); |
| } |
| |
| gfx::RectF PictureLayerTilingSet::CoverageIterator::texture_rect() const { |
| if (!tiling_iter_) |
| return gfx::RectF(); |
| return tiling_iter_.texture_rect(); |
| } |
| |
| Tile* PictureLayerTilingSet::CoverageIterator::operator->() const { |
| if (!tiling_iter_) |
| return nullptr; |
| return *tiling_iter_; |
| } |
| |
| Tile* PictureLayerTilingSet::CoverageIterator::operator*() const { |
| if (!tiling_iter_) |
| return nullptr; |
| return *tiling_iter_; |
| } |
| |
| TileResolution PictureLayerTilingSet::CoverageIterator::resolution() const { |
| const PictureLayerTiling* tiling = CurrentTiling(); |
| DCHECK(tiling); |
| return tiling->resolution(); |
| } |
| |
| PictureLayerTiling* PictureLayerTilingSet::CoverageIterator::CurrentTiling() |
| const { |
| if (current_tiling_ == std::numeric_limits<size_t>::max()) |
| return nullptr; |
| if (current_tiling_ >= set_->tilings_.size()) |
| return nullptr; |
| return set_->tilings_[current_tiling_].get(); |
| } |
| |
| size_t PictureLayerTilingSet::CoverageIterator::NextTiling() const { |
| // Order returned by this method is: |
| // 1. Ideal tiling index |
| // 2. Tiling index < Ideal in decreasing order (higher res than ideal) |
| // 3. Tiling index > Ideal in increasing order (lower res than ideal) |
| // 4. Tiling index > tilings.size() (invalid index) |
| if (current_tiling_ == std::numeric_limits<size_t>::max()) |
| return ideal_tiling_; |
| else if (current_tiling_ > ideal_tiling_) |
| return current_tiling_ + 1; |
| else if (current_tiling_) |
| return current_tiling_ - 1; |
| else |
| return ideal_tiling_ + 1; |
| } |
| |
| PictureLayerTilingSet::CoverageIterator& |
| PictureLayerTilingSet::CoverageIterator::operator++() { |
| bool first_time = current_tiling_ == std::numeric_limits<size_t>::max(); |
| |
| if (!*this && !first_time) |
| return *this; |
| |
| if (tiling_iter_) |
| ++tiling_iter_; |
| |
| // Loop until we find a valid place to stop. |
| while (true) { |
| while (tiling_iter_ && |
| (!*tiling_iter_ || !tiling_iter_->draw_info().IsReadyToDraw())) { |
| missing_region_.Union(tiling_iter_.geometry_rect()); |
| ++tiling_iter_; |
| } |
| if (tiling_iter_) |
| return *this; |
| |
| // If the set of current rects for this tiling is done, go to the next |
| // tiling and set up to iterate through all of the remaining holes. |
| // This will also happen the first time through the loop. |
| if (!region_iter_.has_rect()) { |
| current_tiling_ = NextTiling(); |
| current_region_.Swap(&missing_region_); |
| missing_region_.Clear(); |
| region_iter_ = Region::Iterator(current_region_); |
| |
| // All done and all filled. |
| if (!region_iter_.has_rect()) { |
| current_tiling_ = set_->tilings_.size(); |
| return *this; |
| } |
| |
| // No more valid tiles, return this checkerboard rect. |
| if (current_tiling_ >= set_->tilings_.size()) |
| return *this; |
| } |
| |
| // Pop a rect off. If there are no more tilings, then these will be |
| // treated as geometry with null tiles that the caller can checkerboard. |
| gfx::Rect last_rect = region_iter_.rect(); |
| region_iter_.next(); |
| |
| // Done, found next checkerboard rect to return. |
| if (current_tiling_ >= set_->tilings_.size()) |
| return *this; |
| |
| // Construct a new iterator for the next tiling, but we need to loop |
| // again until we get to a valid one. |
| tiling_iter_ = PictureLayerTiling::CoverageIterator( |
| set_->tilings_[current_tiling_].get(), contents_scale_, last_rect); |
| } |
| |
| return *this; |
| } |
| |
| PictureLayerTilingSet::CoverageIterator::operator bool() const { |
| return current_tiling_ < set_->tilings_.size() || region_iter_.has_rect(); |
| } |
| |
| void PictureLayerTilingSet::AsValueInto( |
| base::trace_event::TracedValue* state) const { |
| for (size_t i = 0; i < tilings_.size(); ++i) { |
| state->BeginDictionary(); |
| tilings_[i]->AsValueInto(state); |
| state->EndDictionary(); |
| } |
| } |
| |
| size_t PictureLayerTilingSet::GPUMemoryUsageInBytes() const { |
| size_t amount = 0; |
| for (size_t i = 0; i < tilings_.size(); ++i) |
| amount += tilings_[i]->GPUMemoryUsageInBytes(); |
| return amount; |
| } |
| |
| PictureLayerTilingSet::TilingRange PictureLayerTilingSet::GetTilingRange( |
| TilingRangeType type) const { |
| // Doesn't seem to be the case right now but if it ever becomes a performance |
| // problem to compute these ranges each time this function is called, we can |
| // compute them only when the tiling set has changed instead. |
| size_t tilings_size = tilings_.size(); |
| TilingRange high_res_range(0, 0); |
| TilingRange low_res_range(tilings_.size(), tilings_.size()); |
| for (size_t i = 0; i < tilings_size; ++i) { |
| const PictureLayerTiling* tiling = tilings_[i].get(); |
| if (tiling->resolution() == HIGH_RESOLUTION) |
| high_res_range = TilingRange(i, i + 1); |
| if (tiling->resolution() == LOW_RESOLUTION) |
| low_res_range = TilingRange(i, i + 1); |
| } |
| |
| TilingRange range(0, 0); |
| switch (type) { |
| case HIGHER_THAN_HIGH_RES: |
| range = TilingRange(0, high_res_range.start); |
| break; |
| case HIGH_RES: |
| range = high_res_range; |
| break; |
| case BETWEEN_HIGH_AND_LOW_RES: |
| // TODO(vmpstr): This code assumes that high res tiling will come before |
| // low res tiling, however there are cases where this assumption is |
| // violated. As a result, it's better to be safe in these situations, |
| // since otherwise we can end up accessing a tiling that doesn't exist. |
| // See crbug.com/429397 for high res tiling appearing after low res |
| // tiling discussion/fixes. |
| if (high_res_range.start <= low_res_range.start) |
| range = TilingRange(high_res_range.end, low_res_range.start); |
| else |
| range = TilingRange(low_res_range.end, high_res_range.start); |
| break; |
| case LOW_RES: |
| range = low_res_range; |
| break; |
| case LOWER_THAN_LOW_RES: |
| range = TilingRange(low_res_range.end, tilings_size); |
| break; |
| } |
| |
| DCHECK_LE(range.start, range.end); |
| return range; |
| } |
| |
| } // namespace cc |