| Index: chrome/renderer/render_widget.cc
|
| ===================================================================
|
| --- chrome/renderer/render_widget.cc (revision 17347)
|
| +++ chrome/renderer/render_widget.cc (working copy)
|
| @@ -32,6 +32,92 @@
|
| using WebKit::WebScreenInfo;
|
| using WebKit::WebSize;
|
|
|
| +namespace {
|
| +
|
| +// A helper class to manage access to the RenderWidget::paint_rects vector.
|
| +class PaintRegion {
|
| + public:
|
| + explicit PaintRegion(std::vector<gfx::Rect>* rects)
|
| + : rects_(*rects),
|
| + coalesce_(false) {
|
| + }
|
| +
|
| + gfx::Rect GetBoundingRect() const {
|
| + gfx::Rect bounding_rect;
|
| + for (size_t i = 0; i < rects_.size(); ++i)
|
| + bounding_rect = bounding_rect.Union(rects_[i]);
|
| + return bounding_rect;
|
| + }
|
| +
|
| + void Add(gfx::Rect rect) {
|
| + if (rect.IsEmpty())
|
| + return;
|
| +
|
| + if (rects_.size() >= kNumRectsThreshold) {
|
| + coalesce_ = true;
|
| + for (size_t i = rects_.size() - 1; i > 0; --i) {
|
| + rects_[0] = rects_[0].Union(rects_[i]);
|
| + rects_.pop_back();
|
| + }
|
| + }
|
| +
|
| + if (coalesce_) {
|
| + DCHECK(rects_.size() == 1);
|
| + rects_[0] = rects_[0].Union(rect);
|
| + } else {
|
| + // Look for all rects that would intersect with this new
|
| + // rect, union them and remove them from the list so that we
|
| + // have only one rect for any pixel in the invalidation.
|
| + std::vector<gfx::Rect>::iterator it = rects_.begin();
|
| + while (it != rects_.end()) {
|
| + if (rect.Intersects(*it)) {
|
| + rect = rect.Union(*it);
|
| + rects_.erase(it);
|
| + // We must go back to the beginning since some rects we previously
|
| + // checked might now intersects with the union of the two rects,
|
| + // even though they didn't intersect with them individually.
|
| + it = rects_.begin();
|
| + } else {
|
| + ++it;
|
| + }
|
| + }
|
| + rects_.push_back(rect);
|
| + }
|
| + }
|
| +
|
| + void Clip(const gfx::Rect& clip_rect) {
|
| + std::vector<gfx::Rect>::iterator it = rects_.begin();
|
| + while (it != rects_.end()) {
|
| + if (!clip_rect.Contains(*it))
|
| + *it = clip_rect.Intersect(*it);
|
| + ++it;
|
| + }
|
| + }
|
| +
|
| + bool Intersects(const gfx::Rect& rect) const {
|
| + for (size_t i = 0; i < rects_.size(); ++i) {
|
| + if (rects_[i].Intersects(rect))
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + private:
|
| + std::vector<gfx::Rect>& rects_;
|
| + bool coalesce_;
|
| + static const size_t kNumRectsThreshold;
|
| +};
|
| +// We use a threshold for the number of rects because in many cases as
|
| +// described here:
|
| +// http://my.opera.com/desktopteam/blog/2008/03/28/painting-performance-fixes
|
| +// we get diminishing returns from using more sub-rectangles, it becomes more
|
| +// efficient to Union the rects. The nubmer 25 is also used in WebKit for
|
| +// deferred paints in the FrameView. It was chosen here based on some
|
| +// manual experiments using the benchmark from the article sited above.
|
| +const size_t PaintRegion::kNumRectsThreshold = 25;
|
| +
|
| +} // namespace
|
| +
|
| RenderWidget::RenderWidget(RenderThreadBase* render_thread, bool activatable)
|
| : routing_id_(MSG_ROUTING_NONE),
|
| webwidget_(NULL),
|
| @@ -200,13 +286,14 @@
|
| // an ACK if we are resized to a non-empty rect.
|
| webwidget_->Resize(new_size);
|
| if (!new_size.IsEmpty()) {
|
| - DCHECK(!paint_rect_.IsEmpty());
|
| + DCHECK(!paint_rects_.empty());
|
|
|
| // This should have caused an invalidation of the entire view. The damaged
|
| // rect could be larger than new_size if we are being made smaller.
|
| - DCHECK_GE(paint_rect_.width(), new_size.width());
|
| - DCHECK_GE(paint_rect_.height(), new_size.height());
|
| -
|
| + DCHECK_GE(PaintRegion(&paint_rects_).GetBoundingRect().width(),
|
| + new_size.width());
|
| + DCHECK_GE(PaintRegion(&paint_rects_).GetBoundingRect().height(),
|
| + new_size.height());
|
| // We will send the Resize_ACK flag once we paint again.
|
| set_next_paint_is_resize_ack();
|
| }
|
| @@ -314,13 +401,13 @@
|
| webwidget_->SetFocus(false);
|
| }
|
|
|
| -void RenderWidget::PaintRect(const gfx::Rect& rect,
|
| - skia::PlatformCanvas* canvas) {
|
| +void RenderWidget::PaintThisRect(const gfx::Rect& rect,
|
| + skia::PlatformCanvas* canvas) {
|
| + // Make sure we don't erase previous rects in the canvas
|
| + SkRect clip_rect;
|
| + clip_rect.iset(rect.x(), rect.y(), rect.right(), rect.bottom());
|
| + canvas->clipRect(clip_rect, SkRegion::kReplace_Op);
|
|
|
| - // Bring the canvas into the coordinate system of the paint rect.
|
| - canvas->translate(static_cast<SkScalar>(-rect.x()),
|
| - static_cast<SkScalar>(-rect.y()));
|
| -
|
| // If there is a custom background, tile it.
|
| if (!background_.empty()) {
|
| SkPaint paint;
|
| @@ -333,22 +420,42 @@
|
| }
|
|
|
| webwidget_->Paint(canvas, rect);
|
| +}
|
|
|
| +void RenderWidget::PaintRect(const gfx::Rect& rect,
|
| + skia::PlatformCanvas* canvas) {
|
| + // Bring the canvas into the coordinate system of the paint rect.
|
| + canvas->translate(static_cast<SkScalar>(-rect.x()),
|
| + static_cast<SkScalar>(-rect.y()));
|
| + PaintThisRect(rect, canvas);
|
| +
|
| // Flush to underlying bitmap. TODO(darin): is this needed?
|
| canvas->getTopPlatformDevice().accessBitmap(false);
|
|
|
| - // Let the subclass observe this paint operations.
|
| + // Let the subclass observe this paint operation.
|
| DidPaint();
|
| }
|
|
|
| +void RenderWidget::PaintRects(const std::vector<gfx::Rect>& rects,
|
| + skia::PlatformCanvas* canvas) {
|
| + for (size_t i = 0; i < rects.size(); ++i)
|
| + PaintThisRect(rects[i], canvas);
|
| +
|
| + // Flush to underlying bitmap. TODO(darin): is this needed?
|
| + canvas->getTopPlatformDevice().accessBitmap(false);
|
| +
|
| + // Let the subclass observe these paint operations.
|
| + DidPaint();
|
| +}
|
| +
|
| void RenderWidget::DoDeferredPaint() {
|
| - if (!webwidget_ || paint_reply_pending() || paint_rect_.IsEmpty())
|
| + if (!webwidget_ || paint_reply_pending() || paint_rects_.empty())
|
| return;
|
|
|
| // When we are hidden, we want to suppress painting, but we still need to
|
| // mark this DoDeferredPaint as complete.
|
| if (is_hidden_ || size_.IsEmpty()) {
|
| - paint_rect_ = gfx::Rect();
|
| + paint_rects_.clear();
|
| needs_repainting_on_restore_ = true;
|
| return;
|
| }
|
| @@ -358,26 +465,36 @@
|
|
|
| // OK, save the current paint_rect to a local since painting may cause more
|
| // invalidation. Some WebCore rendering objects only layout when painted.
|
| - gfx::Rect damaged_rect = paint_rect_;
|
| - paint_rect_ = gfx::Rect();
|
| + std::vector<gfx::Rect> paint_rects = paint_rects_;
|
| + paint_rects_.clear();
|
|
|
| // Compute a buffer for painting and cache it.
|
| + DCHECK(!current_paint_buf_);
|
| +
|
| + // we use the whole view size as opposed to damaged rect size we have a pool
|
| + // of paint buffers anyway, and this size has surely been used at least once
|
| + // to paint the whole view... And it also prevents, somehow, an intermittent
|
| + // bug we get when painting the sub-rectangles with StretchDIBits on Windows.
|
| + gfx::Rect bitmap_rect = gfx::Rect(gfx::Point(0, 0), size_);
|
| skia::PlatformCanvas* canvas =
|
| RenderProcess::current()->GetDrawingCanvas(¤t_paint_buf_,
|
| - damaged_rect);
|
| + bitmap_rect);
|
| if (!canvas) {
|
| NOTREACHED();
|
| return;
|
| }
|
|
|
| - PaintRect(damaged_rect, canvas);
|
| + // We must make sure all our paint rects are within the bitmap bounds.
|
| + PaintRegion(&paint_rects).Clip(bitmap_rect);
|
| + PaintRects(paint_rects, canvas);
|
|
|
| ViewHostMsg_PaintRect_Params params;
|
| - params.bitmap_rect = damaged_rect;
|
| + params.bitmap = current_paint_buf_->id();
|
| + params.bitmap_rect = bitmap_rect;
|
| + params.paint_rects = paint_rects;
|
| params.view_size = size_;
|
| params.plugin_window_moves = plugin_window_moves_;
|
| params.flags = next_paint_flags_;
|
| - params.bitmap = current_paint_buf_->id();
|
|
|
| delete canvas;
|
|
|
| @@ -480,24 +597,21 @@
|
| void RenderWidget::DidInvalidateRect(WebWidget* webwidget,
|
| const WebRect& rect) {
|
| // We only want one pending DoDeferredPaint call at any time...
|
| - bool paint_pending = !paint_rect_.IsEmpty();
|
| + bool paint_pending = !paint_rects_.empty();
|
|
|
| // If this invalidate overlaps with a pending scroll, then we have to
|
| // downgrade to invalidating the scroll rect.
|
| + PaintRegion rect_vector(&paint_rects_);
|
| if (gfx::Rect(rect).Intersects(scroll_rect_)) {
|
| - paint_rect_ = paint_rect_.Union(scroll_rect_);
|
| + rect_vector.Add(scroll_rect_);
|
| scroll_rect_ = gfx::Rect();
|
| }
|
|
|
| - gfx::Rect view_rect(0, 0, size_.width(), size_.height());
|
| - // TODO(iyengar) Investigate why we have painting issues when
|
| - // we ignore invalid regions outside the view.
|
| - // Ignore invalidates that occur outside the bounds of the view
|
| - // TODO(darin): maybe this should move into the paint code?
|
| - // paint_rect_ = view_rect.Intersect(paint_rect_.Union(rect));
|
| - paint_rect_ = paint_rect_.Union(view_rect.Intersect(rect));
|
| + // We don't need to intersect with the view rect here since we have to
|
| + // do it later on in case the view rect changes between invalidations.
|
| + rect_vector.Add(rect);
|
|
|
| - if (paint_rect_.IsEmpty() || paint_reply_pending() || paint_pending)
|
| + if (paint_rects_.empty() || paint_reply_pending() || paint_pending)
|
| return;
|
|
|
| // Perform painting asynchronously. This serves two purposes:
|
| @@ -517,7 +631,8 @@
|
| dy = 0;
|
| }
|
|
|
| - bool intersects_with_painting = paint_rect_.Intersects(clip_rect);
|
| + bool intersects_with_painting =
|
| + PaintRegion(&paint_rects_).Intersects(clip_rect);
|
|
|
| // If we already have a pending scroll operation or if this scroll operation
|
| // intersects the existing paint region, then just failover to invalidating.
|
|
|