1/* 2 * Copyright (C) 2011-2014 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "TileController.h" 28 29#include "IntRect.h" 30#include "PlatformCALayer.h" 31#include "Region.h" 32#include "TileCoverageMap.h" 33#include "TileGrid.h" 34#include <utility> 35#include <wtf/MainThread.h> 36 37#if PLATFORM(IOS) 38#include "TileControllerMemoryHandlerIOS.h" 39#endif 40 41namespace WebCore { 42 43PassOwnPtr<TileController> TileController::create(PlatformCALayer* rootPlatformLayer) 44{ 45 return adoptPtr(new TileController(rootPlatformLayer)); 46} 47 48TileController::TileController(PlatformCALayer* rootPlatformLayer) 49 : m_tileCacheLayer(rootPlatformLayer) 50 , m_tileGrid(std::make_unique<TileGrid>(*this)) 51 , m_tileSize(defaultTileWidth, defaultTileHeight) 52 , m_tileRevalidationTimer(this, &TileController::tileRevalidationTimerFired) 53 , m_zoomedOutContentsScale(0) 54 , m_deviceScaleFactor(owningGraphicsLayer()->platformCALayerDeviceScaleFactor()) 55 , m_tileCoverage(CoverageForVisibleArea) 56 , m_marginTop(0) 57 , m_marginBottom(0) 58 , m_marginLeft(0) 59 , m_marginRight(0) 60 , m_isInWindow(false) 61 , m_scrollingPerformanceLoggingEnabled(false) 62 , m_unparentsOffscreenTiles(false) 63 , m_acceleratesDrawing(false) 64 , m_tilesAreOpaque(false) 65 , m_hasTilesWithTemporaryScaleFactor(false) 66 , m_tileDebugBorderWidth(0) 67 , m_indicatorMode(AsyncScrollingIndication) 68 , m_topContentInset(0) 69{ 70} 71 72TileController::~TileController() 73{ 74 ASSERT(isMainThread()); 75 76#if PLATFORM(IOS) 77 tileControllerMemoryHandler().removeTileController(this); 78#endif 79} 80 81void TileController::tileCacheLayerBoundsChanged() 82{ 83 ASSERT(owningGraphicsLayer()->isCommittingChanges()); 84 setNeedsRevalidateTiles(); 85} 86 87void TileController::setNeedsDisplay() 88{ 89 tileGrid().setNeedsDisplay(); 90 m_zoomedOutTileGrid = nullptr; 91} 92 93void TileController::setNeedsDisplayInRect(const IntRect& rect) 94{ 95 tileGrid().setNeedsDisplayInRect(rect); 96 if (m_zoomedOutTileGrid) 97 m_zoomedOutTileGrid->dropTilesInRect(rect); 98} 99 100void TileController::setContentsScale(float scale) 101{ 102 ASSERT(owningGraphicsLayer()->isCommittingChanges()); 103 104 float deviceScaleFactor = owningGraphicsLayer()->platformCALayerDeviceScaleFactor(); 105 // The scale we get is the product of the page scale factor and device scale factor. 106 // Divide by the device scale factor so we'll get the page scale factor. 107 scale /= deviceScaleFactor; 108 109 if (tileGrid().scale() == scale && m_deviceScaleFactor == deviceScaleFactor && !m_hasTilesWithTemporaryScaleFactor) 110 return; 111 112 m_hasTilesWithTemporaryScaleFactor = false; 113 m_deviceScaleFactor = deviceScaleFactor; 114 115 if (m_coverageMap) 116 m_coverageMap->setDeviceScaleFactor(deviceScaleFactor); 117 118 if (m_zoomedOutTileGrid && m_zoomedOutTileGrid->scale() == scale) { 119 m_tileGrid = WTF::move(m_zoomedOutTileGrid); 120 m_tileGrid->revalidateTiles(0); 121 return; 122 } 123 124 if (m_zoomedOutContentsScale && m_zoomedOutContentsScale == tileGrid().scale() && tileGrid().scale() != scale && !m_hasTilesWithTemporaryScaleFactor) { 125 m_zoomedOutTileGrid = WTF::move(m_tileGrid); 126 m_tileGrid = std::make_unique<TileGrid>(*this); 127 } 128 129 tileGrid().setScale(scale); 130 tileGrid().setNeedsDisplay(); 131} 132 133float TileController::contentsScale() const 134{ 135 return tileGrid().scale() * m_deviceScaleFactor; 136} 137 138float TileController::zoomedOutContentsScale() const 139{ 140 return m_zoomedOutContentsScale * m_deviceScaleFactor; 141} 142 143void TileController::setZoomedOutContentsScale(float scale) 144{ 145 ASSERT(owningGraphicsLayer()->isCommittingChanges()); 146 147 float deviceScaleFactor = owningGraphicsLayer()->platformCALayerDeviceScaleFactor(); 148 scale /= deviceScaleFactor; 149 150 if (m_zoomedOutContentsScale == scale) 151 return; 152 m_zoomedOutContentsScale = scale; 153 154 if (m_zoomedOutTileGrid && m_zoomedOutTileGrid->scale() != m_zoomedOutContentsScale) 155 m_zoomedOutTileGrid = nullptr; 156} 157 158void TileController::setAcceleratesDrawing(bool acceleratesDrawing) 159{ 160 if (m_acceleratesDrawing == acceleratesDrawing) 161 return; 162 m_acceleratesDrawing = acceleratesDrawing; 163 164 tileGrid().updateTileLayerProperties(); 165} 166 167void TileController::setTilesOpaque(bool opaque) 168{ 169 if (opaque == m_tilesAreOpaque) 170 return; 171 m_tilesAreOpaque = opaque; 172 173 tileGrid().updateTileLayerProperties(); 174} 175 176void TileController::setVisibleRect(const FloatRect& visibleRect) 177{ 178 ASSERT(owningGraphicsLayer()->isCommittingChanges()); 179 if (m_visibleRect == visibleRect) 180 return; 181 182 m_visibleRect = visibleRect; 183 setNeedsRevalidateTiles(); 184} 185 186bool TileController::tilesWouldChangeForVisibleRect(const FloatRect& newVisibleRect) const 187{ 188 if (bounds().isEmpty()) 189 return false; 190 return tileGrid().tilesWouldChangeForVisibleRect(newVisibleRect, m_visibleRect); 191} 192 193void TileController::setTopContentInset(float topContentInset) 194{ 195 m_topContentInset = topContentInset; 196 setTiledScrollingIndicatorPosition(FloatPoint(0, m_topContentInset)); 197} 198 199void TileController::setTiledScrollingIndicatorPosition(const FloatPoint& position) 200{ 201 if (!m_coverageMap) 202 return; 203 m_coverageMap->setPosition(position); 204 m_coverageMap->update(); 205} 206 207void TileController::prepopulateRect(const FloatRect& rect) 208{ 209 if (tileGrid().prepopulateRect(rect)) 210 setNeedsRevalidateTiles(); 211} 212 213void TileController::setIsInWindow(bool isInWindow) 214{ 215 if (m_isInWindow == isInWindow) 216 return; 217 218 m_isInWindow = isInWindow; 219 220 if (m_isInWindow) 221 setNeedsRevalidateTiles(); 222 else { 223 const double tileRevalidationTimeout = 4; 224 scheduleTileRevalidation(tileRevalidationTimeout); 225 } 226} 227 228void TileController::setTileCoverage(TileCoverage coverage) 229{ 230 if (coverage == m_tileCoverage) 231 return; 232 233 m_tileCoverage = coverage; 234 setNeedsRevalidateTiles(); 235} 236 237void TileController::revalidateTiles() 238{ 239 ASSERT(owningGraphicsLayer()->isCommittingChanges()); 240 tileGrid().revalidateTiles(0); 241 m_visibleRectAtLastRevalidate = m_visibleRect; 242} 243 244void TileController::forceRepaint() 245{ 246 setNeedsDisplay(); 247} 248 249void TileController::setTileDebugBorderWidth(float borderWidth) 250{ 251 if (m_tileDebugBorderWidth == borderWidth) 252 return; 253 m_tileDebugBorderWidth = borderWidth; 254 255 tileGrid().updateTileLayerProperties(); 256} 257 258void TileController::setTileDebugBorderColor(Color borderColor) 259{ 260 if (m_tileDebugBorderColor == borderColor) 261 return; 262 m_tileDebugBorderColor = borderColor; 263 264 tileGrid().updateTileLayerProperties(); 265} 266 267IntRect TileController::bounds() const 268{ 269 IntPoint boundsOriginIncludingMargin(-leftMarginWidth(), -topMarginHeight()); 270 IntSize boundsSizeIncludingMargin = expandedIntSize(m_tileCacheLayer->bounds().size()); 271 boundsSizeIncludingMargin.expand(leftMarginWidth() + rightMarginWidth(), topMarginHeight() + bottomMarginHeight()); 272 273 return IntRect(boundsOriginIncludingMargin, boundsSizeIncludingMargin); 274} 275 276IntRect TileController::boundsWithoutMargin() const 277{ 278 return IntRect(IntPoint(), expandedIntSize(m_tileCacheLayer->bounds().size())); 279} 280 281IntRect TileController::boundsAtLastRevalidateWithoutMargin() const 282{ 283 IntRect boundsWithoutMargin = IntRect(IntPoint(), m_boundsAtLastRevalidate.size()); 284 boundsWithoutMargin.contract(IntSize(leftMarginWidth() + rightMarginWidth(), topMarginHeight() + bottomMarginHeight())); 285 return boundsWithoutMargin; 286} 287 288FloatRect TileController::computeTileCoverageRect(const FloatRect& previousVisibleRect, const FloatRect& visibleRect) const 289{ 290 // If the page is not in a window (for example if it's in a background tab), we limit the tile coverage rect to the visible rect. 291 if (!m_isInWindow) 292 return visibleRect; 293 294 // FIXME: look at how far the document can scroll in each dimension. 295 float coverageHorizontalSize = visibleRect.width(); 296 float coverageVerticalSize = visibleRect.height(); 297 298#if PLATFORM(IOS) 299 UNUSED_PARAM(previousVisibleRect); 300 return visibleRect; 301#else 302 bool largeVisibleRectChange = !previousVisibleRect.isEmpty() && !visibleRect.intersects(previousVisibleRect); 303 304 // Inflate the coverage rect so that it covers 2x of the visible width and 3x of the visible height. 305 // These values were chosen because it's more common to have tall pages and to scroll vertically, 306 // so we keep more tiles above and below the current area. 307 308 if (m_tileCoverage & CoverageForHorizontalScrolling && !largeVisibleRectChange) 309 coverageHorizontalSize *= 2; 310 311 if (m_tileCoverage & CoverageForVerticalScrolling && !largeVisibleRectChange) 312 coverageVerticalSize *= 3; 313#endif 314 coverageVerticalSize += topMarginHeight() + bottomMarginHeight(); 315 coverageHorizontalSize += leftMarginWidth() + rightMarginWidth(); 316 317 FloatRect coverageBounds = bounds(); 318 float coverageLeft = visibleRect.x() - (coverageHorizontalSize - visibleRect.width()) / 2; 319 coverageLeft = std::min(coverageLeft, coverageBounds.maxX() - coverageHorizontalSize); 320 coverageLeft = std::max(coverageLeft, coverageBounds.x()); 321 322 float coverageTop = visibleRect.y() - (coverageVerticalSize - visibleRect.height()) / 2; 323 coverageTop = std::min(coverageTop, coverageBounds.maxY() - coverageVerticalSize); 324 coverageTop = std::max(coverageTop, coverageBounds.y()); 325 326 return FloatRect(coverageLeft, coverageTop, coverageHorizontalSize, coverageVerticalSize); 327} 328 329void TileController::scheduleTileRevalidation(double interval) 330{ 331 if (m_tileRevalidationTimer.isActive() && m_tileRevalidationTimer.nextFireInterval() < interval) 332 return; 333 334 m_tileRevalidationTimer.startOneShot(interval); 335} 336 337bool TileController::shouldAggressivelyRetainTiles() const 338{ 339 return owningGraphicsLayer()->platformCALayerShouldAggressivelyRetainTiles(m_tileCacheLayer); 340} 341 342bool TileController::shouldTemporarilyRetainTileCohorts() const 343{ 344 return owningGraphicsLayer()->platformCALayerShouldTemporarilyRetainTileCohorts(m_tileCacheLayer); 345} 346 347void TileController::tileRevalidationTimerFired(Timer<TileController>*) 348{ 349 if (!owningGraphicsLayer()) 350 return; 351 352 if (m_isInWindow) { 353 setNeedsRevalidateTiles(); 354 return; 355 } 356 // If we are not visible get rid of the zoomed-out tiles. 357 m_zoomedOutTileGrid = nullptr; 358 359 unsigned validationPolicy = (shouldAggressivelyRetainTiles() ? 0 : TileGrid::PruneSecondaryTiles) | TileGrid::UnparentAllTiles; 360 361 tileGrid().revalidateTiles(validationPolicy); 362} 363 364void TileController::didRevalidateTiles() 365{ 366 m_visibleRectAtLastRevalidate = visibleRect(); 367 m_boundsAtLastRevalidate = bounds(); 368 369 updateTileCoverageMap(); 370} 371 372unsigned TileController::blankPixelCount() const 373{ 374 return tileGrid().blankPixelCount(); 375} 376 377unsigned TileController::blankPixelCountForTiles(const PlatformLayerList& tiles, const FloatRect& visibleRect, const IntPoint& tileTranslation) 378{ 379 Region paintedVisibleTiles; 380 381 for (PlatformLayerList::const_iterator it = tiles.begin(), end = tiles.end(); it != end; ++it) { 382 const PlatformLayer* tileLayer = it->get(); 383 384 FloatRect visiblePart(CGRectOffset(PlatformCALayer::frameForLayer(tileLayer), tileTranslation.x(), tileTranslation.y())); 385 visiblePart.intersect(visibleRect); 386 387 if (!visiblePart.isEmpty()) 388 paintedVisibleTiles.unite(enclosingIntRect(visiblePart)); 389 } 390 391 Region uncoveredRegion(enclosingIntRect(visibleRect)); 392 uncoveredRegion.subtract(paintedVisibleTiles); 393 394 return uncoveredRegion.totalArea(); 395} 396 397void TileController::setNeedsRevalidateTiles() 398{ 399 owningGraphicsLayer()->platformCALayerSetNeedsToRevalidateTiles(); 400} 401 402void TileController::updateTileCoverageMap() 403{ 404 if (m_coverageMap) 405 m_coverageMap->update(); 406} 407 408IntRect TileController::tileGridExtent() const 409{ 410 return tileGrid().extent(); 411} 412 413double TileController::retainedTileBackingStoreMemory() const 414{ 415 double bytes = tileGrid().retainedTileBackingStoreMemory(); 416 if (m_zoomedOutTileGrid) 417 bytes += m_zoomedOutTileGrid->retainedTileBackingStoreMemory(); 418 return bytes; 419} 420 421// Return the rect in layer coords, not tile coords. 422IntRect TileController::tileCoverageRect() const 423{ 424 return tileGrid().tileCoverageRect(); 425} 426 427PlatformCALayer* TileController::tiledScrollingIndicatorLayer() 428{ 429 if (!m_coverageMap) 430 m_coverageMap = std::make_unique<TileCoverageMap>(*this); 431 432 return &m_coverageMap->layer(); 433} 434 435void TileController::setScrollingModeIndication(ScrollingModeIndication scrollingMode) 436{ 437 if (scrollingMode == m_indicatorMode) 438 return; 439 440 m_indicatorMode = scrollingMode; 441 442 updateTileCoverageMap(); 443} 444 445void TileController::setTileMargins(int marginTop, int marginBottom, int marginLeft, int marginRight) 446{ 447 m_marginTop = marginTop; 448 m_marginBottom = marginBottom; 449 m_marginLeft = marginLeft; 450 m_marginRight = marginRight; 451 452 setNeedsRevalidateTiles(); 453} 454 455bool TileController::hasMargins() const 456{ 457 return m_marginTop || m_marginBottom || m_marginLeft || m_marginRight; 458} 459 460bool TileController::hasHorizontalMargins() const 461{ 462 return m_marginLeft || m_marginRight; 463} 464 465bool TileController::hasVerticalMargins() const 466{ 467 return m_marginTop || m_marginBottom; 468} 469 470int TileController::topMarginHeight() const 471{ 472 return m_marginTop / tileGrid().scale(); 473} 474 475int TileController::bottomMarginHeight() const 476{ 477 return m_marginBottom / tileGrid().scale(); 478} 479 480int TileController::leftMarginWidth() const 481{ 482 return m_marginLeft / tileGrid().scale(); 483} 484 485int TileController::rightMarginWidth() const 486{ 487 return m_marginRight / tileGrid().scale(); 488} 489 490RefPtr<PlatformCALayer> TileController::createTileLayer(const IntRect& tileRect, TileGrid& grid) 491{ 492 RefPtr<PlatformCALayer> layer = m_tileCacheLayer->createCompatibleLayerOrTakeFromPool(PlatformCALayer::LayerTypeTiledBackingTileLayer, &grid, tileRect.size()); 493 494 layer->setAnchorPoint(FloatPoint3D()); 495 layer->setPosition(tileRect.location()); 496 layer->setBorderColor(m_tileDebugBorderColor); 497 layer->setBorderWidth(m_tileDebugBorderWidth); 498 layer->setEdgeAntialiasingMask(0); 499 layer->setOpaque(m_tilesAreOpaque); 500#ifndef NDEBUG 501 layer->setName("Tile"); 502#endif 503 504 float temporaryScaleFactor = owningGraphicsLayer()->platformCALayerContentsScaleMultiplierForNewTiles(m_tileCacheLayer); 505 m_hasTilesWithTemporaryScaleFactor |= temporaryScaleFactor != 1; 506 507 layer->setContentsScale(m_deviceScaleFactor * temporaryScaleFactor); 508 layer->setAcceleratesDrawing(m_acceleratesDrawing); 509 510 layer->setNeedsDisplay(); 511 512 return layer; 513} 514 515Vector<RefPtr<PlatformCALayer>> TileController::containerLayers() 516{ 517 Vector<RefPtr<PlatformCALayer>> layerList; 518 if (m_zoomedOutTileGrid) 519 layerList.append(&m_zoomedOutTileGrid->containerLayer()); 520 layerList.append(&tileGrid().containerLayer()); 521 return layerList; 522} 523 524#if PLATFORM(IOS) 525unsigned TileController::numberOfUnparentedTiles() const 526{ 527 unsigned count = tileGrid().numberOfUnparentedTiles(); 528 if (m_zoomedOutTileGrid) 529 count += m_zoomedOutTileGrid->numberOfUnparentedTiles(); 530 return count; 531} 532 533void TileController::removeUnparentedTilesNow() 534{ 535 tileGrid().removeUnparentedTilesNow(); 536 if (m_zoomedOutTileGrid) 537 m_zoomedOutTileGrid->removeUnparentedTilesNow(); 538 539 updateTileCoverageMap(); 540} 541#endif 542 543} // namespace WebCore 544