+2012-02-01 Anders Carlsson <andersca@apple.com>
+
+ 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 <mvujovic@adobe.com>
Add support for fixed and percent min-width on the table element for table-layout: auto to
: m_tileCacheLayer(tileCacheLayer)
, m_tileContainerLayer(adoptCF([[CALayer alloc] init]))
, m_tileSize(tileSize)
+ , m_tileRevalidationTimer(this, &TileCache::tileRevalidationTimerFired)
, m_acceleratesDrawing(false)
, m_tileDebugBorderWidth(0)
{
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()
void TileCache::setNeedsDisplayInRect(const IntRect& rect)
{
- if (m_numTilesInGrid.isZero())
+ if (m_tiles.isEmpty())
return;
// Find the tiles that need to be invalidated.
void TileCache::visibleRectChanged()
{
- // FIXME: Implement.
+ scheduleTileRevalidation();
}
void TileCache::setTileDebugBorderWidth(float borderWidth)
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;
+
+ // 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());
+
+ Vector<TileIndex> tilesToRemove;
- for (int y = 0; y < numTilesInGrid.height(); ++y) {
- for (int x = 0; x < numTilesInGrid.width(); ++x) {
- RetainPtr<WebTileLayer> tileLayer;
+ for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) {
+ const TileIndex& tileIndex = it->first;
- 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());
+ 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();
}