| 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 |