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

View File

@ -18,18 +18,11 @@ void WebWidgetHost::ScheduleAnimation() {
factory_.NewRunnableMethod(&WebWidgetHost::ScheduleComposite), 10);
}
void WebWidgetHost::DiscardBackingStore() {
canvas_.reset();
}
void WebWidgetHost::UpdatePaintRect(const gfx::Rect& rect) {
paint_rect_ = paint_rect_.Union(rect);
}
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));
EnsureTooltip();
}

View File

@ -72,7 +72,6 @@ class WebWidgetHost {
void SetCursor(HCURSOR cursor);
#endif
void DiscardBackingStore();
// 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.
void UpdatePaintRect(const gfx::Rect& rect);
@ -192,6 +191,8 @@ class WebWidgetHost {
WebKit::WebWidget* webwidget_;
scoped_ptr<skia::PlatformCanvas> canvas_;
int canvas_w_;
int canvas_h_;
// True if this widget is a popup widget.
bool popup_;

View File

@ -330,6 +330,8 @@ WebWidgetHost::WebWidgetHost()
: view_(NULL),
paint_delegate_(NULL),
webwidget_(NULL),
canvas_w_(0),
canvas_h_(0),
popup_(false),
scroll_dx_(0),
scroll_dy_(0),
@ -357,11 +359,21 @@ void WebWidgetHost::Paint() {
int height = logical_size_.height();
gfx::Rect client_rect(width, height);
// Allocate a canvas if necessary
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();
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()) {
// memory allocation failed, we can't paint.
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) {
WebWidgetHost* host = new WebWidgetHost();
NSRect content_rect = [parent_view frame];
NSRect content_rect = [parent_view bounds];
host->view_ = [[NSView alloc] initWithFrame:content_rect];
[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";
#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);
// Convert scroll rectangle to the view's coordinate system.
NSRect r = NSRectFromCGRect(damaged_rect.ToCGRect());
// flip to cocoa coordinates
r.origin.y = [view_ frame].size.height - r.size.height - r.origin.y;
r.origin.y = NSHeight([view_ frame]) - NSMaxY(r);
[view_ setNeedsDisplayInRect:r];
}
void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) {
DCHECK(dx || dy);
// If we already have a pending scroll operation or if this scroll operation
// intersects the existing paint region, then just failover to invalidating.
if (!scroll_rect_.IsEmpty() || paint_rect_.Intersects(clip_rect)) {
paint_rect_ = paint_rect_.Union(scroll_rect_);
ResetScrollRect();
paint_rect_ = paint_rect_.Union(clip_rect);
}
gfx::Rect client_rect(NSRectToCGRect([view_ bounds]));
gfx::Rect rect = clip_rect.Intersect(client_rect);
// Convert scroll rectangle to the view's coordinate system, and perform the
// scroll directly, without invalidating the view. In theory this could cause
// some kind of performance issue, since we're not coalescing redraw events,
// 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)];
// We will perform scrolling lazily, when requested to actually paint.
scroll_rect_ = clip_rect;
scroll_dx_ = dx;
scroll_dy_ = dy;
const gfx::Rect saved_paint_rect = paint_rect_;
NSRect r = NSRectFromCGRect(clip_rect.ToCGRect());
// flip to cocoa coordinates
r.origin.y = [view_ frame].size.height - r.size.height - r.origin.y;
[view_ setNeedsDisplayInRect:r];
// Repaint the rectangle that was revealed when scrolling the given rectangle.
// 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() {
@ -98,9 +120,9 @@ WebWidgetHost::WebWidgetHost()
: view_(NULL),
paint_delegate_(NULL),
webwidget_(NULL),
canvas_w_(0),
canvas_h_(0),
popup_(false),
scroll_dx_(0),
scroll_dy_(0),
update_task_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
set_painting(false);
@ -110,24 +132,34 @@ WebWidgetHost::~WebWidgetHost() {
}
void WebWidgetHost::Paint() {
gfx::Rect client_rect(NSRectToCGRect([view_ frame]));
NSGraphicsContext* view_context = [NSGraphicsContext currentContext];
CGContextRef context = static_cast<CGContextRef>([view_context graphicsPort]);
// Allocate a canvas if necessary
if (!canvas_.get()) {
ResetScrollRect();
gfx::Rect client_rect(NSRectToCGRect([view_ bounds]));
gfx::Rect update_rect;
// 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) {
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 =
skia::GetBitmapContext(skia::GetTopDevice(*canvas_));
[NSGraphicsContext setCurrentContext:
NSGraphicsContext* paint_context =
[NSGraphicsContext graphicsContextWithGraphicsPort:bitmap_context
flipped:YES]];
flipped:YES];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:paint_context];
#ifdef WEBWIDGET_HAS_ANIMATE_CHANGES
webwidget_->animate(0.0);
@ -138,14 +170,6 @@ void WebWidgetHost::Paint() {
// This may result in more invalidation
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
// first time we call it. This is necessary because some WebCore rendering
// objects update their layout only when painted.
@ -153,26 +177,27 @@ void WebWidgetHost::Paint() {
paint_rect_ = client_rect.Intersect(paint_rect_);
if (!paint_rect_.IsEmpty()) {
gfx::Rect rect(paint_rect_);
update_rect = (i == 0? rect: update_rect.Union(rect));
paint_rect_ = gfx::Rect();
// DLOG_IF(WARNING, i == 1) << "painting caused additional invalidations";
// DLOG_IF(WARNING, i == 1) << "painting caused additional invalidations";
PaintRect(rect);
}
}
DCHECK(paint_rect_.IsEmpty());
// set the context back to our window
[NSGraphicsContext setCurrentContext: view_context];
[NSGraphicsContext restoreGraphicsState];
// Paint to the screen
if ([view_ lockFocusIfCanDraw]) {
int bitmap_height = CGBitmapContextGetHeight(bitmap_context);
int bitmap_width = CGBitmapContextGetWidth(bitmap_context);
CGRect bitmap_rect = { { 0, 0 },
{ bitmap_width, bitmap_height } };
skia::DrawToNativeContext(canvas_.get(), context, 0,
client_rect.height() - bitmap_height, &bitmap_rect);
NSGraphicsContext* view_context = [NSGraphicsContext currentContext];
CGContextRef context =
static_cast<CGContextRef>([view_context graphicsPort]);
CGRect bitmap_rect = { { update_rect.x(), update_rect.y() },
{ update_rect.width(), update_rect.height() } };
skia::DrawToNativeContext(canvas_.get(), context, update_rect.x(),
client_rect.height() - update_rect.bottom(), &bitmap_rect);
[view_ unlockFocus];
}
}
@ -235,12 +260,6 @@ void WebWidgetHost::SetFocus(bool enable) {
webwidget_->setFocus(enable);
}
void WebWidgetHost::ResetScrollRect() {
scroll_rect_ = gfx::Rect();
scroll_dx_ = 0;
scroll_dy_ = 0;
}
void WebWidgetHost::PaintRect(const gfx::Rect& rect) {
#ifndef NDEBUG
DCHECK(!painting_);

View File

@ -314,16 +314,18 @@ WebWidgetHost::WebWidgetHost()
: view_(NULL),
paint_delegate_(NULL),
webwidget_(NULL),
canvas_w_(0),
canvas_h_(0),
popup_(false),
track_mouse_leave_(false),
ime_notification_(false),
input_method_is_active_(false),
text_input_type_(WebKit::WebTextInputTypeNone),
scroll_dx_(0),
scroll_dy_(0),
update_task_(NULL),
tooltip_view_(NULL),
tooltip_showing_(false),
ime_notification_(false),
input_method_is_active_(false),
text_input_type_(WebKit::WebTextInputTypeNone),
ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
set_painting(false);
}
@ -358,12 +360,34 @@ void WebWidgetHost::Paint() {
gfx::Rect client_rect(width, height);
gfx::Rect damaged_rect;
// Allocate a canvas if necessary
if (!canvas_.get()) {
if (view_) {
// 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();
paint_rect_ = client_rect;
// 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;
canvas_.reset(new skia::PlatformCanvas(
paint_rect_.width(), paint_rect_.height(), true));
// 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