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