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