Connect PPAPI IPC channels for non-SFI mode.
[chromium-blink-merge.git] / cc / layers / tiled_layer.cc
blobd4ec763faea48164e8e7e682f90e4fe4c989297c
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.
5 #include "cc/layers/tiled_layer.h"
7 #include <algorithm>
8 #include <vector>
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"
23 namespace cc {
25 // Maximum predictive expansion of the visible area.
26 static const int kMaxPredictiveTilesCount = 2;
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;
34 class UpdatableTile : public LayerTilingData::Tile {
35 public:
36 static scoped_ptr<UpdatableTile> Create(
37 scoped_ptr<LayerUpdater::Resource> updater_resource) {
38 return make_scoped_ptr(new UpdatableTile(updater_resource.Pass()));
41 LayerUpdater::Resource* updater_resource() { return updater_resource_.get(); }
42 PrioritizedResource* managed_resource() {
43 return updater_resource_->texture();
46 bool is_dirty() const { return !dirty_rect.IsEmpty(); }
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.
53 void ResetUpdateState() {
54 update_rect = gfx::Rect();
55 occluded = false;
56 partial_update = false;
57 valid_for_frame = !is_dirty();
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.
63 void MarkForUpdate() {
64 valid_for_frame = true;
65 update_rect = dirty_rect;
66 dirty_rect = gfx::Rect();
69 gfx::Rect dirty_rect;
70 gfx::Rect update_rect;
71 bool partial_update;
72 bool valid_for_frame;
73 bool occluded;
75 private:
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()) {}
82 scoped_ptr<LayerUpdater::Resource> updater_resource_;
84 DISALLOW_COPY_AND_ASSIGN(UpdatableTile);
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);
97 TiledLayer::~TiledLayer() {}
99 scoped_ptr<LayerImpl> TiledLayer::CreateLayerImpl(LayerTreeImpl* tree_impl) {
100 return TiledLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
103 void TiledLayer::UpdateTileSizeAndTilingOption() {
104 DCHECK(layer_tree_host());
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();
112 gfx::Size tile_size(std::min(default_tile_size.width(), layer_width),
113 std::min(default_tile_size.height(), layer_height));
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;
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;
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);
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);
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());
161 void TiledLayer::SetTileSize(const gfx::Size& size) {
162 tiler_->SetTileSize(size);
165 void TiledLayer::SetBorderTexelOption(
166 LayerTilingData::BorderTexelOption border_texel_option) {
167 tiler_->SetBorderTexelOption(border_texel_option);
170 bool TiledLayer::DrawsContent() const {
171 if (!ContentsScalingLayer::DrawsContent())
172 return false;
174 bool has_more_than_one_tile =
175 tiler_->num_tiles_x() > 1 || tiler_->num_tiles_y() > 1;
176 if (tiling_option_ == NEVER_TILE && has_more_than_one_tile)
177 return false;
179 return true;
182 void TiledLayer::ReduceMemoryUsage() {
183 if (Updater())
184 Updater()->ReduceMemoryUsage();
187 void TiledLayer::SetIsMask(bool is_mask) {
188 set_tiling_option(is_mask ? NEVER_TILE : AUTO_TILE);
191 void TiledLayer::PushPropertiesTo(LayerImpl* layer) {
192 ContentsScalingLayer::PushPropertiesTo(layer);
194 TiledLayerImpl* tiled_layer = static_cast<TiledLayerImpl*>(layer);
196 tiled_layer->set_skips_draw(skips_draw_);
197 tiled_layer->SetTilingData(*tiler_);
198 std::vector<UpdatableTile*> invalid_tiles;
200 for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
201 iter != tiler_->tiles().end();
202 ++iter) {
203 int i = iter->first.first;
204 int j = iter->first.second;
205 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
206 // TODO(enne): This should not ever be null.
207 if (!tile)
208 continue;
210 if (!tile->managed_resource()->have_backing_texture()) {
211 // Evicted tiles get deleted from both layers
212 invalid_tiles.push_back(tile);
213 continue;
216 if (!tile->valid_for_frame) {
217 // Invalidated tiles are set so they can get different debug colors.
218 tiled_layer->PushInvalidTile(i, j);
219 continue;
222 tiled_layer->PushTileProperties(
225 tile->managed_resource()->resource_id(),
226 tile->opaque_rect(),
227 tile->managed_resource()->contents_swizzled());
229 for (std::vector<UpdatableTile*>::const_iterator iter = invalid_tiles.begin();
230 iter != invalid_tiles.end();
231 ++iter)
232 tiler_->TakeTile((*iter)->i(), (*iter)->j());
234 // TiledLayer must push properties every frame, since viewport state and
235 // occlusion from anywhere in the tree can change what the layer decides to
236 // push to the impl tree.
237 needs_push_properties_ = true;
240 PrioritizedResourceManager* TiledLayer::ResourceManager() {
241 if (!layer_tree_host())
242 return NULL;
243 return layer_tree_host()->contents_texture_manager();
246 const PrioritizedResource* TiledLayer::ResourceAtForTesting(int i,
247 int j) const {
248 UpdatableTile* tile = TileAt(i, j);
249 if (!tile)
250 return NULL;
251 return tile->managed_resource();
254 void TiledLayer::SetLayerTreeHost(LayerTreeHost* host) {
255 if (host && host != layer_tree_host()) {
256 for (LayerTilingData::TileMap::const_iterator
257 iter = tiler_->tiles().begin();
258 iter != tiler_->tiles().end();
259 ++iter) {
260 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
261 // TODO(enne): This should not ever be null.
262 if (!tile)
263 continue;
264 tile->managed_resource()->SetTextureManager(
265 host->contents_texture_manager());
268 ContentsScalingLayer::SetLayerTreeHost(host);
271 UpdatableTile* TiledLayer::TileAt(int i, int j) const {
272 return static_cast<UpdatableTile*>(tiler_->TileAt(i, j));
275 UpdatableTile* TiledLayer::CreateTile(int i, int j) {
276 CreateUpdaterIfNeeded();
278 scoped_ptr<UpdatableTile> tile(
279 UpdatableTile::Create(Updater()->CreateResource(ResourceManager())));
280 tile->managed_resource()->SetDimensions(tiler_->tile_size(), texture_format_);
282 UpdatableTile* added_tile = tile.get();
283 tiler_->AddTile(tile.PassAs<LayerTilingData::Tile>(), i, j);
285 added_tile->dirty_rect = tiler_->TileRect(added_tile);
287 // Temporary diagnostic crash.
288 CHECK(added_tile);
289 CHECK(TileAt(i, j));
291 return added_tile;
294 void TiledLayer::SetNeedsDisplayRect(const gfx::RectF& dirty_rect) {
295 InvalidateContentRect(LayerRectToContentRect(dirty_rect));
296 ContentsScalingLayer::SetNeedsDisplayRect(dirty_rect);
299 void TiledLayer::InvalidateContentRect(const gfx::Rect& content_rect) {
300 UpdateBounds();
301 if (tiler_->is_empty() || content_rect.IsEmpty() || skips_draw_)
302 return;
304 for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
305 iter != tiler_->tiles().end();
306 ++iter) {
307 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
308 DCHECK(tile);
309 // TODO(enne): This should not ever be null.
310 if (!tile)
311 continue;
312 gfx::Rect bound = tiler_->TileRect(tile);
313 bound.Intersect(content_rect);
314 tile->dirty_rect.Union(bound);
318 // Returns true if tile is dirty and only part of it needs to be updated.
319 bool TiledLayer::TileOnlyNeedsPartialUpdate(UpdatableTile* tile) {
320 return !tile->dirty_rect.Contains(tiler_->TileRect(tile)) &&
321 tile->managed_resource()->have_backing_texture();
324 bool TiledLayer::UpdateTiles(int left,
325 int top,
326 int right,
327 int bottom,
328 ResourceUpdateQueue* queue,
329 const OcclusionTracker* occlusion,
330 bool* updated) {
331 CreateUpdaterIfNeeded();
333 bool ignore_occlusions = !occlusion;
334 if (!HaveTexturesForTiles(left, top, right, bottom, ignore_occlusions)) {
335 failed_update_ = true;
336 return false;
339 gfx::Rect update_rect;
340 gfx::Rect paint_rect;
341 MarkTilesForUpdate(
342 &update_rect, &paint_rect, left, top, right, bottom, ignore_occlusions);
344 if (occlusion)
345 occlusion->overdraw_metrics()->DidPaint(paint_rect);
347 if (paint_rect.IsEmpty())
348 return true;
350 *updated = true;
351 UpdateTileTextures(
352 update_rect, paint_rect, left, top, right, bottom, queue, occlusion);
353 return true;
356 void TiledLayer::MarkOcclusionsAndRequestTextures(
357 int left,
358 int top,
359 int right,
360 int bottom,
361 const OcclusionTracker* occlusion) {
362 // There is some difficult dependancies between occlusions, recording
363 // occlusion metrics and requesting memory so those are encapsulated in this
364 // function: - We only want to call RequestLate on unoccluded textures (to
365 // preserve memory for other layers when near OOM). - We only want to record
366 // occlusion metrics if all memory requests succeed.
368 int occluded_tile_count = 0;
369 bool succeeded = true;
370 for (int j = top; j <= bottom; ++j) {
371 for (int i = left; i <= right; ++i) {
372 UpdatableTile* tile = TileAt(i, j);
373 DCHECK(tile); // Did SetTexturePriorities get skipped?
374 // TODO(enne): This should not ever be null.
375 if (!tile)
376 continue;
377 // Did ResetUpdateState get skipped? Are we doing more than one occlusion
378 // pass?
379 DCHECK(!tile->occluded);
380 gfx::Rect visible_tile_rect = gfx::IntersectRects(
381 tiler_->tile_bounds(i, j), visible_content_rect());
382 if (occlusion && occlusion->Occluded(render_target(),
383 visible_tile_rect,
384 draw_transform(),
385 draw_transform_is_animating())) {
386 tile->occluded = true;
387 occluded_tile_count++;
388 } else {
389 succeeded &= tile->managed_resource()->RequestLate();
394 if (!succeeded)
395 return;
396 if (occlusion)
397 occlusion->overdraw_metrics()->DidCullTilesForUpload(occluded_tile_count);
400 bool TiledLayer::HaveTexturesForTiles(int left,
401 int top,
402 int right,
403 int bottom,
404 bool ignore_occlusions) {
405 for (int j = top; j <= bottom; ++j) {
406 for (int i = left; i <= right; ++i) {
407 UpdatableTile* tile = TileAt(i, j);
408 DCHECK(tile); // Did SetTexturePriorites get skipped?
409 // TODO(enne): This should not ever be null.
410 if (!tile)
411 continue;
413 // Ensure the entire tile is dirty if we don't have the texture.
414 if (!tile->managed_resource()->have_backing_texture())
415 tile->dirty_rect = tiler_->TileRect(tile);
417 // If using occlusion and the visible region of the tile is occluded,
418 // don't reserve a texture or update the tile.
419 if (tile->occluded && !ignore_occlusions)
420 continue;
422 if (!tile->managed_resource()->can_acquire_backing_texture())
423 return false;
426 return true;
429 void TiledLayer::MarkTilesForUpdate(gfx::Rect* update_rect,
430 gfx::Rect* paint_rect,
431 int left,
432 int top,
433 int right,
434 int bottom,
435 bool ignore_occlusions) {
436 for (int j = top; j <= bottom; ++j) {
437 for (int i = left; i <= right; ++i) {
438 UpdatableTile* tile = TileAt(i, j);
439 DCHECK(tile); // Did SetTexturePriorites get skipped?
440 // TODO(enne): This should not ever be null.
441 if (!tile)
442 continue;
443 if (tile->occluded && !ignore_occlusions)
444 continue;
446 // Prepare update rect from original dirty rects.
447 update_rect->Union(tile->dirty_rect);
449 // TODO(reveman): Decide if partial update should be allowed based on cost
450 // of update. https://bugs.webkit.org/show_bug.cgi?id=77376
451 if (tile->is_dirty() &&
452 !layer_tree_host()->AlwaysUsePartialTextureUpdates()) {
453 // If we get a partial update, we use the same texture, otherwise return
454 // the current texture backing, so we don't update visible textures
455 // non-atomically. If the current backing is in-use, it won't be
456 // deleted until after the commit as the texture manager will not allow
457 // deletion or recycling of in-use textures.
458 if (TileOnlyNeedsPartialUpdate(tile) &&
459 layer_tree_host()->RequestPartialTextureUpdate()) {
460 tile->partial_update = true;
461 } else {
462 tile->dirty_rect = tiler_->TileRect(tile);
463 tile->managed_resource()->ReturnBackingTexture();
467 paint_rect->Union(tile->dirty_rect);
468 tile->MarkForUpdate();
473 void TiledLayer::UpdateTileTextures(const gfx::Rect& update_rect,
474 const gfx::Rect& paint_rect,
475 int left,
476 int top,
477 int right,
478 int bottom,
479 ResourceUpdateQueue* queue,
480 const OcclusionTracker* occlusion) {
481 // The update_rect should be in layer space. So we have to convert the
482 // paint_rect from content space to layer space.
483 float width_scale =
484 paint_properties().bounds.width() /
485 static_cast<float>(content_bounds().width());
486 float height_scale =
487 paint_properties().bounds.height() /
488 static_cast<float>(content_bounds().height());
489 update_rect_ = gfx::ScaleRect(update_rect, width_scale, height_scale);
491 // Calling PrepareToUpdate() calls into WebKit to paint, which may have the
492 // side effect of disabling compositing, which causes our reference to the
493 // texture updater to be deleted. However, we can't free the memory backing
494 // the SkCanvas until the paint finishes, so we grab a local reference here to
495 // hold the updater alive until the paint completes.
496 scoped_refptr<LayerUpdater> protector(Updater());
497 gfx::Rect painted_opaque_rect;
498 Updater()->PrepareToUpdate(paint_rect,
499 tiler_->tile_size(),
500 1.f / width_scale,
501 1.f / height_scale,
502 &painted_opaque_rect);
504 for (int j = top; j <= bottom; ++j) {
505 for (int i = left; i <= right; ++i) {
506 UpdatableTile* tile = TileAt(i, j);
507 DCHECK(tile); // Did SetTexturePriorites get skipped?
508 // TODO(enne): This should not ever be null.
509 if (!tile)
510 continue;
512 gfx::Rect tile_rect = tiler_->tile_bounds(i, j);
514 // Use update_rect as the above loop copied the dirty rect for this frame
515 // to update_rect.
516 gfx::Rect dirty_rect = tile->update_rect;
517 if (dirty_rect.IsEmpty())
518 continue;
520 // Save what was painted opaque in the tile. Keep the old area if the
521 // paint didn't touch it, and didn't paint some other part of the tile
522 // opaque.
523 gfx::Rect tile_painted_rect = gfx::IntersectRects(tile_rect, paint_rect);
524 gfx::Rect tile_painted_opaque_rect =
525 gfx::IntersectRects(tile_rect, painted_opaque_rect);
526 if (!tile_painted_rect.IsEmpty()) {
527 gfx::Rect paint_inside_tile_opaque_rect =
528 gfx::IntersectRects(tile->opaque_rect(), tile_painted_rect);
529 bool paint_inside_tile_opaque_rect_is_non_opaque =
530 !paint_inside_tile_opaque_rect.IsEmpty() &&
531 !tile_painted_opaque_rect.Contains(paint_inside_tile_opaque_rect);
532 bool opaque_paint_not_inside_tile_opaque_rect =
533 !tile_painted_opaque_rect.IsEmpty() &&
534 !tile->opaque_rect().Contains(tile_painted_opaque_rect);
536 if (paint_inside_tile_opaque_rect_is_non_opaque ||
537 opaque_paint_not_inside_tile_opaque_rect)
538 tile->set_opaque_rect(tile_painted_opaque_rect);
541 // source_rect starts as a full-sized tile with border texels included.
542 gfx::Rect source_rect = tiler_->TileRect(tile);
543 source_rect.Intersect(dirty_rect);
544 // Paint rect not guaranteed to line up on tile boundaries, so
545 // make sure that source_rect doesn't extend outside of it.
546 source_rect.Intersect(paint_rect);
548 tile->update_rect = source_rect;
550 if (source_rect.IsEmpty())
551 continue;
553 const gfx::Point anchor = tiler_->TileRect(tile).origin();
555 // Calculate tile-space rectangle to upload into.
556 gfx::Vector2d dest_offset = source_rect.origin() - anchor;
557 CHECK_GE(dest_offset.x(), 0);
558 CHECK_GE(dest_offset.y(), 0);
560 // Offset from paint rectangle to this tile's dirty rectangle.
561 gfx::Vector2d paint_offset = source_rect.origin() - paint_rect.origin();
562 CHECK_GE(paint_offset.x(), 0);
563 CHECK_GE(paint_offset.y(), 0);
564 CHECK_LE(paint_offset.x() + source_rect.width(), paint_rect.width());
565 CHECK_LE(paint_offset.y() + source_rect.height(), paint_rect.height());
567 tile->updater_resource()->Update(
568 queue, source_rect, dest_offset, tile->partial_update);
569 if (occlusion) {
570 occlusion->overdraw_metrics()->
571 DidUpload(gfx::Transform(), source_rect, tile->opaque_rect());
577 // This picks a small animated layer to be anything less than one viewport. This
578 // is specifically for page transitions which are viewport-sized layers. The
579 // extra tile of padding is due to these layers being slightly larger than the
580 // viewport in some cases.
581 bool TiledLayer::IsSmallAnimatedLayer() const {
582 if (!draw_transform_is_animating() && !screen_space_transform_is_animating())
583 return false;
584 gfx::Size viewport_size =
585 layer_tree_host() ? layer_tree_host()->device_viewport_size()
586 : gfx::Size();
587 gfx::Rect content_rect(content_bounds());
588 return content_rect.width() <=
589 viewport_size.width() + tiler_->tile_size().width() &&
590 content_rect.height() <=
591 viewport_size.height() + tiler_->tile_size().height();
594 namespace {
595 // TODO(epenner): Remove this and make this based on distance once distance can
596 // be calculated for offscreen layers. For now, prioritize all small animated
597 // layers after 512 pixels of pre-painting.
598 void SetPriorityForTexture(const gfx::Rect& visible_rect,
599 const gfx::Rect& tile_rect,
600 bool draws_to_root,
601 bool is_small_animated_layer,
602 PrioritizedResource* texture) {
603 int priority = PriorityCalculator::LowestPriority();
604 if (!visible_rect.IsEmpty()) {
605 priority = PriorityCalculator::PriorityFromDistance(
606 visible_rect, tile_rect, draws_to_root);
609 if (is_small_animated_layer) {
610 priority = PriorityCalculator::max_priority(
611 priority, PriorityCalculator::SmallAnimatedLayerMinPriority());
614 if (priority != PriorityCalculator::LowestPriority())
615 texture->set_request_priority(priority);
617 } // namespace
619 void TiledLayer::SetTexturePriorities(const PriorityCalculator& priority_calc) {
620 UpdateBounds();
621 ResetUpdateState();
622 UpdateScrollPrediction();
624 if (tiler_->has_empty_bounds())
625 return;
627 bool draws_to_root = !render_target()->parent();
628 bool small_animated_layer = IsSmallAnimatedLayer();
630 // Minimally create the tiles in the desired pre-paint rect.
631 gfx::Rect create_tiles_rect = IdlePaintRect();
632 if (small_animated_layer)
633 create_tiles_rect = gfx::Rect(content_bounds());
634 if (!create_tiles_rect.IsEmpty()) {
635 int left, top, right, bottom;
636 tiler_->ContentRectToTileIndices(
637 create_tiles_rect, &left, &top, &right, &bottom);
638 for (int j = top; j <= bottom; ++j) {
639 for (int i = left; i <= right; ++i) {
640 if (!TileAt(i, j))
641 CreateTile(i, j);
646 // Now update priorities on all tiles we have in the layer, no matter where
647 // they are.
648 for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
649 iter != tiler_->tiles().end();
650 ++iter) {
651 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
652 // TODO(enne): This should not ever be null.
653 if (!tile)
654 continue;
655 gfx::Rect tile_rect = tiler_->TileRect(tile);
656 SetPriorityForTexture(predicted_visible_rect_,
657 tile_rect,
658 draws_to_root,
659 small_animated_layer,
660 tile->managed_resource());
664 Region TiledLayer::VisibleContentOpaqueRegion() const {
665 if (skips_draw_)
666 return Region();
667 if (contents_opaque())
668 return visible_content_rect();
669 return tiler_->OpaqueRegionInContentRect(visible_content_rect());
672 void TiledLayer::ResetUpdateState() {
673 skips_draw_ = false;
674 failed_update_ = false;
676 LayerTilingData::TileMap::const_iterator end = tiler_->tiles().end();
677 for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
678 iter != end;
679 ++iter) {
680 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
681 // TODO(enne): This should not ever be null.
682 if (!tile)
683 continue;
684 tile->ResetUpdateState();
688 namespace {
689 gfx::Rect ExpandRectByDelta(const gfx::Rect& rect, const gfx::Vector2d& delta) {
690 int width = rect.width() + std::abs(delta.x());
691 int height = rect.height() + std::abs(delta.y());
692 int x = rect.x() + ((delta.x() < 0) ? delta.x() : 0);
693 int y = rect.y() + ((delta.y() < 0) ? delta.y() : 0);
694 return gfx::Rect(x, y, width, height);
698 void TiledLayer::UpdateScrollPrediction() {
699 // This scroll prediction is very primitive and should be replaced by a
700 // a recursive calculation on all layers which uses actual scroll/animation
701 // velocities. To insure this doesn't miss-predict, we only use it to predict
702 // the visible_rect if:
703 // - content_bounds() hasn't changed.
704 // - visible_rect.size() hasn't changed.
705 // These two conditions prevent rotations, scales, pinch-zooms etc. where
706 // the prediction would be incorrect.
707 gfx::Vector2d delta = visible_content_rect().CenterPoint() -
708 previous_visible_rect_.CenterPoint();
709 predicted_scroll_ = -delta;
710 predicted_visible_rect_ = visible_content_rect();
711 if (previous_content_bounds_ == content_bounds() &&
712 previous_visible_rect_.size() == visible_content_rect().size()) {
713 // Only expand the visible rect in the major scroll direction, to prevent
714 // massive paints due to diagonal scrolls.
715 gfx::Vector2d major_scroll_delta =
716 (std::abs(delta.x()) > std::abs(delta.y())) ?
717 gfx::Vector2d(delta.x(), 0) :
718 gfx::Vector2d(0, delta.y());
719 predicted_visible_rect_ =
720 ExpandRectByDelta(visible_content_rect(), major_scroll_delta);
722 // Bound the prediction to prevent unbounded paints, and clamp to content
723 // bounds.
724 gfx::Rect bound = visible_content_rect();
725 bound.Inset(-tiler_->tile_size().width() * kMaxPredictiveTilesCount,
726 -tiler_->tile_size().height() * kMaxPredictiveTilesCount);
727 bound.Intersect(gfx::Rect(content_bounds()));
728 predicted_visible_rect_.Intersect(bound);
730 previous_content_bounds_ = content_bounds();
731 previous_visible_rect_ = visible_content_rect();
734 bool TiledLayer::Update(ResourceUpdateQueue* queue,
735 const OcclusionTracker* occlusion) {
736 DCHECK(!skips_draw_ && !failed_update_); // Did ResetUpdateState get skipped?
738 // Tiled layer always causes commits to wait for activation, as it does
739 // not support pending trees.
740 SetNextCommitWaitsForActivation();
742 bool updated = false;
745 base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_,
746 true);
748 updated |= ContentsScalingLayer::Update(queue, occlusion);
749 UpdateBounds();
752 if (tiler_->has_empty_bounds() || !DrawsContent())
753 return false;
755 // Animation pre-paint. If the layer is small, try to paint it all
756 // immediately whether or not it is occluded, to avoid paint/upload
757 // hiccups while it is animating.
758 if (IsSmallAnimatedLayer()) {
759 int left, top, right, bottom;
760 tiler_->ContentRectToTileIndices(gfx::Rect(content_bounds()),
761 &left,
762 &top,
763 &right,
764 &bottom);
765 UpdateTiles(left, top, right, bottom, queue, NULL, &updated);
766 if (updated)
767 return updated;
768 // This was an attempt to paint the entire layer so if we fail it's okay,
769 // just fallback on painting visible etc. below.
770 failed_update_ = false;
773 if (predicted_visible_rect_.IsEmpty())
774 return updated;
776 // Visible painting. First occlude visible tiles and paint the non-occluded
777 // tiles.
778 int left, top, right, bottom;
779 tiler_->ContentRectToTileIndices(
780 predicted_visible_rect_, &left, &top, &right, &bottom);
781 MarkOcclusionsAndRequestTextures(left, top, right, bottom, occlusion);
782 skips_draw_ = !UpdateTiles(
783 left, top, right, bottom, queue, occlusion, &updated);
784 if (skips_draw_)
785 tiler_->reset();
786 if (skips_draw_ || updated)
787 return true;
789 // If we have already painting everything visible. Do some pre-painting while
790 // idle.
791 gfx::Rect idle_paint_content_rect = IdlePaintRect();
792 if (idle_paint_content_rect.IsEmpty())
793 return updated;
795 // Prepaint anything that was occluded but inside the layer's visible region.
796 if (!UpdateTiles(left, top, right, bottom, queue, NULL, &updated) ||
797 updated)
798 return updated;
800 int prepaint_left, prepaint_top, prepaint_right, prepaint_bottom;
801 tiler_->ContentRectToTileIndices(idle_paint_content_rect,
802 &prepaint_left,
803 &prepaint_top,
804 &prepaint_right,
805 &prepaint_bottom);
807 // Then expand outwards one row/column at a time until we find a dirty
808 // row/column to update. Increment along the major and minor scroll directions
809 // first.
810 gfx::Vector2d delta = -predicted_scroll_;
811 delta = gfx::Vector2d(delta.x() == 0 ? 1 : delta.x(),
812 delta.y() == 0 ? 1 : delta.y());
813 gfx::Vector2d major_delta =
814 (std::abs(delta.x()) > std::abs(delta.y())) ? gfx::Vector2d(delta.x(), 0)
815 : gfx::Vector2d(0, delta.y());
816 gfx::Vector2d minor_delta =
817 (std::abs(delta.x()) <= std::abs(delta.y())) ? gfx::Vector2d(delta.x(), 0)
818 : gfx::Vector2d(0, delta.y());
819 gfx::Vector2d deltas[4] = { major_delta, minor_delta, -major_delta,
820 -minor_delta };
821 for (int i = 0; i < 4; i++) {
822 if (deltas[i].y() > 0) {
823 while (bottom < prepaint_bottom) {
824 ++bottom;
825 if (!UpdateTiles(
826 left, bottom, right, bottom, queue, NULL, &updated) ||
827 updated)
828 return updated;
831 if (deltas[i].y() < 0) {
832 while (top > prepaint_top) {
833 --top;
834 if (!UpdateTiles(
835 left, top, right, top, queue, NULL, &updated) ||
836 updated)
837 return updated;
840 if (deltas[i].x() < 0) {
841 while (left > prepaint_left) {
842 --left;
843 if (!UpdateTiles(
844 left, top, left, bottom, queue, NULL, &updated) ||
845 updated)
846 return updated;
849 if (deltas[i].x() > 0) {
850 while (right < prepaint_right) {
851 ++right;
852 if (!UpdateTiles(
853 right, top, right, bottom, queue, NULL, &updated) ||
854 updated)
855 return updated;
859 return updated;
862 void TiledLayer::OnOutputSurfaceCreated() {
863 // Ensure that all textures are of the right format.
864 for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
865 iter != tiler_->tiles().end();
866 ++iter) {
867 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
868 if (!tile)
869 continue;
870 PrioritizedResource* resource = tile->managed_resource();
871 resource->SetDimensions(resource->size(), texture_format_);
875 bool TiledLayer::NeedsIdlePaint() {
876 // Don't trigger more paints if we failed (as we'll just fail again).
877 if (failed_update_ || visible_content_rect().IsEmpty() ||
878 tiler_->has_empty_bounds() || !DrawsContent())
879 return false;
881 gfx::Rect idle_paint_content_rect = IdlePaintRect();
882 if (idle_paint_content_rect.IsEmpty())
883 return false;
885 int left, top, right, bottom;
886 tiler_->ContentRectToTileIndices(
887 idle_paint_content_rect, &left, &top, &right, &bottom);
889 for (int j = top; j <= bottom; ++j) {
890 for (int i = left; i <= right; ++i) {
891 UpdatableTile* tile = TileAt(i, j);
892 DCHECK(tile); // Did SetTexturePriorities get skipped?
893 if (!tile)
894 continue;
896 bool updated = !tile->update_rect.IsEmpty();
897 bool can_acquire =
898 tile->managed_resource()->can_acquire_backing_texture();
899 bool dirty =
900 tile->is_dirty() || !tile->managed_resource()->have_backing_texture();
901 if (!updated && can_acquire && dirty)
902 return true;
905 return false;
908 gfx::Rect TiledLayer::IdlePaintRect() {
909 // Don't inflate an empty rect.
910 if (visible_content_rect().IsEmpty())
911 return gfx::Rect();
913 gfx::Rect prepaint_rect = visible_content_rect();
914 prepaint_rect.Inset(-tiler_->tile_size().width() * kPrepaintColumns,
915 -tiler_->tile_size().height() * kPrepaintRows);
916 gfx::Rect content_rect(content_bounds());
917 prepaint_rect.Intersect(content_rect);
919 return prepaint_rect;
922 } // namespace cc