diff options
author | iyengar@google.com <iyengar@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-06 05:30:12 +0000 |
---|---|---|
committer | iyengar@google.com <iyengar@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-06 05:30:12 +0000 |
commit | ec7dc1160836695462ee7311d4c02f2c2f61350e (patch) | |
tree | 9fb3d234403ee1b52ca24f603dbedede952a4948 /chrome/browser/render_widget_host.cc | |
parent | e5d478012a5ee0765d35fd62e5090d07f5ae7dde (diff) | |
download | chromium_src-ec7dc1160836695462ee7311d4c02f2c2f61350e.zip chromium_src-ec7dc1160836695462ee7311d4c02f2c2f61350e.tar.gz chromium_src-ec7dc1160836695462ee7311d4c02f2c2f61350e.tar.bz2 |
This fixes http://b/issue?id=1257424, which is a need to implement a global backing store cache. The current backing store cache is only used for invisible tabs and every other RenderWidgetHost holds a reference to its backing store.
This CB proposes a change where in we have a global backing store cache, whose size can be controlled based on strategies like available resources etc. At this point the strategy is not implemented and the size is left at 5.
We no longer maintain a reference to the backing store in the RenderWidgetHost. Every host queries the global cache for its backing store. The cache provides methods to create the backing store and populate it with the required dib.
The other change is to use the renderer dib when the size of the bitmap being painted is the same as the backing store size. This is an attempt to improve performance in operations like scrolling.
Bug=1257424
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@422 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/render_widget_host.cc')
-rw-r--r-- | chrome/browser/render_widget_host.cc | 429 |
1 files changed, 299 insertions, 130 deletions
diff --git a/chrome/browser/render_widget_host.cc b/chrome/browser/render_widget_host.cc index 23cf9e1..8f48a51 100644 --- a/chrome/browser/render_widget_host.cc +++ b/chrome/browser/render_widget_host.cc @@ -54,85 +54,269 @@ static const int kHungRendererDelayMs = 10000; // RenderWidget::BackingStore RenderWidgetHost::BackingStore::BackingStore(const gfx::Size& size) - : size_(size) { + : size_(size), + backing_store_dib_(NULL), + renderer_bitmap_section_(NULL), + original_bitmap_(NULL) { HDC screen_dc = ::GetDC(NULL); hdc_ = CreateCompatibleDC(screen_dc); - int color_depth = GetDeviceCaps(screen_dc, BITSPIXEL); - - // Color depths less than 16 bpp require a palette to be specified in the - // BITMAPINFO structure passed to CreateDIBSection. Instead of creating - // the palette, we specify the desired color depth as 16 which allows the - // OS to come up with an approximation. Tested this with 8bpp. - if (color_depth < 16) - color_depth = 16; - - BITMAPINFOHEADER hdr = {0}; - gfx::CreateBitmapHeaderWithColorDepth(size.width(), size.height(), - color_depth, &hdr); - void* data = NULL; - HANDLE bitmap = CreateDIBSection(hdc_, reinterpret_cast<BITMAPINFO*>(&hdr), - 0, &data, NULL, 0); - SelectObject(hdc_, bitmap); - ::ReleaseDC(NULL, screen_dc); + ReleaseDC(NULL, screen_dc); } RenderWidgetHost::BackingStore::~BackingStore() { DCHECK(hdc_); - HBITMAP bitmap = - reinterpret_cast<HBITMAP>(GetCurrentObject(hdc_, OBJ_BITMAP)); DeleteDC(hdc_); - DeleteObject(bitmap); + + if (backing_store_dib_) { + DeleteObject(backing_store_dib_); + backing_store_dib_ = NULL; + } + + if (renderer_bitmap_section_) { + CloseHandle(renderer_bitmap_section_); + renderer_bitmap_section_ = NULL; + } } -/////////////////////////////////////////////////////////////////////////////// -// RenderWidget::BackingStoreCache +bool RenderWidgetHost::BackingStore::Refresh(HANDLE process, + HANDLE bitmap_section, + const gfx::Rect& bitmap_rect) { + // The bitmap received is valid only in the renderer process. + HANDLE valid_bitmap = + win_util::GetSectionFromProcess(bitmap_section, process, false); + if (!valid_bitmap) + return false; + + if (bitmap_rect.size() == size()) { + CreateDIBSectionBackedByRendererBitmap(bitmap_rect, valid_bitmap); + return true; + } + + if (!backing_store_dib_) { + backing_store_dib_ = CreateDIB(hdc_, size_.width(), size_.height(), true, + NULL); + DCHECK(backing_store_dib_ != NULL); + original_bitmap_ = SelectObject(hdc_, backing_store_dib_); + } + + // TODO(darin): protect against integer overflow + DWORD size = 4 * bitmap_rect.width() * bitmap_rect.height(); + void* backing_store_data = MapViewOfFile(valid_bitmap, FILE_MAP_READ, 0, 0, + size); + // These values are shared with gfx::PlatformDevice + BITMAPINFOHEADER hdr; + gfx::CreateBitmapHeader(bitmap_rect.width(), bitmap_rect.height(), &hdr); + // Account for a bitmap_rect that exceeds the bounds of our view + gfx::Rect view_rect(0, 0, size_.width(), size_.height()); + gfx::Rect paint_rect = view_rect.Intersect(bitmap_rect); -class RenderWidgetHost::BackingStoreCache { + StretchDIBits(hdc_, + paint_rect.x(), + paint_rect.y(), + paint_rect.width(), + paint_rect.height(), + 0, 0, // source x,y + paint_rect.width(), + paint_rect.height(), + backing_store_data, + reinterpret_cast<BITMAPINFO*>(&hdr), + DIB_RGB_COLORS, + SRCCOPY); + + UnmapViewOfFile(backing_store_data); + CloseHandle(valid_bitmap); + return true; +} + +void RenderWidgetHost::BackingStore::CreateDIBSectionBackedByRendererBitmap( + const gfx::Rect& bitmap_rect, HANDLE bitmap_section_from_renderer) { + if (backing_store_dib_ != NULL) { + SelectObject(hdc_, original_bitmap_); + DeleteObject(backing_store_dib_); + backing_store_dib_ = NULL; + } + + if (renderer_bitmap_section_ != NULL) { + CloseHandle(renderer_bitmap_section_); + renderer_bitmap_section_ = NULL; + } + + backing_store_dib_ = CreateDIB(hdc_, bitmap_rect.width(), + bitmap_rect.height(), false, + bitmap_section_from_renderer); + DCHECK(backing_store_dib_ != NULL); + + renderer_bitmap_section_ = bitmap_section_from_renderer; + original_bitmap_ = SelectObject(hdc_, backing_store_dib_); +} + +HANDLE RenderWidgetHost::BackingStore::CreateDIB(HDC dc, int width, int height, + bool use_system_color_depth, + HANDLE section) { + BITMAPINFOHEADER hdr; + + if (use_system_color_depth) { + HDC screen_dc = ::GetDC(NULL); + int color_depth = GetDeviceCaps(screen_dc, BITSPIXEL); + ::ReleaseDC(NULL, screen_dc); + + // Color depths less than 16 bpp require a palette to be specified in the + // BITMAPINFO structure passed to CreateDIBSection. Instead of creating + // the palette, we specify the desired color depth as 16 which allows the + // OS to come up with an approximation. Tested this with 8bpp. + if (color_depth < 16) + color_depth = 16; + + gfx::CreateBitmapHeaderWithColorDepth(width, height, color_depth, &hdr); + } else { + gfx::CreateBitmapHeader(width, height, &hdr); + } + void* data = NULL; + HANDLE dib = + CreateDIBSection(hdc_, reinterpret_cast<BITMAPINFO*>(&hdr), + 0, &data, section, 0); + return dib; +} + + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHost::BackingStoreManager + +// This class manages backing stores in the browsr. Every RenderWidgetHost +// is associated with a backing store which it requests from this class. +// The hosts don't maintain any references to the backing stores. +// These backing stores are maintained in a cache which can be trimmed as +// needed. +class RenderWidgetHost::BackingStoreManager { public: - // The number of backing stores to cache. - enum { kMaxSize = 5 }; + // Returns a backing store which matches the desired dimensions. + // Parameters: + // host + // A pointer to the RenderWidgetHost. + // backing_store_rect + // The desired backing store dimensions. + // Returns a pointer to the backing store on success, NULL on failure. + static BackingStore* 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; + } - static void Add(RenderWidgetHost* host, BackingStore* backing_store) { - // TODO(darin): Re-enable once we have a fix for bug 1143208. - if (!cache_) - cache_ = new Cache(kMaxSize); - cache_->Put(host, backing_store); + return backing_store; } - static BackingStore* Remove(RenderWidgetHost* host) { - // TODO(darin): Re-enable once we have a fix for bug 1143208. + // Returns a backing store which is fully ready for consumption, + // i.e. the bitmap from the renderer has been copied into the + // backing store dc, or the bitmap in the backing store dc references + // the renderer bitmap. + // Parameters: + // host + // A pointer to the RenderWidgetHost. + // backing_store_rect + // The desired backing store dimensions. + // process_handle + // The renderer process handle. + // bitmap_section + // The bitmap section from the renderer. + // bitmap_rect + // The rect to be painted into the backing store + // needs_full_paint + // Set if we need to send out a request to paint the view + // to the renderer. + static BackingStore* PrepareBackingStore(RenderWidgetHost* host, + const gfx::Rect& backing_store_rect, + HANDLE process_handle, + HANDLE bitmap_section, + const gfx::Rect& bitmap_rect, + bool* needs_full_paint) { + BackingStore* backing_store = GetBackingStore(host, + backing_store_rect.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 != backing_store_rect) { + DCHECK(needs_full_paint != NULL); + *needs_full_paint = true; + } + backing_store = CreateBackingStore(host, backing_store_rect); + } + + DCHECK(backing_store != NULL); + backing_store->Refresh(process_handle, bitmap_section, bitmap_rect); + return backing_store; + } + + // Returns a matching backing store for the host. + // Returns NULL if we fail to find one. + static BackingStore* Lookup(RenderWidgetHost* host) { + if (cache_) { + BackingStoreCache::iterator it = cache_->Peek(host); + if (it != cache_->end()) + return it->second; + } + return NULL; + } + + // Removes the backing store for the host. + static void RemoveBackingStore(RenderWidgetHost* host) { if (!cache_) - return NULL; + return; - Cache::iterator it = cache_->Peek(host); + BackingStoreCache::iterator it = cache_->Peek(host); if (it == cache_->end()) - return NULL; - - BackingStore* result = it->second; + return; - // Remove from the cache without deleting the backing store object. - it->second = NULL; cache_->Erase(it); - if (cache_->size() == 0) { + if (cache_->empty()) { delete cache_; cache_ = NULL; } - - return result; } private: - ~BackingStoreCache(); // not intended to be instantiated + // Not intended for instantiation. + ~BackingStoreManager(); + + typedef OwningMRUCache<RenderWidgetHost*, BackingStore*> BackingStoreCache; + static BackingStoreCache* cache_; + + // Returns the size of the backing store cache. + // TODO(iyengar) Make this dynamic, i.e. based on the available resources + // on the machine. + static int GetBackingStoreCacheSize() { + const int kMaxSize = 5; + return kMaxSize; + } - typedef OwningMRUCache<RenderWidgetHost*, BackingStore*> Cache; - static Cache* cache_; + // Creates the backing store for the host based on the dimensions passed in. + // Removes the existing backing store if there is one. + static BackingStore* CreateBackingStore( + RenderWidgetHost* host, const gfx::Rect& backing_store_rect) { + RemoveBackingStore(host); + + BackingStore* backing_store = new BackingStore(backing_store_rect.size()); + int backing_store_cache_size = GetBackingStoreCacheSize(); + if (backing_store_cache_size > 0) { + if (!cache_) + cache_ = new BackingStoreCache(backing_store_cache_size); + cache_->Put(host, backing_store); + } + return backing_store; + } + + DISALLOW_EVIL_CONSTRUCTORS(BackingStoreManager); }; -// static -RenderWidgetHost::BackingStoreCache::Cache* - RenderWidgetHost::BackingStoreCache::cache_ = NULL; +RenderWidgetHost::BackingStoreManager::BackingStoreCache* + RenderWidgetHost::BackingStoreManager::cache_ = NULL; + /////////////////////////////////////////////////////////////////////////////// // RenderWidgetHost @@ -148,7 +332,9 @@ RenderWidgetHost::RenderWidgetHost(RenderProcessHost* process, int routing_id) suppress_view_updating_(false), needs_repainting_on_restore_(false), hung_renderer_factory_(this), - is_unresponsive_(false) { + is_unresponsive_(false), + view_being_painted_(false), + repaint_ack_pending_(false) { if (routing_id_ == MSG_ROUTING_NONE) routing_id_ = process_->widget_helper()->GetNextRoutingID(); @@ -160,7 +346,7 @@ RenderWidgetHost::RenderWidgetHost(RenderProcessHost* process, int routing_id) RenderWidgetHost::~RenderWidgetHost() { // Clear our current or cached backing store if either remains. - backing_store_.reset(BackingStoreCache::Remove(this)); + BackingStoreManager::RemoveBackingStore(this); process_->Release(routing_id_); } @@ -230,21 +416,39 @@ void RenderWidgetHost::OnMsgPaintRect( resize_ack_pending_ = false; } + bool is_repaint_ack = + ViewHostMsg_PaintRect_Flags::is_repaint_ack(params.flags); + if (is_repaint_ack) { + repaint_ack_pending_ = false; + TimeDelta delta = TimeTicks::Now() - repaint_start_time_; + UMA_HISTOGRAM_TIMES(L"MPArch.RWH_RepaintDelta", delta); + } + DCHECK(params.bitmap); DCHECK(!params.bitmap_rect.IsEmpty()); DCHECK(!params.view_size.IsEmpty()); PaintRect(params.bitmap, params.bitmap_rect, params.view_size); + bool using_renderer_bitmap_section = false; + BackingStore* backing_store = BackingStoreManager::Lookup(this); + if (backing_store) { + using_renderer_bitmap_section = + backing_store->using_renderer_bitmap_section(); + } + // ACK early so we can prefetch the next PaintRect if there is a next one. - Send(new ViewMsg_PaintRect_ACK(routing_id_)); + Send(new ViewMsg_PaintRect_ACK(routing_id_, using_renderer_bitmap_section)); // TODO(darin): This should really be done by the view_! MovePluginWindows(params.plugin_window_moves); // The view might be destroyed already. Check for this case. - if (view_ && !suppress_view_updating_) + if (view_ && !suppress_view_updating_) { + view_being_painted_ = true; view_->DidPaintRect(params.bitmap_rect); + view_being_painted_ = false; + } if (paint_observer_.get()) paint_observer_->RenderWidgetHostDidPaint(this); @@ -279,8 +483,11 @@ void RenderWidgetHost::OnMsgScrollRect( MovePluginWindows(params.plugin_window_moves); // The view might be destroyed already. Check for this case - if (view_) + if (view_) { + view_being_painted_ = true; view_->DidScrollRect(params.clip_rect, params.dx, params.dy); + view_being_painted_ = false; + } // Log the time delta for processing a scroll message. TimeDelta delta = TimeTicks::Now() - scroll_start; @@ -398,10 +605,6 @@ void RenderWidgetHost::WasHidden() { // reduce its resource utilization. Send(new ViewMsg_WasHidden(routing_id_)); - // Yield the backing store (allows this memory to be freed). - if (backing_store_.get()) - BackingStoreCache::Add(this, backing_store_.release()); - // TODO(darin): what about constrained windows? it doesn't look like they // see a message when their parent is hidden. maybe there is something more // generic we can do at the TabContents API level instead of relying on @@ -417,13 +620,11 @@ void RenderWidgetHost::WasRestored() { return; is_hidden_ = false; - DCHECK(!backing_store_.get()); - backing_store_.reset(BackingStoreCache::Remove(this)); - + BackingStore* backing_store = BackingStoreManager::Lookup(this); // If we already have a backing store for this widget, then we don't need to // repaint on restore _unless_ we know that our backing store is invalid. bool needs_repainting; - if (needs_repainting_on_restore_ || !backing_store_.get()) { + if (needs_repainting_on_restore_ || !backing_store) { needs_repainting = true; needs_repainting_on_restore_ = false; } else { @@ -569,13 +770,21 @@ RenderWidgetHost::BackingStore* RenderWidgetHost::GetBackingStore() { DCHECK(!is_hidden_) << "GetBackingStore called while hidden!"; // We might have a cached backing store that we can reuse! - if (!backing_store_.get()) - backing_store_.reset(BackingStoreCache::Remove(this)); + BackingStore* backing_store = + BackingStoreManager::GetBackingStore(this, current_size_); + // If we fail to find a backing store in the cache, send out a request + // to the renderer to paint the view if required. + if (!backing_store && !repaint_ack_pending_ && !resize_ack_pending_ && + !view_being_painted_) { + repaint_start_time_ = TimeTicks::Now(); + repaint_ack_pending_ = true; + Send(new ViewMsg_Repaint(routing_id_, current_size_)); + } // When we have asked the RenderWidget to resize, and we are still waiting on // a response, block for a little while to see if we can't get a response // before returning the old (incorrectly sized) backing store. - if (resize_ack_pending_ || !backing_store_.get()) { + if (resize_ack_pending_ || !backing_store) { IPC::Message msg; TimeDelta max_delay = TimeDelta::FromMilliseconds(kPaintMsgTimeoutMS); if (process_->widget_helper()->WaitForPaintMsg(routing_id_, max_delay, @@ -584,53 +793,11 @@ RenderWidgetHost::BackingStore* RenderWidgetHost::GetBackingStore() { ViewHostMsg_PaintRect::Dispatch( &msg, this, &RenderWidgetHost::OnMsgPaintRect); suppress_view_updating_ = false; + backing_store = BackingStoreManager::GetBackingStore(this, current_size_); } } - return backing_store_.get(); -} - -void RenderWidgetHost::EnsureBackingStore(const gfx::Rect& view_rect) { - // We might have a cached backing store that we can reuse! - if (!backing_store_.get()) - backing_store_.reset(BackingStoreCache::Remove(this)); - - // If we already have a backing store, then make sure it is the correct size. - if (backing_store_.get() && backing_store_->size() == view_rect.size()) - return; - - backing_store_.reset(new BackingStore(view_rect.size())); -} - -void RenderWidgetHost::PaintBackingStore(HANDLE bitmap, - const gfx::Rect& bitmap_rect) { - // TODO(darin): protect against integer overflow - DWORD size = 4 * bitmap_rect.width() * bitmap_rect.height(); - void* data = MapViewOfFile(bitmap, FILE_MAP_READ, 0, 0, size); - - // These values are shared with gfx::PlatformDevice - BITMAPINFOHEADER hdr; - gfx::CreateBitmapHeader(bitmap_rect.width(), bitmap_rect.height(), &hdr); - - // Account for a bitmap_rect that exceeds the bounds of our view - gfx::Rect view_rect( - 0, 0, backing_store_->size().width(), backing_store_->size().height()); - gfx::Rect paint_rect = view_rect.Intersect(bitmap_rect); - - StretchDIBits(backing_store_->dc(), - paint_rect.x(), - paint_rect.y(), - paint_rect.width(), - paint_rect.height(), - 0, 0, // source x,y - paint_rect.width(), - paint_rect.height(), - data, - reinterpret_cast<BITMAPINFO*>(&hdr), - DIB_RGB_COLORS, - SRCCOPY); - - UnmapViewOfFile(data); + return backing_store; } void RenderWidgetHost::PaintRect(HANDLE bitmap, const gfx::Rect& bitmap_rect, @@ -640,18 +807,21 @@ void RenderWidgetHost::PaintRect(HANDLE bitmap, const gfx::Rect& bitmap_rect, return; } - // The bitmap received is valid only in the renderer process. - HANDLE valid_bitmap = - win_util::GetSectionFromProcess(bitmap, process_->process(), true); - if (valid_bitmap) { - // We use the view size according to the render view, which may not be - // quite the same as the size of our window. - gfx::Rect view_rect(0, 0, view_size.width(), view_size.height()); - - EnsureBackingStore(view_rect); - - PaintBackingStore(valid_bitmap, bitmap_rect); - CloseHandle(valid_bitmap); + // We use the view size according to the render view, which may not be + // quite the same as the size of our window. + gfx::Rect view_rect(0, 0, view_size.width(), view_size.height()); + + bool needs_full_paint = false; + BackingStore* backing_store = + BackingStoreManager::PrepareBackingStore(this, view_rect, + process_->process(), + bitmap, bitmap_rect, + &needs_full_paint); + DCHECK(backing_store != NULL); + if (needs_full_paint) { + repaint_start_time_ = TimeTicks::Now(); + repaint_ack_pending_ = true; + Send(new ViewMsg_Repaint(routing_id_, view_size)); } } @@ -666,11 +836,12 @@ void RenderWidgetHost::ScrollRect(HANDLE bitmap, const gfx::Rect& bitmap_rect, // TODO(darin): do we need to do something else if our backing store is not // the same size as the advertised view? maybe we just assume there is a // full paint on its way? - if (backing_store_->size() != view_size) + BackingStore* backing_store = BackingStoreManager::Lookup(this); + if (backing_store && backing_store->size() != view_size) return; RECT damaged_rect, r = clip_rect.ToRECT(); - ScrollDC(backing_store_->dc(), dx, dy, NULL, &r, NULL, &damaged_rect); + ScrollDC(backing_store->dc(), dx, dy, NULL, &r, NULL, &damaged_rect); // TODO(darin): this doesn't work if dx and dy are both non-zero! DCHECK(dx == 0 || dy == 0); @@ -678,13 +849,7 @@ void RenderWidgetHost::ScrollRect(HANDLE bitmap, const gfx::Rect& bitmap_rect, // We expect that damaged_rect should equal bitmap_rect. DCHECK(gfx::Rect(damaged_rect) == bitmap_rect); - // The bitmap handle is valid only in the renderer process - HANDLE valid_bitmap = - win_util::GetSectionFromProcess(bitmap, process_->process(), true); - if (valid_bitmap) { - PaintBackingStore(valid_bitmap, bitmap_rect); - CloseHandle(valid_bitmap); - } + backing_store->Refresh(process_->process(), bitmap, bitmap_rect); } void RenderWidgetHost::RestartHangMonitorTimeout() { @@ -702,3 +867,7 @@ void RenderWidgetHost::StartHangMonitorTimeout(int delay) { hung_renderer_factory_.NewRunnableMethod( &RenderWidgetHost::RendererIsUnresponsive), delay); } + +void RenderWidgetHost::RendererExited() { + BackingStoreManager::RemoveBackingStore(this); +} |