- Improve redraw and scrolling performance (issue #360).

- Don't show magenta background when redrawing in release build (issue #300).


git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@299 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt
2011-10-03 15:48:32 +00:00
parent 29386a3abc
commit 9fa941f1d7
6 changed files with 131 additions and 83 deletions

View File

@@ -48,6 +48,7 @@
} }
- (void)drawRect:(NSRect)rect { - (void)drawRect:(NSRect)rect {
#ifndef NDEBUG
CGContextRef context = CGContextRef context =
reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext]
graphicsPort]); graphicsPort]);
@@ -55,13 +56,11 @@
// start by filling the rect with magenta, so that we can see what's drawn // start by filling the rect with magenta, so that we can see what's drawn
CGContextSetRGBFillColor (context, 1, 0, 1, 1); CGContextSetRGBFillColor (context, 1, 0, 1, 1);
CGContextFillRect(context, NSRectToCGRect(rect)); CGContextFillRect(context, NSRectToCGRect(rect));
#endif
if (browser_ && browser_->UIT_GetWebView()) { if (browser_ && browser_->UIT_GetWebView()) {
gfx::Rect client_rect(NSRectToCGRect(rect)); gfx::Rect client_rect(NSRectToCGRect(rect));
// flip from cocoa coordinates client_rect.set_y(NSHeight([self bounds]) - client_rect.bottom());
client_rect.set_y([self frame].size.height -
client_rect.height() - client_rect.y());
browser_->UIT_GetWebViewHost()->UpdatePaintRect(client_rect); browser_->UIT_GetWebViewHost()->UpdatePaintRect(client_rect);
browser_->UIT_GetWebViewHost()->Paint(); browser_->UIT_GetWebViewHost()->Paint();
} }

View File

@@ -18,18 +18,11 @@ void WebWidgetHost::ScheduleAnimation() {
factory_.NewRunnableMethod(&WebWidgetHost::ScheduleComposite), 10); factory_.NewRunnableMethod(&WebWidgetHost::ScheduleComposite), 10);
} }
void WebWidgetHost::DiscardBackingStore() {
canvas_.reset();
}
void WebWidgetHost::UpdatePaintRect(const gfx::Rect& rect) { void WebWidgetHost::UpdatePaintRect(const gfx::Rect& rect) {
paint_rect_ = paint_rect_.Union(rect); paint_rect_ = paint_rect_.Union(rect);
} }
void WebWidgetHost::SetSize(int width, int height) { void WebWidgetHost::SetSize(int width, int height) {
// Force an entire re-paint. TODO(darin): Maybe reuse this memory buffer.
DiscardBackingStore();
webwidget_->resize(WebSize(width, height)); webwidget_->resize(WebSize(width, height));
EnsureTooltip(); EnsureTooltip();
} }

View File

