blob: 9d8fbb1a0f0de3472a5cacef7fc7e597eb6a26a4 [file] [log] [blame]
/*
* Copyright (C) 2011-2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#if USE(CG)
#include "TileGrid.h"
#include "GraphicsContext.h"
#include "LayerPool.h"
#include "Logging.h"
#include "PlatformCALayer.h"
#include "TileController.h"
#include <wtf/MainThread.h>
#include <wtf/text/CString.h>
#include <wtf/text/TextStream.h>
#if PLATFORM(IOS_FAMILY)
#include "TileControllerMemoryHandlerIOS.h"
#endif
namespace WebCore {
#if !LOG_DISABLED
static String validationPolicyAsString(OptionSet<TileGrid::ValidationPolicyFlag> policy)
{
return makeString('[',
policy.contains(TileGrid::PruneSecondaryTiles) ? "prune secondary" : "",
policy.containsAll({ TileGrid::PruneSecondaryTiles, TileGrid::UnparentAllTiles }) ? ", " : "",
policy.contains(TileGrid::PruneSecondaryTiles) ? "unparent all" : "",
']');
}
#endif
TileGrid::TileGrid(TileController& controller)
: m_controller(controller)
, m_containerLayer(controller.rootLayer().createCompatibleLayer(PlatformCALayer::LayerTypeLayer, nullptr))
, m_cohortRemovalTimer(*this, &TileGrid::cohortRemovalTimerFired)
, m_tileSize(kDefaultTileSize, kDefaultTileSize)
{
m_containerLayer.get().setName(TileController::tileGridContainerLayerName());
m_containerLayer.get().setContentsScale(m_controller.deviceScaleFactor());
}
TileGrid::~TileGrid()
{
ASSERT(isMainThread());
for (auto& tile : m_tiles.values())
tile.layer->setOwner(nullptr);
}
void TileGrid::setIsZoomedOutTileGrid(bool isZoomedOutGrid)
{
if (isZoomedOutGrid)
m_containerLayer.get().setName(TileController::zoomedOutTileGridContainerLayerName());
else
m_containerLayer.get().setName(TileController::tileGridContainerLayerName());
}
void TileGrid::setScale(float scale)
{
m_scale = scale;
TransformationMatrix transform;
transform.scale(1 / m_scale);
m_containerLayer->setTransform(transform);
m_controller.setNeedsRevalidateTiles();
m_containerLayer.get().setContentsScale(m_controller.deviceScaleFactor());
for (auto& tile : m_tiles.values())
tile.layer->setContentsScale(m_controller.deviceScaleFactor());
}
void TileGrid::setNeedsDisplay()
{
for (auto& entry : m_tiles) {
TileInfo& tileInfo = entry.value;
IntRect tileRect = rectForTileIndex(entry.key);
if (tileRect.intersects(m_primaryTileCoverageRect) && tileInfo.layer->superlayer())
tileInfo.layer->setNeedsDisplay();
else
tileInfo.hasStaleContent = true;
}
}
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_tileSize;
// For small invalidations, lookup the covered tiles.
if (repaintRectInTileCoords.height() < 2 * tileSize.height() && repaintRectInTileCoords.width() < 2 * tileSize.width()) {
TileIndex topLeft;
TileIndex bottomRight;
if (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);
auto it = m_tiles.find(tileIndex);
if (it != m_tiles.end())
setTileNeedsDisplayInRect(tileIndex, it->value, repaintRectInTileCoords, m_primaryTileCoverageRect);
}
}
}
return;
}
for (auto& entry : m_tiles)
setTileNeedsDisplayInRect(entry.key, entry.value, repaintRectInTileCoords, m_primaryTileCoverageRect);
}
void TileGrid::dropTilesInRect(const IntRect& rect)
{
if (m_tiles.isEmpty())
return;
FloatRect scaledRect(rect);
scaledRect.scale(m_scale);
IntRect dropRectInTileCoords(enclosingIntRect(scaledRect));
Vector<TileIndex> tilesToRemove;
for (auto& index : m_tiles.keys()) {
if (rectForTileIndex(index).intersects(dropRectInTileCoords))
tilesToRemove.append(index);
}
removeTiles(tilesToRemove);
}
void TileGrid::setTileNeedsDisplayInRect(const TileIndex& tileIndex, TileInfo& tileInfo, const IntRect& repaintRectInTileCoords, const IntRect& coverageRectInTileCoords)
{
PlatformCALayer* tileLayer = tileInfo.layer.get();
IntRect tileRect = rectForTileIndex(tileIndex);
FloatRect tileRepaintRect = tileRect;
tileRepaintRect.intersect(repaintRectInTileCoords);
if (tileRepaintRect.isEmpty())
return;
tileRepaintRect.moveBy(-tileRect.location());
// We could test for intersection with the visible rect. This would reduce painting yet more,
// but may make scrolling stale tiles into view more frequent.
if (tileRect.intersects(coverageRectInTileCoords) && tileLayer->superlayer()) {
tileLayer->setNeedsDisplayInRect(tileRepaintRect);
if (m_controller.rootLayer().owner()->platformCALayerShowRepaintCounter(0)) {
FloatRect indicatorRect(0, 0, 52, 27);
tileLayer->setNeedsDisplayInRect(indicatorRect);
}
} else
tileInfo.hasStaleContent = true;
}
void TileGrid::updateTileLayerProperties()
{
bool acceleratesDrawing = m_controller.acceleratesDrawing();
bool deepColor = m_controller.wantsDeepColorBackingStore();
bool subpixelAntialiasedText = m_controller.supportsSubpixelAntialiasedText();
bool opaque = m_controller.tilesAreOpaque();
Color tileDebugBorderColor = m_controller.tileDebugBorderColor();
float tileDebugBorderWidth = m_controller.tileDebugBorderWidth();
for (auto& tileInfo : m_tiles.values()) {
tileInfo.layer->setAcceleratesDrawing(acceleratesDrawing);
tileInfo.layer->setWantsDeepColorBackingStore(deepColor);
tileInfo.layer->setSupportsSubpixelAntialiasedText(subpixelAntialiasedText);
tileInfo.layer->setOpaque(opaque);
tileInfo.layer->setBorderColor(tileDebugBorderColor);
tileInfo.layer->setBorderWidth(tileDebugBorderWidth);
}
}
bool TileGrid::tilesWouldChangeForCoverageRect(const FloatRect& coverageRect) const
{
if (coverageRect.isEmpty())
return false;
FloatRect scaledRect(coverageRect);
scaledRect.scale(m_scale);
IntRect currentCoverageRectInTileCoords(enclosingIntRect(scaledRect));
IntRect tileCoverageRect;
TileIndex topLeft;
TileIndex bottomRight;
if (getTileIndexRangeForRect(currentCoverageRectInTileCoords, topLeft, bottomRight)) {
tileCoverageRect = rectForTileIndex(topLeft);
tileCoverageRect.unite(rectForTileIndex(bottomRight));
}
return tileCoverageRect != m_primaryTileCoverageRect;
}
bool TileGrid::prepopulateRect(const FloatRect& rect)
{
LOG_WITH_STREAM(Tiling, stream << "TileGrid " << this << " prepopulateRect: " << rect);
IntRect enclosingCoverageRect = enclosingIntRect(rect);
if (m_primaryTileCoverageRect.contains(enclosingCoverageRect))
return false;
m_secondaryTileCoverageRects.append(enclosingCoverageRect);
return true;
}
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_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;
}
bool TileGrid::getTileIndexRangeForRect(const IntRect& rect, TileIndex& topLeft, TileIndex& bottomRight) const
{
IntRect clampedRect = m_controller.bounds();
clampedRect.scale(m_scale);
clampedRect.intersect(rect);
if (clampedRect.isEmpty())
return false;
auto tileSize = m_tileSize;
if (clampedRect.x() >= 0)
topLeft.setX(clampedRect.x() / tileSize.width());
else
topLeft.setX(floorf((float)clampedRect.x() / tileSize.width()));
if (clampedRect.y() >= 0)
topLeft.setY(clampedRect.y() / tileSize.height());
else
topLeft.setY(floorf((float)clampedRect.y() / tileSize.height()));
int bottomXRatio = ceil((float)clampedRect.maxX() / tileSize.width());
bottomRight.setX(std::max(bottomXRatio - 1, 0));
int bottomYRatio = ceil((float)clampedRect.maxY() / tileSize.height());
bottomRight.setY(std::max(bottomYRatio - 1, 0));
return true;
}
unsigned TileGrid::blankPixelCount() const
{
PlatformLayerList tiles(m_tiles.size());
for (auto& tile : m_tiles.values()) {
if (auto layer = tile.layer->platformLayer())
tiles.append(layer);
}
return TileController::blankPixelCountForTiles(tiles, m_controller.visibleRect(), IntPoint(0, 0));
}
void TileGrid::removeTiles(Vector<TileGrid::TileIndex>& toRemove)
{
for (size_t i = 0; i < toRemove.size(); ++i) {
TileInfo tileInfo = m_tiles.take(toRemove[i]);
tileInfo.layer->removeFromSuperlayer();
m_tileRepaintCounts.removeAll(tileInfo.layer.get());
tileInfo.layer->moveToLayerPool();
}
}
void TileGrid::removeAllTiles()
{
Vector<TileIndex> tilesToRemove;
tilesToRemove.reserveInitialCapacity(m_tiles.size());
for (auto& entry : m_tiles)
tilesToRemove.uncheckedAppend(entry.key);
removeTiles(tilesToRemove);
}
void TileGrid::removeAllSecondaryTiles()
{
Vector<TileIndex> tilesToRemove;
for (auto& entry : m_tiles) {
const TileInfo& tileInfo = entry.value;
if (tileInfo.cohort == visibleTileCohort)
continue;
tilesToRemove.append(entry.key);
}
removeTiles(tilesToRemove);
}
void TileGrid::removeTilesInCohort(TileCohort cohort)
{
ASSERT(cohort != visibleTileCohort);
Vector<TileIndex> tilesToRemove;
for (auto& entry : m_tiles) {
const TileInfo& tileInfo = entry.value;
if (tileInfo.cohort != cohort)
continue;
tilesToRemove.append(entry.key);
}
removeTiles(tilesToRemove);
}
void TileGrid::revalidateTiles(OptionSet<ValidationPolicyFlag> validationPolicy)
{
FloatRect coverageRect = m_controller.coverageRect();
IntRect bounds = m_controller.bounds();
LOG_WITH_STREAM(Tiling, stream << "TileGrid " << this << " (controller " << &m_controller << ") revalidateTiles: bounds " << bounds << " coverageRect" << coverageRect << " validation: " << validationPolicyAsString(validationPolicy));
FloatRect scaledRect(coverageRect);
scaledRect.scale(m_scale);
IntRect coverageRectInTileCoords(enclosingIntRect(scaledRect));
TileCohort currCohort = nextTileCohort();
unsigned tilesInCohort = 0;
Seconds minimumRevalidationTimerDuration = Seconds::infinity();
bool needsTileRevalidation = false;
auto tileSize = m_controller.computeTileSize();
if (tileSize != m_tileSize) {
removeAllTiles();
m_tileSize = tileSize;
}
// Move tiles newly outside the coverage rect into the cohort map.
for (auto& entry : m_tiles) {
TileInfo& tileInfo = entry.value;
TileIndex tileIndex = entry.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;
tileLayer->removeFromSuperlayer();
} else if (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;
Seconds timeUntilCohortExpires = cohort.timeUntilExpiration();
if (timeUntilCohortExpires > 0_s) {
minimumRevalidationTimerDuration = std::min(minimumRevalidationTimerDuration, timeUntilCohortExpires);
needsTileRevalidation = true;
} else
tileLayer->removeFromSuperlayer();
break;
}
}
}
}
if (needsTileRevalidation)
m_controller.scheduleTileRevalidation(minimumRevalidationTimerDuration);
if (!m_controller.shouldAggressivelyRetainTiles()) {
if (m_controller.shouldTemporarilyRetainTileCohorts())
scheduleCohortRemoval();
else if (tilesInCohort) {
removeTilesInCohort(currCohort);
tilesInCohort = 0;
}
}
if (tilesInCohort)
startedNewCohort(currCohort);
if (validationPolicy & PruneSecondaryTiles) {
removeAllSecondaryTiles();
m_cohortList.clear();
}
if (validationPolicy & UnparentAllTiles) {
for (auto& tile : m_tiles.values())
tile.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;
if (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);
}
}
// Ensure primary tile coverage tiles.
m_primaryTileCoverageRect = ensureTilesForRect(coverageRect, CoverageType::PrimaryTiles);
// Ensure secondary tiles (requested via prepopulateRect).
if (!(validationPolicy & PruneSecondaryTiles)) {
for (auto& secondaryCoverageRect : m_secondaryTileCoverageRects) {
FloatRect secondaryRectInLayerCoordinates(secondaryCoverageRect);
secondaryRectInLayerCoordinates.scale(1 / m_scale);
ensureTilesForRect(secondaryRectInLayerCoordinates, CoverageType::SecondaryTiles);
}
m_secondaryTileCoverageRects.clear();
}
m_controller.didRevalidateTiles();
}
TileGrid::TileCohort TileGrid::nextTileCohort() const
{
if (!m_cohortList.isEmpty())
return m_cohortList.last().cohort + 1;
return 1;
}
void TileGrid::startedNewCohort(TileCohort cohort)
{
m_cohortList.append(TileCohortInfo(cohort, MonotonicTime::now()));
#if PLATFORM(IOS_FAMILY)
if (!m_controller.isInWindow())
tileControllerMemoryHandler().tileControllerGainedUnparentedTiles(&m_controller);
#endif
}
TileGrid::TileCohort TileGrid::newestTileCohort() const
{
return m_cohortList.isEmpty() ? 0 : m_cohortList.last().cohort;
}
TileGrid::TileCohort TileGrid::oldestTileCohort() const
{
return m_cohortList.isEmpty() ? 0 : m_cohortList.first().cohort;
}
void TileGrid::scheduleCohortRemoval()
{
const Seconds cohortRemovalTimerSeconds { 1_s };
// Start the timer, or reschedule the timer from now if it's already active.
if (!m_cohortRemovalTimer.isActive())
m_cohortRemovalTimer.startRepeating(cohortRemovalTimerSeconds);
}
Seconds TileGrid::TileCohortInfo::timeUntilExpiration()
{
Seconds cohortLifeTimeSeconds = 2_s;
MonotonicTime timeThreshold = MonotonicTime::now() - cohortLifeTimeSeconds;
return creationTime - timeThreshold;
}
void TileGrid::cohortRemovalTimerFired()
{
if (m_cohortList.isEmpty()) {
m_cohortRemovalTimer.stop();
return;
}
while (!m_cohortList.isEmpty() && m_cohortList.first().timeUntilExpiration() < 0_s) {
TileCohortInfo firstCohort = m_cohortList.takeFirst();
removeTilesInCohort(firstCohort.cohort);
}
m_controller.updateTileCoverageMap();
}
IntRect TileGrid::ensureTilesForRect(const FloatRect& rect, CoverageType newTileType)
{
if (!m_controller.isInWindow())
return IntRect();
FloatRect scaledRect(rect);
scaledRect.scale(m_scale);
IntRect rectInTileCoords(enclosingIntRect(scaledRect));
TileIndex topLeft;
TileIndex bottomRight;
if (!getTileIndexRangeForRect(rectInTileCoords, topLeft, bottomRight))
return IntRect();
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;
}
if (!tileInfo.layer->superlayer())
m_containerLayer.get().appendSublayer(*tileInfo.layer);
}
}
if (tilesInCohort)
startedNewCohort(currCohort);
LOG_WITH_STREAM(Tiling, stream << "TileGrid " << this << " (bounds " << m_controller.bounds() << ") ensureTilesForRect: " << rect << " covered " << coverageRect);
return coverageRect;
}
IntRect TileGrid::extent() const
{
TileIndex topLeft;
TileIndex bottomRight;
if (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);
}
return IntRect();
}
double TileGrid::retainedTileBackingStoreMemory() const
{
double totalBytes = 0;
for (auto& tileInfo : m_tiles.values()) {
if (tileInfo.layer->superlayer()) {
FloatRect bounds = tileInfo.layer->bounds();
double contentsScale = tileInfo.layer->contentsScale();
totalBytes += 4 * bounds.width() * contentsScale * bounds.height() * contentsScale;
}
}
return totalBytes;
}
// Return the rect in layer coords, not tile coords.
IntRect TileGrid::tileCoverageRect() const
{
IntRect coverageRectInLayerCoords(m_primaryTileCoverageRect);
coverageRectInLayerCoords.scale(1 / m_scale);
return coverageRectInLayerCoords;
}
void TileGrid::drawTileMapContents(CGContextRef context, CGRect layerBounds) const
{
CGContextSetRGBFillColor(context, 0.3, 0.3, 0.3, 1);
CGContextFillRect(context, layerBounds);
CGFloat scaleFactor = layerBounds.size.width / m_controller.bounds().width();
CGFloat contextScale = scaleFactor / m_scale;
CGContextScaleCTM(context, contextScale, contextScale);
for (auto& tileInfo : m_tiles.values()) {
PlatformCALayer* tileLayer = tileInfo.layer.get();
CGFloat red = 1;
CGFloat green = 1;
CGFloat blue = 1;
CGFloat alpha = 1;
if (tileInfo.hasStaleContent) {
red = 0.25;
green = 0.125;
blue = 0;
} else if (m_controller.shouldAggressivelyRetainTiles() && tileInfo.cohort != visibleTileCohort) {
red = 0.8;
green = 0.8;
blue = 0.8;
}
TileCohort newestCohort = newestTileCohort();
TileCohort oldestCohort = oldestTileCohort();
if (!m_controller.shouldAggressivelyRetainTiles() && tileInfo.cohort != visibleTileCohort && newestCohort > oldestCohort)
alpha = 1 - (static_cast<float>((newestCohort - tileInfo.cohort)) / (newestCohort - oldestCohort));
CGContextSetRGBFillColor(context, red, green, blue, alpha);
if (tileLayer->superlayer()) {
CGContextSetLineWidth(context, 0.5 / contextScale);
CGContextSetRGBStrokeColor(context, 0, 0, 0, 1);
} else {
CGContextSetLineWidth(context, 1 / contextScale);
CGContextSetRGBStrokeColor(context, 0.2, 0.1, 0.9, 1);
}
CGRect frame = CGRectMake(tileLayer->position().x(), tileLayer->position().y(), tileLayer->bounds().size().width(), tileLayer->bounds().size().height());
CGContextFillRect(context, frame);
CGContextStrokeRect(context, frame);
CGContextSetRGBFillColor(context, 0, 0, 0, 0.5);
CGContextSaveGState(context);
auto repaintCount = m_tileRepaintCounts.count(tileLayer);
char repaintCountCharacters[lengthOfIntegerAsString(std::numeric_limits<decltype(repaintCount)>::max())];
writeIntegerToBuffer(repaintCount, repaintCountCharacters);
tileLayer->drawTextAtPoint(context, frame.origin.x + 64, frame.origin.y + 192, CGSizeMake(3, -3), 58,
repaintCountCharacters, lengthOfIntegerAsString(repaintCount));
CGContextRestoreGState(context);
}
}
void TileGrid::platformCALayerPaintContents(PlatformCALayer* platformCALayer, GraphicsContext& context, const FloatRect&, GraphicsLayerPaintBehavior layerPaintBehavior)
{
#if PLATFORM(IOS_FAMILY)
if (pthread_main_np())
WebThreadLock();
#endif
if (!platformCALayerRepaintCount(platformCALayer))
layerPaintBehavior |= GraphicsLayerPaintFirstTilePaint;
{
GraphicsContextStateSaver stateSaver(context);
FloatPoint3D layerOrigin = platformCALayer->position();
context.translate(-layerOrigin.x(), -layerOrigin.y());
context.scale(m_scale);
PlatformCALayer::RepaintRectList dirtyRects = PlatformCALayer::collectRectsToPaint(context, platformCALayer);
PlatformCALayer::drawLayerContents(context, &m_controller.rootLayer(), dirtyRects, layerPaintBehavior);
}
int repaintCount = platformCALayerIncrementRepaintCount(platformCALayer);
if (m_controller.rootLayer().owner()->platformCALayerShowRepaintCounter(0))
PlatformCALayer::drawRepaintIndicator(context, platformCALayer, repaintCount, m_controller.tileDebugBorderColor());
if (m_controller.scrollingPerformanceTestingEnabled()) {
FloatRect visiblePart(platformCALayer->position().x(), platformCALayer->position().y(), platformCALayer->bounds().size().width(), platformCALayer->bounds().size().height());
visiblePart.intersect(m_controller.visibleRect());
if (repaintCount == 1 && !visiblePart.isEmpty())
m_controller.logFilledVisibleFreshTile(blankPixelCount());
}
}
float TileGrid::platformCALayerDeviceScaleFactor() const
{
if (auto* layerOwner = m_controller.rootLayer().owner())
return layerOwner->platformCALayerDeviceScaleFactor();
return 1.0f;
}
bool TileGrid::platformCALayerShowDebugBorders() const
{
if (auto* layerOwner = m_controller.rootLayer().owner())
return layerOwner->platformCALayerShowDebugBorders();
return false;
}
bool TileGrid::platformCALayerShowRepaintCounter(PlatformCALayer*) const
{
if (auto* layerOwner = m_controller.rootLayer().owner())
return layerOwner->platformCALayerShowRepaintCounter(nullptr);
return false;
}
bool TileGrid::isUsingDisplayListDrawing(PlatformCALayer*) const
{
if (auto* layerOwner = m_controller.rootLayer().owner())
return layerOwner->isUsingDisplayListDrawing(nullptr);
return false;
}
bool TileGrid::platformCALayerContentsOpaque() const
{
return m_controller.tilesAreOpaque();
}
int TileGrid::platformCALayerRepaintCount(PlatformCALayer* platformCALayer) const
{
return m_tileRepaintCounts.count(platformCALayer);
}
int TileGrid::platformCALayerIncrementRepaintCount(PlatformCALayer* platformCALayer)
{
return m_tileRepaintCounts.add(platformCALayer).iterator->value;
}
#if PLATFORM(IOS_FAMILY)
void TileGrid::removeUnparentedTilesNow()
{
while (!m_cohortList.isEmpty())
removeTilesInCohort(m_cohortList.takeFirst().cohort);
}
#endif
} // namespace WebCore
#endif