| Index: chrome/browser/renderer_host/backing_store_manager.cc
|
| ===================================================================
|
| --- chrome/browser/renderer_host/backing_store_manager.cc (revision 75488)
|
| +++ chrome/browser/renderer_host/backing_store_manager.cc (working copy)
|
| @@ -1,287 +0,0 @@
|
| -// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "chrome/browser/renderer_host/backing_store_manager.h"
|
| -
|
| -#include "base/sys_info.h"
|
| -#include "base/command_line.h"
|
| -#include "chrome/common/chrome_switches.h"
|
| -#include "chrome/browser/renderer_host/backing_store.h"
|
| -#include "chrome/browser/renderer_host/render_widget_host.h"
|
| -#include "chrome/common/chrome_constants.h"
|
| -#include "chrome/common/mru_cache.h"
|
| -#include "chrome/common/notification_service.h"
|
| -
|
| -namespace {
|
| -
|
| -// There are two separate caches, |large_cache| and |small_cache|. large_cache
|
| -// is meant for large items (tabs, popup windows), while small_cache is meant
|
| -// for small items (extension toolstrips and buttons, etc.). The idea is that
|
| -// we'll almost always try to evict from large_cache first since small_cache
|
| -// items will tend to be visible more of the time.
|
| -typedef OwningMRUCache<RenderWidgetHost*, BackingStore*> BackingStoreCache;
|
| -static BackingStoreCache* large_cache = NULL;
|
| -static BackingStoreCache* small_cache = NULL;
|
| -
|
| -// Threshold is based on a single large-monitor-width toolstrip.
|
| -// (32bpp, 32 pixels high, 1920 pixels wide)
|
| -// TODO(erikkay) 32bpp assumption isn't great.
|
| -const size_t kSmallThreshold = 4 * 32 * 1920;
|
| -
|
| -// Pick a large monitor size to use as a multiplier. This is multiplied by the
|
| -// max number of large backing stores (usually tabs) to pick a ceiling on the
|
| -// max memory to use.
|
| -// TODO(erikkay) Perhaps we should actually use monitor size? That way we
|
| -// could make an assertion like "worse case, there are two tabs in the cache".
|
| -// However, the small_cache might mess up these calculations a bit.
|
| -// TODO(erikkay) 32bpp assumption isn't great.
|
| -const size_t kMemoryMultiplier = 4 * 1920 * 1200; // ~9MB
|
| -
|
| -// The maximum number of large BackingStoreCache objects (tabs) to use.
|
| -// Use a minimum of 2, and add one for each 256MB of physical memory you have.
|
| -// Cap at 5, the thinking being that even if you have a gigantic amount of
|
| -// RAM, there's a limit to how much caching helps beyond a certain number
|
| -// of tabs. If users *really* want unlimited stores, allow it via the
|
| -// --disable-backing-store-limit flag.
|
| -static size_t MaxNumberOfBackingStores() {
|
| - static bool unlimited = false;
|
| - const CommandLine& command = *CommandLine::ForCurrentProcess();
|
| - unlimited = command.HasSwitch(switches::kDisableBackingStoreLimit);
|
| -
|
| -
|
| - if (unlimited) {
|
| - // 100 isn't truly unlimited, but given that backing stores count against
|
| - // GDI memory, it's well past any reasonable number. Many systems will
|
| - // begin to fail in strange ways well before they hit 100 stores.
|
| - return 100;
|
| - } else {
|
| - return std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256));
|
| - }
|
| -}
|
| -
|
| -// The maximum about of memory to use for all BackingStoreCache object combined.
|
| -static size_t MaxBackingStoreMemory() {
|
| - // Compute in terms of the number of large monitor's worth of backing-store.
|
| - return MaxNumberOfBackingStores() * kMemoryMultiplier;
|
| -}
|
| -
|
| -// Expires the given |backing_store| from |cache|.
|
| -void ExpireBackingStoreAt(BackingStoreCache* cache,
|
| - BackingStoreCache::iterator backing_store) {
|
| - NotificationService::current()->Notify(
|
| - NotificationType::RENDER_WIDGET_HOST_WILL_DESTROY_BACKING_STORE,
|
| - Source<RenderWidgetHost>(backing_store->first),
|
| - Details<BackingStore>(backing_store->second));
|
| - cache->Erase(backing_store);
|
| -}
|
| -
|
| -size_t ExpireLastBackingStore(BackingStoreCache* cache) {
|
| - if (cache->size() < 1)
|
| - return 0;
|
| -
|
| - // Crazy C++ alert: rbegin.base() is a forward iterator pointing to end(),
|
| - // so we need to do -- to move one back to the actual last item.
|
| - BackingStoreCache::iterator entry = --cache->rbegin().base();
|
| - size_t entry_size = entry->second->MemorySize();
|
| - ExpireBackingStoreAt(cache, entry);
|
| - return entry_size;
|
| -}
|
| -
|
| -void CreateCacheSpace(size_t size) {
|
| - // Given a request for |size|, first free from the large cache (until there's
|
| - // only one item left) and then do the same from the small cache if we still
|
| - // don't have enough.
|
| - while (size > 0 && (large_cache->size() > 1 || small_cache->size() > 1)) {
|
| - BackingStoreCache* cache =
|
| - (large_cache->size() > 1) ? large_cache : small_cache;
|
| - while (size > 0 && cache->size() > 1) {
|
| - size_t entry_size = ExpireLastBackingStore(cache);
|
| - if (size > entry_size)
|
| - size -= entry_size;
|
| - else
|
| - size = 0;
|
| - }
|
| - }
|
| - DCHECK(size == 0);
|
| -}
|
| -
|
| -// Creates the backing store for the host based on the dimensions passed in.
|
| -// Removes the existing backing store if there is one.
|
| -BackingStore* CreateBackingStore(RenderWidgetHost* host,
|
| - const gfx::Size& backing_store_size) {
|
| - // Remove any existing backing store in case we're replacing it.
|
| - BackingStoreManager::RemoveBackingStore(host);
|
| -
|
| - if (!large_cache) {
|
| - large_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT);
|
| - small_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT);
|
| - }
|
| -
|
| - // TODO(erikkay) 32bpp is not always accurate
|
| - size_t new_mem = backing_store_size.GetArea() * 4;
|
| - size_t current_mem = BackingStoreManager::MemorySize();
|
| - size_t max_mem = MaxBackingStoreMemory();
|
| - DCHECK(new_mem < max_mem);
|
| - if (current_mem + new_mem > max_mem) {
|
| - // Need to remove old backing stores to make room for the new one. We
|
| - // don't want to do this when the backing store is being replace by a new
|
| - // one for the same tab, but this case won't get called then: we'll have
|
| - // removed the old one in the RemoveBackingStore above, and the cache
|
| - // won't be over-sized.
|
| - CreateCacheSpace((current_mem + new_mem) - max_mem);
|
| - }
|
| - DCHECK((BackingStoreManager::MemorySize() + new_mem) <= max_mem);
|
| -
|
| - BackingStoreCache* cache;
|
| - if (new_mem > kSmallThreshold) {
|
| - // Limit the number of large backing stores (tabs) to the memory tier number
|
| - // (between 2-5). While we allow a larger amount of memory for people who
|
| - // have large windows, this means that those who use small browser windows
|
| - // won't ever cache more than 5 tabs, so they pay a smaller memory cost.
|
| - if (large_cache->size() >= MaxNumberOfBackingStores())
|
| - ExpireLastBackingStore(large_cache);
|
| - cache = large_cache;
|
| - } else {
|
| - cache = small_cache;
|
| - }
|
| - BackingStore* backing_store = host->AllocBackingStore(backing_store_size);
|
| - if (backing_store)
|
| - cache->Put(host, backing_store);
|
| - return backing_store;
|
| -}
|
| -
|
| -int ComputeTotalArea(const std::vector<gfx::Rect>& rects) {
|
| - // We assume that the given rects are non-overlapping, which is a property of
|
| - // the paint rects generated by the PaintAggregator.
|
| -#ifndef NDEBUG
|
| - for (size_t i = 0; i < rects.size(); ++i) {
|
| - for (size_t j = 0; j < rects.size(); ++j) {
|
| - if (i != j)
|
| - DCHECK(!rects[i].Intersects(rects[j]));
|
| - }
|
| - }
|
| -#endif
|
| - int area = 0;
|
| - for (size_t i = 0; i < rects.size(); ++i)
|
| - area += rects[i].size().GetArea();
|
| - return area;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -// BackingStoreManager ---------------------------------------------------------
|
| -
|
| -// static
|
| -BackingStore* BackingStoreManager::GetBackingStore(
|
| - RenderWidgetHost* host,
|
| - const gfx::Size& desired_size) {
|
| - BackingStore* backing_store = Lookup(host);
|
| - if (backing_store) {
|
| - // If we already have a backing store, then make sure it is the correct
|
| - // size.
|
| - if (backing_store->size() == desired_size)
|
| - return backing_store;
|
| - backing_store = NULL;
|
| - }
|
| -
|
| - return backing_store;
|
| -}
|
| -
|
| -// static
|
| -void BackingStoreManager::PrepareBackingStore(
|
| - RenderWidgetHost* host,
|
| - const gfx::Size& backing_store_size,
|
| - TransportDIB::Id bitmap,
|
| - const gfx::Rect& bitmap_rect,
|
| - const std::vector<gfx::Rect>& copy_rects,
|
| - bool* needs_full_paint) {
|
| - BackingStore* backing_store = GetBackingStore(host, backing_store_size);
|
| - if (!backing_store) {
|
| - // We need to get Webkit to generate a new paint here, as we
|
| - // don't have a previous snapshot.
|
| - if (bitmap_rect.size() != backing_store_size ||
|
| - bitmap_rect.x() != 0 || bitmap_rect.y() != 0 ||
|
| - ComputeTotalArea(copy_rects) != backing_store_size.GetArea() ||
|
| - !(backing_store = CreateBackingStore(host, backing_store_size))) {
|
| - DCHECK(needs_full_paint != NULL);
|
| - *needs_full_paint = true;
|
| - // Makes no sense to paint the transport dib if we are going
|
| - // to request a full paint.
|
| - return;
|
| - }
|
| - }
|
| -
|
| - backing_store->PaintToBackingStore(host->process(), bitmap,
|
| - bitmap_rect, copy_rects);
|
| -}
|
| -
|
| -// static
|
| -BackingStore* BackingStoreManager::Lookup(RenderWidgetHost* host) {
|
| - if (large_cache) {
|
| - BackingStoreCache::iterator it = large_cache->Get(host);
|
| - if (it != large_cache->end())
|
| - return it->second;
|
| -
|
| - // This moves host to the front of the MRU.
|
| - it = small_cache->Get(host);
|
| - if (it != small_cache->end())
|
| - return it->second;
|
| - }
|
| - return NULL;
|
| -}
|
| -
|
| -// static
|
| -void BackingStoreManager::RemoveBackingStore(RenderWidgetHost* host) {
|
| - if (!large_cache)
|
| - return;
|
| -
|
| - BackingStoreCache* cache = large_cache;
|
| - BackingStoreCache::iterator it = cache->Peek(host);
|
| - if (it == cache->end()) {
|
| - cache = small_cache;
|
| - it = cache->Peek(host);
|
| - if (it == cache->end())
|
| - return;
|
| - }
|
| - cache->Erase(it);
|
| -}
|
| -
|
| -// static
|
| -void BackingStoreManager::RemoveAllBackingStores() {
|
| - if (large_cache) {
|
| - large_cache->Clear();
|
| - small_cache->Clear();
|
| - }
|
| -}
|
| -
|
| -// static
|
| -bool BackingStoreManager::ExpireBackingStoreForTest(RenderWidgetHost* host) {
|
| - BackingStoreCache* cache = large_cache;
|
| -
|
| - BackingStoreCache::iterator it = cache->Peek(host);
|
| - if (it == cache->end()) {
|
| - cache = small_cache;
|
| - it = cache->Peek(host);
|
| - if (it == cache->end())
|
| - return false;
|
| - }
|
| - ExpireBackingStoreAt(cache, it);
|
| - return true;
|
| -}
|
| -
|
| -// static
|
| -size_t BackingStoreManager::MemorySize() {
|
| - if (!large_cache)
|
| - return 0;
|
| -
|
| - size_t mem = 0;
|
| - BackingStoreCache::iterator it;
|
| - for (it = large_cache->begin(); it != large_cache->end(); ++it)
|
| - mem += it->second->MemorySize();
|
| -
|
| - for (it = small_cache->begin(); it != small_cache->end(); ++it)
|
| - mem += it->second->MemorySize();
|
| -
|
| - return mem;
|
| -}
|
|
|