2 * Copyright (C) 2011-2014 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "TileController.h"
31 #include "PlatformCALayer.h"
33 #include "TextStream.h"
34 #include "TileCoverageMap.h"
37 #include <wtf/MainThread.h>
40 #include "MemoryPressureHandler.h"
41 #include "TileControllerMemoryHandlerIOS.h"
46 static const auto tileSizeUpdateDelay = std::chrono::milliseconds { 500 };
48 String TileController::tileGridContainerLayerName()
50 return ASCIILiteral("TileGrid Container Layer");
53 String TileController::zoomedOutTileGridContainerLayerName()
55 return ASCIILiteral("Zoomed Out TileGrid Container Layer");
58 TileController::TileController(PlatformCALayer* rootPlatformLayer)
59 : m_tileCacheLayer(rootPlatformLayer)
60 , m_tileGrid(std::make_unique<TileGrid>(*this))
61 , m_tileRevalidationTimer(*this, &TileController::tileRevalidationTimerFired)
62 , m_tileSizeChangeTimer(*this, &TileController::tileSizeChangeTimerFired, tileSizeUpdateDelay)
63 , m_deviceScaleFactor(owningGraphicsLayer()->platformCALayerDeviceScaleFactor())
64 , m_marginEdges(false, false, false, false)
68 TileController::~TileController()
70 ASSERT(isMainThread());
73 tileControllerMemoryHandler().removeTileController(this);
77 void TileController::tileCacheLayerBoundsChanged()
79 ASSERT(owningGraphicsLayer()->isCommittingChanges());
80 setNeedsRevalidateTiles();
81 notePendingTileSizeChange();
84 void TileController::setNeedsDisplay()
86 tileGrid().setNeedsDisplay();
87 clearZoomedOutTileGrid();
90 void TileController::setNeedsDisplayInRect(const IntRect& rect)
92 tileGrid().setNeedsDisplayInRect(rect);
93 if (m_zoomedOutTileGrid)
94 m_zoomedOutTileGrid->dropTilesInRect(rect);
95 updateTileCoverageMap();
98 void TileController::setContentsScale(float scale)
100 ASSERT(owningGraphicsLayer()->isCommittingChanges());
102 float deviceScaleFactor = owningGraphicsLayer()->platformCALayerDeviceScaleFactor();
103 // The scale we get is the product of the page scale factor and device scale factor.
104 // Divide by the device scale factor so we'll get the page scale factor.
105 scale /= deviceScaleFactor;
107 if (tileGrid().scale() == scale && m_deviceScaleFactor == deviceScaleFactor && !m_hasTilesWithTemporaryScaleFactor)
110 m_hasTilesWithTemporaryScaleFactor = false;
111 m_deviceScaleFactor = deviceScaleFactor;
114 m_coverageMap->setDeviceScaleFactor(deviceScaleFactor);
116 if (m_zoomedOutTileGrid && m_zoomedOutTileGrid->scale() == scale) {
117 m_tileGrid = WTFMove(m_zoomedOutTileGrid);
118 m_tileGrid->setIsZoomedOutTileGrid(false);
119 m_tileGrid->revalidateTiles();
124 if (m_zoomedOutContentsScale && m_zoomedOutContentsScale == tileGrid().scale() && tileGrid().scale() != scale && !m_hasTilesWithTemporaryScaleFactor) {
125 m_zoomedOutTileGrid = WTFMove(m_tileGrid);
126 m_zoomedOutTileGrid->setIsZoomedOutTileGrid(true);
127 m_tileGrid = std::make_unique<TileGrid>(*this);
131 tileGrid().setScale(scale);
132 tileGrid().setNeedsDisplay();
135 float TileController::contentsScale() const
137 return tileGrid().scale() * m_deviceScaleFactor;
140 float TileController::zoomedOutContentsScale() const
142 return m_zoomedOutContentsScale * m_deviceScaleFactor;
145 void TileController::setZoomedOutContentsScale(float scale)
147 ASSERT(owningGraphicsLayer()->isCommittingChanges());
149 float deviceScaleFactor = owningGraphicsLayer()->platformCALayerDeviceScaleFactor();
150 scale /= deviceScaleFactor;
152 if (m_zoomedOutContentsScale == scale)
154 m_zoomedOutContentsScale = scale;
156 if (m_zoomedOutTileGrid && m_zoomedOutTileGrid->scale() != m_zoomedOutContentsScale)
157 clearZoomedOutTileGrid();
160 void TileController::setAcceleratesDrawing(bool acceleratesDrawing)
162 if (m_acceleratesDrawing == acceleratesDrawing)
164 m_acceleratesDrawing = acceleratesDrawing;
166 tileGrid().updateTileLayerProperties();
169 void TileController::setTilesOpaque(bool opaque)
171 if (opaque == m_tilesAreOpaque)
173 m_tilesAreOpaque = opaque;
175 tileGrid().updateTileLayerProperties();
178 void TileController::setVisibleRect(const FloatRect& rect)
180 if (rect == m_visibleRect)
183 m_visibleRect = rect;
184 updateTileCoverageMap();
187 void TileController::setCoverageRect(const FloatRect& rect)
189 ASSERT(owningGraphicsLayer()->isCommittingChanges());
190 if (m_coverageRect == rect)
193 m_coverageRect = rect;
194 setNeedsRevalidateTiles();
197 bool TileController::tilesWouldChangeForCoverageRect(const FloatRect& rect) const
199 if (bounds().isEmpty())
202 return tileGrid().tilesWouldChangeForCoverageRect(rect);
205 void TileController::setVelocity(const VelocityData& velocity)
207 bool changeAffectsTileCoverage = m_velocity.velocityOrScaleIsChanging() || velocity.velocityOrScaleIsChanging();
208 m_velocity = velocity;
210 if (changeAffectsTileCoverage)
211 setNeedsRevalidateTiles();
214 void TileController::setScrollability(Scrollability scrollability)
216 if (scrollability == m_scrollability)
219 m_scrollability = scrollability;
220 notePendingTileSizeChange();
223 void TileController::setTopContentInset(float topContentInset)
225 m_topContentInset = topContentInset;
226 setTiledScrollingIndicatorPosition(FloatPoint(0, m_topContentInset));
229 void TileController::setTiledScrollingIndicatorPosition(const FloatPoint& position)
234 m_coverageMap->setPosition(position);
235 updateTileCoverageMap();
238 void TileController::prepopulateRect(const FloatRect& rect)
240 if (tileGrid().prepopulateRect(rect))
241 setNeedsRevalidateTiles();
244 void TileController::setIsInWindow(bool isInWindow)
246 if (m_isInWindow == isInWindow)
249 m_isInWindow = isInWindow;
252 setNeedsRevalidateTiles();
254 const double tileRevalidationTimeout = 4;
255 scheduleTileRevalidation(tileRevalidationTimeout);
259 void TileController::setTileCoverage(TileCoverage coverage)
261 if (coverage == m_tileCoverage)
264 m_tileCoverage = coverage;
265 setNeedsRevalidateTiles();
268 void TileController::revalidateTiles()
270 ASSERT(owningGraphicsLayer()->isCommittingChanges());
271 tileGrid().revalidateTiles();
274 void TileController::forceRepaint()
279 void TileController::setTileDebugBorderWidth(float borderWidth)
281 if (m_tileDebugBorderWidth == borderWidth)
283 m_tileDebugBorderWidth = borderWidth;
285 tileGrid().updateTileLayerProperties();
288 void TileController::setTileDebugBorderColor(Color borderColor)
290 if (m_tileDebugBorderColor == borderColor)
292 m_tileDebugBorderColor = borderColor;
294 tileGrid().updateTileLayerProperties();
297 IntRect TileController::boundsForSize(const FloatSize& size) const
299 IntPoint boundsOriginIncludingMargin(-leftMarginWidth(), -topMarginHeight());
300 IntSize boundsSizeIncludingMargin = expandedIntSize(size);
301 boundsSizeIncludingMargin.expand(leftMarginWidth() + rightMarginWidth(), topMarginHeight() + bottomMarginHeight());
303 return IntRect(boundsOriginIncludingMargin, boundsSizeIncludingMargin);
306 IntRect TileController::bounds() const
308 return boundsForSize(m_tileCacheLayer->bounds().size());
311 IntRect TileController::boundsWithoutMargin() const
313 return IntRect(IntPoint(), expandedIntSize(m_tileCacheLayer->bounds().size()));
316 IntRect TileController::boundsAtLastRevalidateWithoutMargin() const
318 IntRect boundsWithoutMargin = IntRect(IntPoint(), m_boundsAtLastRevalidate.size());
319 boundsWithoutMargin.contract(IntSize(leftMarginWidth() + rightMarginWidth(), topMarginHeight() + bottomMarginHeight()));
320 return boundsWithoutMargin;
324 // Return 'rect' padded evenly on all sides to achieve 'newSize', but make the padding uneven to contain within constrainingRect.
325 static FloatRect expandRectWithinRect(const FloatRect& rect, const FloatSize& newSize, const FloatRect& constrainingRect)
327 ASSERT(newSize.width() >= rect.width() && newSize.height() >= rect.height());
329 FloatSize extraSize = newSize - rect.size();
331 FloatRect expandedRect = rect;
332 expandedRect.inflateX(extraSize.width() / 2);
333 expandedRect.inflateY(extraSize.height() / 2);
335 if (expandedRect.x() < constrainingRect.x())
336 expandedRect.setX(constrainingRect.x());
337 else if (expandedRect.maxX() > constrainingRect.maxX())
338 expandedRect.setX(constrainingRect.maxX() - expandedRect.width());
340 if (expandedRect.y() < constrainingRect.y())
341 expandedRect.setY(constrainingRect.y());
342 else if (expandedRect.maxY() > constrainingRect.maxY())
343 expandedRect.setY(constrainingRect.maxY() - expandedRect.height());
345 return intersection(expandedRect, constrainingRect);
349 void TileController::adjustTileCoverageRect(FloatRect& coverageRect, const FloatSize& newSize, const FloatRect& previousVisibleRect, const FloatRect& visibleRect, float contentsScale) const
351 // 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.
353 coverageRect = visibleRect;
358 // FIXME: unify the iOS and Mac code.
359 UNUSED_PARAM(previousVisibleRect);
361 if (m_tileCoverage == CoverageForVisibleArea || MemoryPressureHandler::singleton().isUnderMemoryPressure()) {
362 coverageRect = visibleRect;
366 double horizontalMargin = kDefaultTileSize / contentsScale;
367 double verticalMargin = kDefaultTileSize / contentsScale;
369 double currentTime = monotonicallyIncreasingTime();
370 double timeDelta = currentTime - m_velocity.lastUpdateTime;
372 FloatRect futureRect = visibleRect;
373 futureRect.setLocation(FloatPoint(
374 futureRect.location().x() + timeDelta * m_velocity.horizontalVelocity,
375 futureRect.location().y() + timeDelta * m_velocity.verticalVelocity));
377 if (m_velocity.horizontalVelocity) {
378 futureRect.setWidth(futureRect.width() + horizontalMargin);
379 if (m_velocity.horizontalVelocity < 0)
380 futureRect.setX(futureRect.x() - horizontalMargin);
383 if (m_velocity.verticalVelocity) {
384 futureRect.setHeight(futureRect.height() + verticalMargin);
385 if (m_velocity.verticalVelocity < 0)
386 futureRect.setY(futureRect.y() - verticalMargin);
389 if (!m_velocity.horizontalVelocity && !m_velocity.verticalVelocity) {
390 if (m_velocity.scaleChangeRate > 0) {
391 coverageRect = visibleRect;
394 futureRect.setWidth(futureRect.width() + horizontalMargin);
395 futureRect.setHeight(futureRect.height() + verticalMargin);
396 futureRect.setX(futureRect.x() - horizontalMargin / 2);
397 futureRect.setY(futureRect.y() - verticalMargin / 2);
400 // Can't use m_tileCacheLayer->bounds() here, because the size of the underlying platform layer
401 // hasn't been updated for the current commit.
402 IntSize contentSize = expandedIntSize(newSize);
403 if (futureRect.maxX() > contentSize.width())
404 futureRect.setX(contentSize.width() - futureRect.width());
405 if (futureRect.maxY() > contentSize.height())
406 futureRect.setY(contentSize.height() - futureRect.height());
407 if (futureRect.x() < 0)
409 if (futureRect.y() < 0)
412 coverageRect.unite(futureRect);
415 UNUSED_PARAM(contentsScale);
417 // FIXME: look at how far the document can scroll in each dimension.
418 FloatSize coverageSize = visibleRect.size();
420 bool largeVisibleRectChange = !previousVisibleRect.isEmpty() && !visibleRect.intersects(previousVisibleRect);
422 // Inflate the coverage rect so that it covers 2x of the visible width and 3x of the visible height.
423 // These values were chosen because it's more common to have tall pages and to scroll vertically,
424 // so we keep more tiles above and below the current area.
425 float widthScale = 1;
426 float heightScale = 1;
428 if (m_tileCoverage & CoverageForHorizontalScrolling && !largeVisibleRectChange)
431 if (m_tileCoverage & CoverageForVerticalScrolling && !largeVisibleRectChange)
434 coverageSize.scale(widthScale, heightScale);
436 FloatRect coverageBounds = boundsForSize(newSize);
438 FloatRect coverage = expandRectWithinRect(visibleRect, coverageSize, coverageBounds);
439 LOG_WITH_STREAM(Scrolling, stream << "TileController::computeTileCoverageRect newSize=" << newSize << " mode " << m_tileCoverage << " expanded to " << coverageSize << " bounds with margin " << coverageBounds << " coverage " << coverage);
440 coverageRect.unite(coverage);
444 void TileController::scheduleTileRevalidation(double interval)
446 if (m_tileRevalidationTimer.isActive() && m_tileRevalidationTimer.nextFireInterval() < interval)
449 m_tileRevalidationTimer.startOneShot(interval);
452 bool TileController::shouldAggressivelyRetainTiles() const
454 return owningGraphicsLayer()->platformCALayerShouldAggressivelyRetainTiles(m_tileCacheLayer);
457 bool TileController::shouldTemporarilyRetainTileCohorts() const
459 return owningGraphicsLayer()->platformCALayerShouldTemporarilyRetainTileCohorts(m_tileCacheLayer);
462 void TileController::willStartLiveResize()
464 m_inLiveResize = true;
467 void TileController::didEndLiveResize()
469 m_inLiveResize = false;
470 m_tileSizeLocked = false; // Let the end of a live resize update the tiles.
473 void TileController::notePendingTileSizeChange()
475 m_tileSizeChangeTimer.restart();
478 void TileController::tileSizeChangeTimerFired()
480 if (!owningGraphicsLayer())
483 m_tileSizeLocked = false;
484 setNeedsRevalidateTiles();
487 IntSize TileController::tileSize() const
489 if (m_inLiveResize || m_tileSizeLocked)
490 return tileGrid().tileSize();
492 if (owningGraphicsLayer()->platformCALayerUseGiantTiles())
493 return IntSize(kGiantTileSize, kGiantTileSize);
495 IntSize tileSize(kDefaultTileSize, kDefaultTileSize);
497 if (m_scrollability == NotScrollable) {
498 IntSize scaledSize = expandedIntSize(boundsWithoutMargin().size() * tileGrid().scale());
499 tileSize = scaledSize.constrainedBetween(IntSize(kDefaultTileSize, kDefaultTileSize), IntSize(kGiantTileSize, kGiantTileSize));
500 } else if (m_scrollability == VerticallyScrollable)
501 tileSize.setWidth(std::min(std::max<int>(ceilf(boundsWithoutMargin().width() * tileGrid().scale()), kDefaultTileSize), kGiantTileSize));
503 LOG_WITH_STREAM(Scrolling, stream << "TileController::tileSize newSize=" << tileSize);
505 m_tileSizeLocked = true;
509 void TileController::clearZoomedOutTileGrid()
511 m_zoomedOutTileGrid = nullptr;
515 void TileController::tileGridsChanged()
517 return owningGraphicsLayer()->platformCALayerCustomSublayersChanged(m_tileCacheLayer);
520 void TileController::tileRevalidationTimerFired()
522 if (!owningGraphicsLayer())
526 setNeedsRevalidateTiles();
529 // If we are not visible get rid of the zoomed-out tiles.
530 clearZoomedOutTileGrid();
532 TileGrid::TileValidationPolicy validationPolicy = (shouldAggressivelyRetainTiles() ? 0 : TileGrid::PruneSecondaryTiles) | TileGrid::UnparentAllTiles;
534 tileGrid().revalidateTiles(validationPolicy);
537 void TileController::didRevalidateTiles()
539 m_boundsAtLastRevalidate = bounds();
541 updateTileCoverageMap();
544 unsigned TileController::blankPixelCount() const
546 return tileGrid().blankPixelCount();
549 unsigned TileController::blankPixelCountForTiles(const PlatformLayerList& tiles, const FloatRect& visibleRect, const IntPoint& tileTranslation)
551 Region paintedVisibleTiles;
553 for (PlatformLayerList::const_iterator it = tiles.begin(), end = tiles.end(); it != end; ++it) {
554 const PlatformLayer* tileLayer = it->get();
556 FloatRect visiblePart(CGRectOffset(PlatformCALayer::frameForLayer(tileLayer), tileTranslation.x(), tileTranslation.y()));
557 visiblePart.intersect(visibleRect);
559 if (!visiblePart.isEmpty())
560 paintedVisibleTiles.unite(enclosingIntRect(visiblePart));
563 Region uncoveredRegion(enclosingIntRect(visibleRect));
564 uncoveredRegion.subtract(paintedVisibleTiles);
566 return uncoveredRegion.totalArea();
569 void TileController::setNeedsRevalidateTiles()
571 owningGraphicsLayer()->platformCALayerSetNeedsToRevalidateTiles();
574 void TileController::updateTileCoverageMap()
577 m_coverageMap->setNeedsUpdate();
580 IntRect TileController::tileGridExtent() const
582 return tileGrid().extent();
585 double TileController::retainedTileBackingStoreMemory() const
587 double bytes = tileGrid().retainedTileBackingStoreMemory();
588 if (m_zoomedOutTileGrid)
589 bytes += m_zoomedOutTileGrid->retainedTileBackingStoreMemory();
593 // Return the rect in layer coords, not tile coords.
594 IntRect TileController::tileCoverageRect() const
596 return tileGrid().tileCoverageRect();
599 PlatformCALayer* TileController::tiledScrollingIndicatorLayer()
602 m_coverageMap = std::make_unique<TileCoverageMap>(*this);
604 return &m_coverageMap->layer();
607 void TileController::setScrollingModeIndication(ScrollingModeIndication scrollingMode)
609 if (scrollingMode == m_indicatorMode)
612 m_indicatorMode = scrollingMode;
614 updateTileCoverageMap();
617 void TileController::setHasMargins(bool marginTop, bool marginBottom, bool marginLeft, bool marginRight)
619 BoxExtent<bool> marginEdges(marginTop, marginRight, marginBottom, marginLeft);
620 if (marginEdges == m_marginEdges)
623 m_marginEdges = marginEdges;
624 setNeedsRevalidateTiles();
627 void TileController::setMarginSize(int marginSize)
629 if (marginSize == m_marginSize)
632 m_marginSize = marginSize;
633 setNeedsRevalidateTiles();
636 bool TileController::hasMargins() const
638 return m_marginSize && (m_marginEdges.top() || m_marginEdges.bottom() || m_marginEdges.left() || m_marginEdges.right());
641 bool TileController::hasHorizontalMargins() const
643 return m_marginSize && (m_marginEdges.left() || m_marginEdges.right());
646 bool TileController::hasVerticalMargins() const
648 return m_marginSize && (m_marginEdges.top() || m_marginEdges.bottom());
651 int TileController::topMarginHeight() const
653 return (m_marginSize * m_marginEdges.top()) / tileGrid().scale();
656 int TileController::bottomMarginHeight() const
658 return (m_marginSize * m_marginEdges.bottom()) / tileGrid().scale();
661 int TileController::leftMarginWidth() const
663 return (m_marginSize * m_marginEdges.left()) / tileGrid().scale();
666 int TileController::rightMarginWidth() const
668 return (m_marginSize * m_marginEdges.right()) / tileGrid().scale();
671 RefPtr<PlatformCALayer> TileController::createTileLayer(const IntRect& tileRect, TileGrid& grid)
673 RefPtr<PlatformCALayer> layer = m_tileCacheLayer->createCompatibleLayerOrTakeFromPool(PlatformCALayer::LayerTypeTiledBackingTileLayer, &grid, tileRect.size());
675 layer->setAnchorPoint(FloatPoint3D());
676 layer->setPosition(tileRect.location());
677 layer->setBorderColor(m_tileDebugBorderColor);
678 layer->setBorderWidth(m_tileDebugBorderWidth);
679 layer->setEdgeAntialiasingMask(0);
680 layer->setOpaque(m_tilesAreOpaque);
682 layer->setName("Tile");
685 float temporaryScaleFactor = owningGraphicsLayer()->platformCALayerContentsScaleMultiplierForNewTiles(m_tileCacheLayer);
686 m_hasTilesWithTemporaryScaleFactor |= temporaryScaleFactor != 1;
688 layer->setContentsScale(m_deviceScaleFactor * temporaryScaleFactor);
689 layer->setAcceleratesDrawing(m_acceleratesDrawing);
691 layer->setNeedsDisplay();
696 Vector<RefPtr<PlatformCALayer>> TileController::containerLayers()
698 Vector<RefPtr<PlatformCALayer>> layerList;
699 if (m_zoomedOutTileGrid)
700 layerList.append(&m_zoomedOutTileGrid->containerLayer());
701 layerList.append(&tileGrid().containerLayer());
706 unsigned TileController::numberOfUnparentedTiles() const
708 unsigned count = tileGrid().numberOfUnparentedTiles();
709 if (m_zoomedOutTileGrid)
710 count += m_zoomedOutTileGrid->numberOfUnparentedTiles();
714 void TileController::removeUnparentedTilesNow()
716 tileGrid().removeUnparentedTilesNow();
717 if (m_zoomedOutTileGrid)
718 m_zoomedOutTileGrid->removeUnparentedTilesNow();
720 updateTileCoverageMap();
724 } // namespace WebCore