void TileGrid::setNeedsDisplayInRect(const IntRect& rect) { if (m_tiles.isEmpty()) return; FloatRect scaledRect(rect); scaledRect.scale(m_scale); IntRect repaintRectInTileCoords(enclosingIntRect(scaledRect)); IntSize tileSize = m_controller.tileSize(); // For small invalidations, lookup the covered tiles. if (repaintRectInTileCoords.height() < 2 * tileSize.height() && repaintRectInTileCoords.width() < 2 * tileSize.width()) { TileIndex topLeft; TileIndex bottomRight; getTileIndexRangeForRect(repaintRectInTileCoords, topLeft, bottomRight); for (int y = topLeft.y(); y <= bottomRight.y(); ++y) { for (int x = topLeft.x(); x <= bottomRight.x(); ++x) { TileIndex tileIndex(x, y); TileMap::iterator it = m_tiles.find(tileIndex); if (it != m_tiles.end()) setTileNeedsDisplayInRect(tileIndex, it->value, repaintRectInTileCoords, m_primaryTileCoverageRect); } } return; } for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) setTileNeedsDisplayInRect(it->key, it->value, repaintRectInTileCoords, m_primaryTileCoverageRect); }
IntRect TileGrid::extent() const { TileIndex topLeft; TileIndex bottomRight; getTileIndexRangeForRect(m_primaryTileCoverageRect, topLeft, bottomRight); // Return index of top, left tile and the number of tiles across and down. return IntRect(topLeft.x(), topLeft.y(), bottomRight.x() - topLeft.x() + 1, bottomRight.y() - topLeft.y() + 1); }
IntRect TileGrid::rectForTileIndex(const TileIndex& tileIndex) const { // FIXME: calculating the scaled size here should match with the rest of calculated sizes where we use the combination of // enclosingIntRect, expandedIntSize (floor vs ceil). // However enclosing this size could reveal gap on root layer's background. see RenderView::backgroundRect() IntSize tileSize = m_controller.tileSize(); IntRect rect(tileIndex.x() * tileSize.width(), tileIndex.y() * tileSize.height(), tileSize.width(), tileSize.height()); IntRect scaledBounds(m_controller.bounds()); scaledBounds.scale(m_scale); rect.intersect(scaledBounds); return rect; }
IntRect TileGrid::ensureTilesForRect(const FloatRect& rect, CoverageType newTileType) { if (m_controller.unparentsOffscreenTiles() && !m_controller.isInWindow()) return IntRect(); FloatRect scaledRect(rect); scaledRect.scale(m_scale); IntRect rectInTileCoords(enclosingIntRect(scaledRect)); TileIndex topLeft; TileIndex bottomRight; getTileIndexRangeForRect(rectInTileCoords, topLeft, bottomRight); TileCohort currCohort = nextTileCohort(); unsigned tilesInCohort = 0; IntRect coverageRect; for (int y = topLeft.y(); y <= bottomRight.y(); ++y) { for (int x = topLeft.x(); x <= bottomRight.x(); ++x) { TileIndex tileIndex(x, y); IntRect tileRect = rectForTileIndex(tileIndex); TileInfo& tileInfo = m_tiles.add(tileIndex, TileInfo()).iterator->value; coverageRect.unite(tileRect); bool shouldChangeTileLayerFrame = false; if (!tileInfo.layer) { tileInfo.layer = m_controller.createTileLayer(tileRect, *this); ASSERT(!m_tileRepaintCounts.contains(tileInfo.layer.get())); } else { // We already have a layer for this tile. Ensure that its size is correct. FloatSize tileLayerSize(tileInfo.layer->bounds().size()); shouldChangeTileLayerFrame = tileLayerSize != FloatSize(tileRect.size()); if (shouldChangeTileLayerFrame) { tileInfo.layer->setBounds(FloatRect(FloatPoint(), tileRect.size())); tileInfo.layer->setPosition(tileRect.location()); tileInfo.layer->setNeedsDisplay(); } } if (newTileType == CoverageType::SecondaryTiles && !tileRect.intersects(m_primaryTileCoverageRect)) { tileInfo.cohort = currCohort; ++tilesInCohort; } bool shouldParentTileLayer = (!m_controller.unparentsOffscreenTiles() || m_controller.isInWindow()) && !tileInfo.layer->superlayer(); if (shouldParentTileLayer) m_containerLayer.get().appendSublayer(*tileInfo.layer); } } if (tilesInCohort) startedNewCohort(currCohort); return coverageRect; }
void TileGrid::revalidateTiles(TileValidationPolicy validationPolicy) { FloatRect coverageRect = m_controller.coverageRect(); IntRect bounds = m_controller.bounds(); if (coverageRect.isEmpty() || bounds.isEmpty()) return; FloatRect scaledRect(coverageRect); scaledRect.scale(m_scale); IntRect coverageRectInTileCoords(enclosingIntRect(scaledRect)); TileCohort currCohort = nextTileCohort(); unsigned tilesInCohort = 0; double minimumRevalidationTimerDuration = std::numeric_limits<double>::max(); bool needsTileRevalidation = false; // Move tiles newly outside the coverage rect into the cohort map. for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) { TileInfo& tileInfo = it->value; TileIndex tileIndex = it->key; PlatformCALayer* tileLayer = tileInfo.layer.get(); IntRect tileRect = rectForTileIndex(tileIndex); if (tileRect.intersects(coverageRectInTileCoords)) { tileInfo.cohort = VisibleTileCohort; if (tileInfo.hasStaleContent) { // FIXME: store a dirty region per layer? tileLayer->setNeedsDisplay(); tileInfo.hasStaleContent = false; } } else { // Add to the currentCohort if not already in one. if (tileInfo.cohort == VisibleTileCohort) { tileInfo.cohort = currCohort; ++tilesInCohort; if (m_controller.unparentsOffscreenTiles()) tileLayer->removeFromSuperlayer(); } else if (m_controller.unparentsOffscreenTiles() && m_controller.shouldAggressivelyRetainTiles() && tileLayer->superlayer()) { // Aggressive tile retention means we'll never remove cohorts, but we need to make sure they're unparented. // We can't immediately unparent cohorts comprised of secondary tiles that never touch the primary coverage rect, // because that would defeat the usefulness of prepopulateRect(); instead, age prepopulated tiles out as if they were being removed. for (auto& cohort : m_cohortList) { if (cohort.cohort != tileInfo.cohort) continue; double timeUntilCohortExpires = cohort.timeUntilExpiration(); if (timeUntilCohortExpires > 0) { minimumRevalidationTimerDuration = std::min(minimumRevalidationTimerDuration, timeUntilCohortExpires); needsTileRevalidation = true; } else tileLayer->removeFromSuperlayer(); break; } } } } if (needsTileRevalidation) m_controller.scheduleTileRevalidation(minimumRevalidationTimerDuration); if (tilesInCohort) startedNewCohort(currCohort); if (!m_controller.shouldAggressivelyRetainTiles()) { if (m_controller.shouldTemporarilyRetainTileCohorts()) scheduleCohortRemoval(); else if (tilesInCohort) removeTilesInCohort(currCohort); } // Ensure primary tile coverage tiles. m_primaryTileCoverageRect = ensureTilesForRect(coverageRect, CoverageType::PrimaryTiles); if (validationPolicy & PruneSecondaryTiles) { removeAllSecondaryTiles(); m_cohortList.clear(); } else { for (auto& secondaryCoverageRect : m_secondaryTileCoverageRects) { FloatRect secondaryRectInLayerCoordinates(secondaryCoverageRect); secondaryRectInLayerCoordinates.scale(1 / m_scale); ensureTilesForRect(secondaryRectInLayerCoordinates, CoverageType::SecondaryTiles); } m_secondaryTileCoverageRects.clear(); } if (m_controller.unparentsOffscreenTiles() && (validationPolicy & UnparentAllTiles)) { for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) it->value.layer->removeFromSuperlayer(); } auto boundsAtLastRevalidate = m_controller.boundsAtLastRevalidate(); if (boundsAtLastRevalidate != bounds) { // If there are margin tiles and the bounds have grown taller or wider, then the tiles that used to // be bottom or right margin tiles need to be invalidated. if (m_controller.hasMargins()) { if (bounds.width() > boundsAtLastRevalidate.width() || bounds.height() > boundsAtLastRevalidate.height()) { IntRect boundsWithoutMargin = m_controller.boundsWithoutMargin(); IntRect oldBoundsWithoutMargin = m_controller.boundsAtLastRevalidateWithoutMargin(); if (bounds.height() > boundsAtLastRevalidate.height()) { IntRect formerBottomMarginRect = IntRect(oldBoundsWithoutMargin.x(), oldBoundsWithoutMargin.height(), oldBoundsWithoutMargin.width(), boundsWithoutMargin.height() - oldBoundsWithoutMargin.height()); setNeedsDisplayInRect(formerBottomMarginRect); } if (bounds.width() > boundsAtLastRevalidate.width()) { IntRect formerRightMarginRect = IntRect(oldBoundsWithoutMargin.width(), oldBoundsWithoutMargin.y(), boundsWithoutMargin.width() - oldBoundsWithoutMargin.width(), oldBoundsWithoutMargin.height()); setNeedsDisplayInRect(formerRightMarginRect); } } } FloatRect scaledBounds(bounds); scaledBounds.scale(m_scale); IntRect boundsInTileCoords(enclosingIntRect(scaledBounds)); TileIndex topLeftForBounds; TileIndex bottomRightForBounds; getTileIndexRangeForRect(boundsInTileCoords, topLeftForBounds, bottomRightForBounds); Vector<TileIndex> tilesToRemove; for (auto& index : m_tiles.keys()) { if (index.y() < topLeftForBounds.y() || index.y() > bottomRightForBounds.y() || index.x() < topLeftForBounds.x() || index.x() > bottomRightForBounds.x()) tilesToRemove.append(index); } removeTiles(tilesToRemove); } m_controller.didRevalidateTiles(); }