1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/resources/picture_layer_tiling.h"
12 #include "base/logging.h"
13 #include "base/trace_event/trace_event.h"
14 #include "base/trace_event/trace_event_argument.h"
15 #include "cc/base/math_util.h"
16 #include "cc/resources/tile.h"
17 #include "cc/resources/tile_priority.h"
18 #include "ui/gfx/geometry/point_conversions.h"
19 #include "ui/gfx/geometry/rect_conversions.h"
20 #include "ui/gfx/geometry/safe_integer_conversions.h"
21 #include "ui/gfx/geometry/size_conversions.h"
26 const float kSoonBorderDistanceViewportPercentage
= 0.15f
;
27 const float kMaxSoonBorderDistanceInScreenPixels
= 312.f
;
31 scoped_ptr
<PictureLayerTiling
> PictureLayerTiling::Create(
33 scoped_refptr
<RasterSource
> raster_source
,
34 PictureLayerTilingClient
* client
,
35 size_t max_tiles_for_interest_area
,
36 float skewport_target_time_in_seconds
,
37 int skewport_extrapolation_limit_in_content_pixels
) {
38 return make_scoped_ptr(new PictureLayerTiling(
39 contents_scale
, raster_source
, client
, max_tiles_for_interest_area
,
40 skewport_target_time_in_seconds
,
41 skewport_extrapolation_limit_in_content_pixels
));
44 PictureLayerTiling::PictureLayerTiling(
46 scoped_refptr
<RasterSource
> raster_source
,
47 PictureLayerTilingClient
* client
,
48 size_t max_tiles_for_interest_area
,
49 float skewport_target_time_in_seconds
,
50 int skewport_extrapolation_limit_in_content_pixels
)
51 : max_tiles_for_interest_area_(max_tiles_for_interest_area
),
52 skewport_target_time_in_seconds_(skewport_target_time_in_seconds
),
53 skewport_extrapolation_limit_in_content_pixels_(
54 skewport_extrapolation_limit_in_content_pixels
),
55 contents_scale_(contents_scale
),
57 raster_source_(raster_source
),
58 resolution_(NON_IDEAL_RESOLUTION
),
59 tiling_data_(gfx::Size(), gfx::Size(), kBorderTexels
),
60 last_impl_frame_time_in_seconds_(0.0),
61 can_require_tiles_for_activation_(false),
62 current_content_to_screen_scale_(0.f
),
63 has_visible_rect_tiles_(false),
64 has_skewport_rect_tiles_(false),
65 has_soon_border_rect_tiles_(false),
66 has_eventually_rect_tiles_(false) {
67 DCHECK(!raster_source
->IsSolidColor());
68 gfx::Size content_bounds
= gfx::ToCeiledSize(
69 gfx::ScaleSize(raster_source_
->GetSize(), contents_scale
));
70 gfx::Size tile_size
= client_
->CalculateTileSize(content_bounds
);
72 DCHECK(!gfx::ToFlooredSize(gfx::ScaleSize(raster_source_
->GetSize(),
73 contents_scale
)).IsEmpty())
74 << "Tiling created with scale too small as contents become empty."
75 << " Layer bounds: " << raster_source_
->GetSize().ToString()
76 << " Contents scale: " << contents_scale
;
78 tiling_data_
.SetTilingSize(content_bounds
);
79 tiling_data_
.SetMaxTextureSize(tile_size
);
82 PictureLayerTiling::~PictureLayerTiling() {
83 for (TileMap::const_iterator it
= tiles_
.begin(); it
!= tiles_
.end(); ++it
)
84 it
->second
->set_shared(false);
88 float PictureLayerTiling::CalculateSoonBorderDistance(
89 const gfx::Rect
& visible_rect_in_content_space
,
90 float content_to_screen_scale
) {
91 float max_dimension
= std::max(visible_rect_in_content_space
.width(),
92 visible_rect_in_content_space
.height());
94 kMaxSoonBorderDistanceInScreenPixels
/ content_to_screen_scale
,
95 max_dimension
* kSoonBorderDistanceViewportPercentage
);
98 Tile
* PictureLayerTiling::CreateTile(int i
,
100 const PictureLayerTiling
* twin_tiling
,
101 PictureLayerTiling
* recycled_twin
) {
102 // Can't have both a (pending or active) twin and a recycled twin tiling.
103 DCHECK_IMPLIES(twin_tiling
, !recycled_twin
);
104 DCHECK_IMPLIES(recycled_twin
, !twin_tiling
);
105 TileMapKey
key(i
, j
);
106 DCHECK(tiles_
.find(key
) == tiles_
.end());
108 gfx::Rect paint_rect
= tiling_data_
.TileBoundsWithBorder(i
, j
);
109 gfx::Rect tile_rect
= paint_rect
;
110 tile_rect
.set_size(tiling_data_
.max_texture_size());
112 // Check our twin for a valid tile.
114 tiling_data_
.max_texture_size() ==
115 twin_tiling
->tiling_data_
.max_texture_size()) {
116 if (Tile
* candidate_tile
= twin_tiling
->TileAt(i
, j
)) {
118 gfx::ScaleToEnclosingRect(paint_rect
, 1.0f
/ contents_scale_
);
119 const Region
* invalidation
= client_
->GetPendingInvalidation();
120 if (!invalidation
|| !invalidation
->Intersects(rect
)) {
121 DCHECK(!candidate_tile
->is_shared());
122 DCHECK_EQ(i
, candidate_tile
->tiling_i_index());
123 DCHECK_EQ(j
, candidate_tile
->tiling_j_index());
124 candidate_tile
->set_shared(true);
125 tiles_
[key
] = candidate_tile
;
126 return candidate_tile
;
131 if (!raster_source_
->CoversRect(tile_rect
, contents_scale_
))
134 // Create a new tile because our twin didn't have a valid one.
135 scoped_refptr
<Tile
> tile
= client_
->CreateTile(contents_scale_
, tile_rect
);
136 DCHECK(!tile
->is_shared());
137 tile
->set_tiling_index(i
, j
);
141 DCHECK(recycled_twin
->tiles_
.find(key
) == recycled_twin
->tiles_
.end());
142 // Do what recycled_twin->CreateTile() would do.
143 tile
->set_shared(true);
144 recycled_twin
->tiles_
[key
] = tile
;
149 void PictureLayerTiling::CreateMissingTilesInLiveTilesRect() {
150 const PictureLayerTiling
* twin_tiling
=
151 client_
->GetPendingOrActiveTwinTiling(this);
152 // There is no recycled twin during commit from the main thread which is when
154 PictureLayerTiling
* null_recycled_twin
= nullptr;
155 DCHECK_EQ(null_recycled_twin
, client_
->GetRecycledTwinTiling(this));
156 bool include_borders
= false;
157 for (TilingData::Iterator
iter(
158 &tiling_data_
, live_tiles_rect_
, include_borders
);
161 TileMapKey key
= iter
.index();
162 TileMap::iterator find
= tiles_
.find(key
);
163 if (find
!= tiles_
.end())
165 CreateTile(key
.first
, key
.second
, twin_tiling
, null_recycled_twin
);
168 VerifyLiveTilesRect(false);
171 void PictureLayerTiling::CloneTilesAndPropertiesFrom(
172 const PictureLayerTiling
& twin_tiling
) {
173 DCHECK_EQ(&twin_tiling
, client_
->GetPendingOrActiveTwinTiling(this));
175 SetRasterSourceAndResize(twin_tiling
.raster_source_
);
176 DCHECK_EQ(twin_tiling
.contents_scale_
, contents_scale_
);
177 DCHECK_EQ(twin_tiling
.raster_source_
, raster_source_
);
178 DCHECK_EQ(twin_tiling
.tile_size().ToString(), tile_size().ToString());
180 resolution_
= twin_tiling
.resolution_
;
182 SetLiveTilesRect(twin_tiling
.live_tiles_rect());
184 // Recreate unshared tiles.
185 std::vector
<TileMapKey
> to_remove
;
186 for (const auto& tile_map_pair
: tiles_
) {
187 TileMapKey key
= tile_map_pair
.first
;
188 Tile
* tile
= tile_map_pair
.second
.get();
189 if (!tile
->is_shared())
190 to_remove
.push_back(key
);
192 // The recycled twin does not exist since there is a pending twin (which is
194 PictureLayerTiling
* null_recycled_twin
= nullptr;
195 DCHECK_EQ(null_recycled_twin
, client_
->GetRecycledTwinTiling(this));
196 for (const auto& key
: to_remove
) {
197 RemoveTileAt(key
.first
, key
.second
, null_recycled_twin
);
198 CreateTile(key
.first
, key
.second
, &twin_tiling
, null_recycled_twin
);
201 // Create any missing tiles from the |twin_tiling|.
202 for (const auto& tile_map_pair
: twin_tiling
.tiles_
) {
203 TileMapKey key
= tile_map_pair
.first
;
204 Tile
* tile
= tile_map_pair
.second
.get();
205 if (!tile
->is_shared())
206 CreateTile(key
.first
, key
.second
, &twin_tiling
, null_recycled_twin
);
209 DCHECK_EQ(twin_tiling
.tiles_
.size(), tiles_
.size());
211 for (const auto& tile_map_pair
: tiles_
)
212 DCHECK(tile_map_pair
.second
->is_shared());
213 VerifyLiveTilesRect(false);
216 UpdateTilePriorityRects(twin_tiling
.current_content_to_screen_scale_
,
217 twin_tiling
.current_visible_rect_
,
218 twin_tiling
.current_skewport_rect_
,
219 twin_tiling
.current_soon_border_rect_
,
220 twin_tiling
.current_eventually_rect_
,
221 twin_tiling
.current_occlusion_in_layer_space_
);
224 void PictureLayerTiling::SetRasterSourceAndResize(
225 scoped_refptr
<RasterSource
> raster_source
) {
226 DCHECK(!raster_source
->IsSolidColor());
227 gfx::Size old_layer_bounds
= raster_source_
->GetSize();
228 raster_source_
.swap(raster_source
);
229 gfx::Size new_layer_bounds
= raster_source_
->GetSize();
230 gfx::Size content_bounds
=
231 gfx::ToCeiledSize(gfx::ScaleSize(new_layer_bounds
, contents_scale_
));
232 gfx::Size tile_size
= client_
->CalculateTileSize(content_bounds
);
234 if (tile_size
!= tiling_data_
.max_texture_size()) {
235 tiling_data_
.SetTilingSize(content_bounds
);
236 tiling_data_
.SetMaxTextureSize(tile_size
);
237 // When the tile size changes, the TilingData positions no longer work
238 // as valid keys to the TileMap, so just drop all tiles and clear the live
244 if (old_layer_bounds
== new_layer_bounds
)
247 // The SetLiveTilesRect() method would drop tiles outside the new bounds,
248 // but may do so incorrectly if resizing the tiling causes the number of
249 // tiles in the tiling_data_ to change.
250 gfx::Rect
content_rect(content_bounds
);
251 int before_left
= tiling_data_
.TileXIndexFromSrcCoord(live_tiles_rect_
.x());
252 int before_top
= tiling_data_
.TileYIndexFromSrcCoord(live_tiles_rect_
.y());
254 tiling_data_
.TileXIndexFromSrcCoord(live_tiles_rect_
.right() - 1);
256 tiling_data_
.TileYIndexFromSrcCoord(live_tiles_rect_
.bottom() - 1);
258 // The live_tiles_rect_ is clamped to stay within the tiling size as we
260 live_tiles_rect_
.Intersect(content_rect
);
261 tiling_data_
.SetTilingSize(content_bounds
);
263 int after_right
= -1;
264 int after_bottom
= -1;
265 if (!live_tiles_rect_
.IsEmpty()) {
267 tiling_data_
.TileXIndexFromSrcCoord(live_tiles_rect_
.right() - 1);
269 tiling_data_
.TileYIndexFromSrcCoord(live_tiles_rect_
.bottom() - 1);
272 // There is no recycled twin since this is run on the pending tiling
273 // during commit, and on the active tree during activate.
274 PictureLayerTiling
* null_recycled_twin
= nullptr;
275 DCHECK_EQ(null_recycled_twin
, client_
->GetRecycledTwinTiling(this));
277 // Drop tiles outside the new layer bounds if the layer shrank.
278 for (int i
= after_right
+ 1; i
<= before_right
; ++i
) {
279 for (int j
= before_top
; j
<= before_bottom
; ++j
)
280 RemoveTileAt(i
, j
, null_recycled_twin
);
282 for (int i
= before_left
; i
<= after_right
; ++i
) {
283 for (int j
= after_bottom
+ 1; j
<= before_bottom
; ++j
)
284 RemoveTileAt(i
, j
, null_recycled_twin
);
287 // If the layer grew, the live_tiles_rect_ is not changed, but a new row
288 // and/or column of tiles may now exist inside the same live_tiles_rect_.
289 const PictureLayerTiling
* twin_tiling
=
290 client_
->GetPendingOrActiveTwinTiling(this);
291 if (after_right
> before_right
) {
292 DCHECK_EQ(after_right
, before_right
+ 1);
293 for (int j
= before_top
; j
<= after_bottom
; ++j
)
294 CreateTile(after_right
, j
, twin_tiling
, null_recycled_twin
);
296 if (after_bottom
> before_bottom
) {
297 DCHECK_EQ(after_bottom
, before_bottom
+ 1);
298 for (int i
= before_left
; i
<= before_right
; ++i
)
299 CreateTile(i
, after_bottom
, twin_tiling
, null_recycled_twin
);
303 void PictureLayerTiling::Invalidate(const Region
& layer_invalidation
) {
304 if (live_tiles_rect_
.IsEmpty())
306 std::vector
<TileMapKey
> new_tile_keys
;
307 gfx::Rect expanded_live_tiles_rect
=
308 tiling_data_
.ExpandRectIgnoringBordersToTileBounds(live_tiles_rect_
);
309 for (Region::Iterator
iter(layer_invalidation
); iter
.has_rect();
311 gfx::Rect layer_rect
= iter
.rect();
312 gfx::Rect content_rect
=
313 gfx::ScaleToEnclosingRect(layer_rect
, contents_scale_
);
314 // Consider tiles inside the live tiles rect even if only their border
315 // pixels intersect the invalidation. But don't consider tiles outside
316 // the live tiles rect with the same conditions, as they won't exist.
317 int border_pixels
= tiling_data_
.border_texels();
318 content_rect
.Inset(-border_pixels
, -border_pixels
);
319 // Avoid needless work by not bothering to invalidate where there aren't
321 content_rect
.Intersect(expanded_live_tiles_rect
);
322 if (content_rect
.IsEmpty())
324 // Since the content_rect includes border pixels already, don't include
325 // borders when iterating to avoid double counting them.
326 bool include_borders
= false;
327 for (TilingData::Iterator
iter(
328 &tiling_data_
, content_rect
, include_borders
);
331 // There is no recycled twin for the pending tree during commit, or for
332 // the active tree during activation.
333 PictureLayerTiling
* null_recycled_twin
= nullptr;
334 DCHECK_EQ(null_recycled_twin
, client_
->GetRecycledTwinTiling(this));
335 if (RemoveTileAt(iter
.index_x(), iter
.index_y(), null_recycled_twin
))
336 new_tile_keys
.push_back(iter
.index());
340 if (!new_tile_keys
.empty()) {
341 // During commit from the main thread, invalidations can never be shared
342 // with the active tree since the active tree has different content there.
343 // And when invalidating an active-tree tiling, it means there was no
344 // pending tiling to clone from.
345 const PictureLayerTiling
* null_twin_tiling
= nullptr;
346 PictureLayerTiling
* null_recycled_twin
= nullptr;
347 DCHECK_EQ(null_recycled_twin
, client_
->GetRecycledTwinTiling(this));
348 for (size_t i
= 0; i
< new_tile_keys
.size(); ++i
) {
349 CreateTile(new_tile_keys
[i
].first
, new_tile_keys
[i
].second
,
350 null_twin_tiling
, null_recycled_twin
);
355 void PictureLayerTiling::SetRasterSourceOnTiles() {
356 // Shared (ie. non-invalidated) tiles on the pending tree are updated to use
357 // the new raster source. When this raster source is activated, the raster
358 // source will remain valid for shared tiles in the active tree.
359 for (TileMap::const_iterator it
= tiles_
.begin(); it
!= tiles_
.end(); ++it
)
360 it
->second
->set_raster_source(raster_source_
);
361 VerifyLiveTilesRect(false);
364 PictureLayerTiling::CoverageIterator::CoverageIterator()
375 PictureLayerTiling::CoverageIterator::CoverageIterator(
376 const PictureLayerTiling
* tiling
,
378 const gfx::Rect
& dest_rect
)
380 dest_rect_(dest_rect
),
381 dest_to_content_scale_(0),
390 if (dest_rect_
.IsEmpty())
393 dest_to_content_scale_
= tiling_
->contents_scale_
/ dest_scale
;
395 gfx::Rect content_rect
=
396 gfx::ScaleToEnclosingRect(dest_rect_
,
397 dest_to_content_scale_
,
398 dest_to_content_scale_
);
399 // IndexFromSrcCoord clamps to valid tile ranges, so it's necessary to
400 // check for non-intersection first.
401 content_rect
.Intersect(gfx::Rect(tiling_
->tiling_size()));
402 if (content_rect
.IsEmpty())
405 left_
= tiling_
->tiling_data_
.TileXIndexFromSrcCoord(content_rect
.x());
406 top_
= tiling_
->tiling_data_
.TileYIndexFromSrcCoord(content_rect
.y());
407 right_
= tiling_
->tiling_data_
.TileXIndexFromSrcCoord(
408 content_rect
.right() - 1);
409 bottom_
= tiling_
->tiling_data_
.TileYIndexFromSrcCoord(
410 content_rect
.bottom() - 1);
417 PictureLayerTiling::CoverageIterator::~CoverageIterator() {
420 PictureLayerTiling::CoverageIterator
&
421 PictureLayerTiling::CoverageIterator::operator++() {
422 if (tile_j_
> bottom_
)
425 bool first_time
= tile_i_
< left_
;
426 bool new_row
= false;
428 if (tile_i_
> right_
) {
432 if (tile_j_
> bottom_
) {
433 current_tile_
= NULL
;
438 current_tile_
= tiling_
->TileAt(tile_i_
, tile_j_
);
440 // Calculate the current geometry rect. Due to floating point rounding
441 // and ToEnclosingRect, tiles might overlap in destination space on the
443 gfx::Rect last_geometry_rect
= current_geometry_rect_
;
445 gfx::Rect content_rect
= tiling_
->tiling_data_
.TileBounds(tile_i_
, tile_j_
);
447 current_geometry_rect_
=
448 gfx::ScaleToEnclosingRect(content_rect
,
449 1 / dest_to_content_scale_
,
450 1 / dest_to_content_scale_
);
452 current_geometry_rect_
.Intersect(dest_rect_
);
457 // Iteration happens left->right, top->bottom. Running off the bottom-right
458 // edge is handled by the intersection above with dest_rect_. Here we make
459 // sure that the new current geometry rect doesn't overlap with the last.
463 min_left
= dest_rect_
.x();
464 min_top
= last_geometry_rect
.bottom();
466 min_left
= last_geometry_rect
.right();
467 min_top
= last_geometry_rect
.y();
470 int inset_left
= std::max(0, min_left
- current_geometry_rect_
.x());
471 int inset_top
= std::max(0, min_top
- current_geometry_rect_
.y());
472 current_geometry_rect_
.Inset(inset_left
, inset_top
, 0, 0);
475 DCHECK_EQ(last_geometry_rect
.right(), current_geometry_rect_
.x());
476 DCHECK_EQ(last_geometry_rect
.bottom(), current_geometry_rect_
.bottom());
477 DCHECK_EQ(last_geometry_rect
.y(), current_geometry_rect_
.y());
483 gfx::Rect
PictureLayerTiling::CoverageIterator::geometry_rect() const {
484 return current_geometry_rect_
;
487 gfx::RectF
PictureLayerTiling::CoverageIterator::texture_rect() const {
488 gfx::PointF tex_origin
=
489 tiling_
->tiling_data_
.TileBoundsWithBorder(tile_i_
, tile_j_
).origin();
491 // Convert from dest space => content space => texture space.
492 gfx::RectF
texture_rect(current_geometry_rect_
);
493 texture_rect
.Scale(dest_to_content_scale_
,
494 dest_to_content_scale_
);
495 texture_rect
.Intersect(gfx::Rect(tiling_
->tiling_size()));
496 if (texture_rect
.IsEmpty())
498 texture_rect
.Offset(-tex_origin
.OffsetFromOrigin());
503 bool PictureLayerTiling::RemoveTileAt(int i
,
505 PictureLayerTiling
* recycled_twin
) {
506 TileMap::iterator found
= tiles_
.find(TileMapKey(i
, j
));
507 if (found
== tiles_
.end())
509 found
->second
->set_shared(false);
512 // Recycled twin does not also have a recycled twin, so pass null.
513 recycled_twin
->RemoveTileAt(i
, j
, nullptr);
518 void PictureLayerTiling::Reset() {
519 live_tiles_rect_
= gfx::Rect();
520 PictureLayerTiling
* recycled_twin
= client_
->GetRecycledTwinTiling(this);
521 for (TileMap::const_iterator it
= tiles_
.begin(); it
!= tiles_
.end(); ++it
) {
522 it
->second
->set_shared(false);
524 recycled_twin
->RemoveTileAt(it
->first
.first
, it
->first
.second
, nullptr);
529 gfx::Rect
PictureLayerTiling::ComputeSkewport(
530 double current_frame_time_in_seconds
,
531 const gfx::Rect
& visible_rect_in_content_space
) const {
532 gfx::Rect skewport
= visible_rect_in_content_space
;
533 if (last_impl_frame_time_in_seconds_
== 0.0)
537 current_frame_time_in_seconds
- last_impl_frame_time_in_seconds_
;
538 if (time_delta
== 0.0)
541 double extrapolation_multiplier
=
542 skewport_target_time_in_seconds_
/ time_delta
;
544 int old_x
= last_visible_rect_in_content_space_
.x();
545 int old_y
= last_visible_rect_in_content_space_
.y();
546 int old_right
= last_visible_rect_in_content_space_
.right();
547 int old_bottom
= last_visible_rect_in_content_space_
.bottom();
549 int new_x
= visible_rect_in_content_space
.x();
550 int new_y
= visible_rect_in_content_space
.y();
551 int new_right
= visible_rect_in_content_space
.right();
552 int new_bottom
= visible_rect_in_content_space
.bottom();
554 // Compute the maximum skewport based on
555 // |skewport_extrapolation_limit_in_content_pixels_|.
556 gfx::Rect max_skewport
= skewport
;
557 max_skewport
.Inset(-skewport_extrapolation_limit_in_content_pixels_
,
558 -skewport_extrapolation_limit_in_content_pixels_
);
560 // Inset the skewport by the needed adjustment.
561 skewport
.Inset(extrapolation_multiplier
* (new_x
- old_x
),
562 extrapolation_multiplier
* (new_y
- old_y
),
563 extrapolation_multiplier
* (old_right
- new_right
),
564 extrapolation_multiplier
* (old_bottom
- new_bottom
));
566 // Clip the skewport to |max_skewport|.
567 skewport
.Intersect(max_skewport
);
569 // Finally, ensure that visible rect is contained in the skewport.
570 skewport
.Union(visible_rect_in_content_space
);
574 bool PictureLayerTiling::ComputeTilePriorityRects(
575 const gfx::Rect
& viewport_in_layer_space
,
576 float ideal_contents_scale
,
577 double current_frame_time_in_seconds
,
578 const Occlusion
& occlusion_in_layer_space
) {
579 if (!NeedsUpdateForFrameAtTimeAndViewport(current_frame_time_in_seconds
,
580 viewport_in_layer_space
)) {
581 // This should never be zero for the purposes of has_ever_been_updated().
582 DCHECK_NE(current_frame_time_in_seconds
, 0.0);
586 gfx::Rect visible_rect_in_content_space
=
587 gfx::ScaleToEnclosingRect(viewport_in_layer_space
, contents_scale_
);
589 if (tiling_size().IsEmpty()) {
590 last_impl_frame_time_in_seconds_
= current_frame_time_in_seconds
;
591 last_viewport_in_layer_space_
= viewport_in_layer_space
;
592 last_visible_rect_in_content_space_
= visible_rect_in_content_space
;
596 // Calculate the skewport.
597 gfx::Rect skewport
= ComputeSkewport(current_frame_time_in_seconds
,
598 visible_rect_in_content_space
);
599 DCHECK(skewport
.Contains(visible_rect_in_content_space
));
601 // Calculate the eventually/live tiles rect.
602 gfx::Size tile_size
= tiling_data_
.max_texture_size();
603 int64 eventually_rect_area
=
604 max_tiles_for_interest_area_
* tile_size
.width() * tile_size
.height();
606 gfx::Rect eventually_rect
=
607 ExpandRectEquallyToAreaBoundedBy(visible_rect_in_content_space
,
608 eventually_rect_area
,
609 gfx::Rect(tiling_size()),
612 DCHECK(eventually_rect
.IsEmpty() ||
613 gfx::Rect(tiling_size()).Contains(eventually_rect
))
614 << "tiling_size: " << tiling_size().ToString()
615 << " eventually_rect: " << eventually_rect
.ToString();
617 // Calculate the soon border rect.
618 float content_to_screen_scale
= ideal_contents_scale
/ contents_scale_
;
619 gfx::Rect soon_border_rect
= visible_rect_in_content_space
;
620 float border
= CalculateSoonBorderDistance(visible_rect_in_content_space
,
621 content_to_screen_scale
);
622 soon_border_rect
.Inset(-border
, -border
, -border
, -border
);
624 last_impl_frame_time_in_seconds_
= current_frame_time_in_seconds
;
625 last_viewport_in_layer_space_
= viewport_in_layer_space
;
626 last_visible_rect_in_content_space_
= visible_rect_in_content_space
;
628 SetLiveTilesRect(eventually_rect
);
629 UpdateTilePriorityRects(
630 content_to_screen_scale
, visible_rect_in_content_space
, skewport
,
631 soon_border_rect
, eventually_rect
, occlusion_in_layer_space
);
635 void PictureLayerTiling::UpdateTilePriorityRects(
636 float content_to_screen_scale
,
637 const gfx::Rect
& visible_rect_in_content_space
,
638 const gfx::Rect
& skewport
,
639 const gfx::Rect
& soon_border_rect
,
640 const gfx::Rect
& eventually_rect
,
641 const Occlusion
& occlusion_in_layer_space
) {
642 current_visible_rect_
= visible_rect_in_content_space
;
643 current_skewport_rect_
= skewport
;
644 current_soon_border_rect_
= soon_border_rect
;
645 current_eventually_rect_
= eventually_rect
;
646 current_occlusion_in_layer_space_
= occlusion_in_layer_space
;
647 current_content_to_screen_scale_
= content_to_screen_scale
;
649 gfx::Rect
tiling_rect(tiling_size());
650 has_visible_rect_tiles_
= tiling_rect
.Intersects(current_visible_rect_
);
651 has_skewport_rect_tiles_
= tiling_rect
.Intersects(current_skewport_rect_
);
652 has_soon_border_rect_tiles_
=
653 tiling_rect
.Intersects(current_soon_border_rect_
);
654 has_eventually_rect_tiles_
= tiling_rect
.Intersects(current_eventually_rect_
);
657 void PictureLayerTiling::SetLiveTilesRect(
658 const gfx::Rect
& new_live_tiles_rect
) {
659 DCHECK(new_live_tiles_rect
.IsEmpty() ||
660 gfx::Rect(tiling_size()).Contains(new_live_tiles_rect
))
661 << "tiling_size: " << tiling_size().ToString()
662 << " new_live_tiles_rect: " << new_live_tiles_rect
.ToString();
663 if (live_tiles_rect_
== new_live_tiles_rect
)
666 PictureLayerTiling
* recycled_twin
= client_
->GetRecycledTwinTiling(this);
668 // Iterate to delete all tiles outside of our new live_tiles rect.
669 for (TilingData::DifferenceIterator
iter(&tiling_data_
,
671 new_live_tiles_rect
);
674 RemoveTileAt(iter
.index_x(), iter
.index_y(), recycled_twin
);
677 const PictureLayerTiling
* twin_tiling
=
678 client_
->GetPendingOrActiveTwinTiling(this);
680 // Iterate to allocate new tiles for all regions with newly exposed area.
681 for (TilingData::DifferenceIterator
iter(&tiling_data_
,
686 TileMapKey
key(iter
.index());
687 CreateTile(key
.first
, key
.second
, twin_tiling
, recycled_twin
);
690 live_tiles_rect_
= new_live_tiles_rect
;
691 VerifyLiveTilesRect(false);
693 recycled_twin
->live_tiles_rect_
= live_tiles_rect_
;
694 recycled_twin
->VerifyLiveTilesRect(true);
698 void PictureLayerTiling::VerifyLiveTilesRect(bool is_on_recycle_tree
) const {
700 for (auto it
= tiles_
.begin(); it
!= tiles_
.end(); ++it
) {
701 if (!it
->second
.get())
703 DCHECK(it
->first
.first
< tiling_data_
.num_tiles_x())
704 << this << " " << it
->first
.first
<< "," << it
->first
.second
705 << " num_tiles_x " << tiling_data_
.num_tiles_x() << " live_tiles_rect "
706 << live_tiles_rect_
.ToString();
707 DCHECK(it
->first
.second
< tiling_data_
.num_tiles_y())
708 << this << " " << it
->first
.first
<< "," << it
->first
.second
709 << " num_tiles_y " << tiling_data_
.num_tiles_y() << " live_tiles_rect "
710 << live_tiles_rect_
.ToString();
711 DCHECK(tiling_data_
.TileBounds(it
->first
.first
, it
->first
.second
)
712 .Intersects(live_tiles_rect_
))
713 << this << " " << it
->first
.first
<< "," << it
->first
.second
715 << tiling_data_
.TileBounds(it
->first
.first
, it
->first
.second
).ToString()
716 << " live_tiles_rect " << live_tiles_rect_
.ToString();
717 DCHECK_IMPLIES(is_on_recycle_tree
, it
->second
->is_shared());
722 bool PictureLayerTiling::IsTileOccluded(const Tile
* tile
) const {
725 if (!current_occlusion_in_layer_space_
.HasOcclusion())
728 gfx::Rect tile_query_rect
=
729 gfx::IntersectRects(tile
->content_rect(), current_visible_rect_
);
731 // Explicitly check if the tile is outside the viewport. If so, we need to
732 // return false, since occlusion for this tile is unknown.
733 // TODO(vmpstr): Since the current visible rect is really a viewport in
734 // layer space, we should probably clip tile query rect to tiling bounds
735 // or live tiles rect.
736 if (tile_query_rect
.IsEmpty())
739 if (contents_scale_
!= 1.f
) {
741 gfx::ScaleToEnclosingRect(tile_query_rect
, 1.0f
/ contents_scale_
);
744 return current_occlusion_in_layer_space_
.IsOccluded(tile_query_rect
);
747 bool PictureLayerTiling::IsTileRequiredForActivationIfVisible(
748 const Tile
* tile
) const {
749 DCHECK_EQ(PENDING_TREE
, client_
->GetTree());
751 // This function assumes that the tile is visible (i.e. in the viewport). The
752 // caller needs to make sure that this condition is met to ensure we don't
753 // block activation on tiles outside of the viewport.
755 // If we are not allowed to mark tiles as required for activation, then don't
757 if (!can_require_tiles_for_activation_
)
760 if (resolution_
!= HIGH_RESOLUTION
)
763 if (IsTileOccluded(tile
))
766 if (client_
->RequiresHighResToDraw())
769 const PictureLayerTiling
* twin_tiling
=
770 client_
->GetPendingOrActiveTwinTiling(this);
774 if (twin_tiling
->raster_source()->GetSize() != raster_source()->GetSize())
777 if (twin_tiling
->current_visible_rect_
!= current_visible_rect_
)
781 twin_tiling
->TileAt(tile
->tiling_i_index(), tile
->tiling_j_index());
782 // If twin tile is missing, it might not have a recording, so we don't need
783 // this tile to be required for activation.
790 bool PictureLayerTiling::IsTileRequiredForDrawIfVisible(
791 const Tile
* tile
) const {
792 DCHECK_EQ(ACTIVE_TREE
, client_
->GetTree());
794 // This function assumes that the tile is visible (i.e. in the viewport).
796 if (resolution_
!= HIGH_RESOLUTION
)
799 if (IsTileOccluded(tile
))
805 void PictureLayerTiling::UpdateTileAndTwinPriority(Tile
* tile
) const {
806 WhichTree tree
= client_
->GetTree();
807 WhichTree twin_tree
= tree
== ACTIVE_TREE
? PENDING_TREE
: ACTIVE_TREE
;
809 UpdateTilePriorityForTree(tile
, tree
);
811 const PictureLayerTiling
* twin_tiling
=
812 client_
->GetPendingOrActiveTwinTiling(this);
813 if (!tile
->is_shared() || !twin_tiling
) {
814 tile
->SetPriority(twin_tree
, TilePriority());
815 tile
->set_is_occluded(twin_tree
, false);
816 if (twin_tree
== PENDING_TREE
)
817 tile
->set_required_for_activation(false);
819 tile
->set_required_for_draw(false);
823 twin_tiling
->UpdateTilePriorityForTree(tile
, twin_tree
);
826 void PictureLayerTiling::UpdateTilePriorityForTree(Tile
* tile
,
827 WhichTree tree
) const {
828 // TODO(vmpstr): This code should return the priority instead of setting it on
829 // the tile. This should be a part of the change to move tile priority from
830 // tiles into iterators.
831 TilePriority::PriorityBin max_tile_priority_bin
=
832 client_
->GetMaxTilePriorityBin();
834 DCHECK_EQ(TileAt(tile
->tiling_i_index(), tile
->tiling_j_index()), tile
);
835 gfx::Rect tile_bounds
=
836 tiling_data_
.TileBounds(tile
->tiling_i_index(), tile
->tiling_j_index());
838 if (max_tile_priority_bin
<= TilePriority::NOW
&&
839 current_visible_rect_
.Intersects(tile_bounds
)) {
840 tile
->SetPriority(tree
, TilePriority(resolution_
, TilePriority::NOW
, 0));
841 if (tree
== PENDING_TREE
) {
842 tile
->set_required_for_activation(
843 IsTileRequiredForActivationIfVisible(tile
));
845 tile
->set_required_for_draw(IsTileRequiredForDrawIfVisible(tile
));
847 tile
->set_is_occluded(tree
, IsTileOccluded(tile
));
851 if (tree
== PENDING_TREE
)
852 tile
->set_required_for_activation(false);
854 tile
->set_required_for_draw(false);
855 tile
->set_is_occluded(tree
, false);
857 DCHECK_GT(current_content_to_screen_scale_
, 0.f
);
858 float distance_to_visible
=
859 current_visible_rect_
.ManhattanInternalDistance(tile_bounds
) *
860 current_content_to_screen_scale_
;
862 if (max_tile_priority_bin
<= TilePriority::SOON
&&
863 (current_soon_border_rect_
.Intersects(tile_bounds
) ||
864 current_skewport_rect_
.Intersects(tile_bounds
))) {
867 TilePriority(resolution_
, TilePriority::SOON
, distance_to_visible
));
873 TilePriority(resolution_
, TilePriority::EVENTUALLY
, distance_to_visible
));
876 void PictureLayerTiling::GetAllTilesForTracing(
877 std::set
<const Tile
*>* tiles
) const {
878 for (TileMap::const_iterator it
= tiles_
.begin(); it
!= tiles_
.end(); ++it
)
879 tiles
->insert(it
->second
.get());
882 void PictureLayerTiling::AsValueInto(
883 base::trace_event::TracedValue
* state
) const {
884 state
->SetInteger("num_tiles", tiles_
.size());
885 state
->SetDouble("content_scale", contents_scale_
);
886 MathUtil::AddToTracedValue("tiling_size", tiling_size(), state
);
889 size_t PictureLayerTiling::GPUMemoryUsageInBytes() const {
891 for (TileMap::const_iterator it
= tiles_
.begin(); it
!= tiles_
.end(); ++it
) {
892 const Tile
* tile
= it
->second
.get();
893 amount
+= tile
->GPUMemoryUsageInBytes();
898 PictureLayerTiling::RectExpansionCache::RectExpansionCache()
899 : previous_target(0) {
904 // This struct represents an event at which the expending rect intersects
905 // one of its boundaries. 4 intersection events will occur during expansion.
907 enum { BOTTOM
, TOP
, LEFT
, RIGHT
} edge
;
912 // Compute the delta to expand from edges to cover target_area.
913 int ComputeExpansionDelta(int num_x_edges
, int num_y_edges
,
914 int width
, int height
,
916 // Compute coefficients for the quadratic equation:
917 // a*x^2 + b*x + c = 0
918 int a
= num_y_edges
* num_x_edges
;
919 int b
= num_y_edges
* width
+ num_x_edges
* height
;
920 int64 c
= static_cast<int64
>(width
) * height
- target_area
;
922 // Compute the delta for our edges using the quadratic equation.
924 (a
== 0) ? -c
/ b
: (-b
+ static_cast<int>(std::sqrt(
925 static_cast<int64
>(b
) * b
- 4.0 * a
* c
))) /
927 return std::max(0, delta
);
932 gfx::Rect
PictureLayerTiling::ExpandRectEquallyToAreaBoundedBy(
933 const gfx::Rect
& starting_rect
,
935 const gfx::Rect
& bounding_rect
,
936 RectExpansionCache
* cache
) {
937 if (starting_rect
.IsEmpty())
938 return starting_rect
;
941 cache
->previous_start
== starting_rect
&&
942 cache
->previous_bounds
== bounding_rect
&&
943 cache
->previous_target
== target_area
)
944 return cache
->previous_result
;
947 cache
->previous_start
= starting_rect
;
948 cache
->previous_bounds
= bounding_rect
;
949 cache
->previous_target
= target_area
;
952 DCHECK(!bounding_rect
.IsEmpty());
953 DCHECK_GT(target_area
, 0);
955 // Expand the starting rect to cover target_area, if it is smaller than it.
956 int delta
= ComputeExpansionDelta(
957 2, 2, starting_rect
.width(), starting_rect
.height(), target_area
);
958 gfx::Rect expanded_starting_rect
= starting_rect
;
960 expanded_starting_rect
.Inset(-delta
, -delta
);
962 gfx::Rect rect
= IntersectRects(expanded_starting_rect
, bounding_rect
);
963 if (rect
.IsEmpty()) {
964 // The starting_rect and bounding_rect are far away.
966 cache
->previous_result
= rect
;
969 if (delta
>= 0 && rect
== expanded_starting_rect
) {
970 // The starting rect already covers the entire bounding_rect and isn't too
971 // large for the target_area.
973 cache
->previous_result
= rect
;
977 // Continue to expand/shrink rect to let it cover target_area.
979 // These values will be updated by the loop and uses as the output.
980 int origin_x
= rect
.x();
981 int origin_y
= rect
.y();
982 int width
= rect
.width();
983 int height
= rect
.height();
985 // In the beginning we will consider 2 edges in each dimension.
989 // Create an event list.
990 EdgeEvent events
[] = {
991 { EdgeEvent::BOTTOM
, &num_y_edges
, rect
.y() - bounding_rect
.y() },
992 { EdgeEvent::TOP
, &num_y_edges
, bounding_rect
.bottom() - rect
.bottom() },
993 { EdgeEvent::LEFT
, &num_x_edges
, rect
.x() - bounding_rect
.x() },
994 { EdgeEvent::RIGHT
, &num_x_edges
, bounding_rect
.right() - rect
.right() }
997 // Sort the events by distance (closest first).
998 if (events
[0].distance
> events
[1].distance
) std::swap(events
[0], events
[1]);
999 if (events
[2].distance
> events
[3].distance
) std::swap(events
[2], events
[3]);
1000 if (events
[0].distance
> events
[2].distance
) std::swap(events
[0], events
[2]);
1001 if (events
[1].distance
> events
[3].distance
) std::swap(events
[1], events
[3]);
1002 if (events
[1].distance
> events
[2].distance
) std::swap(events
[1], events
[2]);
1004 for (int event_index
= 0; event_index
< 4; event_index
++) {
1005 const EdgeEvent
& event
= events
[event_index
];
1007 int delta
= ComputeExpansionDelta(
1008 num_x_edges
, num_y_edges
, width
, height
, target_area
);
1010 // Clamp delta to our event distance.
1011 if (delta
> event
.distance
)
1012 delta
= event
.distance
;
1014 // Adjust the edge count for this kind of edge.
1017 // Apply the delta to the edges and edge events.
1018 for (int i
= event_index
; i
< 4; i
++) {
1019 switch (events
[i
].edge
) {
1020 case EdgeEvent::BOTTOM
:
1024 case EdgeEvent::TOP
:
1027 case EdgeEvent::LEFT
:
1031 case EdgeEvent::RIGHT
:
1035 events
[i
].distance
-= delta
;
1038 // If our delta is less then our event distance, we're done.
1039 if (delta
< event
.distance
)
1043 gfx::Rect
result(origin_x
, origin_y
, width
, height
);
1045 cache
->previous_result
= result
;