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