| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/renderer_host/backing_store_manager.h" | 5 #include "chrome/browser/renderer_host/backing_store_manager.h" |
| 6 | 6 |
| 7 #include "base/sys_info.h" | 7 #include "base/sys_info.h" |
| 8 #include "chrome/browser/renderer_host/backing_store.h" | 8 #include "chrome/browser/renderer_host/backing_store.h" |
| 9 #include "chrome/browser/renderer_host/render_widget_host.h" | 9 #include "chrome/browser/renderer_host/render_widget_host.h" |
| 10 #include "chrome/browser/renderer_host/render_widget_host_painting_observer.h" | 10 #include "chrome/browser/renderer_host/render_widget_host_painting_observer.h" |
| 11 #include "chrome/common/chrome_constants.h" | 11 #include "chrome/common/chrome_constants.h" |
| 12 | 12 |
| 13 |
| 13 namespace { | 14 namespace { |
| 14 | 15 |
| 16 // There are two separate caches, |large_cache| and |small_cache|. large_cache |
| 17 // is meant for large items (tabs, popup windows), while small_cache is meant |
| 18 // for small items (extension toolstrips and buttons, etc.). The idea is that |
| 19 // we'll almost always try to evict from large_cache first since small_cache |
| 20 // items will tend to be visible more of the time. |
| 15 typedef OwningMRUCache<RenderWidgetHost*, BackingStore*> BackingStoreCache; | 21 typedef OwningMRUCache<RenderWidgetHost*, BackingStore*> BackingStoreCache; |
| 16 static BackingStoreCache* cache = NULL; | 22 static BackingStoreCache* large_cache = NULL; |
| 23 static BackingStoreCache* small_cache = NULL; |
| 17 | 24 |
| 18 // Returns the size of the backing store cache. | 25 // Threshold is based on a large-monitor width toolstrip. |
| 19 static size_t GetBackingStoreCacheSize() { | 26 // TODO(erikkay) 32bpp assumption isn't great. |
| 20 // This uses a similar approach to GetMaxRendererProcessCount. The goal | 27 const size_t kSmallThreshold = 4 * 32 * 1920; |
| 21 // is to reduce memory pressure and swapping on low-resource machines. | |
| 22 static const size_t kMaxDibCountByRamTier[] = { | |
| 23 2, // less than 256MB | |
| 24 3, // 256MB | |
| 25 4, // 512MB | |
| 26 5 // 768MB and above | |
| 27 }; | |
| 28 | 28 |
| 29 static size_t max_size = kMaxDibCountByRamTier[ | 29 // Previously, the backing store cache was based on a set number of backing |
| 30 std::min(base::SysInfo::AmountOfPhysicalMemoryMB() / 256, | 30 // stores, regardless of their size. The numbers were chosen based on a user |
| 31 static_cast<int>(arraysize(kMaxDibCountByRamTier)) - 1)]; | 31 // with a maximized browser on a large monitor. Now that the cache is based on |
| 32 return max_size; | 32 // total memory size of the backing stores, we'll keep an approximation of the |
| 33 // numbers from the previous algorithm by choosing a large monitor backing store |
| 34 //size as our multiplier. |
| 35 // TODO(erikkay) Perhaps we should actually use monitor size? That way we |
| 36 // could make an assertion like "worse case, there are two tabs in the cache". |
| 37 // However, the small_cache might mess up these calculations a bit. |
| 38 // TODO(erikkay) 32bpp assumption isn't great. |
| 39 const size_t kMemoryMultiplier = 4 * 1920 * 1200; // ~9MB |
| 40 |
| 41 static size_t GetBackingStoreCacheMemorySize() { |
| 42 // Compute in terms of the number of large monitor's worth of backing-store. |
| 43 // Use a minimum of 2, and add one for each 256MB of physical memory you have. |
| 44 // Cap at 5, the thinking being that even if you have a gigantic amount of |
| 45 // RAM, there's a limit to how much caching helps beyond a certain number |
| 46 // of tabs. |
| 47 size_t mem_tier = std::min(5, |
| 48 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256)); |
| 49 return mem_tier * kMemoryMultiplier; |
| 33 } | 50 } |
| 34 | 51 |
| 35 // Expires the given backing store from the cache. | 52 // Expires the given |backing_store| from |cache|. |
| 36 void ExpireBackingStoreAt(BackingStoreCache::iterator backing_store) { | 53 void ExpireBackingStoreAt(BackingStoreCache* cache, |
| 54 BackingStoreCache::iterator backing_store) { |
| 37 RenderWidgetHost* rwh = backing_store->second->render_widget_host(); | 55 RenderWidgetHost* rwh = backing_store->second->render_widget_host(); |
| 38 if (rwh->painting_observer()) { | 56 if (rwh->painting_observer()) { |
| 39 rwh->painting_observer()->WidgetWillDestroyBackingStore( | 57 rwh->painting_observer()->WidgetWillDestroyBackingStore( |
| 40 backing_store->first, | 58 backing_store->first, |
| 41 backing_store->second); | 59 backing_store->second); |
| 42 } | 60 } |
| 43 cache->Erase(backing_store); | 61 cache->Erase(backing_store); |
| 44 } | 62 } |
| 45 | 63 |
| 64 void CreateCacheSpace(size_t size) { |
| 65 // Given a request for |size|, first free from the large cache (until there's |
| 66 // only one item left) and then do the same from the small cache if we still |
| 67 // don't have enough. |
| 68 while (size > 0 && (large_cache->size() > 1 || small_cache->size() > 1)) { |
| 69 BackingStoreCache* cache = |
| 70 (large_cache->size() > 1) ? large_cache : small_cache; |
| 71 while (size > 0 && cache->size() > 1) { |
| 72 // Crazy C++ alert: rbegin.base() is a forward iterator pointing to end(), |
| 73 // so we need to do -- to move one back to the actual last item. |
| 74 BackingStoreCache::iterator entry = --cache->rbegin().base(); |
| 75 size_t entry_size = entry->second->MemorySize(); |
| 76 ExpireBackingStoreAt(cache, entry); |
| 77 if (size > entry_size) |
| 78 size -= entry_size; |
| 79 else |
| 80 size = 0; |
| 81 } |
| 82 } |
| 83 DCHECK(size == 0); |
| 84 } |
| 85 |
| 46 // Creates the backing store for the host based on the dimensions passed in. | 86 // Creates the backing store for the host based on the dimensions passed in. |
| 47 // Removes the existing backing store if there is one. | 87 // Removes the existing backing store if there is one. |
| 48 BackingStore* CreateBackingStore(RenderWidgetHost* host, | 88 BackingStore* CreateBackingStore(RenderWidgetHost* host, |
| 49 const gfx::Size& backing_store_size) { | 89 const gfx::Size& backing_store_size) { |
| 50 // Remove any existing backing store in case we're replacing it. | 90 // Remove any existing backing store in case we're replacing it. |
| 51 BackingStoreManager::RemoveBackingStore(host); | 91 BackingStoreManager::RemoveBackingStore(host); |
| 52 | 92 |
| 53 size_t max_cache_size = GetBackingStoreCacheSize(); | 93 if (!large_cache) { |
| 54 if (max_cache_size > 0 && !cache) | 94 large_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT); |
| 55 cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT); | 95 small_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT); |
| 96 } |
| 56 | 97 |
| 57 if (cache->size() >= max_cache_size) { | 98 // TODO(erikkay) 32bpp is not always accurate |
| 58 // Need to remove an old backing store to make room for the new one. We | 99 size_t new_mem = backing_store_size.GetArea() * 4; |
| 100 size_t current_mem = BackingStoreManager::MemorySize(); |
| 101 size_t max_mem = GetBackingStoreCacheMemorySize(); |
| 102 DCHECK(new_mem < max_mem); |
| 103 if (current_mem + new_mem > max_mem) { |
| 104 // Need to remove old backing stores to make room for the new one. We |
| 59 // don't want to do this when the backing store is being replace by a new | 105 // don't want to do this when the backing store is being replace by a new |
| 60 // one for the same tab, but this case won't get called then: we'll have | 106 // one for the same tab, but this case won't get called then: we'll have |
| 61 // removed the onld one in the RemoveBackingStore above, and the cache | 107 // removed the onld one in the RemoveBackingStore above, and the cache |
| 62 // won't be over-sized. | 108 // won't be over-sized. |
| 63 // | 109 CreateCacheSpace((current_mem + new_mem) - max_mem); |
| 64 // Crazy C++ alert: rbegin.base() is a forward iterator pointing to end(), | |
| 65 // so we need to do -- to move one back to the actual last item. | |
| 66 ExpireBackingStoreAt(--cache->rbegin().base()); | |
| 67 } | 110 } |
| 111 DCHECK((BackingStoreManager::MemorySize() + new_mem) < max_mem); |
| 68 | 112 |
| 69 BackingStore* backing_store = host->AllocBackingStore(backing_store_size); | 113 BackingStore* backing_store = host->AllocBackingStore(backing_store_size); |
| 70 if (max_cache_size > 0) | 114 if (new_mem > kSmallThreshold) |
| 71 cache->Put(host, backing_store); | 115 large_cache->Put(host, backing_store); |
| 116 else |
| 117 small_cache->Put(host, backing_store); |
| 72 return backing_store; | 118 return backing_store; |
| 73 } | 119 } |
| 74 | 120 |
| 75 } // namespace | 121 } // namespace |
| 76 | 122 |
| 77 // BackingStoreManager --------------------------------------------------------- | 123 // BackingStoreManager --------------------------------------------------------- |
| 78 | 124 |
| 79 // static | 125 // static |
| 80 BackingStore* BackingStoreManager::GetBackingStore( | 126 BackingStore* BackingStoreManager::GetBackingStore( |
| 81 RenderWidgetHost* host, | 127 RenderWidgetHost* host, |
| (...skipping 30 matching lines...) Expand all Loading... |
| 112 backing_store = CreateBackingStore(host, backing_store_size); | 158 backing_store = CreateBackingStore(host, backing_store_size); |
| 113 } | 159 } |
| 114 | 160 |
| 115 DCHECK(backing_store != NULL); | 161 DCHECK(backing_store != NULL); |
| 116 backing_store->PaintRect(process_handle, bitmap, bitmap_rect); | 162 backing_store->PaintRect(process_handle, bitmap, bitmap_rect); |
| 117 return backing_store; | 163 return backing_store; |
| 118 } | 164 } |
| 119 | 165 |
| 120 // static | 166 // static |
| 121 BackingStore* BackingStoreManager::Lookup(RenderWidgetHost* host) { | 167 BackingStore* BackingStoreManager::Lookup(RenderWidgetHost* host) { |
| 122 if (cache) { | 168 if (large_cache) { |
| 123 BackingStoreCache::iterator it = cache->Peek(host); | 169 BackingStoreCache::iterator it = large_cache->Get(host); |
| 124 if (it != cache->end()) | 170 if (it != large_cache->end()) |
| 171 return it->second; |
| 172 |
| 173 // This moves host to the front of the MRU. |
| 174 it = small_cache->Get(host); |
| 175 if (it != small_cache->end()) |
| 125 return it->second; | 176 return it->second; |
| 126 } | 177 } |
| 127 return NULL; | 178 return NULL; |
| 128 } | 179 } |
| 129 | 180 |
| 130 // static | 181 // static |
| 131 void BackingStoreManager::RemoveBackingStore(RenderWidgetHost* host) { | 182 void BackingStoreManager::RemoveBackingStore(RenderWidgetHost* host) { |
| 132 if (!cache) | 183 if (!large_cache) |
| 133 return; | 184 return; |
| 134 | 185 |
| 186 BackingStoreCache* cache = large_cache; |
| 135 BackingStoreCache::iterator it = cache->Peek(host); | 187 BackingStoreCache::iterator it = cache->Peek(host); |
| 136 if (it == cache->end()) | 188 if (it == cache->end()) { |
| 137 return; | 189 cache = small_cache; |
| 190 it = cache->Peek(host); |
| 191 if (it == cache->end()) |
| 192 return; |
| 193 } |
| 138 cache->Erase(it); | 194 cache->Erase(it); |
| 139 | |
| 140 if (cache->empty()) { | |
| 141 delete cache; | |
| 142 cache = NULL; | |
| 143 } | |
| 144 } | 195 } |
| 145 | 196 |
| 146 // static | 197 // static |
| 147 bool BackingStoreManager::ExpireBackingStoreForTest(RenderWidgetHost* host) { | 198 bool BackingStoreManager::ExpireBackingStoreForTest(RenderWidgetHost* host) { |
| 199 BackingStoreCache* cache = large_cache; |
| 200 |
| 148 BackingStoreCache::iterator it = cache->Peek(host); | 201 BackingStoreCache::iterator it = cache->Peek(host); |
| 149 if (it == cache->end()) | 202 if (it == cache->end()) { |
| 150 return false; | 203 cache = small_cache; |
| 151 ExpireBackingStoreAt(it); | 204 it = cache->Peek(host); |
| 205 if (it == cache->end()) |
| 206 return false; |
| 207 } |
| 208 ExpireBackingStoreAt(cache, it); |
| 152 return true; | 209 return true; |
| 153 } | 210 } |
| 211 |
| 212 // static |
| 213 size_t BackingStoreManager::MemorySize() { |
| 214 if (!large_cache) |
| 215 return 0; |
| 216 |
| 217 size_t mem = 0; |
| 218 BackingStoreCache::iterator it; |
| 219 for (it = large_cache->begin(); it != large_cache->end(); ++it) |
| 220 mem += it->second->MemorySize(); |
| 221 |
| 222 for (it = small_cache->begin(); it != small_cache->end(); ++it) |
| 223 mem += it->second->MemorySize(); |
| 224 |
| 225 return mem; |
| 226 } |
| OLD | NEW |