Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(120)

Side by Side Diff: chrome/browser/renderer_host/backing_store_manager.cc

Issue 6532073: Move core pieces of browser\renderer_host to src\content. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698