| 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 "config.h" | |
| 6 | |
| 7 #if USE(ACCELERATED_COMPOSITING) | |
| 8 | |
| 9 #include "base/basictypes.h" | |
| 10 #include "TiledLayerChromium.h" | |
| 11 | |
| 12 #include "CCLayerImpl.h" | |
| 13 #include "CCLayerTreeHost.h" | |
| 14 #include "CCOverdrawMetrics.h" | |
| 15 #include "CCTextureUpdateQueue.h" | |
| 16 #include "CCTiledLayerImpl.h" | |
| 17 #include "GraphicsContext3D.h" | |
| 18 #include "Region.h" | |
| 19 #include <wtf/CurrentTime.h> | |
| 20 #include <wtf/MathExtras.h> | |
| 21 | |
| 22 using namespace std; | |
| 23 using WebKit::WebTransformationMatrix; | |
| 24 | |
| 25 namespace cc { | |
| 26 | |
| 27 class UpdatableTile : public CCLayerTilingData::Tile { | |
| 28 public: | |
| 29 static PassOwnPtr<UpdatableTile> create(PassOwnPtr<LayerTextureUpdater::Text
ure> texture) | |
| 30 { | |
| 31 return adoptPtr(new UpdatableTile(texture)); | |
| 32 } | |
| 33 | |
| 34 LayerTextureUpdater::Texture* texture() { return m_texture.get(); } | |
| 35 CCPrioritizedTexture* managedTexture() { return m_texture->texture(); } | |
| 36 | |
| 37 bool isDirty() const { return !dirtyRect.isEmpty(); } | |
| 38 | |
| 39 // Reset update state for the current frame. This should occur before painti
ng | |
| 40 // for all layers. Since painting one layer can invalidate another layer | |
| 41 // after it has already painted, mark all non-dirty tiles as valid before pa
inting | |
| 42 // such that invalidations during painting won't prevent them from being pus
hed. | |
| 43 void resetUpdateState() | |
| 44 { | |
| 45 updateRect = IntRect(); | |
| 46 occluded = false; | |
| 47 partialUpdate = false; | |
| 48 validForFrame = !isDirty(); | |
| 49 } | |
| 50 | |
| 51 // This promises to update the tile and therefore also guarantees the tile | |
| 52 // will be valid for this frame. dirtyRect is copied into updateRect so | |
| 53 // we can continue to track re-entrant invalidations that occur during paint
ing. | |
| 54 void markForUpdate() | |
| 55 { | |
| 56 validForFrame = true; | |
| 57 updateRect = dirtyRect; | |
| 58 dirtyRect = IntRect(); | |
| 59 } | |
| 60 | |
| 61 IntRect dirtyRect; | |
| 62 IntRect updateRect; | |
| 63 bool partialUpdate; | |
| 64 bool validForFrame; | |
| 65 bool occluded; | |
| 66 bool isInUseOnImpl; | |
| 67 private: | |
| 68 explicit UpdatableTile(PassOwnPtr<LayerTextureUpdater::Texture> texture) | |
| 69 : partialUpdate(false) | |
| 70 , validForFrame(false) | |
| 71 , occluded(false) | |
| 72 , isInUseOnImpl(false) | |
| 73 , m_texture(texture) | |
| 74 { | |
| 75 } | |
| 76 | |
| 77 OwnPtr<LayerTextureUpdater::Texture> m_texture; | |
| 78 | |
| 79 DISALLOW_COPY_AND_ASSIGN(UpdatableTile); | |
| 80 }; | |
| 81 | |
| 82 TiledLayerChromium::TiledLayerChromium() | |
| 83 : LayerChromium() | |
| 84 , m_textureFormat(GraphicsContext3D::INVALID_ENUM) | |
| 85 , m_skipsDraw(false) | |
| 86 , m_failedUpdate(false) | |
| 87 , m_sampledTexelFormat(LayerTextureUpdater::SampledTexelFormatInvalid) | |
| 88 , m_tilingOption(AutoTile) | |
| 89 { | |
| 90 m_tiler = CCLayerTilingData::create(IntSize(), CCLayerTilingData::HasBorderT
exels); | |
| 91 } | |
| 92 | |
| 93 TiledLayerChromium::~TiledLayerChromium() | |
| 94 { | |
| 95 } | |
| 96 | |
| 97 scoped_ptr<CCLayerImpl> TiledLayerChromium::createCCLayerImpl() | |
| 98 { | |
| 99 return CCTiledLayerImpl::create(id()).PassAs<CCLayerImpl>(); | |
| 100 } | |
| 101 | |
| 102 void TiledLayerChromium::updateTileSizeAndTilingOption() | |
| 103 { | |
| 104 ASSERT(layerTreeHost()); | |
| 105 | |
| 106 const IntSize& defaultTileSize = layerTreeHost()->settings().defaultTileSize
; | |
| 107 const IntSize& maxUntiledLayerSize = layerTreeHost()->settings().maxUntiledL
ayerSize; | |
| 108 int layerWidth = contentBounds().width(); | |
| 109 int layerHeight = contentBounds().height(); | |
| 110 | |
| 111 const IntSize tileSize(min(defaultTileSize.width(), layerWidth), min(default
TileSize.height(), layerHeight)); | |
| 112 | |
| 113 // Tile if both dimensions large, or any one dimension large and the other | |
| 114 // extends into a second tile but the total layer area isn't larger than tha
t | |
| 115 // of the largest possible untiled layer. This heuristic allows for long ski
nny layers | |
| 116 // (e.g. scrollbars) that are Nx1 tiles to minimize wasted texture space but
still avoids | |
| 117 // creating very large tiles. | |
| 118 const bool anyDimensionLarge = layerWidth > maxUntiledLayerSize.width() || l
ayerHeight > maxUntiledLayerSize.height(); | |
| 119 const bool anyDimensionOneTile = (layerWidth <= defaultTileSize.width() || l
ayerHeight <= defaultTileSize.height()) | |
| 120 && (layerWidth * layerHeight) <= (maxUntil
edLayerSize.width() * maxUntiledLayerSize.height()); | |
| 121 const bool autoTiled = anyDimensionLarge && !anyDimensionOneTile; | |
| 122 | |
| 123 bool isTiled; | |
| 124 if (m_tilingOption == AlwaysTile) | |
| 125 isTiled = true; | |
| 126 else if (m_tilingOption == NeverTile) | |
| 127 isTiled = false; | |
| 128 else | |
| 129 isTiled = autoTiled; | |
| 130 | |
| 131 IntSize requestedSize = isTiled ? tileSize : contentBounds(); | |
| 132 const int maxSize = layerTreeHost()->rendererCapabilities().maxTextureSize; | |
| 133 IntSize clampedSize = requestedSize.shrunkTo(IntSize(maxSize, maxSize)); | |
| 134 setTileSize(clampedSize); | |
| 135 } | |
| 136 | |
| 137 void TiledLayerChromium::updateBounds() | |
| 138 { | |
| 139 IntSize oldBounds = m_tiler->bounds(); | |
| 140 IntSize newBounds = contentBounds(); | |
| 141 if (oldBounds == newBounds) | |
| 142 return; | |
| 143 m_tiler->setBounds(newBounds); | |
| 144 | |
| 145 // Invalidate any areas that the new bounds exposes. | |
| 146 Region oldRegion(IntRect(IntPoint(), oldBounds)); | |
| 147 Region newRegion(IntRect(IntPoint(), newBounds)); | |
| 148 newRegion.subtract(oldRegion); | |
| 149 Vector<WebCore::IntRect> rects = newRegion.rects(); | |
| 150 for (size_t i = 0; i < rects.size(); ++i) | |
| 151 invalidateContentRect(rects[i]); | |
| 152 } | |
| 153 | |
| 154 void TiledLayerChromium::setTileSize(const IntSize& size) | |
| 155 { | |
| 156 m_tiler->setTileSize(size); | |
| 157 } | |
| 158 | |
| 159 void TiledLayerChromium::setBorderTexelOption(CCLayerTilingData::BorderTexelOpti
on borderTexelOption) | |
| 160 { | |
| 161 m_tiler->setBorderTexelOption(borderTexelOption); | |
| 162 } | |
| 163 | |
| 164 bool TiledLayerChromium::drawsContent() const | |
| 165 { | |
| 166 if (!LayerChromium::drawsContent()) | |
| 167 return false; | |
| 168 | |
| 169 bool hasMoreThanOneTile = m_tiler->numTilesX() > 1 || m_tiler->numTilesY() >
1; | |
| 170 if (m_tilingOption == NeverTile && hasMoreThanOneTile) | |
| 171 return false; | |
| 172 | |
| 173 return true; | |
| 174 } | |
| 175 | |
| 176 bool TiledLayerChromium::needsContentsScale() const | |
| 177 { | |
| 178 return true; | |
| 179 } | |
| 180 | |
| 181 IntSize TiledLayerChromium::contentBounds() const | |
| 182 { | |
| 183 return IntSize(lroundf(bounds().width() * contentsScale()), lroundf(bounds()
.height() * contentsScale())); | |
| 184 } | |
| 185 | |
| 186 void TiledLayerChromium::setTilingOption(TilingOption tilingOption) | |
| 187 { | |
| 188 m_tilingOption = tilingOption; | |
| 189 } | |
| 190 | |
| 191 void TiledLayerChromium::setIsMask(bool isMask) | |
| 192 { | |
| 193 setTilingOption(isMask ? NeverTile : AutoTile); | |
| 194 } | |
| 195 | |
| 196 void TiledLayerChromium::pushPropertiesTo(CCLayerImpl* layer) | |
| 197 { | |
| 198 LayerChromium::pushPropertiesTo(layer); | |
| 199 | |
| 200 CCTiledLayerImpl* tiledLayer = static_cast<CCTiledLayerImpl*>(layer); | |
| 201 | |
| 202 tiledLayer->setSkipsDraw(m_skipsDraw); | |
| 203 tiledLayer->setContentsSwizzled(m_sampledTexelFormat != LayerTextureUpdater:
:SampledTexelFormatRGBA); | |
| 204 tiledLayer->setTilingData(*m_tiler); | |
| 205 Vector<UpdatableTile*> invalidTiles; | |
| 206 | |
| 207 for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begi
n(); iter != m_tiler->tiles().end(); ++iter) { | |
| 208 #if WTF_NEW_HASHMAP_ITERATORS_INTERFACE | |
| 209 int i = iter->key.first; | |
| 210 int j = iter->key.second; | |
| 211 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->value.get()); | |
| 212 #else | |
| 213 int i = iter->first.first; | |
| 214 int j = iter->first.second; | |
| 215 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()); | |
| 216 #endif | |
| 217 // FIXME: This should not ever be null. | |
| 218 if (!tile) | |
| 219 continue; | |
| 220 | |
| 221 tile->isInUseOnImpl = false; | |
| 222 | |
| 223 if (!tile->managedTexture()->haveBackingTexture()) { | |
| 224 // Evicted tiles get deleted from both layers | |
| 225 invalidTiles.append(tile); | |
| 226 continue; | |
| 227 } | |
| 228 | |
| 229 if (!tile->validForFrame) { | |
| 230 // Invalidated tiles are set so they can get different debug colors. | |
| 231 tiledLayer->pushInvalidTile(i, j); | |
| 232 continue; | |
| 233 } | |
| 234 | |
| 235 tiledLayer->pushTileProperties(i, j, tile->managedTexture()->resourceId(
), tile->opaqueRect()); | |
| 236 tile->isInUseOnImpl = true; | |
| 237 } | |
| 238 for (Vector<UpdatableTile*>::const_iterator iter = invalidTiles.begin(); ite
r != invalidTiles.end(); ++iter) | |
| 239 m_tiler->takeTile((*iter)->i(), (*iter)->j()); | |
| 240 } | |
| 241 | |
| 242 CCPrioritizedTextureManager* TiledLayerChromium::textureManager() const | |
| 243 { | |
| 244 if (!layerTreeHost()) | |
| 245 return 0; | |
| 246 return layerTreeHost()->contentsTextureManager(); | |
| 247 } | |
| 248 | |
| 249 void TiledLayerChromium::setLayerTreeHost(CCLayerTreeHost* host) | |
| 250 { | |
| 251 if (host && host != layerTreeHost()) { | |
| 252 for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().
begin(); iter != m_tiler->tiles().end(); ++iter) { | |
| 253 #if WTF_NEW_HASHMAP_ITERATORS_INTERFACE | |
| 254 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->value.get())
; | |
| 255 #else | |
| 256 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()
); | |
| 257 #endif | |
| 258 // FIXME: This should not ever be null. | |
| 259 if (!tile) | |
| 260 continue; | |
| 261 tile->managedTexture()->setTextureManager(host->contentsTextureManag
er()); | |
| 262 } | |
| 263 } | |
| 264 LayerChromium::setLayerTreeHost(host); | |
| 265 } | |
| 266 | |
| 267 UpdatableTile* TiledLayerChromium::tileAt(int i, int j) const | |
| 268 { | |
| 269 return static_cast<UpdatableTile*>(m_tiler->tileAt(i, j)); | |
| 270 } | |
| 271 | |
| 272 UpdatableTile* TiledLayerChromium::createTile(int i, int j) | |
| 273 { | |
| 274 createTextureUpdaterIfNeeded(); | |
| 275 | |
| 276 OwnPtr<UpdatableTile> tile(UpdatableTile::create(textureUpdater()->createTex
ture(textureManager()))); | |
| 277 tile->managedTexture()->setDimensions(m_tiler->tileSize(), m_textureFormat); | |
| 278 | |
| 279 UpdatableTile* addedTile = tile.get(); | |
| 280 m_tiler->addTile(tile.release(), i, j); | |
| 281 | |
| 282 addedTile->dirtyRect = m_tiler->tileRect(addedTile); | |
| 283 | |
| 284 // Temporary diagnostic crash. | |
| 285 if (!addedTile) | |
| 286 CRASH(); | |
| 287 if (!tileAt(i, j)) | |
| 288 CRASH(); | |
| 289 | |
| 290 return addedTile; | |
| 291 } | |
| 292 | |
| 293 void TiledLayerChromium::setNeedsDisplayRect(const FloatRect& dirtyRect) | |
| 294 { | |
| 295 float contentsWidthScale = static_cast<float>(contentBounds().width()) / bou
nds().width(); | |
| 296 float contentsHeightScale = static_cast<float>(contentBounds().height()) / b
ounds().height(); | |
| 297 FloatRect scaledDirtyRect(dirtyRect); | |
| 298 scaledDirtyRect.scale(contentsWidthScale, contentsHeightScale); | |
| 299 IntRect dirty = enclosingIntRect(scaledDirtyRect); | |
| 300 invalidateContentRect(dirty); | |
| 301 LayerChromium::setNeedsDisplayRect(dirtyRect); | |
| 302 } | |
| 303 | |
| 304 void TiledLayerChromium::setUseLCDText(bool useLCDText) | |
| 305 { | |
| 306 LayerChromium::setUseLCDText(useLCDText); | |
| 307 | |
| 308 CCLayerTilingData::BorderTexelOption borderTexelOption; | |
| 309 #if OS(ANDROID) | |
| 310 // Always want border texels and GL_LINEAR due to pinch zoom. | |
| 311 borderTexelOption = CCLayerTilingData::HasBorderTexels; | |
| 312 #else | |
| 313 borderTexelOption = useLCDText ? CCLayerTilingData::NoBorderTexels : CCLayer
TilingData::HasBorderTexels; | |
| 314 #endif | |
| 315 setBorderTexelOption(borderTexelOption); | |
| 316 } | |
| 317 | |
| 318 void TiledLayerChromium::invalidateContentRect(const IntRect& contentRect) | |
| 319 { | |
| 320 updateBounds(); | |
| 321 if (m_tiler->isEmpty() || contentRect.isEmpty() || m_skipsDraw) | |
| 322 return; | |
| 323 | |
| 324 for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begi
n(); iter != m_tiler->tiles().end(); ++iter) { | |
| 325 #if WTF_NEW_HASHMAP_ITERATORS_INTERFACE | |
| 326 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->value.get()); | |
| 327 #else | |
| 328 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()); | |
| 329 #endif | |
| 330 ASSERT(tile); | |
| 331 // FIXME: This should not ever be null. | |
| 332 if (!tile) | |
| 333 continue; | |
| 334 IntRect bound = m_tiler->tileRect(tile); | |
| 335 bound.intersect(contentRect); | |
| 336 tile->dirtyRect.unite(bound); | |
| 337 } | |
| 338 } | |
| 339 | |
| 340 // Returns true if tile is dirty and only part of it needs to be updated. | |
| 341 bool TiledLayerChromium::tileOnlyNeedsPartialUpdate(UpdatableTile* tile) | |
| 342 { | |
| 343 return !tile->dirtyRect.contains(m_tiler->tileRect(tile)); | |
| 344 } | |
| 345 | |
| 346 // Dirty tiles with valid textures needs buffered update to guarantee that | |
| 347 // we don't modify textures currently used for drawing by the impl thread. | |
| 348 bool TiledLayerChromium::tileNeedsBufferedUpdate(UpdatableTile* tile) | |
| 349 { | |
| 350 if (!tile->managedTexture()->haveBackingTexture()) | |
| 351 return false; | |
| 352 | |
| 353 if (!tile->isDirty()) | |
| 354 return false; | |
| 355 | |
| 356 if (!tile->isInUseOnImpl) | |
| 357 return false; | |
| 358 | |
| 359 return true; | |
| 360 } | |
| 361 | |
| 362 | |
| 363 bool TiledLayerChromium::updateTiles(int left, int top, int right, int bottom, C
CTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStat
s& stats, bool& didPaint) | |
| 364 { | |
| 365 didPaint = false; | |
| 366 createTextureUpdaterIfNeeded(); | |
| 367 | |
| 368 bool ignoreOcclusions = !occlusion; | |
| 369 if (!haveTexturesForTiles(left, top, right, bottom, ignoreOcclusions)) { | |
| 370 m_failedUpdate = true; | |
| 371 return false; | |
| 372 } | |
| 373 | |
| 374 IntRect paintRect = markTilesForUpdate(left, top, right, bottom, ignoreOcclu
sions); | |
| 375 | |
| 376 if (occlusion) | |
| 377 occlusion->overdrawMetrics().didPaint(paintRect); | |
| 378 | |
| 379 if (paintRect.isEmpty()) | |
| 380 return true; | |
| 381 | |
| 382 didPaint = true; | |
| 383 updateTileTextures(paintRect, left, top, right, bottom, queue, occlusion, st
ats); | |
| 384 return true; | |
| 385 } | |
| 386 | |
| 387 void TiledLayerChromium::markOcclusionsAndRequestTextures(int left, int top, int
right, int bottom, const CCOcclusionTracker* occlusion) | |
| 388 { | |
| 389 // There is some difficult dependancies between occlusions, recording occlus
ion metrics | |
| 390 // and requesting memory so those are encapsulated in this function: | |
| 391 // - We only want to call requestLate on unoccluded textures (to preserve | |
| 392 // memory for other layers when near OOM). | |
| 393 // - We only want to record occlusion metrics if all memory requests succeed
. | |
| 394 | |
| 395 int occludedTileCount = 0; | |
| 396 bool succeeded = true; | |
| 397 for (int j = top; j <= bottom; ++j) { | |
| 398 for (int i = left; i <= right; ++i) { | |
| 399 UpdatableTile* tile = tileAt(i, j); | |
| 400 ASSERT(tile); // Did setTexturePriorities get skipped? | |
| 401 // FIXME: This should not ever be null. | |
| 402 if (!tile) | |
| 403 continue; | |
| 404 ASSERT(!tile->occluded); // Did resetUpdateState get skipped? Are we
doing more than one occlusion pass? | |
| 405 IntRect visibleTileRect = intersection(m_tiler->tileBounds(i, j), vi
sibleContentRect()); | |
| 406 if (occlusion && occlusion->occluded(this, visibleTileRect)) { | |
| 407 tile->occluded = true; | |
| 408 occludedTileCount++; | |
| 409 } else { | |
| 410 succeeded &= tile->managedTexture()->requestLate(); | |
| 411 } | |
| 412 } | |
| 413 } | |
| 414 | |
| 415 if (!succeeded) | |
| 416 return; | |
| 417 | |
| 418 // FIXME: Remove the loop and just pass the count! | |
| 419 for (int i = 0; i < occludedTileCount; i++) | |
| 420 occlusion->overdrawMetrics().didCullTileForUpload(); | |
| 421 } | |
| 422 | |
| 423 bool TiledLayerChromium::haveTexturesForTiles(int left, int top, int right, int
bottom, bool ignoreOcclusions) | |
| 424 { | |
| 425 for (int j = top; j <= bottom; ++j) { | |
| 426 for (int i = left; i <= right; ++i) { | |
| 427 UpdatableTile* tile = tileAt(i, j); | |
| 428 ASSERT(tile); // Did setTexturePriorites get skipped? | |
| 429 // FIXME: This should not ever be null. | |
| 430 if (!tile) | |
| 431 continue; | |
| 432 | |
| 433 // Ensure the entire tile is dirty if we don't have the texture. | |
| 434 if (!tile->managedTexture()->haveBackingTexture()) | |
| 435 tile->dirtyRect = m_tiler->tileRect(tile); | |
| 436 | |
| 437 // If using occlusion and the visible region of the tile is occluded
, | |
| 438 // don't reserve a texture or update the tile. | |
| 439 if (tile->occluded && !ignoreOcclusions) | |
| 440 continue; | |
| 441 | |
| 442 if (!tile->managedTexture()->canAcquireBackingTexture()) | |
| 443 return false; | |
| 444 } | |
| 445 } | |
| 446 return true; | |
| 447 } | |
| 448 | |
| 449 IntRect TiledLayerChromium::markTilesForUpdate(int left, int top, int right, int
bottom, bool ignoreOcclusions) | |
| 450 { | |
| 451 IntRect paintRect; | |
| 452 for (int j = top; j <= bottom; ++j) { | |
| 453 for (int i = left; i <= right; ++i) { | |
| 454 UpdatableTile* tile = tileAt(i, j); | |
| 455 ASSERT(tile); // Did setTexturePriorites get skipped? | |
| 456 // FIXME: This should not ever be null. | |
| 457 if (!tile) | |
| 458 continue; | |
| 459 if (tile->occluded && !ignoreOcclusions) | |
| 460 continue; | |
| 461 paintRect.unite(tile->dirtyRect); | |
| 462 tile->markForUpdate(); | |
| 463 } | |
| 464 } | |
| 465 return paintRect; | |
| 466 } | |
| 467 | |
| 468 void TiledLayerChromium::updateTileTextures(const IntRect& paintRect, int left,
int top, int right, int bottom, CCTextureUpdateQueue& queue, const CCOcclusionTr
acker* occlusion, CCRenderingStats& stats) | |
| 469 { | |
| 470 // The updateRect should be in layer space. So we have to convert the paintR
ect from content space to layer space. | |
| 471 m_updateRect = FloatRect(paintRect); | |
| 472 float widthScale = bounds().width() / static_cast<float>(contentBounds().wid
th()); | |
| 473 float heightScale = bounds().height() / static_cast<float>(contentBounds().h
eight()); | |
| 474 m_updateRect.scale(widthScale, heightScale); | |
| 475 | |
| 476 // Calling prepareToUpdate() calls into WebKit to paint, which may have the
side | |
| 477 // effect of disabling compositing, which causes our reference to the textur
e updater to be deleted. | |
| 478 // However, we can't free the memory backing the SkCanvas until the paint fi
nishes, | |
| 479 // so we grab a local reference here to hold the updater alive until the pai
nt completes. | |
| 480 RefPtr<LayerTextureUpdater> protector(textureUpdater()); | |
| 481 IntRect paintedOpaqueRect; | |
| 482 textureUpdater()->prepareToUpdate(paintRect, m_tiler->tileSize(), 1 / widthS
cale, 1 / heightScale, paintedOpaqueRect, stats); | |
| 483 | |
| 484 for (int j = top; j <= bottom; ++j) { | |
| 485 for (int i = left; i <= right; ++i) { | |
| 486 UpdatableTile* tile = tileAt(i, j); | |
| 487 ASSERT(tile); // Did setTexturePriorites get skipped? | |
| 488 // FIXME: This should not ever be null. | |
| 489 if (!tile) | |
| 490 continue; | |
| 491 | |
| 492 IntRect tileRect = m_tiler->tileBounds(i, j); | |
| 493 | |
| 494 // Use updateRect as the above loop copied the dirty rect for this f
rame to updateRect. | |
| 495 const IntRect& dirtyRect = tile->updateRect; | |
| 496 if (dirtyRect.isEmpty()) | |
| 497 continue; | |
| 498 | |
| 499 // Save what was painted opaque in the tile. Keep the old area if th
e paint didn't touch it, and didn't paint some | |
| 500 // other part of the tile opaque. | |
| 501 IntRect tilePaintedRect = intersection(tileRect, paintRect); | |
| 502 IntRect tilePaintedOpaqueRect = intersection(tileRect, paintedOpaque
Rect); | |
| 503 if (!tilePaintedRect.isEmpty()) { | |
| 504 IntRect paintInsideTileOpaqueRect = intersection(tile->opaqueRec
t(), tilePaintedRect); | |
| 505 bool paintInsideTileOpaqueRectIsNonOpaque = !tilePaintedOpaqueRe
ct.contains(paintInsideTileOpaqueRect); | |
| 506 bool opaquePaintNotInsideTileOpaqueRect = !tilePaintedOpaqueRect
.isEmpty() && !tile->opaqueRect().contains(tilePaintedOpaqueRect); | |
| 507 | |
| 508 if (paintInsideTileOpaqueRectIsNonOpaque || opaquePaintNotInside
TileOpaqueRect) | |
| 509 tile->setOpaqueRect(tilePaintedOpaqueRect); | |
| 510 } | |
| 511 | |
| 512 // sourceRect starts as a full-sized tile with border texels include
d. | |
| 513 IntRect sourceRect = m_tiler->tileRect(tile); | |
| 514 sourceRect.intersect(dirtyRect); | |
| 515 // Paint rect not guaranteed to line up on tile boundaries, so | |
| 516 // make sure that sourceRect doesn't extend outside of it. | |
| 517 sourceRect.intersect(paintRect); | |
| 518 | |
| 519 tile->updateRect = sourceRect; | |
| 520 | |
| 521 if (sourceRect.isEmpty()) | |
| 522 continue; | |
| 523 | |
| 524 tile->texture()->prepareRect(sourceRect, stats); | |
| 525 if (occlusion) | |
| 526 occlusion->overdrawMetrics().didUpload(WebTransformationMatrix()
, sourceRect, tile->opaqueRect()); | |
| 527 | |
| 528 const IntPoint anchor = m_tiler->tileRect(tile).location(); | |
| 529 | |
| 530 // Calculate tile-space rectangle to upload into. | |
| 531 IntSize destOffset(sourceRect.x() - anchor.x(), sourceRect.y() - anc
hor.y()); | |
| 532 if (destOffset.width() < 0) | |
| 533 CRASH(); | |
| 534 if (destOffset.height() < 0) | |
| 535 CRASH(); | |
| 536 | |
| 537 // Offset from paint rectangle to this tile's dirty rectangle. | |
| 538 IntPoint paintOffset(sourceRect.x() - paintRect.x(), sourceRect.y()
- paintRect.y()); | |
| 539 if (paintOffset.x() < 0) | |
| 540 CRASH(); | |
| 541 if (paintOffset.y() < 0) | |
| 542 CRASH(); | |
| 543 if (paintOffset.x() + sourceRect.width() > paintRect.width()) | |
| 544 CRASH(); | |
| 545 if (paintOffset.y() + sourceRect.height() > paintRect.height()) | |
| 546 CRASH(); | |
| 547 | |
| 548 TextureUploader::Parameters upload = { tile->texture(), sourceRect,
destOffset }; | |
| 549 if (tile->partialUpdate) | |
| 550 queue.appendPartialUpload(upload); | |
| 551 else | |
| 552 queue.appendFullUpload(upload); | |
| 553 } | |
| 554 } | |
| 555 } | |
| 556 | |
| 557 namespace { | |
| 558 // This picks a small animated layer to be anything less than one viewport. This | |
| 559 // is specifically for page transitions which are viewport-sized layers. The ext
ra | |
| 560 // 64 pixels is due to these layers being slightly larger than the viewport in s
ome cases. | |
| 561 bool isSmallAnimatedLayer(TiledLayerChromium* layer) | |
| 562 { | |
| 563 if (!layer->drawTransformIsAnimating() && !layer->screenSpaceTransformIsAnim
ating()) | |
| 564 return false; | |
| 565 IntSize viewportSize = layer->layerTreeHost() ? layer->layerTreeHost()->devi
ceViewportSize() : IntSize(); | |
| 566 IntRect contentRect(IntPoint::zero(), layer->contentBounds()); | |
| 567 return contentRect.width() <= viewportSize.width() + 64 | |
| 568 && contentRect.height() <= viewportSize.height() + 64; | |
| 569 } | |
| 570 | |
| 571 // FIXME: Remove this and make this based on distance once distance can be calcu
lated | |
| 572 // for offscreen layers. For now, prioritize all small animated layers after 512 | |
| 573 // pixels of pre-painting. | |
| 574 void setPriorityForTexture(const IntRect& visibleRect, | |
| 575 const IntRect& tileRect, | |
| 576 bool drawsToRoot, | |
| 577 bool isSmallAnimatedLayer, | |
| 578 CCPrioritizedTexture* texture) | |
| 579 { | |
| 580 int priority = CCPriorityCalculator::lowestPriority(); | |
| 581 if (!visibleRect.isEmpty()) | |
| 582 priority = CCPriorityCalculator::priorityFromDistance(visibleRect, tileR
ect, drawsToRoot); | |
| 583 if (isSmallAnimatedLayer) | |
| 584 priority = CCPriorityCalculator::maxPriority(priority, CCPriorityCalcula
tor::smallAnimatedLayerMinPriority()); | |
| 585 if (priority != CCPriorityCalculator::lowestPriority()) | |
| 586 texture->setRequestPriority(priority); | |
| 587 } | |
| 588 } | |
| 589 | |
| 590 void TiledLayerChromium::setTexturePriorities(const CCPriorityCalculator& priori
tyCalc) | |
| 591 { | |
| 592 updateBounds(); | |
| 593 resetUpdateState(); | |
| 594 | |
| 595 if (m_tiler->hasEmptyBounds()) | |
| 596 return; | |
| 597 | |
| 598 bool drawsToRoot = !renderTarget()->parent(); | |
| 599 bool smallAnimatedLayer = isSmallAnimatedLayer(this); | |
| 600 | |
| 601 // Minimally create the tiles in the desired pre-paint rect. | |
| 602 IntRect createTilesRect = idlePaintRect(); | |
| 603 if (!createTilesRect.isEmpty()) { | |
| 604 int left, top, right, bottom; | |
| 605 m_tiler->contentRectToTileIndices(createTilesRect, left, top, right, bot
tom); | |
| 606 for (int j = top; j <= bottom; ++j) { | |
| 607 for (int i = left; i <= right; ++i) { | |
| 608 if (!tileAt(i, j)) | |
| 609 createTile(i, j); | |
| 610 } | |
| 611 } | |
| 612 } | |
| 613 | |
| 614 // Also, minimally create all tiles for small animated layers and also | |
| 615 // double-buffer them since we have limited their size to be reasonable. | |
| 616 IntRect doubleBufferedRect = visibleContentRect(); | |
| 617 if (smallAnimatedLayer) | |
| 618 doubleBufferedRect = IntRect(IntPoint::zero(), contentBounds()); | |
| 619 | |
| 620 // Create additional textures for double-buffered updates when needed. | |
| 621 // These textures must stay alive while the updated textures are incremental
ly | |
| 622 // uploaded, swapped atomically via pushProperties, and finally deleted | |
| 623 // after the commit is complete, after which they can be recycled. | |
| 624 if (!doubleBufferedRect.isEmpty()) { | |
| 625 int left, top, right, bottom; | |
| 626 m_tiler->contentRectToTileIndices(doubleBufferedRect, left, top, right,
bottom); | |
| 627 for (int j = top; j <= bottom; ++j) { | |
| 628 for (int i = left; i <= right; ++i) { | |
| 629 UpdatableTile* tile = tileAt(i, j); | |
| 630 if (!tile) | |
| 631 tile = createTile(i, j); | |
| 632 // We need an additional texture if the tile needs a buffered-up
date and it's not a partial update. | |
| 633 // FIXME: Decide if partial update should be allowed based on co
st | |
| 634 // of update. https://bugs.webkit.org/show_bug.cgi?id=77376 | |
| 635 if (!layerTreeHost() || !layerTreeHost()->bufferedUpdates() || !
tileNeedsBufferedUpdate(tile)) | |
| 636 continue; | |
| 637 if (tileOnlyNeedsPartialUpdate(tile) && layerTreeHost()->request
PartialTextureUpdate()) { | |
| 638 tile->partialUpdate = true; | |
| 639 continue; | |
| 640 } | |
| 641 | |
| 642 IntRect tileRect = m_tiler->tileRect(tile); | |
| 643 tile->dirtyRect = tileRect; | |
| 644 LayerTextureUpdater::Texture* backBuffer = tile->texture(); | |
| 645 setPriorityForTexture(visibleContentRect(), tile->dirtyRect, dra
wsToRoot, smallAnimatedLayer, backBuffer->texture()); | |
| 646 scoped_ptr<CCPrioritizedTexture> frontBuffer = CCPrioritizedText
ure::create(backBuffer->texture()->textureManager(), | |
| 647
backBuffer->texture()->size(), | |
| 648
backBuffer->texture()->format()); | |
| 649 // Swap backBuffer into frontBuffer and add it to delete after c
ommit queue. | |
| 650 backBuffer->swapTextureWith(frontBuffer); | |
| 651 layerTreeHost()->deleteTextureAfterCommit(frontBuffer.Pass()); | |
| 652 } | |
| 653 } | |
| 654 } | |
| 655 | |
| 656 // Now update priorities on all tiles we have in the layer, no matter where
they are. | |
| 657 for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begi
n(); iter != m_tiler->tiles().end(); ++iter) { | |
| 658 #if WTF_NEW_HASHMAP_ITERATORS_INTERFACE | |
| 659 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->value.get()); | |
| 660 #else | |
| 661 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()); | |
| 662 #endif | |
| 663 // FIXME: This should not ever be null. | |
| 664 if (!tile) | |
| 665 continue; | |
| 666 IntRect tileRect = m_tiler->tileRect(tile); | |
| 667 setPriorityForTexture(visibleContentRect(), tileRect, drawsToRoot, small
AnimatedLayer, tile->managedTexture()); | |
| 668 } | |
| 669 } | |
| 670 | |
| 671 Region TiledLayerChromium::visibleContentOpaqueRegion() const | |
| 672 { | |
| 673 if (m_skipsDraw) | |
| 674 return Region(); | |
| 675 if (contentsOpaque()) | |
| 676 return visibleContentRect(); | |
| 677 return m_tiler->opaqueRegionInContentRect(visibleContentRect()); | |
| 678 } | |
| 679 | |
| 680 void TiledLayerChromium::resetUpdateState() | |
| 681 { | |
| 682 m_skipsDraw = false; | |
| 683 m_failedUpdate = false; | |
| 684 | |
| 685 CCLayerTilingData::TileMap::const_iterator end = m_tiler->tiles().end(); | |
| 686 for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begi
n(); iter != end; ++iter) { | |
| 687 #if WTF_NEW_HASHMAP_ITERATORS_INTERFACE | |
| 688 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->value.get()); | |
| 689 #else | |
| 690 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get()); | |
| 691 #endif | |
| 692 // FIXME: This should not ever be null. | |
| 693 if (!tile) | |
| 694 continue; | |
| 695 tile->resetUpdateState(); | |
| 696 } | |
| 697 } | |
| 698 | |
| 699 void TiledLayerChromium::update(CCTextureUpdateQueue& queue, const CCOcclusionTr
acker* occlusion, CCRenderingStats& stats) | |
| 700 { | |
| 701 ASSERT(!m_skipsDraw && !m_failedUpdate); // Did resetUpdateState get skipped
? | |
| 702 updateBounds(); | |
| 703 if (m_tiler->hasEmptyBounds() || !drawsContent()) | |
| 704 return; | |
| 705 | |
| 706 bool didPaint = false; | |
| 707 | |
| 708 // Animation pre-paint. If the layer is small, try to paint it all | |
| 709 // immediately whether or not it is occluded, to avoid paint/upload | |
| 710 // hiccups while it is animating. | |
| 711 if (isSmallAnimatedLayer(this)) { | |
| 712 int left, top, right, bottom; | |
| 713 m_tiler->contentRectToTileIndices(IntRect(IntPoint::zero(), contentBound
s()), left, top, right, bottom); | |
| 714 updateTiles(left, top, right, bottom, queue, 0, stats, didPaint); | |
| 715 if (didPaint) | |
| 716 return; | |
| 717 // This was an attempt to paint the entire layer so if we fail it's okay
, | |
| 718 // just fallback on painting visible etc. below. | |
| 719 m_failedUpdate = false; | |
| 720 } | |
| 721 | |
| 722 if (visibleContentRect().isEmpty()) | |
| 723 return; | |
| 724 | |
| 725 // Visible painting. First occlude visible tiles and paint the non-occluded
tiles. | |
| 726 int left, top, right, bottom; | |
| 727 m_tiler->contentRectToTileIndices(visibleContentRect(), left, top, right, bo
ttom); | |
| 728 markOcclusionsAndRequestTextures(left, top, right, bottom, occlusion); | |
| 729 m_skipsDraw = !updateTiles(left, top, right, bottom, queue, occlusion, stats
, didPaint); | |
| 730 if (m_skipsDraw) | |
| 731 m_tiler->reset(); | |
| 732 if (m_skipsDraw || didPaint) | |
| 733 return; | |
| 734 | |
| 735 // If we have already painting everything visible. Do some pre-painting whil
e idle. | |
| 736 IntRect idlePaintContentRect = idlePaintRect(); | |
| 737 if (idlePaintContentRect.isEmpty()) | |
| 738 return; | |
| 739 | |
| 740 // Prepaint anything that was occluded but inside the layer's visible region
. | |
| 741 if (!updateTiles(left, top, right, bottom, queue, 0, stats, didPaint) || did
Paint) | |
| 742 return; | |
| 743 | |
| 744 int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom; | |
| 745 m_tiler->contentRectToTileIndices(idlePaintContentRect, prepaintLeft, prepai
ntTop, prepaintRight, prepaintBottom); | |
| 746 | |
| 747 // Then expand outwards from the visible area until we find a dirty row or c
olumn to update. | |
| 748 while (left > prepaintLeft || top > prepaintTop || right < prepaintRight ||
bottom < prepaintBottom) { | |
| 749 if (bottom < prepaintBottom) { | |
| 750 ++bottom; | |
| 751 if (!updateTiles(left, bottom, right, bottom, queue, 0, stats, didPa
int) || didPaint) | |
| 752 return; | |
| 753 } | |
| 754 if (top > prepaintTop) { | |
| 755 --top; | |
| 756 if (!updateTiles(left, top, right, top, queue, 0, stats, didPaint) |
| didPaint) | |
| 757 return; | |
| 758 } | |
| 759 if (left > prepaintLeft) { | |
| 760 --left; | |
| 761 if (!updateTiles(left, top, left, bottom, queue, 0, stats, didPaint)
|| didPaint) | |
| 762 return; | |
| 763 } | |
| 764 if (right < prepaintRight) { | |
| 765 ++right; | |
| 766 if (!updateTiles(right, top, right, bottom, queue, 0, stats, didPain
t) || didPaint) | |
| 767 return; | |
| 768 } | |
| 769 } | |
| 770 } | |
| 771 | |
| 772 bool TiledLayerChromium::needsIdlePaint() | |
| 773 { | |
| 774 // Don't trigger more paints if we failed (as we'll just fail again). | |
| 775 if (m_failedUpdate || visibleContentRect().isEmpty() || m_tiler->hasEmptyBou
nds() || !drawsContent()) | |
| 776 return false; | |
| 777 | |
| 778 IntRect idlePaintContentRect = idlePaintRect(); | |
| 779 if (idlePaintContentRect.isEmpty()) | |
| 780 return false; | |
| 781 | |
| 782 int left, top, right, bottom; | |
| 783 m_tiler->contentRectToTileIndices(idlePaintContentRect, left, top, right, bo
ttom); | |
| 784 | |
| 785 for (int j = top; j <= bottom; ++j) { | |
| 786 for (int i = left; i <= right; ++i) { | |
| 787 UpdatableTile* tile = tileAt(i, j); | |
| 788 ASSERT(tile); // Did setTexturePriorities get skipped? | |
| 789 if (!tile) | |
| 790 continue; | |
| 791 | |
| 792 bool updated = !tile->updateRect.isEmpty(); | |
| 793 bool canAcquire = tile->managedTexture()->canAcquireBackingTexture()
; | |
| 794 bool dirty = tile->isDirty() || !tile->managedTexture()->haveBacking
Texture(); | |
| 795 if (!updated && canAcquire && dirty) | |
| 796 return true; | |
| 797 } | |
| 798 } | |
| 799 return false; | |
| 800 } | |
| 801 | |
| 802 IntRect TiledLayerChromium::idlePaintRect() | |
| 803 { | |
| 804 // Don't inflate an empty rect. | |
| 805 if (visibleContentRect().isEmpty()) | |
| 806 return IntRect(); | |
| 807 | |
| 808 // FIXME: This can be made a lot larger now! We should increase | |
| 809 // this slowly while insuring it doesn't cause any perf issues. | |
| 810 IntRect prepaintRect = visibleContentRect(); | |
| 811 prepaintRect.inflateX(m_tiler->tileSize().width()); | |
| 812 prepaintRect.inflateY(m_tiler->tileSize().height() * 2); | |
| 813 IntRect contentRect(IntPoint::zero(), contentBounds()); | |
| 814 prepaintRect.intersect(contentRect); | |
| 815 | |
| 816 return prepaintRect; | |
| 817 } | |
| 818 | |
| 819 } | |
| 820 #endif // USE(ACCELERATED_COMPOSITING) | |
| OLD | NEW |