1/* 2 * Copyright (C) 2011 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "LegacyTileLayerPool.h" 28 29#if PLATFORM(IOS) 30 31#include "LegacyTileLayer.h" 32#include "LegacyTileGrid.h" 33#include "Logging.h" 34#include "MemoryPressureHandler.h" 35#include <wtf/CurrentTime.h> 36#include <wtf/NeverDestroyed.h> 37 38namespace WebCore { 39 40static const double capacityDecayTime = 5; 41 42LegacyTileLayerPool::LegacyTileLayerPool() 43 : m_totalBytes(0) 44 , m_capacity(0) 45 , m_lastAddTime(0) 46 , m_needsPrune(false) 47{ 48} 49 50LegacyTileLayerPool* LegacyTileLayerPool::sharedPool() 51{ 52 static NeverDestroyed<LegacyTileLayerPool> sharedPool; 53 return &sharedPool.get(); 54} 55 56unsigned LegacyTileLayerPool::bytesBackingLayerWithPixelSize(const IntSize& size) 57{ 58 return size.width() * size.height() * 4; 59} 60 61LegacyTileLayerPool::LayerList& LegacyTileLayerPool::listOfLayersWithSize(const IntSize& size, AccessType accessType) 62{ 63 ASSERT(!m_layerPoolMutex.tryLock()); 64 HashMap<IntSize, LayerList>::iterator it = m_reuseLists.find(size); 65 if (it == m_reuseLists.end()) { 66 it = m_reuseLists.add(size, LayerList()).iterator; 67 m_sizesInPruneOrder.append(size); 68 } else if (accessType == MarkAsUsed) { 69 m_sizesInPruneOrder.remove(m_sizesInPruneOrder.reverseFind(size)); 70 m_sizesInPruneOrder.append(size); 71 } 72 return it->value; 73} 74 75void LegacyTileLayerPool::addLayer(const RetainPtr<LegacyTileLayer>& layer) 76{ 77 IntSize layerSize([layer.get() frame].size); 78 layerSize.scale([layer.get() contentsScale]); 79 if (!canReuseLayerWithSize(layerSize)) 80 return; 81 82 if (memoryPressureHandler().isUnderMemoryPressure()) { 83 LOG(MemoryPressure, "Under memory pressure: %s, totalBytes: %d", __PRETTY_FUNCTION__, m_totalBytes); 84 return; 85 } 86 87 MutexLocker locker(m_layerPoolMutex); 88 listOfLayersWithSize(layerSize).prepend(layer); 89 m_totalBytes += bytesBackingLayerWithPixelSize(layerSize); 90 91 m_lastAddTime = currentTime(); 92 schedulePrune(); 93} 94 95RetainPtr<LegacyTileLayer> LegacyTileLayerPool::takeLayerWithSize(const IntSize& size) 96{ 97 if (!canReuseLayerWithSize(size)) 98 return nil; 99 MutexLocker locker(m_layerPoolMutex); 100 LayerList& reuseList = listOfLayersWithSize(size, MarkAsUsed); 101 if (reuseList.isEmpty()) 102 return nil; 103 m_totalBytes -= bytesBackingLayerWithPixelSize(size); 104 return reuseList.takeFirst(); 105} 106 107void LegacyTileLayerPool::setCapacity(unsigned capacity) 108{ 109 MutexLocker reuseLocker(m_layerPoolMutex); 110 if (capacity < m_capacity) 111 schedulePrune(); 112 m_capacity = capacity; 113} 114 115unsigned LegacyTileLayerPool::decayedCapacity() const 116{ 117 // Decay to one quarter over capacityDecayTime 118 double timeSinceLastAdd = currentTime() - m_lastAddTime; 119 if (timeSinceLastAdd > capacityDecayTime) 120 return m_capacity / 4; 121 float decayProgess = float(timeSinceLastAdd / capacityDecayTime); 122 return m_capacity / 4 + m_capacity * 3 / 4 * (1.f - decayProgess); 123} 124 125void LegacyTileLayerPool::schedulePrune() 126{ 127 ASSERT(!m_layerPoolMutex.tryLock()); 128 if (m_needsPrune) 129 return; 130 m_needsPrune = true; 131 dispatch_time_t nextPruneTime = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC); 132 dispatch_after(nextPruneTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 133 prune(); 134 }); 135} 136 137void LegacyTileLayerPool::prune() 138{ 139 MutexLocker locker(m_layerPoolMutex); 140 ASSERT(m_needsPrune); 141 m_needsPrune = false; 142 unsigned shrinkTo = decayedCapacity(); 143 while (m_totalBytes > shrinkTo) { 144 ASSERT(!m_sizesInPruneOrder.isEmpty()); 145 IntSize sizeToDrop = m_sizesInPruneOrder.first(); 146 LayerList& oldestReuseList = m_reuseLists.find(sizeToDrop)->value; 147 if (oldestReuseList.isEmpty()) { 148 m_reuseLists.remove(sizeToDrop); 149 m_sizesInPruneOrder.remove(0); 150 continue; 151 } 152#if LOG_TILING 153 NSLog(@"dropping layer of size %d x %d", sizeToDrop.width(), sizeToDrop.height()); 154#endif 155 m_totalBytes -= bytesBackingLayerWithPixelSize(sizeToDrop); 156 // The last element in the list is the oldest, hence most likely not to 157 // still have a backing store. 158 oldestReuseList.removeLast(); 159 } 160 if (currentTime() - m_lastAddTime <= capacityDecayTime) 161 schedulePrune(); 162} 163 164void LegacyTileLayerPool::drain() 165{ 166 MutexLocker reuseLocker(m_layerPoolMutex); 167 m_reuseLists.clear(); 168 m_sizesInPruneOrder.clear(); 169 m_totalBytes = 0; 170} 171 172} // namespace WebCore 173 174#endif // PLATFORM(IOS) 175