diff options
author | amanda@chromium.org <amanda@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-19 18:59:22 +0000 |
---|---|---|
committer | amanda@chromium.org <amanda@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-19 18:59:22 +0000 |
commit | b9471f847796ea07f4a5b3464502c51c2d48cc3f (patch) | |
tree | 6ebf50edea23f931fe0582fcef04c77061e685d3 /chrome/browser/renderer_host | |
parent | 76b3af422d58822e17523064aca0a3de116a3457 (diff) | |
download | chromium_src-b9471f847796ea07f4a5b3464502c51c2d48cc3f.zip chromium_src-b9471f847796ea07f4a5b3464502c51c2d48cc3f.tar.gz chromium_src-b9471f847796ea07f4a5b3464502c51c2d48cc3f.tar.bz2 |
Re-implement BackingStore on the Mac as a CGLayer instead of a Skia canvas,
to get better performance. As a side effect, remove the ugly stopgap scrolling code. Do not close 14823 with this fix, but it should help (primary motivation was improving the plugin drawing path).
Paul: review
rafael: please check the change to extension_tabs_module.cc
John, Rohit: FYI, comments welcome
BUG=14823,
TEST=scrolling and plugin performance should improve
Review URL: http://codereview.chromium.org/171054
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@23722 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/renderer_host')
-rw-r--r-- | chrome/browser/renderer_host/backing_store.h | 10 | ||||
-rw-r--r-- | chrome/browser/renderer_host/backing_store_mac.cc | 140 | ||||
-rw-r--r-- | chrome/browser/renderer_host/backing_store_mac.mm | 103 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host_view_mac.mm | 10 |
4 files changed, 115 insertions, 148 deletions
diff --git a/chrome/browser/renderer_host/backing_store.h b/chrome/browser/renderer_host/backing_store.h index 49922e78..aea197b 100644 --- a/chrome/browser/renderer_host/backing_store.h +++ b/chrome/browser/renderer_host/backing_store.h @@ -15,6 +15,7 @@ #if defined(OS_WIN) #include <windows.h> #elif defined(OS_MACOSX) +#include "base/scoped_cftyperef.h" #include "skia/ext/platform_canvas.h" #elif defined(OS_LINUX) #include "chrome/common/x11_util.h" @@ -60,7 +61,12 @@ class BackingStore { // Returns true if we should convert to the monitor profile when painting. static bool ColorManagementEnabled(); #elif defined(OS_MACOSX) - skia::PlatformCanvas* canvas() { return &canvas_; } + // A CGLayer that stores the contents of the backing store, cached in GPU + // memory if possible. + CGLayerRef cg_layer() { return cg_layer_; } + // Paint the layer into a graphics context--if the target is a window, + // this should be a GPU->GPU copy (and therefore very fast). + void PaintToRect(const gfx::Rect& dest_rect, CGContextRef target); #elif defined(OS_LINUX) Display* display() const { return display_; } XID root_window() const { return root_window_; }; @@ -112,7 +118,7 @@ class BackingStore { // Number of bits per pixel of the screen. int color_depth_; #elif defined(OS_MACOSX) - skia::PlatformCanvas canvas_; + scoped_cftyperef<CGLayerRef> cg_layer_; #elif defined(OS_LINUX) // Paints the bitmap from the renderer onto the backing store without // using Xrender to composite the pixmaps. diff --git a/chrome/browser/renderer_host/backing_store_mac.cc b/chrome/browser/renderer_host/backing_store_mac.cc deleted file mode 100644 index d87b292..0000000 --- a/chrome/browser/renderer_host/backing_store_mac.cc +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) 2006-2008 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.h" - -#include "base/logging.h" -#include "chrome/common/transport_dib.h" -#include "skia/ext/platform_canvas.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkCanvas.h" - -BackingStore::BackingStore(RenderWidgetHost* widget, const gfx::Size& size) - : render_widget_host_(widget), - size_(size) { - if (!canvas_.initialize(size.width(), size.height(), true)) - SK_CRASH(); -} - -BackingStore::~BackingStore() { -} - -size_t BackingStore::MemorySize() { - // Always 4 bytes per pixel. - return size_.GetArea() * 4; -} - -void BackingStore::PaintRect(base::ProcessHandle process, - TransportDIB* bitmap, - const gfx::Rect& bitmap_rect) { - SkBitmap skbitmap; - skbitmap.setConfig(SkBitmap::kARGB_8888_Config, bitmap_rect.width(), - bitmap_rect.height(), 4 * bitmap_rect.width()); - - skbitmap.setPixels(bitmap->memory()); - - canvas_.drawBitmap(skbitmap, bitmap_rect.x(), bitmap_rect.y()); -} - -void BackingStore::ScrollRect(base::ProcessHandle process, - TransportDIB* bitmap, - const gfx::Rect& bitmap_rect, - int dx, int dy, - const gfx::Rect& clip_rect, - const gfx::Size& view_size) { - // WARNING: this is temporary code until a real solution is found for Mac and - // Linux. - // - // On Windows, there's a ScrollDC call which performs horiz and vertical - // scrolling - // - // clip_rect: MSDN says "The only bits that will be painted are the - // bits that remain inside this rectangle after the scroll operation has been - // completed." - // - // The Windows code always sets the whole backing store as the source of the - // scroll. Thus, we only have to worry about pixels which will end up inside - // the clipping rectangle. (Note that the clipping rectangle is not - // translated by the scroll.) - - // We only support scrolling in one direction at a time. - DCHECK(dx == 0 || dy == 0); - - // We assume |clip_rect| is contained within the backing store. - DCHECK(clip_rect.bottom() <= canvas_.getDevice()->height()); - DCHECK(clip_rect.right() <= canvas_.getDevice()->width()); - - const SkBitmap &backing_bitmap = canvas_.getDevice()->accessBitmap(true); - const int stride = backing_bitmap.rowBytes(); - uint8_t* x = static_cast<uint8_t*>(backing_bitmap.getPixels()); - - if (dx) { - // Do not memmove any data if the scroll distance (|dx|) is greater - // than |clip_rect.width()|. If this is true, then none of the - // previous pixels will still be on the screen after the scroll. - // The call to PaintRect() below will redraw the entire area. - if (abs(dx) < clip_rect.width()) { - // Horizontal scroll. According to msdn, positive values of |dx| scroll - // left, but in practice this seems reversed. TODO(port): figure this - // out. For now just reverse the sign. - dx *= -1; - - // This is the number of bytes to move per line at 4 bytes per pixel. - const int len = (clip_rect.width() - abs(dx)) * 4; - - // Move |x| to the first pixel of the first row. - x += clip_rect.y() * stride; - x += clip_rect.x() * 4; - - // If we are scrolling left, move |x| to the |dx|^th pixel. - if (dx < 0) { - x -= dx * 4; - } - - for (int i = clip_rect.y(); i < clip_rect.bottom(); ++i) { - // Note that overlapping regions requires memmove, not memcpy. - memmove(x, x + dx * 4, len); - x += stride; - } - } - } else if (dy) { - // Vertical scroll. The above warning about not copying data if - // |dy| > |clip_rect.height()| technically applies here too, but - // is implicitly taken care of by the termination condition in the - // for loop, so we do not need to explicitly check against |dy|. - - // TODO(port): According to msdn, positive values of |dy| scroll - // down, but in practice this seems reversed. Figure this - // out. For now just reverse the sign. - dy *= -1; - - const int len = clip_rect.width() * 4; - - // For down scrolls, we copy bottom-up (in screen coordinates). - // For up scrolls, we copy top-down. - if (dy > 0) { - // Move |x| to the first pixel of the first row of the clip rect. - x += clip_rect.y() * stride; - x += clip_rect.x() * 4; - - for (int i = 0; i < clip_rect.height() - dy; ++i) { - memcpy(x, x + stride * dy, len); - x += stride; - } - } else { - // Move |x| to the first pixel of the last row of the clip rect. - x += (clip_rect.bottom() - 1) * stride; - x += clip_rect.x() * 4; - - for (int i = 0; i < clip_rect.height() + dy; ++i) { - memcpy(x, x + stride * dy, len); - x -= stride; - } - } - } - - // Now paint the new bitmap data. - PaintRect(process, bitmap, bitmap_rect); - return; -} diff --git a/chrome/browser/renderer_host/backing_store_mac.mm b/chrome/browser/renderer_host/backing_store_mac.mm new file mode 100644 index 0000000..e445f7d --- /dev/null +++ b/chrome/browser/renderer_host/backing_store_mac.mm @@ -0,0 +1,103 @@ +// Copyright (c) 2006-2008 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. + +#import <Cocoa/Cocoa.h> + +#include "chrome/browser/renderer_host/backing_store.h" + +#include "base/logging.h" +#include "chrome/common/transport_dib.h" +#include "skia/ext/platform_canvas.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkCanvas.h" + +// Mac Backing Stores: +// +// Since backing stores are only ever written to or drawn into windows, we keep +// our backing store in a CGLayer that can get cached in GPU memory. This +// allows acclerated drawing into the layer and lets scrolling and such happen +// all or mostly on the GPU, which is good for performance. + +BackingStore::BackingStore(RenderWidgetHost* widget, const gfx::Size& size) + : render_widget_host_(widget), + size_(size), + cg_layer_(CGLayerCreateWithContext(static_cast<CGContextRef>( + [[NSGraphicsContext currentContext] graphicsPort]), size.ToCGSize(), + NULL)) { + CHECK(cg_layer_.get() != NULL); +} + +BackingStore::~BackingStore() { +} + +size_t BackingStore::MemorySize() { + // Estimate memory usage as 4 bytes per pixel. + return size_.GetArea() * 4; +} + +// Paint the contents of a TransportDIB into a rectangle of our CGLayer +void BackingStore::PaintRect(base::ProcessHandle process, + TransportDIB* bitmap, + const gfx::Rect& bitmap_rect) { + scoped_cftyperef<CGColorSpaceRef> color_space(CGColorSpaceCreateDeviceRGB()); + + scoped_cftyperef<CGDataProviderRef> data_provider( + CGDataProviderCreateWithData(NULL, bitmap->memory(), + bitmap_rect.width() * bitmap_rect.height() * 4, NULL)); + + scoped_cftyperef<CGImageRef> image(CGImageCreate(bitmap_rect.width(), + bitmap_rect.height(), 8, 32, 4 * bitmap_rect.width(), color_space, + kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, data_provider, + NULL, false, kCGRenderingIntentDefault)); + + // The CGLayer's origin is in the lower left, but flipping the CTM would + // cause the image to get drawn upside down. So we move the rectangle + // to the right position before drawing the image. + CGContextRef layer = CGLayerGetContext(cg_layer()); + gfx::Rect paint_rect = bitmap_rect; + paint_rect.set_y(size_.height() - bitmap_rect.bottom()); + CGContextDrawImage(layer, paint_rect.ToCGRect(), image); +} + +// Scroll the contents of our CGLayer +void BackingStore::ScrollRect(base::ProcessHandle process, + TransportDIB* bitmap, + const gfx::Rect& bitmap_rect, + int dx, int dy, + const gfx::Rect& clip_rect, + const gfx::Size& view_size) { + // "Scroll" the contents of the layer by creating a new CGLayer, + // copying the contents of the old one into the new one offset by the scroll + // amount, swapping in the new CGLayer, and then painting in the new data. + // + // The Windows code always sets the whole backing store as the source of the + // scroll. Thus, we only have to worry about pixels which will end up inside + // the clipping rectangle. (Note that the clipping rectangle is not + // translated by the scroll.) + + // We assume |clip_rect| is contained within the backing store. + CGSize layer_size = CGLayerGetSize(cg_layer()); + DCHECK(clip_rect.bottom() <= layer_size.height); + DCHECK(clip_rect.right() <= layer_size.width); + + if ((dx && abs(dx) < layer_size.width) || + (dy && abs(dy) < layer_size.height)) { + // + scoped_cftyperef<CGLayerRef> new_layer(CGLayerCreateWithContext( + CGLayerGetContext(cg_layer()), layer_size, NULL)); + CGContextRef layer = CGLayerGetContext(new_layer); + CGContextDrawLayerAtPoint(layer, CGPointMake(0, 0), cg_layer()); + CGContextSaveGState(layer); + CGContextClipToRect(layer, CGRectMake(clip_rect.x(), + size_.height() - clip_rect.bottom(), + clip_rect.width(), + clip_rect.height())); + CGContextDrawLayerAtPoint(layer, CGPointMake(dx, -dy), cg_layer()); + CGContextRestoreGState(layer); + cg_layer_.swap(new_layer); + } + // Now paint the new bitmap data into the CGLayer + PaintRect(process, bitmap, bitmap_rect); + return; +} diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.mm b/chrome/browser/renderer_host/render_widget_host_view_mac.mm index ae77260..18674bb 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm +++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm @@ -545,11 +545,11 @@ void RenderWidgetHostViewMac::SetActive(bool active) { renderWidgetHostView_->about_to_validate_and_paint_ = true; BackingStore* backing_store = renderWidgetHostView_->render_widget_host_->GetBackingStore(true); - skia::PlatformCanvas* canvas = backing_store->canvas(); renderWidgetHostView_->about_to_validate_and_paint_ = false; dirtyRect = renderWidgetHostView_->invalid_rect_; if (backing_store) { + NSRect view_bounds = [self bounds]; gfx::Rect damaged_rect([self NSRectToRect:dirtyRect]); gfx::Rect bitmap_rect(0, 0, @@ -561,11 +561,9 @@ void RenderWidgetHostViewMac::SetActive(bool active) { CGContextRef context = static_cast<CGContextRef>( [[NSGraphicsContext currentContext] graphicsPort]); - CGRect paint_rect_cg = paint_rect.ToCGRect(); - NSRect paint_rect_ns = [self RectToNSRect:paint_rect]; - canvas->getTopPlatformDevice().DrawToContext( - context, paint_rect_ns.origin.x, paint_rect_ns.origin.y, - &paint_rect_cg); + // TODO: add clipping to dirtyRect if it improves drawing performance. + CGContextDrawLayerAtPoint(context, CGPointMake(0.0, 0.0), + backing_store->cg_layer()); } // Fill the remaining portion of the damaged_rect with white |