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