#include "config.h"
#include "LayerPool.h"
#include "Logging.h"
#include <wtf/NeverDestroyed.h>
namespace WebCore {
static const Seconds capacityDecayTime { 5_s };
LayerPool::LayerPool()
: m_totalBytes(0)
, m_maxBytesForPool(48 * 1024 * 1024)
, m_pruneTimer(*this, &LayerPool::pruneTimerFired)
{
allLayerPools().add(this);
}
LayerPool::~LayerPool()
{
allLayerPools().remove(this);
}
HashSet<LayerPool*>& LayerPool::allLayerPools()
{
static NeverDestroyed<HashSet<LayerPool*>> allLayerPools;
return allLayerPools.get();
}
unsigned LayerPool::backingStoreBytesForSize(const IntSize& size)
{
return (size.area() * 4).unsafeGet();
}
LayerPool::LayerList& LayerPool::listOfLayersWithSize(const IntSize& size, AccessType accessType)
{
HashMap<IntSize, LayerList>::iterator it = m_reuseLists.find(size);
if (it == m_reuseLists.end()) {
it = m_reuseLists.add(size, LayerList()).iterator;
m_sizesInPruneOrder.append(size);
} else if (accessType == MarkAsUsed) {
m_sizesInPruneOrder.remove(m_sizesInPruneOrder.reverseFind(size));
m_sizesInPruneOrder.append(size);
}
return it->value;
}
void LayerPool::addLayer(const RefPtr<PlatformCALayer>& layer)
{
IntSize layerSize(expandedIntSize(layer->bounds().size()));
if (!canReuseLayerWithSize(layerSize))
return;
listOfLayersWithSize(layerSize).prepend(layer);
m_totalBytes += backingStoreBytesForSize(layerSize);
m_lastAddTime = MonotonicTime::now();
schedulePrune();
}
RefPtr<PlatformCALayer> LayerPool::takeLayerWithSize(const IntSize& size)
{
if (!canReuseLayerWithSize(size))
return nullptr;
LayerList& reuseList = listOfLayersWithSize(size, MarkAsUsed);
if (reuseList.isEmpty())
return nullptr;
m_totalBytes -= backingStoreBytesForSize(size);
return reuseList.takeFirst();
}
unsigned LayerPool::decayedCapacity() const
{
Seconds timeSinceLastAdd = MonotonicTime::now() - m_lastAddTime;
if (timeSinceLastAdd > capacityDecayTime)
return m_maxBytesForPool / 4;
float decayProgess = float(timeSinceLastAdd / capacityDecayTime);
return m_maxBytesForPool / 4 + m_maxBytesForPool * 3 / 4 * (1 - decayProgess);
}
void LayerPool::schedulePrune()
{
if (m_pruneTimer.isActive())
return;
m_pruneTimer.startOneShot(1_s);
}
void LayerPool::pruneTimerFired()
{
unsigned shrinkTo = decayedCapacity();
while (m_totalBytes > shrinkTo) {
ASSERT(!m_sizesInPruneOrder.isEmpty());
IntSize sizeToDrop = m_sizesInPruneOrder.first();
LayerList& oldestReuseList = m_reuseLists.find(sizeToDrop)->value;
if (oldestReuseList.isEmpty()) {
m_reuseLists.remove(sizeToDrop);
m_sizesInPruneOrder.remove(0);
continue;
}
m_totalBytes -= backingStoreBytesForSize(sizeToDrop);
oldestReuseList.remove(--oldestReuseList.end());
}
if (MonotonicTime::now() - m_lastAddTime <= capacityDecayTime)
schedulePrune();
}
void LayerPool::drain()
{
m_reuseLists.clear();
m_sizesInPruneOrder.clear();
m_totalBytes = 0;
}
}