@@ -72,7 +72,6 @@ class WebWidgetHost {
void SetCursor(HCURSOR cursor); void SetCursor(HCURSOR cursor);
#endif #endif
void DiscardBackingStore();
// Allow clients to update the paint rect. For example, if we get a gdk // Allow clients to update the paint rect. For example, if we get a gdk
// expose or WM_PAINT event, we need to update the paint rect. // expose or WM_PAINT event, we need to update the paint rect.
void UpdatePaintRect(const gfx::Rect& rect); void UpdatePaintRect(const gfx::Rect& rect);
@@ -192,6 +191,8 @@ class WebWidgetHost {
WebKit::WebWidget* webwidget_; WebKit::WebWidget* webwidget_;
scoped_ptr<skia::PlatformCanvas> canvas_; scoped_ptr<skia::PlatformCanvas> canvas_;
int canvas_w_;
int canvas_h_;
// True if this widget is a popup widget. // True if this widget is a popup widget.
bool popup_; bool popup_;

View File

@@ -330,6 +330,8 @@ WebWidgetHost::WebWidgetHost()
: view_(NULL), : view_(NULL),
paint_delegate_(NULL), paint_delegate_(NULL),
webwidget_(NULL), webwidget_(NULL),
canvas_w_(0),
canvas_h_(0),
popup_(false), popup_(false),
scroll_dx_(0), scroll_dx_(0),
scroll_dy_(0), scroll_dy_(0),
@@ -357,11 +359,21 @@ void WebWidgetHost::Paint() {
int height = logical_size_.height(); int height = logical_size_.height();
gfx::Rect client_rect(width, height); gfx::Rect client_rect(width, height);
// Allocate a canvas if necessary // Number of pixels that the canvas is allowed to differ from the client area.
if (!canvas_.get()) { const int kCanvasGrowSize = 128;
if (!canvas_.get() ||
canvas_w_ < client_rect.width() ||
canvas_h_ < client_rect.height() ||
canvas_w_ > client_rect.width() + kCanvasGrowSize * 2 ||
canvas_h_ > client_rect.height() + kCanvasGrowSize * 2) {
ResetScrollRect(); ResetScrollRect();
paint_rect_ = client_rect; paint_rect_ = client_rect;
canvas_.reset(new skia::PlatformCanvas(width, height, true));
// Resize the canvas to be within a reasonable size of the client area.
canvas_w_ = client_rect.width() + kCanvasGrowSize;
canvas_h_ = client_rect.height() + kCanvasGrowSize;
canvas_.reset(new skia::PlatformCanvas(canvas_w_, canvas_h_, true));
if (!canvas_.get()) { if (!canvas_.get()) {
// memory allocation failed, we can't paint. // memory allocation failed, we can't paint.
LOG(ERROR) << "Failed to allocate memory for " << width << "x" << height; LOG(ERROR) << "Failed to allocate memory for " << width << "x" << height;

View File

@@ -35,7 +35,7 @@ WebWidgetHost* WebWidgetHost::Create(NSView* parent_view,
PaintDelegate* paint_delegate) { PaintDelegate* paint_delegate) {
WebWidgetHost* host = new WebWidgetHost(); WebWidgetHost* host = new WebWidgetHost();
NSRect content_rect = [parent_view frame]; NSRect content_rect = [parent_view bounds];
host->view_ = [[NSView alloc] initWithFrame:content_rect]; host->view_ = [[NSView alloc] initWithFrame:content_rect];
[parent_view addSubview:host->view_]; [parent_view addSubview:host->view_];
@@ -50,40 +50,62 @@ void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) {
DLOG_IF(WARNING, painting_) << "unexpected invalidation while painting"; DLOG_IF(WARNING, painting_) << "unexpected invalidation while painting";
#endif #endif
// If this invalidate overlaps with a pending scroll, then we have to
// downgrade to invalidating the scroll rect.
if (damaged_rect.Intersects(scroll_rect_)) {
paint_rect_ = paint_rect_.Union(scroll_rect_);
ResetScrollRect();
}
paint_rect_ = paint_rect_.Union(damaged_rect); paint_rect_ = paint_rect_.Union(damaged_rect);
// Convert scroll rectangle to the view's coordinate system.
NSRect r = NSRectFromCGRect(damaged_rect.ToCGRect()); NSRect r = NSRectFromCGRect(damaged_rect.ToCGRect());
// flip to cocoa coordinates r.origin.y = NSHeight([view_ frame]) - NSMaxY(r);
r.origin.y = [view_ frame].size.height - r.size.height - r.origin.y;
[view_ setNeedsDisplayInRect:r]; [view_ setNeedsDisplayInRect:r];
} }
void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) {
DCHECK(dx || dy); DCHECK(dx || dy);
// If we already have a pending scroll operation or if this scroll operation gfx::Rect client_rect(NSRectToCGRect([view_ bounds]));
// intersects the existing paint region, then just failover to invalidating. gfx::Rect rect = clip_rect.Intersect(client_rect);
if (!scroll_rect_.IsEmpty() || paint_rect_.Intersects(clip_rect)) {
paint_rect_ = paint_rect_.Union(scroll_rect_);
ResetScrollRect();
paint_rect_ = paint_rect_.Union(clip_rect);
}
// We will perform scrolling lazily, when requested to actually paint. // Convert scroll rectangle to the view's coordinate system, and perform the
scroll_rect_ = clip_rect; // scroll directly, without invalidating the view. In theory this could cause
scroll_dx_ = dx; // some kind of performance issue, since we're not coalescing redraw events,
scroll_dy_ = dy; // but in practice we get much smoother scrolling of big views, since just
// copying the pixels within the window is much faster than redrawing them.
NSRect r = NSRectFromCGRect(rect.ToCGRect());
r.origin.y = NSHeight([view_ bounds]) - NSMaxY(r);
[view_ scrollRect:r by:NSMakeSize(dx, -dy)];
NSRect r = NSRectFromCGRect(clip_rect.ToCGRect()); const gfx::Rect saved_paint_rect = paint_rect_;
// flip to cocoa coordinates
r.origin.y = [view_ frame].size.height - r.size.height - r.origin.y; // Repaint the rectangle that was revealed when scrolling the given rectangle.
[view_ setNeedsDisplayInRect:r]; // We don't want to invalidate the rectangle, because that would cause the
// invalidated area to be L-shaped, because of this narrow area, and the
// scrollbar area that also could be invalidated, and the union of those two
// rectangles is pretty much the entire view area, and we would not save any
// work by doing the scrollRect: call above.
if (dx > 0)
paint_rect_ = gfx::Rect(rect.x(), rect.y(), dx, rect.height());
else if (dx < 0)
paint_rect_ = gfx::Rect(rect.right() + dx, rect.y(), -dx, rect.height());
else if (dy > 0)
paint_rect_ = gfx::Rect(rect.x(), rect.y(), rect.width(), dy);
else if (dy < 0)
paint_rect_ = gfx::Rect(rect.x(), rect.bottom() + dy, rect.width(), -dy);
Paint();
// Also make sure we repaint the area that the rectangle was pushed over. This
// is required to make sure we don't clobber the scrollbar on that side of the
// view. Also do the repaint immediately here, so that we don't get into the
// L-shaped issue described above.
if (dx > 0)
paint_rect_ = gfx::Rect(rect.right(), rect.y(), dx, rect.height());
else if (dx < 0)
paint_rect_ = gfx::Rect(rect.x() + dx, rect.y(), -dx, rect.height());
else if (dy > 0)
paint_rect_ = gfx::Rect(rect.x(), rect.bottom(), rect.width(), dx);
else if (dy < 0)
paint_rect_ = gfx::Rect(rect.x(), rect.y() + dy, rect.width(), -dx);
Paint();
paint_rect_ = saved_paint_rect;
} }
void WebWidgetHost::ScheduleComposite() { void WebWidgetHost::ScheduleComposite() {
@@ -98,9 +120,9 @@ WebWidgetHost::WebWidgetHost()
: view_(NULL), : view_(NULL),
paint_delegate_(NULL), paint_delegate_(NULL),
webwidget_(NULL), webwidget_(NULL),
canvas_w_(0),
canvas_h_(0),
popup_(false), popup_(false),
scroll_dx_(0),
scroll_dy_(0),
update_task_(NULL), update_task_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) { ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
set_painting(false); set_painting(false);
@@ -110,24 +132,34 @@ WebWidgetHost::~WebWidgetHost() {
} }
void WebWidgetHost::Paint() { void WebWidgetHost::Paint() {
gfx::Rect client_rect(NSRectToCGRect([view_ frame])); gfx::Rect client_rect(NSRectToCGRect([view_ bounds]));
NSGraphicsContext* view_context = [NSGraphicsContext currentContext]; gfx::Rect update_rect;
CGContextRef context = static_cast<CGContextRef>([view_context graphicsPort]);
// Allocate a canvas if necessary // Number of pixels that the canvas is allowed to differ from the client area.
if (!canvas_.get()) { const int kCanvasGrowSize = 128;
ResetScrollRect();
if (!canvas_.get() ||
canvas_w_ < client_rect.width() ||
canvas_h_ < client_rect.height() ||
canvas_w_ > client_rect.width() + kCanvasGrowSize * 2 ||
canvas_h_ > client_rect.height() + kCanvasGrowSize * 2) {
paint_rect_ = client_rect; paint_rect_ = client_rect;
canvas_.reset(new skia::PlatformCanvas(
paint_rect_.width(), paint_rect_.height(), true)); // Resize the canvas to be within a reasonable size of the client area.
canvas_w_ = client_rect.width() + kCanvasGrowSize;
canvas_h_ = client_rect.height() + kCanvasGrowSize;
canvas_.reset(new skia::PlatformCanvas(canvas_w_, canvas_h_, true));
} }
// make sure webkit draws into our bitmap, not the window // Draw into the graphics context of the canvas instead of the view's context.
// The view's context is pushed onto the context stack, to be restored below.
CGContextRef bitmap_context = CGContextRef bitmap_context =
skia::GetBitmapContext(skia::GetTopDevice(*canvas_)); skia::GetBitmapContext(skia::GetTopDevice(*canvas_));
[NSGraphicsContext setCurrentContext: NSGraphicsContext* paint_context =
[NSGraphicsContext graphicsContextWithGraphicsPort:bitmap_context [NSGraphicsContext graphicsContextWithGraphicsPort:bitmap_context
flipped:YES]]; flipped:YES];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:paint_context];
#ifdef WEBWIDGET_HAS_ANIMATE_CHANGES #ifdef WEBWIDGET_HAS_ANIMATE_CHANGES
webwidget_->animate(0.0); webwidget_->animate(0.0);
@@ -138,14 +170,6 @@ void WebWidgetHost::Paint() {
// This may result in more invalidation // This may result in more invalidation
webwidget_->layout(); webwidget_->layout();
// Scroll the canvas if necessary
scroll_rect_ = client_rect.Intersect(scroll_rect_);
if (!scroll_rect_.IsEmpty()) {
// add to invalidate rect, since there's no equivalent of ScrollDC.
paint_rect_ = paint_rect_.Union(scroll_rect_);
}
ResetScrollRect();
// Paint the canvas if necessary. Allow painting to generate extra rects the // Paint the canvas if necessary. Allow painting to generate extra rects the
// first time we call it. This is necessary because some WebCore rendering // first time we call it. This is necessary because some WebCore rendering
// objects update their layout only when painted. // objects update their layout only when painted.
@@ -153,6 +177,7 @@ void WebWidgetHost::Paint() {
paint_rect_ = client_rect.Intersect(paint_rect_); paint_rect_ = client_rect.Intersect(paint_rect_);
if (!paint_rect_.IsEmpty()) { if (!paint_rect_.IsEmpty()) {
gfx::Rect rect(paint_rect_); gfx::Rect rect(paint_rect_);
update_rect = (i == 0? rect: update_rect.Union(rect));
paint_rect_ = gfx::Rect(); paint_rect_ = gfx::Rect();
// DLOG_IF(WARNING, i == 1) << "painting caused additional invalidations"; // DLOG_IF(WARNING, i == 1) << "painting caused additional invalidations";
@@ -162,17 +187,17 @@ void WebWidgetHost::Paint() {
DCHECK(paint_rect_.IsEmpty()); DCHECK(paint_rect_.IsEmpty());
// set the context back to our window // set the context back to our window
[NSGraphicsContext setCurrentContext: view_context]; [NSGraphicsContext restoreGraphicsState];
// Paint to the screen // Paint to the screen
if ([view_ lockFocusIfCanDraw]) { if ([view_ lockFocusIfCanDraw]) {
int bitmap_height = CGBitmapContextGetHeight(bitmap_context); NSGraphicsContext* view_context = [NSGraphicsContext currentContext];
int bitmap_width = CGBitmapContextGetWidth(bitmap_context); CGContextRef context =
CGRect bitmap_rect = { { 0, 0 }, static_cast<CGContextRef>([view_context graphicsPort]);
{ bitmap_width, bitmap_height } }; CGRect bitmap_rect = { { update_rect.x(), update_rect.y() },
skia::DrawToNativeContext(canvas_.get(), context, 0, { update_rect.width(), update_rect.height() } };
client_rect.height() - bitmap_height, &bitmap_rect); skia::DrawToNativeContext(canvas_.get(), context, update_rect.x(),
client_rect.height() - update_rect.bottom(), &bitmap_rect);
[view_ unlockFocus]; [view_ unlockFocus];
} }
} }
@@ -235,12 +260,6 @@ void WebWidgetHost::SetFocus(bool enable) {
webwidget_->setFocus(enable); webwidget_->setFocus(enable);
} }
void WebWidgetHost::ResetScrollRect() {
scroll_rect_ = gfx::Rect();
scroll_dx_ = 0;
scroll_dy_ = 0;
}
void WebWidgetHost::PaintRect(const gfx::Rect& rect) { void WebWidgetHost::PaintRect(const gfx::Rect& rect) {
#ifndef NDEBUG #ifndef NDEBUG
DCHECK(!painting_); DCHECK(!painting_);

View File

@@ -314,16 +314,18 @@ WebWidgetHost::WebWidgetHost()
: view_(NULL), : view_(NULL),
paint_delegate_(NULL), paint_delegate_(NULL),
webwidget_(NULL), webwidget_(NULL),
canvas_w_(0),
canvas_h_(0),
popup_(false), popup_(false),
track_mouse_leave_(false), track_mouse_leave_(false),
ime_notification_(false),
input_method_is_active_(false),
text_input_type_(WebKit::WebTextInputTypeNone),
scroll_dx_(0), scroll_dx_(0),
scroll_dy_(0), scroll_dy_(0),
update_task_(NULL), update_task_(NULL),
tooltip_view_(NULL), tooltip_view_(NULL),
tooltip_showing_(false), tooltip_showing_(false),
ime_notification_(false),
input_method_is_active_(false),
text_input_type_(WebKit::WebTextInputTypeNone),
ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) { ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
set_painting(false); set_painting(false);
} }
@@ -358,12 +360,34 @@ void WebWidgetHost::Paint() {
gfx::Rect client_rect(width, height); gfx::Rect client_rect(width, height);
gfx::Rect damaged_rect; gfx::Rect damaged_rect;
// Allocate a canvas if necessary if (view_) {
if (!canvas_.get()) { // Number of pixels that the canvas is allowed to differ from the client
// area.
const int kCanvasGrowSize = 128;
if (!canvas_.get() ||
canvas_w_ < client_rect.width() ||
canvas_h_ < client_rect.height() ||
canvas_w_ > client_rect.width() + kCanvasGrowSize * 2 ||
canvas_h_ > client_rect.height() + kCanvasGrowSize * 2) {
ResetScrollRect(); ResetScrollRect();
paint_rect_ = client_rect; paint_rect_ = client_rect;
canvas_.reset(new skia::PlatformCanvas(
paint_rect_.width(), paint_rect_.height(), true)); // Resize the canvas to be within a reasonable size of the client area.
canvas_w_ = client_rect.width() + kCanvasGrowSize;
canvas_h_ = client_rect.height() + kCanvasGrowSize;
canvas_.reset(new skia::PlatformCanvas(canvas_w_, canvas_h_, true));
}
} else if(!canvas_.get() || canvas_w_ != client_rect.width() ||
canvas_h_ != client_rect.height()) {
ResetScrollRect();
paint_rect_ = client_rect;
// For non-window-based rendering the canvas must be the exact size of the
// client area.
canvas_w_ = client_rect.width();
canvas_h_ = client_rect.height();
canvas_.reset(new skia::PlatformCanvas(canvas_w_, canvas_h_, true));
} }
#ifdef WEBWIDGET_HAS_ANIMATE_CHANGES #ifdef WEBWIDGET_HAS_ANIMATE_CHANGES