1 // Copyright 2011 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.
4
5 #include "cc/layers/tiled_layer.h"
6
7 #include <algorithm>
8 #include <vector>
9
10 #include "base/auto_reset.h"
11 #include "base/basictypes.h"
12 #include "build/build_config.h"
13 #include "cc/debug/overdraw_metrics.h"
14 #include "cc/layers/layer_impl.h"
15 #include "cc/layers/tiled_layer_impl.h"
16 #include "cc/resources/layer_updater.h"
17 #include "cc/resources/prioritized_resource.h"
18 #include "cc/resources/priority_calculator.h"
19 #include "cc/trees/layer_tree_host.h"
20 #include "third_party/khronos/GLES2/gl2.h"
21 #include "ui/gfx/rect_conversions.h"
22
23 namespace cc {
24
25 // Maximum predictive expansion of the visible area.
26 static const int kMaxPredictiveTilesCount = 2;
27
28 // Number of rows/columns of tiles to pre-paint.
29 // We should increase these further as all textures are
30 // prioritized and we insure performance doesn't suffer.
31 static const int kPrepaintRows = 4;
32 static const int kPrepaintColumns = 2;
33
34 class UpdatableTile : public LayerTilingData::Tile {
35 public:
Create(scoped_ptr<LayerUpdater::Resource> updater_resource)36 static scoped_ptr<UpdatableTile> Create(
37 scoped_ptr<LayerUpdater::Resource> updater_resource) {
38 return make_scoped_ptr(new UpdatableTile(updater_resource.Pass()));
39 }
40
updater_resource()41 LayerUpdater::Resource* updater_resource() { return updater_resource_.get(); }
managed_resource()42 PrioritizedResource* managed_resource() {
43 return updater_resource_->texture();
44 }
45
is_dirty() const46 bool is_dirty() const { return !dirty_rect.IsEmpty(); }
47
48 // Reset update state for the current frame. This should occur before painting
49 // for all layers. Since painting one layer can invalidate another layer after
50 // it has already painted, mark all non-dirty tiles as valid before painting
51 // such that invalidations during painting won't prevent them from being
52 // pushed.
ResetUpdateState()53 void ResetUpdateState() {
54 update_rect = gfx::Rect();
55 occluded = false;
56 partial_update = false;
57 valid_for_frame = !is_dirty();
58 }
59
60 // This promises to update the tile and therefore also guarantees the tile
61 // will be valid for this frame. dirty_rect is copied into update_rect so we
62 // can continue to track re-entrant invalidations that occur during painting.
MarkForUpdate()63 void MarkForUpdate() {
64 valid_for_frame = true;
65 update_rect = dirty_rect;
66 dirty_rect = gfx::Rect();
67 }
68
69 gfx::Rect dirty_rect;
70 gfx::Rect update_rect;
71 bool partial_update;
72 bool valid_for_frame;
73 bool occluded;
74
75 private:
UpdatableTile(scoped_ptr<LayerUpdater::Resource> updater_resource)76 explicit UpdatableTile(scoped_ptr<LayerUpdater::Resource> updater_resource)
77 : partial_update(false),
78 valid_for_frame(false),
79 occluded(false),
80 updater_resource_(updater_resource.Pass()) {}
81
82 scoped_ptr<LayerUpdater::Resource> updater_resource_;
83
84 DISALLOW_COPY_AND_ASSIGN(UpdatableTile);
85 };
86
TiledLayer()87 TiledLayer::TiledLayer()
88 : ContentsScalingLayer(),
89 texture_format_(RGBA_8888),
90 skips_draw_(false),
91 failed_update_(false),
92 tiling_option_(AUTO_TILE) {
93 tiler_ =
94 LayerTilingData::Create(gfx::Size(), LayerTilingData::HAS_BORDER_TEXELS);
95 }
96
~TiledLayer()97 TiledLayer::~TiledLayer() {}
98
CreateLayerImpl(LayerTreeImpl * tree_impl)99 scoped_ptr<LayerImpl> TiledLayer::CreateLayerImpl(LayerTreeImpl* tree_impl) {
100 return TiledLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
101 }
102
UpdateTileSizeAndTilingOption()103 void TiledLayer::UpdateTileSizeAndTilingOption() {
104 DCHECK(layer_tree_host());
105
106 gfx::Size default_tile_size = layer_tree_host()->settings().default_tile_size;
107 gfx::Size max_untiled_layer_size =
108 layer_tree_host()->settings().max_untiled_layer_size;
109 int layer_width = content_bounds().width();
110 int layer_height = content_bounds().height();
111
112 gfx::Size tile_size(std::min(default_tile_size.width(), layer_width),
113 std::min(default_tile_size.height(), layer_height));
114
115 // Tile if both dimensions large, or any one dimension large and the other
116 // extends into a second tile but the total layer area isn't larger than that
117 // of the largest possible untiled layer. This heuristic allows for long
118 // skinny layers (e.g. scrollbars) that are Nx1 tiles to minimize wasted
119 // texture space but still avoids creating very large tiles.
120 bool any_dimension_large = layer_width > max_untiled_layer_size.width() ||
121 layer_height > max_untiled_layer_size.height();
122 bool any_dimension_one_tile =
123 (layer_width <= default_tile_size.width() ||
124 layer_height <= default_tile_size.height()) &&
125 (layer_width * layer_height) <= (max_untiled_layer_size.width() *
126 max_untiled_layer_size.height());
127 bool auto_tiled = any_dimension_large && !any_dimension_one_tile;
128
129 bool is_tiled;
130 if (tiling_option_ == ALWAYS_TILE)
131 is_tiled = true;
132 else if (tiling_option_ == NEVER_TILE)
133 is_tiled = false;
134 else
135 is_tiled = auto_tiled;
136
137 gfx::Size requested_size = is_tiled ? tile_size : content_bounds();
138 const int max_size =
139 layer_tree_host()->GetRendererCapabilities().max_texture_size;
140 requested_size.SetToMin(gfx::Size(max_size, max_size));
141 SetTileSize(requested_size);
142 }
143
UpdateBounds()144 void TiledLayer::UpdateBounds() {
145 gfx::Size old_bounds = tiler_->bounds();
146 gfx::Size new_bounds = content_bounds();
147 if (old_bounds == new_bounds)
148 return;
149 tiler_->SetBounds(new_bounds);
150
151 // Invalidate any areas that the new bounds exposes.
152 Region old_region = gfx::Rect(old_bounds);
153 Region new_region = gfx::Rect(new_bounds);
154 new_region.Subtract(old_region);
155 for (Region::Iterator new_rects(new_region);
156 new_rects.has_rect();
157 new_rects.next())
158 InvalidateContentRect(new_rects.rect());
159 }
160
SetTileSize(gfx::Size size)161 void TiledLayer::SetTileSize(gfx::Size size) { tiler_->SetTileSize(size); }
162
SetBorderTexelOption(LayerTilingData::BorderTexelOption border_texel_option)163 void TiledLayer::SetBorderTexelOption(
164 LayerTilingData::BorderTexelOption border_texel_option) {
165 tiler_->SetBorderTexelOption(border_texel_option);
166 }
167
DrawsContent() const168 bool TiledLayer::DrawsContent() const {
169 if (!ContentsScalingLayer::DrawsContent())
170 return false;
171
172 bool has_more_than_one_tile =
173 tiler_->num_tiles_x() > 1 || tiler_->num_tiles_y() > 1;
174 if (tiling_option_ == NEVER_TILE && has_more_than_one_tile)
175 return false;
176
177 return true;
178 }
179
ReduceMemoryUsage()180 void TiledLayer::ReduceMemoryUsage() {
181 if (Updater())
182 Updater()->ReduceMemoryUsage();
183 }
184
SetIsMask(bool is_mask)185 void TiledLayer::SetIsMask(bool is_mask) {
186 set_tiling_option(is_mask ? NEVER_TILE : AUTO_TILE);
187 }
188
PushPropertiesTo(LayerImpl * layer)189 void TiledLayer::PushPropertiesTo(LayerImpl* layer) {
190 ContentsScalingLayer::PushPropertiesTo(layer);
191
192 TiledLayerImpl* tiled_layer = static_cast<TiledLayerImpl*>(layer);
193
194 tiled_layer->set_skips_draw(skips_draw_);
195 tiled_layer->SetTilingData(*tiler_);
196 std::vector<UpdatableTile*> invalid_tiles;
197
198 for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
199 iter != tiler_->tiles().end();
200 ++iter) {
201 int i = iter->first.first;
202 int j = iter->first.second;
203 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
204 // TODO(enne): This should not ever be null.
205 if (!tile)
206 continue;
207
208 if (!tile->managed_resource()->have_backing_texture()) {
209 // Evicted tiles get deleted from both layers
210 invalid_tiles.push_back(tile);
211 continue;
212 }
213
214 if (!tile->valid_for_frame) {
215 // Invalidated tiles are set so they can get different debug colors.
216 tiled_layer->PushInvalidTile(i, j);
217 continue;
218 }
219
220 tiled_layer->PushTileProperties(
221 i,
222 j,
223 tile->managed_resource()->resource_id(),
224 tile->opaque_rect(),
225 tile->managed_resource()->contents_swizzled());
226 }
227 for (std::vector<UpdatableTile*>::const_iterator iter = invalid_tiles.begin();
228 iter != invalid_tiles.end();
229 ++iter)
230 tiler_->TakeTile((*iter)->i(), (*iter)->j());
231
232 // TiledLayer must push properties every frame, since viewport state and
233 // occlusion from anywhere in the tree can change what the layer decides to
234 // push to the impl tree.
235 needs_push_properties_ = true;
236 }
237
ResourceManager()238 PrioritizedResourceManager* TiledLayer::ResourceManager() {
239 if (!layer_tree_host())
240 return NULL;
241 return layer_tree_host()->contents_texture_manager();
242 }
243
ResourceAtForTesting(int i,int j) const244 const PrioritizedResource* TiledLayer::ResourceAtForTesting(int i,
245 int j) const {
246 UpdatableTile* tile = TileAt(i, j);
247 if (!tile)
248 return NULL;
249 return tile->managed_resource();
250 }
251
SetLayerTreeHost(LayerTreeHost * host)252 void TiledLayer::SetLayerTreeHost(LayerTreeHost* host) {
253 if (host && host != layer_tree_host()) {
254 for (LayerTilingData::TileMap::const_iterator
255 iter = tiler_->tiles().begin();
256 iter != tiler_->tiles().end();
257 ++iter) {
258 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
259 // TODO(enne): This should not ever be null.
260 if (!tile)
261 continue;
262 tile->managed_resource()->SetTextureManager(
263 host->contents_texture_manager());
264 }
265 }
266 ContentsScalingLayer::SetLayerTreeHost(host);
267 }
268
TileAt(int i,int j) const269 UpdatableTile* TiledLayer::TileAt(int i, int j) const {
270 return static_cast<UpdatableTile*>(tiler_->TileAt(i, j));
271 }
272
CreateTile(int i,int j)273 UpdatableTile* TiledLayer::CreateTile(int i, int j) {
274 CreateUpdaterIfNeeded();
275
276 scoped_ptr<UpdatableTile> tile(
277 UpdatableTile::Create(Updater()->CreateResource(ResourceManager())));
278 tile->managed_resource()->SetDimensions(tiler_->tile_size(), texture_format_);
279
280 UpdatableTile* added_tile = tile.get();
281 tiler_->AddTile(tile.PassAs<LayerTilingData::Tile>(), i, j);
282
283 added_tile->dirty_rect = tiler_->TileRect(added_tile);
284
285 // Temporary diagnostic crash.
286 CHECK(added_tile);
287 CHECK(TileAt(i, j));
288
289 return added_tile;
290 }
291
SetNeedsDisplayRect(const gfx::RectF & dirty_rect)292 void TiledLayer::SetNeedsDisplayRect(const gfx::RectF& dirty_rect) {
293 InvalidateContentRect(LayerRectToContentRect(dirty_rect));
294 ContentsScalingLayer::SetNeedsDisplayRect(dirty_rect);
295 }
296
InvalidateContentRect(gfx::Rect content_rect)297 void TiledLayer::InvalidateContentRect(gfx::Rect content_rect) {
298 UpdateBounds();
299 if (tiler_->is_empty() || content_rect.IsEmpty() || skips_draw_)
300 return;
301
302 for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
303 iter != tiler_->tiles().end();
304 ++iter) {
305 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
306 DCHECK(tile);
307 // TODO(enne): This should not ever be null.
308 if (!tile)
309 continue;
310 gfx::Rect bound = tiler_->TileRect(tile);
311 bound.Intersect(content_rect);
312 tile->dirty_rect.Union(bound);
313 }
314 }
315
316 // Returns true if tile is dirty and only part of it needs to be updated.
TileOnlyNeedsPartialUpdate(UpdatableTile * tile)317 bool TiledLayer::TileOnlyNeedsPartialUpdate(UpdatableTile* tile) {
318 return !tile->dirty_rect.Contains(tiler_->TileRect(tile)) &&
319 tile->managed_resource()->have_backing_texture();
320 }
321
UpdateTiles(int left,int top,int right,int bottom,ResourceUpdateQueue * queue,const OcclusionTracker * occlusion,bool * updated)322 bool TiledLayer::UpdateTiles(int left,
323 int top,
324 int right,
325 int bottom,
326 ResourceUpdateQueue* queue,
327 const OcclusionTracker* occlusion,
328 bool* updated) {
329 CreateUpdaterIfNeeded();
330
331 bool ignore_occlusions = !occlusion;
332 if (!HaveTexturesForTiles(left, top, right, bottom, ignore_occlusions)) {
333 failed_update_ = true;
334 return false;
335 }
336
337 gfx::Rect update_rect;
338 gfx::Rect paint_rect;
339 MarkTilesForUpdate(
340 &update_rect, &paint_rect, left, top, right, bottom, ignore_occlusions);
341
342 if (occlusion)
343 occlusion->overdraw_metrics()->DidPaint(paint_rect);
344
345 if (paint_rect.IsEmpty())
346 return true;
347
348 *updated = true;
349 UpdateTileTextures(
350 update_rect, paint_rect, left, top, right, bottom, queue, occlusion);
351 return true;
352 }
353
MarkOcclusionsAndRequestTextures(int left,int top,int right,int bottom,const OcclusionTracker * occlusion)354 void TiledLayer::MarkOcclusionsAndRequestTextures(
355 int left,
356 int top,
357 int right,
358 int bottom,
359 const OcclusionTracker* occlusion) {
360 // There is some difficult dependancies between occlusions, recording
361 // occlusion metrics and requesting memory so those are encapsulated in this
362 // function: - We only want to call RequestLate on unoccluded textures (to
363 // preserve memory for other layers when near OOM). - We only want to record
364 // occlusion metrics if all memory requests succeed.
365
366 int occluded_tile_count = 0;
367 bool succeeded = true;
368 for (int j = top; j <= bottom; ++j) {
369 for (int i = left; i <= right; ++i) {
370 UpdatableTile* tile = TileAt(i, j);
371 DCHECK(tile); // Did SetTexturePriorities get skipped?
372 // TODO(enne): This should not ever be null.
373 if (!tile)
374 continue;
375 // Did ResetUpdateState get skipped? Are we doing more than one occlusion
376 // pass?
377 DCHECK(!tile->occluded);
378 gfx::Rect visible_tile_rect = gfx::IntersectRects(
379 tiler_->tile_bounds(i, j), visible_content_rect());
380 if (occlusion && occlusion->Occluded(render_target(),
381 visible_tile_rect,
382 draw_transform(),
383 draw_transform_is_animating())) {
384 tile->occluded = true;
385 occluded_tile_count++;
386 } else {
387 succeeded &= tile->managed_resource()->RequestLate();
388 }
389 }
390 }
391
392 if (!succeeded)
393 return;
394 if (occlusion)
395 occlusion->overdraw_metrics()->DidCullTilesForUpload(occluded_tile_count);
396 }
397
HaveTexturesForTiles(int left,int top,int right,int bottom,bool ignore_occlusions)398 bool TiledLayer::HaveTexturesForTiles(int left,
399 int top,
400 int right,
401 int bottom,
402 bool ignore_occlusions) {
403 for (int j = top; j <= bottom; ++j) {
404 for (int i = left; i <= right; ++i) {
405 UpdatableTile* tile = TileAt(i, j);
406 DCHECK(tile); // Did SetTexturePriorites get skipped?
407 // TODO(enne): This should not ever be null.
408 if (!tile)
409 continue;
410
411 // Ensure the entire tile is dirty if we don't have the texture.
412 if (!tile->managed_resource()->have_backing_texture())
413 tile->dirty_rect = tiler_->TileRect(tile);
414
415 // If using occlusion and the visible region of the tile is occluded,
416 // don't reserve a texture or update the tile.
417 if (tile->occluded && !ignore_occlusions)
418 continue;
419
420 if (!tile->managed_resource()->can_acquire_backing_texture())
421 return false;
422 }
423 }
424 return true;
425 }
426
MarkTilesForUpdate(gfx::Rect * update_rect,gfx::Rect * paint_rect,int left,int top,int right,int bottom,bool ignore_occlusions)427 void TiledLayer::MarkTilesForUpdate(gfx::Rect* update_rect,
428 gfx::Rect* paint_rect,
429 int left,
430 int top,
431 int right,
432 int bottom,
433 bool ignore_occlusions) {
434 for (int j = top; j <= bottom; ++j) {
435 for (int i = left; i <= right; ++i) {
436 UpdatableTile* tile = TileAt(i, j);
437 DCHECK(tile); // Did SetTexturePriorites get skipped?
438 // TODO(enne): This should not ever be null.
439 if (!tile)
440 continue;
441 if (tile->occluded && !ignore_occlusions)
442 continue;
443
444 // Prepare update rect from original dirty rects.
445 update_rect->Union(tile->dirty_rect);
446
447 // TODO(reveman): Decide if partial update should be allowed based on cost
448 // of update. https://bugs.webkit.org/show_bug.cgi?id=77376
449 if (tile->is_dirty() &&
450 !layer_tree_host()->AlwaysUsePartialTextureUpdates()) {
451 // If we get a partial update, we use the same texture, otherwise return
452 // the current texture backing, so we don't update visible textures
453 // non-atomically. If the current backing is in-use, it won't be
454 // deleted until after the commit as the texture manager will not allow
455 // deletion or recycling of in-use textures.
456 if (TileOnlyNeedsPartialUpdate(tile) &&
457 layer_tree_host()->RequestPartialTextureUpdate()) {
458 tile->partial_update = true;
459 } else {
460 tile->dirty_rect = tiler_->TileRect(tile);
461 tile->managed_resource()->ReturnBackingTexture();
462 }
463 }
464
465 paint_rect->Union(tile->dirty_rect);
466 tile->MarkForUpdate();
467 }
468 }
469 }
470
UpdateTileTextures(gfx::Rect update_rect,gfx::Rect paint_rect,int left,int top,int right,int bottom,ResourceUpdateQueue * queue,const OcclusionTracker * occlusion)471 void TiledLayer::UpdateTileTextures(gfx::Rect update_rect,
472 gfx::Rect paint_rect,
473 int left,
474 int top,
475 int right,
476 int bottom,
477 ResourceUpdateQueue* queue,
478 const OcclusionTracker* occlusion) {
479 // The update_rect should be in layer space. So we have to convert the
480 // paint_rect from content space to layer space.
481 float width_scale =
482 paint_properties().bounds.width() /
483 static_cast<float>(content_bounds().width());
484 float height_scale =
485 paint_properties().bounds.height() /
486 static_cast<float>(content_bounds().height());
487 update_rect_ = gfx::ScaleRect(update_rect, width_scale, height_scale);
488
489 // Calling PrepareToUpdate() calls into WebKit to paint, which may have the
490 // side effect of disabling compositing, which causes our reference to the
491 // texture updater to be deleted. However, we can't free the memory backing
492 // the SkCanvas until the paint finishes, so we grab a local reference here to
493 // hold the updater alive until the paint completes.
494 scoped_refptr<LayerUpdater> protector(Updater());
495 gfx::Rect painted_opaque_rect;
496 Updater()->PrepareToUpdate(paint_rect,
497 tiler_->tile_size(),
498 1.f / width_scale,
499 1.f / height_scale,
500 &painted_opaque_rect);
501
502 for (int j = top; j <= bottom; ++j) {
503 for (int i = left; i <= right; ++i) {
504 UpdatableTile* tile = TileAt(i, j);
505 DCHECK(tile); // Did SetTexturePriorites get skipped?
506 // TODO(enne): This should not ever be null.
507 if (!tile)
508 continue;
509
510 gfx::Rect tile_rect = tiler_->tile_bounds(i, j);
511
512 // Use update_rect as the above loop copied the dirty rect for this frame
513 // to update_rect.
514 gfx::Rect dirty_rect = tile->update_rect;
515 if (dirty_rect.IsEmpty())
516 continue;
517
518 // Save what was painted opaque in the tile. Keep the old area if the
519 // paint didn't touch it, and didn't paint some other part of the tile
520 // opaque.
521 gfx::Rect tile_painted_rect = gfx::IntersectRects(tile_rect, paint_rect);
522 gfx::Rect tile_painted_opaque_rect =
523 gfx::IntersectRects(tile_rect, painted_opaque_rect);
524 if (!tile_painted_rect.IsEmpty()) {
525 gfx::Rect paint_inside_tile_opaque_rect =
526 gfx::IntersectRects(tile->opaque_rect(), tile_painted_rect);
527 bool paint_inside_tile_opaque_rect_is_non_opaque =
528 !paint_inside_tile_opaque_rect.IsEmpty() &&
529 !tile_painted_opaque_rect.Contains(paint_inside_tile_opaque_rect);
530 bool opaque_paint_not_inside_tile_opaque_rect =
531 !tile_painted_opaque_rect.IsEmpty() &&
532 !tile->opaque_rect().Contains(tile_painted_opaque_rect);
533
534 if (paint_inside_tile_opaque_rect_is_non_opaque ||
535 opaque_paint_not_inside_tile_opaque_rect)
536 tile->set_opaque_rect(tile_painted_opaque_rect);
537 }
538
539 // source_rect starts as a full-sized tile with border texels included.
540 gfx::Rect source_rect = tiler_->TileRect(tile);
541 source_rect.Intersect(dirty_rect);
542 // Paint rect not guaranteed to line up on tile boundaries, so
543 // make sure that source_rect doesn't extend outside of it.
544 source_rect.Intersect(paint_rect);
545
546 tile->update_rect = source_rect;
547
548 if (source_rect.IsEmpty())
549 continue;
550
551 const gfx::Point anchor = tiler_->TileRect(tile).origin();
552
553 // Calculate tile-space rectangle to upload into.
554 gfx::Vector2d dest_offset = source_rect.origin() - anchor;
555 CHECK_GE(dest_offset.x(), 0);
556 CHECK_GE(dest_offset.y(), 0);
557
558 // Offset from paint rectangle to this tile's dirty rectangle.
559 gfx::Vector2d paint_offset = source_rect.origin() - paint_rect.origin();
560 CHECK_GE(paint_offset.x(), 0);
561 CHECK_GE(paint_offset.y(), 0);
562 CHECK_LE(paint_offset.x() + source_rect.width(), paint_rect.width());
563 CHECK_LE(paint_offset.y() + source_rect.height(), paint_rect.height());
564
565 tile->updater_resource()->Update(
566 queue, source_rect, dest_offset, tile->partial_update);
567 if (occlusion) {
568 occlusion->overdraw_metrics()->
569 DidUpload(gfx::Transform(), source_rect, tile->opaque_rect());
570 }
571 }
572 }
573 }
574
575 // This picks a small animated layer to be anything less than one viewport. This
576 // is specifically for page transitions which are viewport-sized layers. The
577 // extra tile of padding is due to these layers being slightly larger than the
578 // viewport in some cases.
IsSmallAnimatedLayer() const579 bool TiledLayer::IsSmallAnimatedLayer() const {
580 if (!draw_transform_is_animating() && !screen_space_transform_is_animating())
581 return false;
582 gfx::Size viewport_size =
583 layer_tree_host() ? layer_tree_host()->device_viewport_size()
584 : gfx::Size();
585 gfx::Rect content_rect(content_bounds());
586 return content_rect.width() <=
587 viewport_size.width() + tiler_->tile_size().width() &&
588 content_rect.height() <=
589 viewport_size.height() + tiler_->tile_size().height();
590 }
591
592 namespace {
593 // TODO(epenner): Remove this and make this based on distance once distance can
594 // be calculated for offscreen layers. For now, prioritize all small animated
595 // layers after 512 pixels of pre-painting.
SetPriorityForTexture(gfx::Rect visible_rect,gfx::Rect tile_rect,bool draws_to_root,bool is_small_animated_layer,PrioritizedResource * texture)596 void SetPriorityForTexture(gfx::Rect visible_rect,
597 gfx::Rect tile_rect,
598 bool draws_to_root,
599 bool is_small_animated_layer,
600 PrioritizedResource* texture) {
601 int priority = PriorityCalculator::LowestPriority();
602 if (!visible_rect.IsEmpty()) {
603 priority = PriorityCalculator::PriorityFromDistance(
604 visible_rect, tile_rect, draws_to_root);
605 }
606
607 if (is_small_animated_layer) {
608 priority = PriorityCalculator::max_priority(
609 priority, PriorityCalculator::SmallAnimatedLayerMinPriority());
610 }
611
612 if (priority != PriorityCalculator::LowestPriority())
613 texture->set_request_priority(priority);
614 }
615 } // namespace
616
SetTexturePriorities(const PriorityCalculator & priority_calc)617 void TiledLayer::SetTexturePriorities(const PriorityCalculator& priority_calc) {
618 UpdateBounds();
619 ResetUpdateState();
620 UpdateScrollPrediction();
621
622 if (tiler_->has_empty_bounds())
623 return;
624
625 bool draws_to_root = !render_target()->parent();
626 bool small_animated_layer = IsSmallAnimatedLayer();
627
628 // Minimally create the tiles in the desired pre-paint rect.
629 gfx::Rect create_tiles_rect = IdlePaintRect();
630 if (small_animated_layer)
631 create_tiles_rect = gfx::Rect(content_bounds());
632 if (!create_tiles_rect.IsEmpty()) {
633 int left, top, right, bottom;
634 tiler_->ContentRectToTileIndices(
635 create_tiles_rect, &left, &top, &right, &bottom);
636 for (int j = top; j <= bottom; ++j) {
637 for (int i = left; i <= right; ++i) {
638 if (!TileAt(i, j))
639 CreateTile(i, j);
640 }
641 }
642 }
643
644 // Now update priorities on all tiles we have in the layer, no matter where
645 // they are.
646 for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
647 iter != tiler_->tiles().end();
648 ++iter) {
649 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
650 // TODO(enne): This should not ever be null.
651 if (!tile)
652 continue;
653 gfx::Rect tile_rect = tiler_->TileRect(tile);
654 SetPriorityForTexture(predicted_visible_rect_,
655 tile_rect,
656 draws_to_root,
657 small_animated_layer,
658 tile->managed_resource());
659 }
660 }
661
VisibleContentOpaqueRegion() const662 Region TiledLayer::VisibleContentOpaqueRegion() const {
663 if (skips_draw_)
664 return Region();
665 if (contents_opaque())
666 return visible_content_rect();
667 return tiler_->OpaqueRegionInContentRect(visible_content_rect());
668 }
669
ResetUpdateState()670 void TiledLayer::ResetUpdateState() {
671 skips_draw_ = false;
672 failed_update_ = false;
673
674 LayerTilingData::TileMap::const_iterator end = tiler_->tiles().end();
675 for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
676 iter != end;
677 ++iter) {
678 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
679 // TODO(enne): This should not ever be null.
680 if (!tile)
681 continue;
682 tile->ResetUpdateState();
683 }
684 }
685
686 namespace {
ExpandRectByDelta(gfx::Rect rect,gfx::Vector2d delta)687 gfx::Rect ExpandRectByDelta(gfx::Rect rect, gfx::Vector2d delta) {
688 int width = rect.width() + std::abs(delta.x());
689 int height = rect.height() + std::abs(delta.y());
690 int x = rect.x() + ((delta.x() < 0) ? delta.x() : 0);
691 int y = rect.y() + ((delta.y() < 0) ? delta.y() : 0);
692 return gfx::Rect(x, y, width, height);
693 }
694 }
695
UpdateScrollPrediction()696 void TiledLayer::UpdateScrollPrediction() {
697 // This scroll prediction is very primitive and should be replaced by a
698 // a recursive calculation on all layers which uses actual scroll/animation
699 // velocities. To insure this doesn't miss-predict, we only use it to predict
700 // the visible_rect if:
701 // - content_bounds() hasn't changed.
702 // - visible_rect.size() hasn't changed.
703 // These two conditions prevent rotations, scales, pinch-zooms etc. where
704 // the prediction would be incorrect.
705 gfx::Vector2d delta = visible_content_rect().CenterPoint() -
706 previous_visible_rect_.CenterPoint();
707 predicted_scroll_ = -delta;
708 predicted_visible_rect_ = visible_content_rect();
709 if (previous_content_bounds_ == content_bounds() &&
710 previous_visible_rect_.size() == visible_content_rect().size()) {
711 // Only expand the visible rect in the major scroll direction, to prevent
712 // massive paints due to diagonal scrolls.
713 gfx::Vector2d major_scroll_delta =
714 (std::abs(delta.x()) > std::abs(delta.y())) ?
715 gfx::Vector2d(delta.x(), 0) :
716 gfx::Vector2d(0, delta.y());
717 predicted_visible_rect_ =
718 ExpandRectByDelta(visible_content_rect(), major_scroll_delta);
719
720 // Bound the prediction to prevent unbounded paints, and clamp to content
721 // bounds.
722 gfx::Rect bound = visible_content_rect();
723 bound.Inset(-tiler_->tile_size().width() * kMaxPredictiveTilesCount,
724 -tiler_->tile_size().height() * kMaxPredictiveTilesCount);
725 bound.Intersect(gfx::Rect(content_bounds()));
726 predicted_visible_rect_.Intersect(bound);
727 }
728 previous_content_bounds_ = content_bounds();
729 previous_visible_rect_ = visible_content_rect();
730 }
731
Update(ResourceUpdateQueue * queue,const OcclusionTracker * occlusion)732 bool TiledLayer::Update(ResourceUpdateQueue* queue,
733 const OcclusionTracker* occlusion) {
734 DCHECK(!skips_draw_ && !failed_update_); // Did ResetUpdateState get skipped?
735
736 // Tiled layer always causes commits to wait for activation, as it does
737 // not support pending trees.
738 SetNextCommitWaitsForActivation();
739
740 bool updated = false;
741
742 {
743 base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_,
744 true);
745
746 updated |= ContentsScalingLayer::Update(queue, occlusion);
747 UpdateBounds();
748 }
749
750 if (tiler_->has_empty_bounds() || !DrawsContent())
751 return false;
752
753 // Animation pre-paint. If the layer is small, try to paint it all
754 // immediately whether or not it is occluded, to avoid paint/upload
755 // hiccups while it is animating.
756 if (IsSmallAnimatedLayer()) {
757 int left, top, right, bottom;
758 tiler_->ContentRectToTileIndices(gfx::Rect(content_bounds()),
759 &left,
760 &top,
761 &right,
762 &bottom);
763 UpdateTiles(left, top, right, bottom, queue, NULL, &updated);
764 if (updated)
765 return updated;
766 // This was an attempt to paint the entire layer so if we fail it's okay,
767 // just fallback on painting visible etc. below.
768 failed_update_ = false;
769 }
770
771 if (predicted_visible_rect_.IsEmpty())
772 return updated;
773
774 // Visible painting. First occlude visible tiles and paint the non-occluded
775 // tiles.
776 int left, top, right, bottom;
777 tiler_->ContentRectToTileIndices(
778 predicted_visible_rect_, &left, &top, &right, &bottom);
779 MarkOcclusionsAndRequestTextures(left, top, right, bottom, occlusion);
780 skips_draw_ = !UpdateTiles(
781 left, top, right, bottom, queue, occlusion, &updated);
782 if (skips_draw_)
783 tiler_->reset();
784 if (skips_draw_ || updated)
785 return true;
786
787 // If we have already painting everything visible. Do some pre-painting while
788 // idle.
789 gfx::Rect idle_paint_content_rect = IdlePaintRect();
790 if (idle_paint_content_rect.IsEmpty())
791 return updated;
792
793 // Prepaint anything that was occluded but inside the layer's visible region.
794 if (!UpdateTiles(left, top, right, bottom, queue, NULL, &updated) ||
795 updated)
796 return updated;
797
798 int prepaint_left, prepaint_top, prepaint_right, prepaint_bottom;
799 tiler_->ContentRectToTileIndices(idle_paint_content_rect,
800 &prepaint_left,
801 &prepaint_top,
802 &prepaint_right,
803 &prepaint_bottom);
804
805 // Then expand outwards one row/column at a time until we find a dirty
806 // row/column to update. Increment along the major and minor scroll directions
807 // first.
808 gfx::Vector2d delta = -predicted_scroll_;
809 delta = gfx::Vector2d(delta.x() == 0 ? 1 : delta.x(),
810 delta.y() == 0 ? 1 : delta.y());
811 gfx::Vector2d major_delta =
812 (std::abs(delta.x()) > std::abs(delta.y())) ? gfx::Vector2d(delta.x(), 0)
813 : gfx::Vector2d(0, delta.y());
814 gfx::Vector2d minor_delta =
815 (std::abs(delta.x()) <= std::abs(delta.y())) ? gfx::Vector2d(delta.x(), 0)
816 : gfx::Vector2d(0, delta.y());
817 gfx::Vector2d deltas[4] = { major_delta, minor_delta, -major_delta,
818 -minor_delta };
819 for (int i = 0; i < 4; i++) {
820 if (deltas[i].y() > 0) {
821 while (bottom < prepaint_bottom) {
822 ++bottom;
823 if (!UpdateTiles(
824 left, bottom, right, bottom, queue, NULL, &updated) ||
825 updated)
826 return updated;
827 }
828 }
829 if (deltas[i].y() < 0) {
830 while (top > prepaint_top) {
831 --top;
832 if (!UpdateTiles(
833 left, top, right, top, queue, NULL, &updated) ||
834 updated)
835 return updated;
836 }
837 }
838 if (deltas[i].x() < 0) {
839 while (left > prepaint_left) {
840 --left;
841 if (!UpdateTiles(
842 left, top, left, bottom, queue, NULL, &updated) ||
843 updated)
844 return updated;
845 }
846 }
847 if (deltas[i].x() > 0) {
848 while (right < prepaint_right) {
849 ++right;
850 if (!UpdateTiles(
851 right, top, right, bottom, queue, NULL, &updated) ||
852 updated)
853 return updated;
854 }
855 }
856 }
857 return updated;
858 }
859
OnOutputSurfaceCreated()860 void TiledLayer::OnOutputSurfaceCreated() {
861 // Ensure that all textures are of the right format.
862 for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
863 iter != tiler_->tiles().end();
864 ++iter) {
865 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
866 if (!tile)
867 continue;
868 PrioritizedResource* resource = tile->managed_resource();
869 resource->SetDimensions(resource->size(), texture_format_);
870 }
871 }
872
NeedsIdlePaint()873 bool TiledLayer::NeedsIdlePaint() {
874 // Don't trigger more paints if we failed (as we'll just fail again).
875 if (failed_update_ || visible_content_rect().IsEmpty() ||
876 tiler_->has_empty_bounds() || !DrawsContent())
877 return false;
878
879 gfx::Rect idle_paint_content_rect = IdlePaintRect();
880 if (idle_paint_content_rect.IsEmpty())
881 return false;
882
883 int left, top, right, bottom;
884 tiler_->ContentRectToTileIndices(
885 idle_paint_content_rect, &left, &top, &right, &bottom);
886
887 for (int j = top; j <= bottom; ++j) {
888 for (int i = left; i <= right; ++i) {
889 UpdatableTile* tile = TileAt(i, j);
890 DCHECK(tile); // Did SetTexturePriorities get skipped?
891 if (!tile)
892 continue;
893
894 bool updated = !tile->update_rect.IsEmpty();
895 bool can_acquire =
896 tile->managed_resource()->can_acquire_backing_texture();
897 bool dirty =
898 tile->is_dirty() || !tile->managed_resource()->have_backing_texture();
899 if (!updated && can_acquire && dirty)
900 return true;
901 }
902 }
903 return false;
904 }
905
IdlePaintRect()906 gfx::Rect TiledLayer::IdlePaintRect() {
907 // Don't inflate an empty rect.
908 if (visible_content_rect().IsEmpty())
909 return gfx::Rect();
910
911 gfx::Rect prepaint_rect = visible_content_rect();
912 prepaint_rect.Inset(-tiler_->tile_size().width() * kPrepaintColumns,
913 -tiler_->tile_size().height() * kPrepaintRows);
914 gfx::Rect content_rect(content_bounds());
915 prepaint_rect.Intersect(content_rect);
916
917 return prepaint_rect;
918 }
919
920 } // namespace cc
921