| 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 |
| 14 namespace { | 14 namespace { |
| 15 | 15 |
| 16 // There are two separate caches, |large_cache| and |small_cache|. large_cache | 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 | 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 | 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 | 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. | 20 // items will tend to be visible more of the time. |
| 21 typedef OwningMRUCache<RenderWidgetHost*, BackingStore*> BackingStoreCache; | 21 typedef OwningMRUCache<RenderWidgetHost*, BackingStore*> BackingStoreCache; |
| 22 static BackingStoreCache* large_cache = NULL; | 22 static BackingStoreCache* large_cache = NULL; |
| 23 static BackingStoreCache* small_cache = NULL; | 23 static BackingStoreCache* small_cache = NULL; |
| 24 | 24 |
| 25 // Threshold is based on a large-monitor width toolstrip. | 25 // Threshold is based on a single large-monitor-width toolstrip. |
| 26 // (32bpp, 32 pixels high, 1920 pixels wide) |
| 26 // TODO(erikkay) 32bpp assumption isn't great. | 27 // TODO(erikkay) 32bpp assumption isn't great. |
| 27 const size_t kSmallThreshold = 4 * 32 * 1920; | 28 const size_t kSmallThreshold = 4 * 32 * 1920; |
| 28 | 29 |
| 29 // Previously, the backing store cache was based on a set number of backing | 30 // Pick a large monitor size to use as a multiplier. This is multiplied by the |
| 30 // stores, regardless of their size. The numbers were chosen based on a user | 31 // max number of large backing stores (usually tabs) to pick a ceiling on the |
| 31 // with a maximized browser on a large monitor. Now that the cache is based on | 32 // max memory to use. |
| 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 | 33 // 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". | 34 // 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. | 35 // However, the small_cache might mess up these calculations a bit. |
| 38 // TODO(erikkay) 32bpp assumption isn't great. | 36 // TODO(erikkay) 32bpp assumption isn't great. |
| 39 const size_t kMemoryMultiplier = 4 * 1920 * 1200; // ~9MB | 37 const size_t kMemoryMultiplier = 4 * 1920 * 1200; // ~9MB |
| 40 | 38 |
| 41 static size_t GetBackingStoreCacheMemorySize() { | 39 // The maximum number of large BackingStoreCache objects (tabs) to use. |
| 40 // Use a minimum of 2, and add one for each 256MB of physical memory you have. |
| 41 // Cap at 5, the thinking being that even if you have a gigantic amount of |
| 42 // RAM, there's a limit to how much caching helps beyond a certain number |
| 43 // of tabs. |
| 44 static size_t MaxNumberOfBackingStores() { |
| 45 return std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256)); |
| 46 } |
| 47 |
| 48 // The maximum about of memory to use for all BackingStoreCache object combined. |
| 49 // We use this |
| 50 static size_t MaxBackingStoreMemory() { |
| 42 // Compute in terms of the number of large monitor's worth of backing-store. | 51 // 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. | 52 return MaxNumberOfBackingStores() * kMemoryMultiplier; |
| 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; | |
| 50 } | 53 } |
| 51 | 54 |
| 52 // Expires the given |backing_store| from |cache|. | 55 // Expires the given |backing_store| from |cache|. |
| 53 void ExpireBackingStoreAt(BackingStoreCache* cache, | 56 void ExpireBackingStoreAt(BackingStoreCache* cache, |
| 54 BackingStoreCache::iterator backing_store) { | 57 BackingStoreCache::iterator backing_store) { |
| 55 RenderWidgetHost* rwh = backing_store->second->render_widget_host(); | 58 RenderWidgetHost* rwh = backing_store->second->render_widget_host(); |
| 56 if (rwh->painting_observer()) { | 59 if (rwh->painting_observer()) { |
| 57 rwh->painting_observer()->WidgetWillDestroyBackingStore( | 60 rwh->painting_observer()->WidgetWillDestroyBackingStore( |
| 58 backing_store->first, | 61 backing_store->first, |
| 59 backing_store->second); | 62 backing_store->second); |
| 60 } | 63 } |
| 61 cache->Erase(backing_store); | 64 cache->Erase(backing_store); |
| 62 } | 65 } |
| 63 | 66 |
| 67 size_t ExpireLastBackingStore(BackingStoreCache* cache) { |
| 68 if (cache->size() < 1) |
| 69 return 0; |
| 70 |
| 71 // Crazy C++ alert: rbegin.base() is a forward iterator pointing to end(), |
| 72 // so we need to do -- to move one back to the actual last item. |
| 73 BackingStoreCache::iterator entry = --cache->rbegin().base(); |
| 74 size_t entry_size = entry->second->MemorySize(); |
| 75 ExpireBackingStoreAt(cache, entry); |
| 76 return entry_size; |
| 77 } |
| 78 |
| 64 void CreateCacheSpace(size_t size) { | 79 void CreateCacheSpace(size_t size) { |
| 65 // Given a request for |size|, first free from the large cache (until there's | 80 // 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 | 81 // only one item left) and then do the same from the small cache if we still |
| 67 // don't have enough. | 82 // don't have enough. |
| 68 while (size > 0 && (large_cache->size() > 1 || small_cache->size() > 1)) { | 83 while (size > 0 && (large_cache->size() > 1 || small_cache->size() > 1)) { |
| 69 BackingStoreCache* cache = | 84 BackingStoreCache* cache = |
| 70 (large_cache->size() > 1) ? large_cache : small_cache; | 85 (large_cache->size() > 1) ? large_cache : small_cache; |
| 71 while (size > 0 && cache->size() > 1) { | 86 while (size > 0 && cache->size() > 1) { |
| 72 // Crazy C++ alert: rbegin.base() is a forward iterator pointing to end(), | 87 size_t entry_size = ExpireLastBackingStore(cache); |
| 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) | 88 if (size > entry_size) |
| 78 size -= entry_size; | 89 size -= entry_size; |
| 79 else | 90 else |
| 80 size = 0; | 91 size = 0; |
| 81 } | 92 } |
| 82 } | 93 } |
| 83 DCHECK(size == 0); | 94 DCHECK(size == 0); |
| 84 } | 95 } |
| 85 | 96 |
| 86 // Creates the backing store for the host based on the dimensions passed in. | 97 // Creates the backing store for the host based on the dimensions passed in. |
| 87 // Removes the existing backing store if there is one. | 98 // Removes the existing backing store if there is one. |
| 88 BackingStore* CreateBackingStore(RenderWidgetHost* host, | 99 BackingStore* CreateBackingStore(RenderWidgetHost* host, |
| 89 const gfx::Size& backing_store_size) { | 100 const gfx::Size& backing_store_size) { |
| 90 // Remove any existing backing store in case we're replacing it. | 101 // Remove any existing backing store in case we're replacing it. |
| 91 BackingStoreManager::RemoveBackingStore(host); | 102 BackingStoreManager::RemoveBackingStore(host); |
| 92 | 103 |
| 93 if (!large_cache) { | 104 if (!large_cache) { |
| 94 large_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT); | 105 large_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT); |
| 95 small_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT); | 106 small_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT); |
| 96 } | 107 } |
| 97 | 108 |
| 98 // TODO(erikkay) 32bpp is not always accurate | 109 // TODO(erikkay) 32bpp is not always accurate |
| 99 size_t new_mem = backing_store_size.GetArea() * 4; | 110 size_t new_mem = backing_store_size.GetArea() * 4; |
| 100 size_t current_mem = BackingStoreManager::MemorySize(); | 111 size_t current_mem = BackingStoreManager::MemorySize(); |
| 101 size_t max_mem = GetBackingStoreCacheMemorySize(); | 112 size_t max_mem = MaxBackingStoreMemory(); |
| 102 DCHECK(new_mem < max_mem); | 113 DCHECK(new_mem < max_mem); |
| 103 if (current_mem + new_mem > max_mem) { | 114 if (current_mem + new_mem > max_mem) { |
| 104 // Need to remove old backing stores to make room for the new one. We | 115 // Need to remove old backing stores to make room for the new one. We |
| 105 // don't want to do this when the backing store is being replace by a new | 116 // don't want to do this when the backing store is being replace by a new |
| 106 // one for the same tab, but this case won't get called then: we'll have | 117 // one for the same tab, but this case won't get called then: we'll have |
| 107 // removed the onld one in the RemoveBackingStore above, and the cache | 118 // removed the old one in the RemoveBackingStore above, and the cache |
| 108 // won't be over-sized. | 119 // won't be over-sized. |
| 109 CreateCacheSpace((current_mem + new_mem) - max_mem); | 120 CreateCacheSpace((current_mem + new_mem) - max_mem); |
| 110 } | 121 } |
| 111 DCHECK((BackingStoreManager::MemorySize() + new_mem) < max_mem); | 122 DCHECK((BackingStoreManager::MemorySize() + new_mem) < max_mem); |
| 112 | 123 |
| 124 BackingStoreCache* cache; |
| 125 if (new_mem > kSmallThreshold) { |
| 126 // Limit the number of large backing stores (tabs) to the memory tier number |
| 127 // (between 2-5). While we allow a larger amount of memory for people who |
| 128 // have large windows, this means that those who use small browser windows |
| 129 // won't ever cache more than 5 tabs, so they pay a smaller memory cost. |
| 130 if (large_cache->size() >= MaxNumberOfBackingStores()) |
| 131 ExpireLastBackingStore(large_cache); |
| 132 cache = large_cache; |
| 133 } else { |
| 134 cache = small_cache; |
| 135 } |
| 113 BackingStore* backing_store = host->AllocBackingStore(backing_store_size); | 136 BackingStore* backing_store = host->AllocBackingStore(backing_store_size); |
| 114 if (new_mem > kSmallThreshold) | 137 cache->Put(host, backing_store); |
| 115 large_cache->Put(host, backing_store); | |
| 116 else | |
| 117 small_cache->Put(host, backing_store); | |
| 118 return backing_store; | 138 return backing_store; |
| 119 } | 139 } |
| 120 | 140 |
| 121 } // namespace | 141 } // namespace |
| 122 | 142 |
| 123 // BackingStoreManager --------------------------------------------------------- | 143 // BackingStoreManager --------------------------------------------------------- |
| 124 | 144 |
| 125 // static | 145 // static |
| 126 BackingStore* BackingStoreManager::GetBackingStore( | 146 BackingStore* BackingStoreManager::GetBackingStore( |
| 127 RenderWidgetHost* host, | 147 RenderWidgetHost* host, |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 217 size_t mem = 0; | 237 size_t mem = 0; |
| 218 BackingStoreCache::iterator it; | 238 BackingStoreCache::iterator it; |
| 219 for (it = large_cache->begin(); it != large_cache->end(); ++it) | 239 for (it = large_cache->begin(); it != large_cache->end(); ++it) |
| 220 mem += it->second->MemorySize(); | 240 mem += it->second->MemorySize(); |
| 221 | 241 |
| 222 for (it = small_cache->begin(); it != small_cache->end(); ++it) | 242 for (it = small_cache->begin(); it != small_cache->end(); ++it) |
| 223 mem += it->second->MemorySize(); | 243 mem += it->second->MemorySize(); |
| 224 | 244 |
| 225 return mem; | 245 return mem; |
| 226 } | 246 } |
| OLD | NEW |