Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / backing_store_manager.cc
1 // Copyright (c) 2012 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 "content/browser/renderer_host/backing_store_manager.h"
6
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/containers/mru_cache.h"
10 #include "base/sys_info.h"
11 #include "content/browser/renderer_host/backing_store.h"
12 #include "content/browser/renderer_host/render_widget_host_impl.h"
13 #include "content/public/common/content_switches.h"
14
15 namespace content {
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 popups, HTML5 notifications). 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 base::OwningMRUCache<RenderWidgetHost*, BackingStore*>
24     BackingStoreCache;
25 BackingStoreCache* large_cache = NULL;
26 BackingStoreCache* small_cache = NULL;
27
28 // Threshold is based on a single large-monitor-width toolstrip.
29 // (32bpp, 32 pixels high, 1920 pixels wide)
30 // TODO(aa): The extension system no longer supports toolstrips, but we think
31 // this might be helping for other examples of small HTML views in Chrome.
32 // Maybe this cache should be redesigned to simply prefer smaller objects to
33 // larger ones, rather than having a fixed threshold.
34 // For more background, see: crbug.com/100506.
35 const size_t kSmallThreshold = 4 * 32 * 1920;
36
37 // Pick a large monitor size to use as a multiplier.  This is multiplied by the
38 // max number of large backing stores (usually tabs) to pick a ceiling on the
39 // max memory to use.
40 // TODO(erikkay) Perhaps we should actually use monitor size?  That way we
41 // could make an assertion like "worse case, there are two tabs in the cache".
42 // However, the small_cache might mess up these calculations a bit.
43 // TODO(erikkay) 32bpp assumption isn't great.
44 const size_t kMemoryMultiplier = 4 * 1920 * 1200;  // ~9MB
45
46 // The maximum number of large BackingStoreCache objects (tabs) to use.
47 // Use a minimum of 2, and add one for each 256MB of physical memory you have.
48 // Cap at 5, the thinking being that even if you have a gigantic amount of
49 // RAM, there's a limit to how much caching helps beyond a certain number
50 // of tabs. If users *really* want unlimited stores, allow it via the
51 // --disable-backing-store-limit flag.
52 static size_t MaxNumberOfBackingStores() {
53   static bool unlimited = false;
54   const CommandLine& command = *CommandLine::ForCurrentProcess();
55   unlimited = command.HasSwitch(switches::kDisableBackingStoreLimit);
56
57
58   if (unlimited) {
59     // 100 isn't truly unlimited, but given that backing stores count against
60     // GDI memory, it's well past any reasonable number. Many systems will
61     // begin to fail in strange ways well before they hit 100 stores.
62     return 100;
63   } else {
64     return std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256));
65   }
66 }
67
68 // The maximum about of memory to use for all BackingStoreCache object combined.
69 static size_t MaxBackingStoreMemory() {
70   // Compute in terms of the number of large monitor's worth of backing-store.
71   return MaxNumberOfBackingStores() * kMemoryMultiplier;
72 }
73
74 // Expires the given |backing_store| from |cache|.
75 void ExpireBackingStoreAt(BackingStoreCache* cache,
76                           BackingStoreCache::iterator backing_store) {
77   cache->Erase(backing_store);
78 }
79
80 size_t ExpireLastBackingStore(BackingStoreCache* cache) {
81   if (cache->size() < 1)
82     return 0;
83
84   // Crazy C++ alert: rbegin.base() is a forward iterator pointing to end(),
85   // so we need to do -- to move one back to the actual last item.
86   BackingStoreCache::iterator entry = --cache->rbegin().base();
87   size_t entry_size = entry->second->MemorySize();
88   ExpireBackingStoreAt(cache, entry);
89   return entry_size;
90 }
91
92 void CreateCacheSpace(size_t size) {
93   // Given a request for |size|, first free from the large cache (until there's
94   // only one item left) and then do the same from the small cache if we still
95   // don't have enough.
96   while (size > 0 && (large_cache->size() > 1 || small_cache->size() > 1)) {
97     BackingStoreCache* cache =
98         (large_cache->size() > 1) ? large_cache : small_cache;
99     while (size > 0 && cache->size() > 1) {
100       size_t entry_size = ExpireLastBackingStore(cache);
101       if (size > entry_size)
102         size -= entry_size;
103       else
104         size = 0;
105     }
106   }
107   DCHECK(size == 0);
108 }
109
110 // Creates the backing store for the host based on the dimensions passed in.
111 // Removes the existing backing store if there is one.
112 BackingStore* CreateBackingStore(RenderWidgetHost* host,
113                                  const gfx::Size& backing_store_size) {
114   // Remove any existing backing store in case we're replacing it.
115   BackingStoreManager::RemoveBackingStore(host);
116
117   if (!large_cache) {
118     large_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT);
119     small_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT);
120   }
121
122   // TODO(erikkay) 32bpp is not always accurate
123   size_t new_mem = backing_store_size.GetArea() * 4;
124   size_t current_mem = BackingStoreManager::MemorySize();
125   size_t max_mem = MaxBackingStoreMemory();
126   DCHECK(new_mem < max_mem);
127   if (current_mem + new_mem > max_mem) {
128     // Need to remove old backing stores to make room for the new one. We
129     // don't want to do this when the backing store is being replace by a new
130     // one for the same WebContents, but this case won't get called then: we'll
131     // have removed the old one in the RemoveBackingStore above, and the cache
132     // won't be over-sized.
133     CreateCacheSpace((current_mem + new_mem) - max_mem);
134   }
135   DCHECK((BackingStoreManager::MemorySize() + new_mem) <= max_mem);
136
137   BackingStoreCache* cache;
138   if (new_mem > kSmallThreshold) {
139     // Limit the number of large backing stores (tabs) to the memory tier number
140     // (between 2-5). While we allow a larger amount of memory for people who
141     // have large windows, this means that those who use small browser windows
142     // won't ever cache more than 5 tabs, so they pay a smaller memory cost.
143     if (large_cache->size() >= MaxNumberOfBackingStores())
144       ExpireLastBackingStore(large_cache);
145     cache = large_cache;
146   } else {
147     cache = small_cache;
148   }
149   BackingStore* backing_store = RenderWidgetHostImpl::From(
150       host)->AllocBackingStore(backing_store_size);
151   if (backing_store)
152     cache->Put(host, backing_store);
153   return backing_store;
154 }
155
156 int ComputeTotalArea(const std::vector<gfx::Rect>& rects) {
157   // We assume that the given rects are non-overlapping, which is a property of
158   // the paint rects generated by the PaintAggregator.
159 #ifndef NDEBUG
160   for (size_t i = 0; i < rects.size(); ++i) {
161     for (size_t j = 0; j < rects.size(); ++j) {
162       if (i != j)
163         DCHECK(!rects[i].Intersects(rects[j]));
164     }
165   }
166 #endif
167   int area = 0;
168   for (size_t i = 0; i < rects.size(); ++i)
169     area += rects[i].size().GetArea();
170   return area;
171 }
172
173 }  // namespace
174
175 // BackingStoreManager ---------------------------------------------------------
176
177 // static
178 BackingStore* BackingStoreManager::GetBackingStore(
179     RenderWidgetHost* host,
180     const gfx::Size& desired_size) {
181   BackingStore* backing_store = Lookup(host);
182   if (backing_store) {
183     // If we already have a backing store, then make sure it is the correct
184     // size.
185     if (backing_store->size() == desired_size)
186       return backing_store;
187     backing_store = NULL;
188   }
189
190   return backing_store;
191 }
192
193 // static
194 void BackingStoreManager::PrepareBackingStore(
195     RenderWidgetHost* host,
196     const gfx::Size& backing_store_size,
197     TransportDIB::Id bitmap,
198     const gfx::Rect& bitmap_rect,
199     const std::vector<gfx::Rect>& copy_rects,
200     float scale_factor,
201     const base::Closure& completion_callback,
202     bool* needs_full_paint,
203     bool* scheduled_completion_callback) {
204   BackingStore* backing_store = GetBackingStore(host, backing_store_size);
205   if (!backing_store) {
206     // We need to get Webkit to generate a new paint here, as we
207     // don't have a previous snapshot.
208     if (bitmap_rect.size() != backing_store_size ||
209         bitmap_rect.x() != 0 || bitmap_rect.y() != 0 ||
210         ComputeTotalArea(copy_rects) != backing_store_size.GetArea() ||
211         !(backing_store = CreateBackingStore(host, backing_store_size))) {
212       DCHECK(needs_full_paint != NULL);
213       *needs_full_paint = true;
214       *scheduled_completion_callback = false;
215       // Makes no sense to paint the transport dib if we are going
216       // to request a full paint.
217       return;
218     }
219   }
220
221   backing_store->PaintToBackingStore(host->GetProcess(), bitmap,
222                                      bitmap_rect, copy_rects, scale_factor,
223                                      completion_callback,
224                                      scheduled_completion_callback);
225 }
226
227 // static
228 BackingStore* BackingStoreManager::Lookup(RenderWidgetHost* host) {
229   if (large_cache) {
230     BackingStoreCache::iterator it = large_cache->Get(host);
231     if (it != large_cache->end())
232       return it->second;
233
234     // This moves host to the front of the MRU.
235     it = small_cache->Get(host);
236     if (it != small_cache->end())
237       return it->second;
238   }
239   return NULL;
240 }
241
242 // static
243 void BackingStoreManager::RemoveBackingStore(RenderWidgetHost* host) {
244   if (!large_cache)
245     return;
246
247   BackingStoreCache* cache = large_cache;
248   BackingStoreCache::iterator it = cache->Peek(host);
249   if (it == cache->end()) {
250     cache = small_cache;
251     it = cache->Peek(host);
252     if (it == cache->end())
253       return;
254   }
255   cache->Erase(it);
256 }
257
258 // static
259 size_t BackingStoreManager::MemorySize() {
260   if (!large_cache)
261     return 0;
262
263   size_t mem = 0;
264   BackingStoreCache::iterator it;
265   for (it = large_cache->begin(); it != large_cache->end(); ++it)
266     mem += it->second->MemorySize();
267
268   for (it = small_cache->begin(); it != small_cache->end(); ++it)
269     mem += it->second->MemorySize();
270
271   return mem;
272 }
273
274 }  // namespace content