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