e9fb37a1ba9508bb198ea2f1a38593249b92153e
[WebKit-https.git] / Source / WebCore / platform / graphics / ca / mac / TileController.mm
1 /*
2  * Copyright (C) 2011, 2012, 2013 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 #import "config.h"
27 #import "TileController.h"
28
29 #import "GraphicsContext.h"
30 #import "IntRect.h"
31 #import "PlatformCALayer.h"
32 #import "Region.h"
33 #import "TileGrid.h"
34 #if !PLATFORM(IOS)
35 #import "LayerPool.h"
36 #endif
37 #import "WebLayer.h"
38 #import <wtf/MainThread.h>
39 #import <utility>
40
41 #if PLATFORM(IOS)
42 #import "TileControllerMemoryHandlerIOS.h"
43 #endif
44
45 namespace WebCore {
46
47 enum TileValidationPolicyFlag {
48     PruneSecondaryTiles = 1 << 0,
49     UnparentAllTiles = 1 << 1
50 };
51
52 PassOwnPtr<TileController> TileController::create(PlatformCALayer* rootPlatformLayer)
53 {
54     return adoptPtr(new TileController(rootPlatformLayer));
55 }
56
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)
63     , m_deviceScaleFactor(1)
64     , m_tileCoverage(CoverageForVisibleArea)
65     , m_marginTop(0)
66     , m_marginBottom(0)
67     , m_marginLeft(0)
68     , m_marginRight(0)
69     , m_isInWindow(false)
70     , m_scrollingPerformanceLoggingEnabled(false)
71     , m_unparentsOffscreenTiles(false)
72     , m_acceleratesDrawing(false)
73     , m_tilesAreOpaque(false)
74     , m_hasTilesWithTemporaryScaleFactor(false)
75     , m_tileDebugBorderWidth(0)
76     , m_indicatorMode(AsyncScrollingIndication)
77 {
78 }
79
80 TileController::~TileController()
81 {
82     ASSERT(isMainThread());
83
84 #if PLATFORM(IOS)
85     tileControllerMemoryHandler().removeTileController(this);
86 #endif
87
88     if (m_tiledScrollingIndicatorLayer)
89         m_tiledScrollingIndicatorLayer->setOwner(nullptr);
90 }
91
92 void TileController::tileCacheLayerBoundsChanged()
93 {
94     ASSERT(owningGraphicsLayer()->isCommittingChanges());
95     setNeedsRevalidateTiles();
96 }
97
98 void TileController::setNeedsDisplay()
99 {
100     tileGrid().setNeedsDisplay();
101 }
102
103 void TileController::setNeedsDisplayInRect(const IntRect& rect)
104 {
105     tileGrid().setNeedsDisplayInRect(rect);
106 }
107
108 void TileController::platformCALayerPaintContents(PlatformCALayer* platformCALayer, GraphicsContext& context, const FloatRect&)
109 {
110 #if PLATFORM(IOS)
111     if (pthread_main_np())
112         WebThreadLock();
113 #endif
114
115     if (platformCALayer == m_tiledScrollingIndicatorLayer.get()) {
116         tileGrid().drawTileMapContents(context.platformContext(), m_tiledScrollingIndicatorLayer->bounds());
117         return;
118     }
119
120     {
121         GraphicsContextStateSaver stateSaver(context);
122
123         FloatPoint3D layerOrigin = platformCALayer->position();
124         context.translate(-layerOrigin.x(), -layerOrigin.y());
125         context.scale(FloatSize(tileGrid().scale(), tileGrid().scale()));
126
127         RepaintRectList dirtyRects = collectRectsToPaint(context.platformContext(), platformCALayer);
128         drawLayerContents(context.platformContext(), m_tileCacheLayer, dirtyRects);
129     }
130
131     int repaintCount = platformCALayerIncrementRepaintCount(platformCALayer);
132     if (owningGraphicsLayer()->platformCALayerShowRepaintCounter(0))
133         drawRepaintIndicator(context.platformContext(), platformCALayer, repaintCount, cachedCGColor(m_tileDebugBorderColor, ColorSpaceDeviceRGB));
134
135     if (scrollingPerformanceLoggingEnabled()) {
136         FloatRect visiblePart(platformCALayer->position().x(), platformCALayer->position().y(), platformCALayer->bounds().size().width(), platformCALayer->bounds().size().height());
137         visiblePart.intersect(visibleRect());
138
139         if (repaintCount == 1 && !visiblePart.isEmpty())
140             WTFLogAlways("SCROLLING: Filled visible fresh tile. Time: %f Unfilled Pixels: %u\n", WTF::monotonicallyIncreasingTime(), blankPixelCount());
141     }
142 }
143
144 float TileController::platformCALayerDeviceScaleFactor() const
145 {
146     return owningGraphicsLayer()->platformCALayerDeviceScaleFactor();
147 }
148
149 bool TileController::platformCALayerShowDebugBorders() const
150 {
151     return owningGraphicsLayer()->platformCALayerShowDebugBorders();
152 }
153
154 bool TileController::platformCALayerShowRepaintCounter(PlatformCALayer*) const
155 {
156     return owningGraphicsLayer()->platformCALayerShowRepaintCounter(0);
157 }
158
159 float TileController::scale() const
160 {
161     return tileGrid().scale();
162 }
163
164 void TileController::setScale(float scale)
165 {
166     ASSERT(owningGraphicsLayer()->isCommittingChanges());
167
168     float deviceScaleFactor = platformCALayerDeviceScaleFactor();
169
170     // The scale we get is the product of the page scale factor and device scale factor.
171     // Divide by the device scale factor so we'll get the page scale factor.
172     scale /= deviceScaleFactor;
173
174     if (tileGrid().scale() == scale && m_deviceScaleFactor == deviceScaleFactor && !m_hasTilesWithTemporaryScaleFactor)
175         return;
176
177     m_hasTilesWithTemporaryScaleFactor = false;
178     m_deviceScaleFactor = deviceScaleFactor;
179
180     tileGrid().setScale(scale);
181 }
182
183 void TileController::setAcceleratesDrawing(bool acceleratesDrawing)
184 {
185     if (m_acceleratesDrawing == acceleratesDrawing)
186         return;
187     m_acceleratesDrawing = acceleratesDrawing;
188
189     tileGrid().updateTilerLayerProperties();
190 }
191
192 void TileController::setTilesOpaque(bool opaque)
193 {
194     if (opaque == m_tilesAreOpaque)
195         return;
196     m_tilesAreOpaque = opaque;
197
198     tileGrid().updateTilerLayerProperties();
199 }
200
201 void TileController::setVisibleRect(const FloatRect& visibleRect)
202 {
203     ASSERT(owningGraphicsLayer()->isCommittingChanges());
204     if (m_visibleRect == visibleRect)
205         return;
206
207     m_visibleRect = visibleRect;
208     setNeedsRevalidateTiles();
209 }
210
211 bool TileController::tilesWouldChangeForVisibleRect(const FloatRect& newVisibleRect) const
212 {
213     if (bounds().isEmpty())
214         return false;
215     return tileGrid().tilesWouldChangeForVisibleRect(newVisibleRect, m_visibleRect);
216 }
217
218 void TileController::setExposedRect(const FloatRect& exposedRect)
219 {
220     if (m_exposedRect == exposedRect)
221         return;
222
223     m_exposedRect = exposedRect;
224     setNeedsRevalidateTiles();
225 }
226
227 void TileController::prepopulateRect(const FloatRect& rect)
228 {
229     if (tileGrid().prepopulateRect(rect))
230         setNeedsRevalidateTiles();
231 }
232
233 void TileController::setIsInWindow(bool isInWindow)
234 {
235     if (m_isInWindow == isInWindow)
236         return;
237
238     m_isInWindow = isInWindow;
239
240     if (m_isInWindow)
241         setNeedsRevalidateTiles();
242     else {
243         const double tileRevalidationTimeout = 4;
244         scheduleTileRevalidation(tileRevalidationTimeout);
245     }
246 }
247
248 void TileController::setTileCoverage(TileCoverage coverage)
249 {
250     if (coverage == m_tileCoverage)
251         return;
252
253     m_tileCoverage = coverage;
254     setNeedsRevalidateTiles();
255 }
256
257 void TileController::revalidateTiles()
258 {
259     ASSERT(owningGraphicsLayer()->isCommittingChanges());
260     tileGrid().revalidateTiles(0);
261     m_visibleRectAtLastRevalidate = m_visibleRect;
262 }
263
264 void TileController::forceRepaint()
265 {
266     setNeedsDisplay();
267 }
268
269 void TileController::setTileDebugBorderWidth(float borderWidth)
270 {
271     if (m_tileDebugBorderWidth == borderWidth)
272         return;
273     m_tileDebugBorderWidth = borderWidth;
274
275     tileGrid().updateTilerLayerProperties();
276 }
277
278 void TileController::setTileDebugBorderColor(Color borderColor)
279 {
280     if (m_tileDebugBorderColor == borderColor)
281         return;
282     m_tileDebugBorderColor = borderColor;
283
284     tileGrid().updateTilerLayerProperties();
285 }
286
287 IntRect TileController::bounds() const
288 {
289     IntPoint boundsOriginIncludingMargin(-leftMarginWidth(), -topMarginHeight());
290     IntSize boundsSizeIncludingMargin = expandedIntSize(m_tileCacheLayer->bounds().size());
291     boundsSizeIncludingMargin.expand(leftMarginWidth() + rightMarginWidth(), topMarginHeight() + bottomMarginHeight());
292
293     return IntRect(boundsOriginIncludingMargin, boundsSizeIncludingMargin);
294 }
295
296 IntRect TileController::boundsWithoutMargin() const
297 {
298     return IntRect(IntPoint(), expandedIntSize(m_tileCacheLayer->bounds().size()));
299 }
300
301 IntRect TileController::boundsAtLastRevalidateWithoutMargin() const
302 {
303     IntRect boundsWithoutMargin = IntRect(IntPoint(), m_boundsAtLastRevalidate.size());
304     boundsWithoutMargin.contract(IntSize(leftMarginWidth() + rightMarginWidth(), topMarginHeight() + bottomMarginHeight()));
305     return boundsWithoutMargin;
306 }
307
308 FloatRect TileController::computeTileCoverageRect(const FloatRect& previousVisibleRect, const FloatRect& visibleRect) const
309 {
310     // 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.
311     if (!m_isInWindow)
312         return visibleRect;
313
314     // FIXME: look at how far the document can scroll in each dimension.
315     float coverageHorizontalSize = visibleRect.width();
316     float coverageVerticalSize = visibleRect.height();
317
318 #if PLATFORM(IOS)
319     UNUSED_PARAM(previousVisibleRect);
320     return visibleRect;
321 #else
322     bool largeVisibleRectChange = !previousVisibleRect.isEmpty() && !visibleRect.intersects(previousVisibleRect);
323
324     // Inflate the coverage rect so that it covers 2x of the visible width and 3x of the visible height.
325     // These values were chosen because it's more common to have tall pages and to scroll vertically,
326     // so we keep more tiles above and below the current area.
327
328     if (m_tileCoverage & CoverageForHorizontalScrolling && !largeVisibleRectChange)
329         coverageHorizontalSize *= 2;
330
331     if (m_tileCoverage & CoverageForVerticalScrolling && !largeVisibleRectChange)
332         coverageVerticalSize *= 3;
333 #endif
334     coverageVerticalSize += topMarginHeight() + bottomMarginHeight();
335     coverageHorizontalSize += leftMarginWidth() + rightMarginWidth();
336
337     FloatRect coverageBounds = bounds();
338     float coverageLeft = visibleRect.x() - (coverageHorizontalSize - visibleRect.width()) / 2;
339     coverageLeft = std::min(coverageLeft, coverageBounds.maxX() - coverageHorizontalSize);
340     coverageLeft = std::max(coverageLeft, coverageBounds.x());
341
342     float coverageTop = visibleRect.y() - (coverageVerticalSize - visibleRect.height()) / 2;
343     coverageTop = std::min(coverageTop, coverageBounds.maxY() - coverageVerticalSize);
344     coverageTop = std::max(coverageTop, coverageBounds.y());
345
346     return FloatRect(coverageLeft, coverageTop, coverageHorizontalSize, coverageVerticalSize);
347 }
348
349 void TileController::scheduleTileRevalidation(double interval)
350 {
351     if (m_tileRevalidationTimer.isActive() && m_tileRevalidationTimer.nextFireInterval() < interval)
352         return;
353
354     m_tileRevalidationTimer.startOneShot(interval);
355 }
356
357 bool TileController::shouldAggressivelyRetainTiles() const
358 {
359     return owningGraphicsLayer()->platformCALayerShouldAggressivelyRetainTiles(m_tileCacheLayer);
360 }
361
362 bool TileController::shouldTemporarilyRetainTileCohorts() const
363 {
364     return owningGraphicsLayer()->platformCALayerShouldTemporarilyRetainTileCohorts(m_tileCacheLayer);
365 }
366
367 void TileController::tileRevalidationTimerFired(Timer<TileController>*)
368 {
369     if (m_isInWindow) {
370         setNeedsRevalidateTiles();
371         return;
372     }
373
374     TileGrid::TileValidationPolicyFlags validationPolicy = (shouldAggressivelyRetainTiles() ? 0 : PruneSecondaryTiles) | UnparentAllTiles;
375
376     tileGrid().revalidateTiles(validationPolicy);
377 }
378
379 void TileController::didRevalidateTiles()
380 {
381     m_visibleRectAtLastRevalidate = visibleRect();
382     m_boundsAtLastRevalidate = bounds();
383
384     updateTileCoverageMap();
385 }
386
387 unsigned TileController::blankPixelCount() const
388 {
389     return tileGrid().blankPixelCount();
390 }
391
392 unsigned TileController::blankPixelCountForTiles(const PlatformLayerList& tiles, const FloatRect& visibleRect, const IntPoint& tileTranslation)
393 {
394     Region paintedVisibleTiles;
395
396     for (PlatformLayerList::const_iterator it = tiles.begin(), end = tiles.end(); it != end; ++it) {
397         const PlatformLayer* tileLayer = it->get();
398
399         FloatRect visiblePart(CGRectOffset([tileLayer frame], tileTranslation.x(), tileTranslation.y()));
400         visiblePart.intersect(visibleRect);
401
402         if (!visiblePart.isEmpty())
403             paintedVisibleTiles.unite(enclosingIntRect(visiblePart));
404     }
405
406     Region uncoveredRegion(enclosingIntRect(visibleRect));
407     uncoveredRegion.subtract(paintedVisibleTiles);
408
409     return uncoveredRegion.totalArea();
410 }
411
412 void TileController::setNeedsRevalidateTiles()
413 {
414     owningGraphicsLayer()->platformCALayerSetNeedsToRevalidateTiles();
415 }
416
417 void TileController::updateTileCoverageMap()
418 {
419     if (!m_tiledScrollingIndicatorLayer)
420         return;
421     FloatRect containerBounds = bounds();
422     FloatRect visibleRect = this->visibleRect();
423
424     visibleRect.intersect(tileGrid().scaledExposedRect());
425     visibleRect.contract(4, 4); // Layer is positioned 2px from top and left edges.
426
427     float widthScale = 1;
428     float scale = 1;
429     if (!containerBounds.isEmpty()) {
430         widthScale = std::min<float>(visibleRect.width() / containerBounds.width(), 0.1);
431         scale = std::min(widthScale, visibleRect.height() / containerBounds.height());
432     }
433     
434     float indicatorScale = scale * tileGrid().scale();
435     FloatRect mapBounds = containerBounds;
436     mapBounds.scale(indicatorScale, indicatorScale);
437
438     if (!m_exposedRect.isInfinite())
439         m_tiledScrollingIndicatorLayer->setPosition(m_exposedRect.location() + FloatPoint(2, 2));
440     else
441         m_tiledScrollingIndicatorLayer->setPosition(FloatPoint(2, 2));
442
443     m_tiledScrollingIndicatorLayer->setBounds(mapBounds);
444     m_tiledScrollingIndicatorLayer->setNeedsDisplay();
445
446     visibleRect.scale(indicatorScale, indicatorScale);
447     visibleRect.expand(2, 2);
448     m_visibleRectIndicatorLayer->setPosition(visibleRect.location());
449     m_visibleRectIndicatorLayer->setBounds(FloatRect(FloatPoint(), visibleRect.size()));
450
451     Color visibleRectIndicatorColor;
452     switch (m_indicatorMode) {
453     case SynchronousScrollingBecauseOfStyleIndication:
454         visibleRectIndicatorColor = Color(255, 0, 0);
455         break;
456     case SynchronousScrollingBecauseOfEventHandlersIndication:
457         visibleRectIndicatorColor = Color(255, 255, 0);
458         break;
459     case AsyncScrollingIndication:
460         visibleRectIndicatorColor = Color(0, 200, 0);
461         break;
462     }
463
464     m_visibleRectIndicatorLayer->setBorderColor(visibleRectIndicatorColor);
465 }
466
467 IntRect TileController::tileGridExtent() const
468 {
469     return tileGrid().extent();
470 }
471
472 double TileController::retainedTileBackingStoreMemory() const
473 {
474     return tileGrid().retainedTileBackingStoreMemory();
475 }
476
477 // Return the rect in layer coords, not tile coords.
478 IntRect TileController::tileCoverageRect() const
479 {
480     return tileGrid().tileCoverageRect();
481 }
482
483 PlatformCALayer* TileController::tiledScrollingIndicatorLayer()
484 {
485     if (!m_tiledScrollingIndicatorLayer) {
486         m_tiledScrollingIndicatorLayer = m_tileCacheLayer->createCompatibleLayer(PlatformCALayer::LayerTypeSimpleLayer, this);
487         m_tiledScrollingIndicatorLayer->setOpacity(0.75);
488         m_tiledScrollingIndicatorLayer->setAnchorPoint(FloatPoint3D());
489         m_tiledScrollingIndicatorLayer->setBorderColor(Color::black);
490         m_tiledScrollingIndicatorLayer->setBorderWidth(1);
491         m_tiledScrollingIndicatorLayer->setPosition(FloatPoint(2, 2));
492
493         m_visibleRectIndicatorLayer = m_tileCacheLayer->createCompatibleLayer(PlatformCALayer::LayerTypeLayer, nullptr);
494         m_visibleRectIndicatorLayer->setBorderWidth(2);
495         m_visibleRectIndicatorLayer->setAnchorPoint(FloatPoint3D());
496         m_visibleRectIndicatorLayer->setBorderColor(Color(255, 0, 0));
497
498         m_tiledScrollingIndicatorLayer->appendSublayer(m_visibleRectIndicatorLayer.get());
499
500         updateTileCoverageMap();
501     }
502
503     return m_tiledScrollingIndicatorLayer.get();
504 }
505
506 void TileController::setScrollingModeIndication(ScrollingModeIndication scrollingMode)
507 {
508     if (scrollingMode == m_indicatorMode)
509         return;
510
511     m_indicatorMode = scrollingMode;
512
513     updateTileCoverageMap();
514 }
515
516 void TileController::setTileMargins(int marginTop, int marginBottom, int marginLeft, int marginRight)
517 {
518     m_marginTop = marginTop;
519     m_marginBottom = marginBottom;
520     m_marginLeft = marginLeft;
521     m_marginRight = marginRight;
522
523     setNeedsRevalidateTiles();
524 }
525
526 bool TileController::hasMargins() const
527 {
528     return m_marginTop || m_marginBottom || m_marginLeft || m_marginRight;
529 }
530
531 int TileController::topMarginHeight() const
532 {
533     return m_marginTop;
534 }
535
536 int TileController::bottomMarginHeight() const
537 {
538     return m_marginBottom;
539 }
540
541 int TileController::leftMarginWidth() const
542 {
543     return m_marginLeft;
544 }
545
546 int TileController::rightMarginWidth() const
547 {
548     return m_marginRight;
549 }
550
551 RefPtr<PlatformCALayer> TileController::createTileLayer(const IntRect& tileRect)
552 {
553 #if PLATFORM(IOS)
554     RefPtr<PlatformCALayer> layer;
555 #else
556     RefPtr<PlatformCALayer> layer = LayerPool::sharedPool()->takeLayerWithSize(tileRect.size());
557 #endif
558
559     if (layer) {
560         m_tileRepaintCounts.remove(layer.get());
561         layer->setOwner(this);
562     } else
563         layer = m_tileCacheLayer->createCompatibleLayer(PlatformCALayer::LayerTypeTiledBackingTileLayer, this);
564
565     layer->setAnchorPoint(FloatPoint3D());
566     layer->setBounds(FloatRect(FloatPoint(), tileRect.size()));
567     layer->setPosition(tileRect.location());
568     layer->setBorderColor(m_tileDebugBorderColor);
569     layer->setBorderWidth(m_tileDebugBorderWidth);
570     layer->setEdgeAntialiasingMask(0);
571     layer->setOpaque(m_tilesAreOpaque);
572 #ifndef NDEBUG
573     layer->setName("Tile");
574 #endif
575
576     float temporaryScaleFactor = owningGraphicsLayer()->platformCALayerContentsScaleMultiplierForNewTiles(m_tileCacheLayer);
577     m_hasTilesWithTemporaryScaleFactor |= temporaryScaleFactor != 1;
578
579     layer->setContentsScale(m_deviceScaleFactor * temporaryScaleFactor);
580     layer->setAcceleratesDrawing(m_acceleratesDrawing);
581
582     layer->setNeedsDisplay();
583
584     return layer;
585 }
586
587 int TileController::platformCALayerIncrementRepaintCount(PlatformCALayer* platformCALayer)
588 {
589     int repaintCount = 0;
590
591     if (m_tileRepaintCounts.contains(platformCALayer))
592         repaintCount = m_tileRepaintCounts.get(platformCALayer);
593
594     m_tileRepaintCounts.set(platformCALayer, ++repaintCount);
595
596     return repaintCount;
597 }
598
599 Vector<RefPtr<PlatformCALayer>> TileController::containerLayers()
600 {
601     Vector<RefPtr<PlatformCALayer>> layerList(1);
602     layerList[0] = &tileGrid().containerLayer();
603     return layerList;
604 }
605     
606 #if PLATFORM(IOS)
607 unsigned TileController::numberOfUnparentedTiles() const
608 {
609     return tileGrid().numberOfUnparentedTiles();
610 }
611
612 void TileController::removeUnparentedTilesNow()
613 {
614     tileGrid().removeUnparentedTilesNow();
615
616     updateTileCoverageMap();
617 }
618 #endif
619
620 } // namespace WebCore