| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/renderer_host/backing_store_manager.h" | |
| 6 | |
| 7 #include "base/sys_info.h" | |
| 8 #include "base/command_line.h" | |
| 9 #include "chrome/common/chrome_switches.h" | |
| 10 #include "chrome/browser/renderer_host/backing_store.h" | |
| 11 #include "chrome/browser/renderer_host/render_widget_host.h" | |
| 12 #include "chrome/common/chrome_constants.h" | |
| 13 #include "chrome/common/mru_cache.h" | |
| 14 #include "chrome/common/notification_service.h" | |
| 15 | |
| 16 namespace { | |
| 17 | |
| 18 // There are two separate caches, |large_cache| and |small_cache|. large_cache | |
| 19 // is meant for large items (tabs, popup windows), while small_cache is meant | |
| 20 // for small items (extension toolstrips and buttons, etc.). The idea is that | |
| 21 // we'll almost always try to evict from large_cache first since small_cache | |
| 22 // items will tend to be visible more of the time. | |
| 23 typedef OwningMRUCache<RenderWidgetHost*, BackingStore*> BackingStoreCache; | |
| 24 static BackingStoreCache* large_cache = NULL; | |
| 25 static BackingStoreCache* small_cache = NULL; | |
| 26 | |
| 27 // Threshold is based on a single large-monitor-width toolstrip. | |
| 28 // (32bpp, 32 pixels high, 1920 pixels wide) | |
| 29 // TODO(erikkay) 32bpp assumption isn't great. | |
| 30 const size_t kSmallThreshold = 4 * 32 * 1920; | |
| 31 | |
| 32 // Pick a large monitor size to use as a multiplier. This is multiplied by the | |
| 33 // max number of large backing stores (usually tabs) to pick a ceiling on the | |
| 34 // max memory to use. | |
| 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 // The maximum number of large BackingStoreCache objects (tabs) to use. | |
| 42 // Use a minimum of 2, and add one for each 256MB of physical memory you have. | |
| 43 // Cap at 5, the thinking being that even if you have a gigantic amount of | |
| 44 // RAM, there's a limit to how much caching helps beyond a certain number | |
| 45 // of tabs. If users *really* want unlimited stores, allow it via the | |
| 46 // --disable-backing-store-limit flag. | |
| 47 static size_t MaxNumberOfBackingStores() { | |
| 48 static bool unlimited = false; | |
| 49 const CommandLine& command = *CommandLine::ForCurrentProcess(); | |
| 50 unlimited = command.HasSwitch(switches::kDisableBackingStoreLimit); | |
| 51 | |
| 52 | |
| 53 if (unlimited) { | |
| 54 // 100 isn't truly unlimited, but given that backing stores count against | |
| 55 // GDI memory, it's well past any reasonable number. Many systems will | |
| 56 // begin to fail in strange ways well before they hit 100 stores. | |
| 57 return 100; | |
| 58 } else { | |
| 59 return std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256)); | |
| 60 } | |
| 61 } | |
| 62 | |
| 63 // The maximum about of memory to use for all BackingStoreCache object combined. | |
| 64 static size_t MaxBackingStoreMemory() { | |
| 65 // Compute in terms of the number of large monitor's worth of backing-store. | |
| 66 return MaxNumberOfBackingStores() * kMemoryMultiplier; | |
| 67 } | |
| 68 | |
| 69 // Expires the given |backing_store| from |cache|. | |
| 70 void ExpireBackingStoreAt(BackingStoreCache* cache, | |
| 71 BackingStoreCache::iterator backing_store) { | |
| 72 NotificationService::current()->Notify( | |
| 73 NotificationType::RENDER_WIDGET_HOST_WILL_DESTROY_BACKING_STORE, | |
| 74 Source<RenderWidgetHost>(backing_store->first), | |
| 75 Details<BackingStore>(backing_store->second)); | |
| 76 cache->Erase(backing_store); | |
| 77 } | |
| 78 | |
| 79 size_t ExpireLastBackingStore(BackingStoreCache* cache) { | |
| 80 if (cache->size() < 1) | |
| 81 return 0; | |
| 82 | |
| 83 // Crazy C++ alert: rbegin.base() is a forward iterator pointing to end(), | |
| 84 // so we need to do -- to move one back to the actual last item. | |
| 85 BackingStoreCache::iterator entry = --cache->rbegin().base(); | |
| 86 size_t entry_size = entry->second->MemorySize(); | |
| 87 ExpireBackingStoreAt(cache, entry); | |
| 88 return entry_size; | |
| 89 } | |
| 90 | |
| 91 void CreateCacheSpace(size_t size) { | |
| 92 // Given a request for |size|, first free from the large cache (until there's | |
| 93 // only one item left) and then do the same from the small cache if we still | |
| 94 // don't have enough. | |
| 95 while (size > 0 && (large_cache->size() > 1 || small_cache->size() > 1)) { | |
| 96 BackingStoreCache* cache = | |
| 97 (large_cache->size() > 1) ? large_cache : small_cache; | |
| 98 while (size > 0 && cache->size() > 1) { | |
| 99 size_t entry_size = ExpireLastBackingStore(cache); | |
| 100 if (size > entry_size) | |
| 101 size -= entry_size; | |
| 102 else | |
| 103 size = 0; | |
| 104 } | |
| 105 } | |
| 106 DCHECK(size == 0); | |
| 107 } | |
| 108 | |
| 109 // Creates the backing store for the host based on the dimensions passed in. | |
| 110 // Removes the existing backing store if there is one. | |
| 111 BackingStore* CreateBackingStore(RenderWidgetHost* host, | |
| 112 const gfx::Size& backing_store_size) { | |
| 113 // Remove any existing backing store in case we're replacing it. | |
| 114 BackingStoreManager::RemoveBackingStore(host); | |
| 115 | |
| 116 if (!large_cache) { | |
| 117 large_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT); | |
| 118 small_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT); | |
| 119 } | |
| 120 | |
| 121 // TODO(erikkay) 32bpp is not always accurate | |
| 122 size_t new_mem = backing_store_size.GetArea() * 4; | |
| 123 size_t current_mem = BackingStoreManager::MemorySize(); | |
| 124 size_t max_mem = MaxBackingStoreMemory(); | |
| 125 DCHECK(new_mem < max_mem); | |
| 126 if (current_mem + new_mem > max_mem) { | |
| 127 // Need to remove old backing stores to make room for the new one. We | |
| 128 // don't want to do this when the backing store is being replace by a new | |
| 129 // one for the same tab, but this case won't get called then: we'll have | |
| 130 // removed the old one in the RemoveBackingStore above, and the cache | |
| 131 // won't be over-sized. | |
| 132 CreateCacheSpace((current_mem + new_mem) - max_mem); | |
| 133 } | |
| 134 DCHECK((BackingStoreManager::MemorySize() + new_mem) <= max_mem); | |
| 135 | |
| 136 BackingStoreCache* cache; | |
| 137 if (new_mem > kSmallThreshold) { | |
| 138 // Limit the number of large backing stores (tabs) to the memory tier number | |
| 139 // (between 2-5). While we allow a larger amount of memory for people who | |
| 140 // have large windows, this means that those who use small browser windows | |
| 141 // won't ever cache more than 5 tabs, so they pay a smaller memory cost. | |
| 142 if (large_cache->size() >= MaxNumberOfBackingStores()) | |
| 143 ExpireLastBackingStore(large_cache); | |
| 144 cache = large_cache; | |
| 145 } else { | |
| 146 cache = small_cache; | |
| 147 } | |
| 148 BackingStore* backing_store = host->AllocBackingStore(backing_store_size); | |
| 149 if (backing_store) | |
| 150 cache->Put(host, backing_store); | |
| 151 return backing_store; | |
| 152 } | |
| 153 | |
| 154 int ComputeTotalArea(const std::vector<gfx::Rect>& rects) { | |
| 155 // We assume that the given rects are non-overlapping, which is a property of | |
| 156 // the paint rects generated by the PaintAggregator. | |
| 157 #ifndef NDEBUG | |
| 158 for (size_t i = 0; i < rects.size(); ++i) { | |
| 159 for (size_t j = 0; j < rects.size(); ++j) { | |
| 160 if (i != j) | |
| 161 DCHECK(!rects[i].Intersects(rects[j])); | |
| 162 } | |
| 163 } | |
| 164 #endif | |
| 165 int area = 0; | |
| 166 for (size_t i = 0; i < rects.size(); ++i) | |
| 167 area += rects[i].size().GetArea(); | |
| 168 return area; | |
| 169 } | |
| 170 | |
| 171 } // namespace | |
| 172 | |
| 173 // BackingStoreManager --------------------------------------------------------- | |
| 174 | |
| 175 // static | |
| 176 BackingStore* BackingStoreManager::GetBackingStore( | |
| 177 RenderWidgetHost* host, | |
| 178 const gfx::Size& desired_size) { | |
| 179 BackingStore* backing_store = Lookup(host); | |
| 180 if (backing_store) { | |
| 181 // If we already have a backing store, then make sure it is the correct | |
| 182 // size. | |
| 183 if (backing_store->size() == desired_size) | |
| 184 return backing_store; | |
| 185 backing_store = NULL; | |
| 186 } | |
| 187 | |
| 188 return backing_store; | |
| 189 } | |
| 190 | |
| 191 // static | |
| 192 void BackingStoreManager::PrepareBackingStore( | |
| 193 RenderWidgetHost* host, | |
| 194 const gfx::Size& backing_store_size, | |
| 195 TransportDIB::Id bitmap, | |
| 196 const gfx::Rect& bitmap_rect, | |
| 197 const std::vector<gfx::Rect>& copy_rects, | |
| 198 bool* needs_full_paint) { | |
| 199 BackingStore* backing_store = GetBackingStore(host, backing_store_size); | |
| 200 if (!backing_store) { | |
| 201 // We need to get Webkit to generate a new paint here, as we | |
| 202 // don't have a previous snapshot. | |
| 203 if (bitmap_rect.size() != backing_store_size || | |
| 204 bitmap_rect.x() != 0 || bitmap_rect.y() != 0 || | |
| 205 ComputeTotalArea(copy_rects) != backing_store_size.GetArea() || | |
| 206 !(backing_store = CreateBackingStore(host, backing_store_size))) { | |
| 207 DCHECK(needs_full_paint != NULL); | |
| 208 *needs_full_paint = true; | |
| 209 // Makes no sense to paint the transport dib if we are going | |
| 210 // to request a full paint. | |
| 211 return; | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 backing_store->PaintToBackingStore(host->process(), bitmap, | |
| 216 bitmap_rect, copy_rects); | |
| 217 } | |
| 218 | |
| 219 // static | |
| 220 BackingStore* BackingStoreManager::Lookup(RenderWidgetHost* host) { | |
| 221 if (large_cache) { | |
| 222 BackingStoreCache::iterator it = large_cache->Get(host); | |
| 223 if (it != large_cache->end()) | |
| 224 return it->second; | |
| 225 | |
| 226 // This moves host to the front of the MRU. | |
| 227 it = small_cache->Get(host); | |
| 228 if (it != small_cache->end()) | |
| 229 return it->second; | |
| 230 } | |
| 231 return NULL; | |
| 232 } | |
| 233 | |
| 234 // static | |
| 235 void BackingStoreManager::RemoveBackingStore(RenderWidgetHost* host) { | |
| 236 if (!large_cache) | |
| 237 return; | |
| 238 | |
| 239 BackingStoreCache* cache = large_cache; | |
| 240 BackingStoreCache::iterator it = cache->Peek(host); | |
| 241 if (it == cache->end()) { | |
| 242 cache = small_cache; | |
| 243 it = cache->Peek(host); | |
| 244 if (it == cache->end()) | |
| 245 return; | |
| 246 } | |
| 247 cache->Erase(it); | |
| 248 } | |
| 249 | |
| 250 // static | |
| 251 void BackingStoreManager::RemoveAllBackingStores() { | |
| 252 if (large_cache) { | |
| 253 large_cache->Clear(); | |
| 254 small_cache->Clear(); | |
| 255 } | |
| 256 } | |
| 257 | |
| 258 // static | |
| 259 bool BackingStoreManager::ExpireBackingStoreForTest(RenderWidgetHost* host) { | |
| 260 BackingStoreCache* cache = large_cache; | |
| 261 | |
| 262 BackingStoreCache::iterator it = cache->Peek(host); | |
| 263 if (it == cache->end()) { | |
| 264 cache = small_cache; | |
| 265 it = cache->Peek(host); | |
| 266 if (it == cache->end()) | |
| 267 return false; | |
| 268 } | |
| 269 ExpireBackingStoreAt(cache, it); | |
| 270 return true; | |
| 271 } | |
| 272 | |
| 273 // static | |
| 274 size_t BackingStoreManager::MemorySize() { | |
| 275 if (!large_cache) | |
| 276 return 0; | |
| 277 | |
| 278 size_t mem = 0; | |
| 279 BackingStoreCache::iterator it; | |
| 280 for (it = large_cache->begin(); it != large_cache->end(); ++it) | |
| 281 mem += it->second->MemorySize(); | |
| 282 | |
| 283 for (it = small_cache->begin(); it != small_cache->end(); ++it) | |
| 284 mem += it->second->MemorySize(); | |
| 285 | |
| 286 return mem; | |
| 287 } | |
| OLD | NEW |