- Revision
- 106482
- Author
- [email protected]
- Date
- 2012-02-01 11:59:47 -0800 (Wed, 01 Feb 2012)
Log Message
Tile cache doesn't have an upper limit
https://bugs.webkit.org/show_bug.cgi?id=77564
<rdar://problem/10710744>
Reviewed by Darin Adler.
Cache enough tiles to cover 3x the visible height and 2x the visible width of the page,
and drop tiles that are outside that area.
* platform/graphics/ca/GraphicsLayerCA.cpp:
(WebCore::GraphicsLayerCA::platformCALayerDidCreateTiles):
Call GraphicsLayerClient::notifySyncRequired here, which will schedule a layer flush and ensure that
the page layout is up to date before the new tiles are painted.
* platform/graphics/ca/PlatformCALayerClient.h:
Add platformCALayerDidCreateTiles member function.
* platform/graphics/ca/mac/TileCache.h:
Update for new/removed member functions and member variables.
* platform/graphics/ca/mac/TileCache.mm:
(WebCore::TileCache::TileCache):
Initialize the tile revalidation timer.
(WebCore::TileCache::tileCacheLayerBoundsChanged):
If we don't have any tiles at all right now, revalidate the tiles immediately. Otherwise,
schedule the revalidation timer.
(WebCore::TileCache::setNeedsDisplayInRect):
Return early if we have no tiles.
(WebCore::TileCache::visibleRectChanged):
Schedule tile revalidation.
(WebCore::TileCache::rectForTileIndex):
New helper function that returns the bounds rect of a tile given its tile index.
(WebCore::TileCache::getTileIndexRangeForRect):
Clamp the rect to the bounds of the tile cache layer.
(WebCore::TileCache::scheduleTileRevalidation):
Schedule the revalidation timer if it hasn't already been scheduled.
(WebCore::TileCache::tileRevalidationTimerFired):
Call revalidateTiles.
(WebCore::TileCache::revalidateTiles):
Compute the tile coverage rect and remove all tiles that are outside. Create new tiles for any
parts of the tile coverage rect that don't have tiles already.
(WebCore::TileCache::tileLayerAtIndex):
Remove invalid assertions.
Modified Paths
Diff
Modified: trunk/Source/WebCore/ChangeLog (106481 => 106482)
--- trunk/Source/WebCore/ChangeLog 2012-02-01 19:52:06 UTC (rev 106481)
+++ trunk/Source/WebCore/ChangeLog 2012-02-01 19:59:47 UTC (rev 106482)
@@ -1,3 +1,58 @@
+2012-02-01 Anders Carlsson <[email protected]>
+
+ Tile cache doesn't have an upper limit
+ https://bugs.webkit.org/show_bug.cgi?id=77564
+ <rdar://problem/10710744>
+
+ Reviewed by Darin Adler.
+
+ Cache enough tiles to cover 3x the visible height and 2x the visible width of the page,
+ and drop tiles that are outside that area.
+
+ * platform/graphics/ca/GraphicsLayerCA.cpp:
+ (WebCore::GraphicsLayerCA::platformCALayerDidCreateTiles):
+ Call GraphicsLayerClient::notifySyncRequired here, which will schedule a layer flush and ensure that
+ the page layout is up to date before the new tiles are painted.
+
+ * platform/graphics/ca/PlatformCALayerClient.h:
+ Add platformCALayerDidCreateTiles member function.
+
+ * platform/graphics/ca/mac/TileCache.h:
+ Update for new/removed member functions and member variables.
+
+ * platform/graphics/ca/mac/TileCache.mm:
+ (WebCore::TileCache::TileCache):
+ Initialize the tile revalidation timer.
+
+ (WebCore::TileCache::tileCacheLayerBoundsChanged):
+ If we don't have any tiles at all right now, revalidate the tiles immediately. Otherwise,
+ schedule the revalidation timer.
+
+ (WebCore::TileCache::setNeedsDisplayInRect):
+ Return early if we have no tiles.
+
+ (WebCore::TileCache::visibleRectChanged):
+ Schedule tile revalidation.
+
+ (WebCore::TileCache::rectForTileIndex):
+ New helper function that returns the bounds rect of a tile given its tile index.
+
+ (WebCore::TileCache::getTileIndexRangeForRect):
+ Clamp the rect to the bounds of the tile cache layer.
+
+ (WebCore::TileCache::scheduleTileRevalidation):
+ Schedule the revalidation timer if it hasn't already been scheduled.
+
+ (WebCore::TileCache::tileRevalidationTimerFired):
+ Call revalidateTiles.
+
+ (WebCore::TileCache::revalidateTiles):
+ Compute the tile coverage rect and remove all tiles that are outside. Create new tiles for any
+ parts of the tile coverage rect that don't have tiles already.
+
+ (WebCore::TileCache::tileLayerAtIndex):
+ Remove invalid assertions.
+
2012-02-01 Max Vujovic <[email protected]>
Add support for fixed and percent min-width on the table element for table-layout: auto to
Modified: trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp (106481 => 106482)
--- trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp 2012-02-01 19:52:06 UTC (rev 106481)
+++ trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp 2012-02-01 19:59:47 UTC (rev 106482)
@@ -933,6 +933,15 @@
paintGraphicsLayerContents(context, clip);
}
+void GraphicsLayerCA::platformCALayerDidCreateTiles()
+{
+ ASSERT(m_layer->layerType() == PlatformCALayer::LayerTypeTileCacheLayer);
+
+ // Ensure that the layout is up to date before any individual tiles are painted by telling the client
+ // that it needs to sync its layer state, which will end up scheduling the layer flusher.
+ client()->notifySyncRequired(this);
+}
+
void GraphicsLayerCA::commitLayerChangesBeforeSublayers(float pageScaleFactor, const FloatPoint& positionRelativeToBase)
{
if (!m_uncommittedChanges)
Modified: trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.h (106481 => 106482)
--- trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.h 2012-02-01 19:52:06 UTC (rev 106481)
+++ trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.h 2012-02-01 19:59:47 UTC (rev 106482)
@@ -157,6 +157,7 @@
virtual bool platformCALayerContentsOpaque() const { return contentsOpaque(); }
virtual bool platformCALayerDrawsContent() const { return drawsContent(); }
virtual void platformCALayerLayerDidDisplay(PlatformLayer* layer) { return layerDidDisplay(layer); }
+ virtual void platformCALayerDidCreateTiles() override;
void updateOpacityOnLayer();
Modified: trunk/Source/WebCore/platform/graphics/ca/PlatformCALayerClient.h (106481 => 106482)
--- trunk/Source/WebCore/platform/graphics/ca/PlatformCALayerClient.h 2012-02-01 19:52:06 UTC (rev 106481)
+++ trunk/Source/WebCore/platform/graphics/ca/PlatformCALayerClient.h 2012-02-01 19:59:47 UTC (rev 106482)
@@ -59,6 +59,8 @@
virtual bool platformCALayerDrawsContent() const = 0;
virtual void platformCALayerLayerDidDisplay(PlatformLayer*) = 0;
+ virtual void platformCALayerDidCreateTiles() = 0;
+
protected:
virtual ~PlatformCALayerClient() {}
};
Modified: trunk/Source/WebCore/platform/graphics/ca/mac/TileCache.h (106481 => 106482)
--- trunk/Source/WebCore/platform/graphics/ca/mac/TileCache.h 2012-02-01 19:52:06 UTC (rev 106481)
+++ trunk/Source/WebCore/platform/graphics/ca/mac/TileCache.h 2012-02-01 19:59:47 UTC (rev 106482)
@@ -28,6 +28,7 @@
#include "IntPointHash.h"
#include "IntSize.h"
+#include "Timer.h"
#include <wtf/HashMap.h>
#include <wtf/Noncopyable.h>
#include <wtf/PassOwnPtr.h>
@@ -73,12 +74,14 @@
TileCache(WebTileCacheLayer*, const IntSize& tileSize);
FloatRect visibleRect() const;
+ IntRect bounds() const;
- IntRect bounds() const;
+ IntRect rectForTileIndex(const TileIndex&) const;
void getTileIndexRangeForRect(const IntRect&, TileIndex& topLeft, TileIndex& bottomRight);
- IntSize numTilesForGridSize(const IntSize&) const;
- void resizeTileGrid(const IntSize& numTiles);
+ void scheduleTileRevalidation();
+ void tileRevalidationTimerFired(Timer<TileCache>*);
+ void revalidateTiles();
WebTileLayer* tileLayerAtIndex(const TileIndex&) const;
RetainPtr<WebTileLayer> createTileLayer();
@@ -89,11 +92,9 @@
RetainPtr<CALayer> m_tileContainerLayer;
const IntSize m_tileSize;
- // Number of tiles in each dimension.
- IntSize m_numTilesInGrid;
-
typedef HashMap<TileIndex, RetainPtr<WebTileLayer> > TileMap;
TileMap m_tiles;
+ Timer<TileCache> m_tileRevalidationTimer;
bool m_acceleratesDrawing;
Modified: trunk/Source/WebCore/platform/graphics/ca/mac/TileCache.mm (106481 => 106482)
--- trunk/Source/WebCore/platform/graphics/ca/mac/TileCache.mm 2012-02-01 19:52:06 UTC (rev 106481)
+++ trunk/Source/WebCore/platform/graphics/ca/mac/TileCache.mm 2012-02-01 19:59:47 UTC (rev 106482)
@@ -52,6 +52,7 @@
: m_tileCacheLayer(tileCacheLayer)
, m_tileContainerLayer(adoptCF([[CALayer alloc] init]))
, m_tileSize(tileSize)
+ , m_tileRevalidationTimer(this, &TileCache::tileRevalidationTimerFired)
, m_acceleratesDrawing(false)
, m_tileDebugBorderWidth(0)
{
@@ -63,11 +64,14 @@
void TileCache::tileCacheLayerBoundsChanged()
{
- IntSize numTilesInGrid = numTilesForGridSize(bounds().size());
- if (numTilesInGrid == m_numTilesInGrid)
+ if (m_tiles.isEmpty()) {
+ // We must revalidate immediately instead of using a timer when there are
+ // no tiles to avoid a flash when transitioning from one page to another.
+ revalidateTiles();
return;
+ }
- resizeTileGrid(numTilesInGrid);
+ scheduleTileRevalidation();
}
void TileCache::setNeedsDisplay()
@@ -77,7 +81,7 @@
void TileCache::setNeedsDisplayInRect(const IntRect& rect)
{
- if (m_numTilesInGrid.isZero())
+ if (m_tiles.isEmpty())
return;
// Find the tiles that need to be invalidated.
@@ -169,7 +173,7 @@
void TileCache::visibleRectChanged()
{
- // FIXME: Implement.
+ scheduleTileRevalidation();
}
void TileCache::setTileDebugBorderWidth(float borderWidth)
@@ -219,60 +223,104 @@
return IntRect(IntPoint(), IntSize([m_tileCacheLayer bounds].size));
}
+IntRect TileCache::rectForTileIndex(const TileIndex& tileIndex) const
+{
+ return IntRect(IntPoint(tileIndex.x() * m_tileSize.width(), tileIndex.y() * m_tileSize.height()), m_tileSize);
+}
+
void TileCache::getTileIndexRangeForRect(const IntRect& rect, TileIndex& topLeft, TileIndex& bottomRight)
{
- topLeft.setX(max(rect.x() / m_tileSize.width(), 0));
- topLeft.setY(max(rect.y() / m_tileSize.height(), 0));
- bottomRight.setX(min(rect.maxX() / m_tileSize.width(), m_numTilesInGrid.width() - 1));
- bottomRight.setY(min(rect.maxY() / m_tileSize.height(), m_numTilesInGrid.height() - 1));
+ IntRect clampedRect = intersection(rect, bounds());
+
+ topLeft.setX(max(clampedRect.x() / m_tileSize.width(), 0));
+ topLeft.setY(max(clampedRect.y() / m_tileSize.height(), 0));
+ bottomRight.setX(max(clampedRect.maxX() / m_tileSize.width(), 0));
+ bottomRight.setY(max(clampedRect.maxY() / m_tileSize.height(), 0));
}
-IntSize TileCache::numTilesForGridSize(const IntSize& gridSize) const
+void TileCache::scheduleTileRevalidation()
{
- int numXTiles = ceil(static_cast<double>(gridSize.width()) / m_tileSize.width());
- int numYTiles = ceil(static_cast<double>(gridSize.height()) / m_tileSize.height());
+ if (m_tileRevalidationTimer.isActive())
+ return;
- return IntSize(numXTiles, numYTiles);
+ m_tileRevalidationTimer.startOneShot(0);
}
-void TileCache::resizeTileGrid(const IntSize& numTilesInGrid)
+void TileCache::tileRevalidationTimerFired(Timer<TileCache>*)
{
- [CATransaction begin];
- [CATransaction setDisableActions:YES];
+ revalidateTiles();
+}
- RetainPtr<NSMutableArray> newSublayers = adoptNS([[NSMutableArray alloc] initWithCapacity:numTilesInGrid.width() * numTilesInGrid.height()]);
+void TileCache::revalidateTiles()
+{
+ IntRect tileCoverageRect = enclosingIntRect(visibleRect());
+ if (tileCoverageRect.isEmpty())
+ return;
- for (int y = 0; y < numTilesInGrid.height(); ++y) {
- for (int x = 0; x < numTilesInGrid.width(); ++x) {
- RetainPtr<WebTileLayer> tileLayer;
+ // Inflate the coverage rect so that it covers 2x of the visible width and 3x of the visible height.
+ // These values were chosen because it's more common to have tall pages and to scroll vertically,
+ // so we keep more tiles above and below the current area.
+ tileCoverageRect.inflateX(tileCoverageRect.width() / 2);
+ tileCoverageRect.inflateY(tileCoverageRect.height());
- if (x < m_numTilesInGrid.width() && y < m_numTilesInGrid.height()) {
- // We can reuse the tile layer at this index.
- tileLayer = tileLayerAtIndex(TileIndex(x, y));
- } else {
- tileLayer = createTileLayer();
- m_tiles.set(TileIndex(x, y), tileLayer.get());
+ Vector<TileIndex> tilesToRemove;
+
+ for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
+ const TileIndex& tileIndex = it->first;
+
+ WebTileLayer* tileLayer = it->second.get();
+
+ if (!rectForTileIndex(tileIndex).intersects(tileCoverageRect)) {
+ // Remove this layer.
+ [tileLayer removeFromSuperlayer];
+ [tileLayer setTileCache:0];
+
+ tilesToRemove.append(tileIndex);
+ }
+ }
+
+ // FIXME: Be more clever about which tiles to remove. We might not want to remove all
+ // the tiles that are outside the coverage rect. When we know that we're going to be scrolling up,
+ // we might want to remove the ones below the coverage rect but keep the ones above.
+ for (size_t i = 0; i < tilesToRemove.size(); ++i)
+ m_tiles.remove(tilesToRemove[i]);
+
+ TileIndex topLeft;
+ TileIndex bottomRight;
+ getTileIndexRangeForRect(tileCoverageRect, topLeft, bottomRight);
+
+ bool didCreateNewTiles = false;
+
+ for (int y = topLeft.y(); y <= bottomRight.y(); ++y) {
+ for (int x = topLeft.x(); x <= bottomRight.x(); ++x) {
+ TileIndex tileIndex(x, y);
+
+ pair<TileMap::iterator, bool> result = m_tiles.add(tileIndex, 0);
+ if (result.first->second) {
+ // We already have a layer for this tile.
+ continue;
}
+ didCreateNewTiles = true;
+
+ RetainPtr<WebTileLayer> tileLayer = createTileLayer();
+ result.first->second = tileLayer.get();
+
+ [tileLayer.get() setNeedsDisplay];
[tileLayer.get() setPosition:CGPointMake(x * m_tileSize.width(), y * m_tileSize.height())];
- [newSublayers.get() addObject:tileLayer.get()];
+ [m_tileContainerLayer.get() addSublayer:tileLayer.get()];
}
}
- // FIXME: Make sure to call setTileCache:0 on the layers that get thrown away here.
- [m_tileContainerLayer.get() setSublayers:newSublayers.get()];
- m_numTilesInGrid = numTilesInGrid;
+ if (!didCreateNewTiles)
+ return;
- [CATransaction commit];
+ PlatformCALayer* platformLayer = PlatformCALayer::platformCALayer(m_tileCacheLayer);
+ platformLayer->owner()->platformCALayerDidCreateTiles();
}
WebTileLayer* TileCache::tileLayerAtIndex(const TileIndex& index) const
{
- ASSERT(index.x() >= 0);
- ASSERT(index.x() <= m_numTilesInGrid.width());
- ASSERT(index.y() >= 0);
- ASSERT(index.y() <= m_numTilesInGrid.height());
-
return m_tiles.get(index).get();
}