| /* |
| * Copyright (C) 2012, 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. ``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 |
| * 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" |
| #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 |
| { |
| // Decay to one quarter over capacityDecayTime |
| 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); |
| // The last element in the list is the oldest, hence most likely not to |
| // still have a backing store. |
| oldestReuseList.remove(--oldestReuseList.end()); |
| } |
| if (MonotonicTime::now() - m_lastAddTime <= capacityDecayTime) |
| schedulePrune(); |
| } |
| |
| void LayerPool::drain() |
| { |
| m_reuseLists.clear(); |
| m_sizesInPruneOrder.clear(); |
| m_totalBytes = 0; |
| } |
| |
| } |