2 * Copyright (C) 2011, 2012, 2013 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 #import "TileController.h"
29 #import "GraphicsContext.h"
31 #import "PlatformCALayer.h"
38 #import <wtf/MainThread.h>
42 #import "TileControllerMemoryHandlerIOS.h"
47 enum TileValidationPolicyFlag {
48 PruneSecondaryTiles = 1 << 0,
49 UnparentAllTiles = 1 << 1
52 PassOwnPtr<TileController> TileController::create(PlatformCALayer* rootPlatformLayer)
54 return adoptPtr(new TileController(rootPlatformLayer));
57 TileController::TileController(PlatformCALayer* rootPlatformLayer)
58 : m_tileCacheLayer(rootPlatformLayer)
59 , m_tileGrid(std::make_unique<TileGrid>(*this))
60 , m_tileSize(defaultTileWidth, defaultTileHeight)
61 , m_exposedRect(FloatRect::infiniteRect())
62 , m_tileRevalidationTimer(this, &TileController::tileRevalidationTimerFired)
64 , m_deviceScaleFactor(1)
65 , m_tileCoverage(CoverageForVisibleArea)
71 , m_scrollingPerformanceLoggingEnabled(false)
72 , m_unparentsOffscreenTiles(false)
73 , m_acceleratesDrawing(false)
74 , m_tilesAreOpaque(false)
75 , m_hasTilesWithTemporaryScaleFactor(false)
76 , m_tileDebugBorderWidth(0)
77 , m_indicatorMode(AsyncScrollingIndication)
81 TileController::~TileController()
83 ASSERT(isMainThread());
86 tileControllerMemoryHandler().removeTileController(this);
89 if (m_tiledScrollingIndicatorLayer)
90 m_tiledScrollingIndicatorLayer->setOwner(nullptr);
93 void TileController::tileCacheLayerBoundsChanged()
95 ASSERT(owningGraphicsLayer()->isCommittingChanges());
96 setNeedsRevalidateTiles();
99 void TileController::setNeedsDisplay()
101 tileGrid().setNeedsDisplay();
104 void TileController::setNeedsDisplayInRect(const IntRect& rect)
106 tileGrid().setNeedsDisplayInRect(rect);
109 void TileController::platformCALayerPaintContents(PlatformCALayer* platformCALayer, GraphicsContext& context, const FloatRect&)
112 if (pthread_main_np())
116 if (platformCALayer == m_tiledScrollingIndicatorLayer.get()) {
117 tileGrid().drawTileMapContents(context.platformContext(), m_tiledScrollingIndicatorLayer->bounds());
122 GraphicsContextStateSaver stateSaver(context);
124 FloatPoint3D layerOrigin = platformCALayer->position();
125 context.translate(-layerOrigin.x(), -layerOrigin.y());
126 context.scale(FloatSize(tileGrid().scale(), tileGrid().scale()));
128 RepaintRectList dirtyRects = collectRectsToPaint(context.platformContext(), platformCALayer);
129 drawLayerContents(context.platformContext(), m_tileCacheLayer, dirtyRects);
132 int repaintCount = platformCALayerIncrementRepaintCount(platformCALayer);
133 if (owningGraphicsLayer()->platformCALayerShowRepaintCounter(0))
134 drawRepaintIndicator(context.platformContext(), platformCALayer, repaintCount, cachedCGColor(m_tileDebugBorderColor, ColorSpaceDeviceRGB));
136 if (scrollingPerformanceLoggingEnabled()) {
137 FloatRect visiblePart(platformCALayer->position().x(), platformCALayer->position().y(), platformCALayer->bounds().size().width(), platformCALayer->bounds().size().height());
138 visiblePart.intersect(visibleRect());
140 if (repaintCount == 1 && !visiblePart.isEmpty())
141 WTFLogAlways("SCROLLING: Filled visible fresh tile. Time: %f Unfilled Pixels: %u\n", WTF::monotonicallyIncreasingTime(), blankPixelCount());
145 float TileController::platformCALayerDeviceScaleFactor() const
147 return owningGraphicsLayer()->platformCALayerDeviceScaleFactor();
150 bool TileController::platformCALayerShowDebugBorders() const
152 return owningGraphicsLayer()->platformCALayerShowDebugBorders();
155 bool TileController::platformCALayerShowRepaintCounter(PlatformCALayer*) const
157 return owningGraphicsLayer()->platformCALayerShowRepaintCounter(0);
160 void TileController::setContentsScale(float scale)
162 m_contentsScale = scale;
164 ASSERT(owningGraphicsLayer()->isCommittingChanges());
166 float deviceScaleFactor = platformCALayerDeviceScaleFactor();
168 // The scale we get is the product of the page scale factor and device scale factor.
169 // Divide by the device scale factor so we'll get the page scale factor.
170 scale /= deviceScaleFactor;
172 if (tileGrid().scale() == scale && m_deviceScaleFactor == deviceScaleFactor && !m_hasTilesWithTemporaryScaleFactor)
175 m_hasTilesWithTemporaryScaleFactor = false;
176 m_deviceScaleFactor = deviceScaleFactor;
178 tileGrid().setScale(scale);
181 void TileController::setAcceleratesDrawing(bool acceleratesDrawing)
183 if (m_acceleratesDrawing == acceleratesDrawing)
185 m_acceleratesDrawing = acceleratesDrawing;
187 tileGrid().updateTilerLayerProperties();
190 void TileController::setTilesOpaque(bool opaque)
192 if (opaque == m_tilesAreOpaque)
194 m_tilesAreOpaque = opaque;
196 tileGrid().updateTilerLayerProperties();
199 void TileController::setVisibleRect(const FloatRect& visibleRect)
201 ASSERT(owningGraphicsLayer()->isCommittingChanges());
202 if (m_visibleRect == visibleRect)
205 m_visibleRect = visibleRect;
206 setNeedsRevalidateTiles();
209 bool TileController::tilesWouldChangeForVisibleRect(const FloatRect& newVisibleRect) const
211 if (bounds().isEmpty())
213 return tileGrid().tilesWouldChangeForVisibleRect(newVisibleRect, m_visibleRect);
216 void TileController::setExposedRect(const FloatRect& exposedRect)
218 if (m_exposedRect == exposedRect)
221 m_exposedRect = exposedRect;
222 setNeedsRevalidateTiles();
225 void TileController::prepopulateRect(const FloatRect& rect)
227 if (tileGrid().prepopulateRect(rect))
228 setNeedsRevalidateTiles();
231 void TileController::setIsInWindow(bool isInWindow)
233 if (m_isInWindow == isInWindow)
236 m_isInWindow = isInWindow;
239 setNeedsRevalidateTiles();
241 const double tileRevalidationTimeout = 4;
242 scheduleTileRevalidation(tileRevalidationTimeout);
246 void TileController::setTileCoverage(TileCoverage coverage)
248 if (coverage == m_tileCoverage)
251 m_tileCoverage = coverage;
252 setNeedsRevalidateTiles();
255 void TileController::revalidateTiles()
257 ASSERT(owningGraphicsLayer()->isCommittingChanges());
258 tileGrid().revalidateTiles(0);
259 m_visibleRectAtLastRevalidate = m_visibleRect;
262 void TileController::forceRepaint()
267 void TileController::setTileDebugBorderWidth(float borderWidth)
269 if (m_tileDebugBorderWidth == borderWidth)
271 m_tileDebugBorderWidth = borderWidth;
273 tileGrid().updateTilerLayerProperties();
276 void TileController::setTileDebugBorderColor(Color borderColor)
278 if (m_tileDebugBorderColor == borderColor)
280 m_tileDebugBorderColor = borderColor;
282 tileGrid().updateTilerLayerProperties();
285 IntRect TileController::bounds() const
287 IntPoint boundsOriginIncludingMargin(-leftMarginWidth(), -topMarginHeight());
288 IntSize boundsSizeIncludingMargin = expandedIntSize(m_tileCacheLayer->bounds().size());
289 boundsSizeIncludingMargin.expand(leftMarginWidth() + rightMarginWidth(), topMarginHeight() + bottomMarginHeight());
291 return IntRect(boundsOriginIncludingMargin, boundsSizeIncludingMargin);
294 IntRect TileController::boundsWithoutMargin() const
296 return IntRect(IntPoint(), expandedIntSize(m_tileCacheLayer->bounds().size()));
299 IntRect TileController::boundsAtLastRevalidateWithoutMargin() const
301 IntRect boundsWithoutMargin = IntRect(IntPoint(), m_boundsAtLastRevalidate.size());
302 boundsWithoutMargin.contract(IntSize(leftMarginWidth() + rightMarginWidth(), topMarginHeight() + bottomMarginHeight()));
303 return boundsWithoutMargin;
306 FloatRect TileController::computeTileCoverageRect(const FloatRect& previousVisibleRect, const FloatRect& visibleRect) const
308 // 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.
312 // FIXME: look at how far the document can scroll in each dimension.
313 float coverageHorizontalSize = visibleRect.width();
314 float coverageVerticalSize = visibleRect.height();
317 UNUSED_PARAM(previousVisibleRect);
320 bool largeVisibleRectChange = !previousVisibleRect.isEmpty() && !visibleRect.intersects(previousVisibleRect);
322 // Inflate the coverage rect so that it covers 2x of the visible width and 3x of the visible height.
323 // These values were chosen because it's more common to have tall pages and to scroll vertically,
324 // so we keep more tiles above and below the current area.
326 if (m_tileCoverage & CoverageForHorizontalScrolling && !largeVisibleRectChange)
327 coverageHorizontalSize *= 2;
329 if (m_tileCoverage & CoverageForVerticalScrolling && !largeVisibleRectChange)
330 coverageVerticalSize *= 3;
332 coverageVerticalSize += topMarginHeight() + bottomMarginHeight();
333 coverageHorizontalSize += leftMarginWidth() + rightMarginWidth();
335 FloatRect coverageBounds = bounds();
336 float coverageLeft = visibleRect.x() - (coverageHorizontalSize - visibleRect.width()) / 2;
337 coverageLeft = std::min(coverageLeft, coverageBounds.maxX() - coverageHorizontalSize);
338 coverageLeft = std::max(coverageLeft, coverageBounds.x());
340 float coverageTop = visibleRect.y() - (coverageVerticalSize - visibleRect.height()) / 2;
341 coverageTop = std::min(coverageTop, coverageBounds.maxY() - coverageVerticalSize);
342 coverageTop = std::max(coverageTop, coverageBounds.y());
344 return FloatRect(coverageLeft, coverageTop, coverageHorizontalSize, coverageVerticalSize);
347 void TileController::scheduleTileRevalidation(double interval)
349 if (m_tileRevalidationTimer.isActive() && m_tileRevalidationTimer.nextFireInterval() < interval)
352 m_tileRevalidationTimer.startOneShot(interval);
355 bool TileController::shouldAggressivelyRetainTiles() const
357 return owningGraphicsLayer()->platformCALayerShouldAggressivelyRetainTiles(m_tileCacheLayer);
360 bool TileController::shouldTemporarilyRetainTileCohorts() const
362 return owningGraphicsLayer()->platformCALayerShouldTemporarilyRetainTileCohorts(m_tileCacheLayer);
365 void TileController::tileRevalidationTimerFired(Timer<TileController>*)
368 setNeedsRevalidateTiles();
372 TileGrid::TileValidationPolicyFlags validationPolicy = (shouldAggressivelyRetainTiles() ? 0 : PruneSecondaryTiles) | UnparentAllTiles;
374 tileGrid().revalidateTiles(validationPolicy);
377 void TileController::didRevalidateTiles()
379 m_visibleRectAtLastRevalidate = visibleRect();
380 m_boundsAtLastRevalidate = bounds();
382 updateTileCoverageMap();
385 unsigned TileController::blankPixelCount() const
387 return tileGrid().blankPixelCount();
390 unsigned TileController::blankPixelCountForTiles(const PlatformLayerList& tiles, const FloatRect& visibleRect, const IntPoint& tileTranslation)
392 Region paintedVisibleTiles;
394 for (PlatformLayerList::const_iterator it = tiles.begin(), end = tiles.end(); it != end; ++it) {
395 const PlatformLayer* tileLayer = it->get();
397 FloatRect visiblePart(CGRectOffset([tileLayer frame], tileTranslation.x(), tileTranslation.y()));
398 visiblePart.intersect(visibleRect);
400 if (!visiblePart.isEmpty())
401 paintedVisibleTiles.unite(enclosingIntRect(visiblePart));
404 Region uncoveredRegion(enclosingIntRect(visibleRect));
405 uncoveredRegion.subtract(paintedVisibleTiles);
407 return uncoveredRegion.totalArea();
410 void TileController::setNeedsRevalidateTiles()
412 owningGraphicsLayer()->platformCALayerSetNeedsToRevalidateTiles();
415 void TileController::updateTileCoverageMap()
417 if (!m_tiledScrollingIndicatorLayer)
419 FloatRect containerBounds = bounds();
420 FloatRect visibleRect = this->visibleRect();
422 visibleRect.intersect(tileGrid().scaledExposedRect());
423 visibleRect.contract(4, 4); // Layer is positioned 2px from top and left edges.
425 float widthScale = 1;
427 if (!containerBounds.isEmpty()) {
428 widthScale = std::min<float>(visibleRect.width() / containerBounds.width(), 0.1);
429 scale = std::min(widthScale, visibleRect.height() / containerBounds.height());
432 float indicatorScale = scale * tileGrid().scale();
433 FloatRect mapBounds = containerBounds;
434 mapBounds.scale(indicatorScale, indicatorScale);
436 if (!m_exposedRect.isInfinite())
437 m_tiledScrollingIndicatorLayer->setPosition(m_exposedRect.location() + FloatPoint(2, 2));
439 m_tiledScrollingIndicatorLayer->setPosition(FloatPoint(2, 2));
441 m_tiledScrollingIndicatorLayer->setBounds(mapBounds);
442 m_tiledScrollingIndicatorLayer->setNeedsDisplay();
444 visibleRect.scale(indicatorScale, indicatorScale);
445 visibleRect.expand(2, 2);
446 m_visibleRectIndicatorLayer->setPosition(visibleRect.location());
447 m_visibleRectIndicatorLayer->setBounds(FloatRect(FloatPoint(), visibleRect.size()));
449 Color visibleRectIndicatorColor;
450 switch (m_indicatorMode) {
451 case SynchronousScrollingBecauseOfStyleIndication:
452 visibleRectIndicatorColor = Color(255, 0, 0);
454 case SynchronousScrollingBecauseOfEventHandlersIndication:
455 visibleRectIndicatorColor = Color(255, 255, 0);
457 case AsyncScrollingIndication:
458 visibleRectIndicatorColor = Color(0, 200, 0);
462 m_visibleRectIndicatorLayer->setBorderColor(visibleRectIndicatorColor);
465 IntRect TileController::tileGridExtent() const
467 return tileGrid().extent();
470 double TileController::retainedTileBackingStoreMemory() const
472 return tileGrid().retainedTileBackingStoreMemory();
475 // Return the rect in layer coords, not tile coords.
476 IntRect TileController::tileCoverageRect() const
478 return tileGrid().tileCoverageRect();
481 PlatformCALayer* TileController::tiledScrollingIndicatorLayer()
483 if (!m_tiledScrollingIndicatorLayer) {
484 m_tiledScrollingIndicatorLayer = m_tileCacheLayer->createCompatibleLayer(PlatformCALayer::LayerTypeSimpleLayer, this);
485 m_tiledScrollingIndicatorLayer->setOpacity(0.75);
486 m_tiledScrollingIndicatorLayer->setAnchorPoint(FloatPoint3D());
487 m_tiledScrollingIndicatorLayer->setBorderColor(Color::black);
488 m_tiledScrollingIndicatorLayer->setBorderWidth(1);
489 m_tiledScrollingIndicatorLayer->setPosition(FloatPoint(2, 2));
491 m_visibleRectIndicatorLayer = m_tileCacheLayer->createCompatibleLayer(PlatformCALayer::LayerTypeLayer, nullptr);
492 m_visibleRectIndicatorLayer->setBorderWidth(2);
493 m_visibleRectIndicatorLayer->setAnchorPoint(FloatPoint3D());
494 m_visibleRectIndicatorLayer->setBorderColor(Color(255, 0, 0));
496 m_tiledScrollingIndicatorLayer->appendSublayer(m_visibleRectIndicatorLayer.get());
498 updateTileCoverageMap();
501 return m_tiledScrollingIndicatorLayer.get();
504 void TileController::setScrollingModeIndication(ScrollingModeIndication scrollingMode)
506 if (scrollingMode == m_indicatorMode)
509 m_indicatorMode = scrollingMode;
511 updateTileCoverageMap();
514 void TileController::setTileMargins(int marginTop, int marginBottom, int marginLeft, int marginRight)
516 m_marginTop = marginTop;
517 m_marginBottom = marginBottom;
518 m_marginLeft = marginLeft;
519 m_marginRight = marginRight;
521 setNeedsRevalidateTiles();
524 bool TileController::hasMargins() const
526 return m_marginTop || m_marginBottom || m_marginLeft || m_marginRight;
529 int TileController::topMarginHeight() const
534 int TileController::bottomMarginHeight() const
536 return m_marginBottom;
539 int TileController::leftMarginWidth() const
544 int TileController::rightMarginWidth() const
546 return m_marginRight;
549 RefPtr<PlatformCALayer> TileController::createTileLayer(const IntRect& tileRect)
552 RefPtr<PlatformCALayer> layer;
554 RefPtr<PlatformCALayer> layer = LayerPool::sharedPool()->takeLayerWithSize(tileRect.size());
558 m_tileRepaintCounts.remove(layer.get());
559 layer->setOwner(this);
561 layer = m_tileCacheLayer->createCompatibleLayer(PlatformCALayer::LayerTypeTiledBackingTileLayer, this);
563 layer->setAnchorPoint(FloatPoint3D());
564 layer->setBounds(FloatRect(FloatPoint(), tileRect.size()));
565 layer->setPosition(tileRect.location());
566 layer->setBorderColor(m_tileDebugBorderColor);
567 layer->setBorderWidth(m_tileDebugBorderWidth);
568 layer->setEdgeAntialiasingMask(0);
569 layer->setOpaque(m_tilesAreOpaque);
571 layer->setName("Tile");
574 float temporaryScaleFactor = owningGraphicsLayer()->platformCALayerContentsScaleMultiplierForNewTiles(m_tileCacheLayer);
575 m_hasTilesWithTemporaryScaleFactor |= temporaryScaleFactor != 1;
577 layer->setContentsScale(m_deviceScaleFactor * temporaryScaleFactor);
578 layer->setAcceleratesDrawing(m_acceleratesDrawing);
580 layer->setNeedsDisplay();
585 int TileController::platformCALayerIncrementRepaintCount(PlatformCALayer* platformCALayer)
587 int repaintCount = 0;
589 if (m_tileRepaintCounts.contains(platformCALayer))
590 repaintCount = m_tileRepaintCounts.get(platformCALayer);
592 m_tileRepaintCounts.set(platformCALayer, ++repaintCount);
597 Vector<RefPtr<PlatformCALayer>> TileController::containerLayers()
599 Vector<RefPtr<PlatformCALayer>> layerList(1);
600 layerList[0] = &tileGrid().containerLayer();
605 unsigned TileController::numberOfUnparentedTiles() const
607 return tileGrid().numberOfUnparentedTiles();
610 void TileController::removeUnparentedTilesNow()
612 tileGrid().removeUnparentedTilesNow();
614 updateTileCoverageMap();
618 } // namespace WebCore