From 0dd9815a9ae48eef6e8b7d89a1f38916a55f469b Mon Sep 17 00:00:00 2001 From: "enne@chromium.org" Date: Fri, 7 Jun 2013 09:16:34 +0000 Subject: cc: Clear impl-side painting canvases more efficiently This change pipes the contents opaque flag from the layer to the pile recording. If this flag is true, the layer promises that it will raster something opaque covering layer bounds. This lets us avoid a clear for all but the internal edge texels which need to be cleared for filtering during rasterization and the external edge texels which need to be cleared for filtering during drawing. Any non-opaque layer now just gets cleared to transparent, as that seemed to be about 4x faster on average than drawing a rect even for rects that didn't cover the entire canvas. R=vmpstr@chromium.org BUG=246782 Review URL: https://chromiumcodereview.appspot.com/16580011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@204779 0039d316-1c4b-4281-b951-d872f2087c98 --- cc/layers/picture_layer.cc | 1 + cc/resources/picture_pile.cc | 2 + cc/resources/picture_pile.h | 1 + cc/resources/picture_pile_base.cc | 3 ++ cc/resources/picture_pile_base.h | 1 + cc/resources/picture_pile_impl.cc | 72 +++++++++++++------------- cc/resources/picture_pile_impl_unittest.cc | 82 ++++++++++++++++++++++++++++++ cc/resources/picture_pile_unittest.cc | 6 +++ cc/test/fake_picture_pile_impl.cc | 2 + cc/test/fake_picture_pile_impl.h | 8 +++ 10 files changed, 142 insertions(+), 36 deletions(-) (limited to 'cc') diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc index 48889f3..179a2cf 100644 --- a/cc/layers/picture_layer.cc +++ b/cc/layers/picture_layer.cc @@ -93,6 +93,7 @@ void PictureLayer::Update(ResourceUpdateQueue*, devtools_instrumentation::kPaintLayer, id()); pile_->Update(client_, SafeOpaqueBackgroundColor(), + contents_opaque(), pile_invalidation_, visible_layer_rect, stats); diff --git a/cc/resources/picture_pile.cc b/cc/resources/picture_pile.cc index d4d7a68..df48c96 100644 --- a/cc/resources/picture_pile.cc +++ b/cc/resources/picture_pile.cc @@ -34,10 +34,12 @@ PicturePile::~PicturePile() { void PicturePile::Update( ContentLayerClient* painter, SkColor background_color, + bool contents_opaque, const Region& invalidation, gfx::Rect visible_layer_rect, RenderingStats* stats) { background_color_ = background_color; + contents_opaque_ = contents_opaque; gfx::Rect interest_rect = visible_layer_rect; interest_rect.Inset( diff --git a/cc/resources/picture_pile.h b/cc/resources/picture_pile.h index af44cef..33ddc7e 100644 --- a/cc/resources/picture_pile.h +++ b/cc/resources/picture_pile.h @@ -22,6 +22,7 @@ class CC_EXPORT PicturePile : public PicturePileBase { void Update( ContentLayerClient* painter, SkColor background_color, + bool contents_opaque, const Region& invalidation, gfx::Rect visible_layer_rect, RenderingStats* stats); diff --git a/cc/resources/picture_pile_base.cc b/cc/resources/picture_pile_base.cc index ed1b6a2..d982f9f 100644 --- a/cc/resources/picture_pile_base.cc +++ b/cc/resources/picture_pile_base.cc @@ -26,6 +26,7 @@ namespace cc { PicturePileBase::PicturePileBase() : min_contents_scale_(0), background_color_(SkColorSetARGBInline(0, 0, 0, 0)), + contents_opaque_(false), slow_down_raster_scale_factor_for_debug_(0), show_debug_picture_borders_(false), num_raster_threads_(0) { @@ -42,6 +43,7 @@ PicturePileBase::PicturePileBase(const PicturePileBase* other) min_contents_scale_(other->min_contents_scale_), tile_grid_info_(other->tile_grid_info_), background_color_(other->background_color_), + contents_opaque_(other->contents_opaque_), slow_down_raster_scale_factor_for_debug_( other->slow_down_raster_scale_factor_for_debug_), show_debug_picture_borders_(other->show_debug_picture_borders_), @@ -55,6 +57,7 @@ PicturePileBase::PicturePileBase( min_contents_scale_(other->min_contents_scale_), tile_grid_info_(other->tile_grid_info_), background_color_(other->background_color_), + contents_opaque_(other->contents_opaque_), slow_down_raster_scale_factor_for_debug_( other->slow_down_raster_scale_factor_for_debug_), show_debug_picture_borders_(other->show_debug_picture_borders_), diff --git a/cc/resources/picture_pile_base.h b/cc/resources/picture_pile_base.h index d1c5754..6a2e83c 100644 --- a/cc/resources/picture_pile_base.h +++ b/cc/resources/picture_pile_base.h @@ -66,6 +66,7 @@ class CC_EXPORT PicturePileBase : public base::RefCounted { float min_contents_scale_; SkTileGridPicture::TileGridInfo tile_grid_info_; SkColor background_color_; + bool contents_opaque_; int slow_down_raster_scale_factor_for_debug_; bool show_debug_picture_borders_; int num_raster_threads_; diff --git a/cc/resources/picture_pile_impl.cc b/cc/resources/picture_pile_impl.cc index 496ca09..5025115 100644 --- a/cc/resources/picture_pile_impl.cc +++ b/cc/resources/picture_pile_impl.cc @@ -94,49 +94,49 @@ void PicturePileImpl::RasterToBitmap( gfx::Rect canvas_rect, float contents_scale, RasterStats* raster_stats) { - - canvas->save(); - canvas->translate(-canvas_rect.x(), -canvas_rect.y()); - - gfx::SizeF total_content_size = gfx::ScaleSize(tiling_.total_size(), - contents_scale); - gfx::Rect total_content_rect(gfx::ToCeiledSize(total_content_size)); - gfx::Rect content_rect = total_content_rect; - content_rect.Intersect(canvas_rect); - #ifndef NDEBUG // Any non-painted areas will be left in this color. canvas->clear(DebugColors::NonPaintedFillColor()); #endif // NDEBUG - // TODO(enne): this needs to be rolled together with the border clear - SkPaint paint; - paint.setAntiAlias(false); - paint.setXfermodeMode(SkXfermode::kClear_Mode); - canvas->drawRect(gfx::RectToSkRect(content_rect), paint); - - // Clear one texel inside the right/bottom edge of the content rect, - // as it may only be partially covered by the picture playback. - // Also clear one texel outside the right/bottom edge of the content rect, - // as it may get blended in by linear filtering when zoomed in. - gfx::Rect deflated_content_rect = total_content_rect; - deflated_content_rect.Inset(0, 0, 1, 1); - - gfx::Rect canvas_outside_content_rect = canvas_rect; - canvas_outside_content_rect.Subtract(deflated_content_rect); - - if (!canvas_outside_content_rect.IsEmpty()) { - gfx::Rect inflated_content_rect = total_content_rect; - inflated_content_rect.Inset(0, 0, -1, -1); - canvas->clipRect(gfx::RectToSkRect(inflated_content_rect), - SkRegion::kReplace_Op); - canvas->clipRect(gfx::RectToSkRect(deflated_content_rect), - SkRegion::kDifference_Op); - canvas->drawColor(background_color_, SkXfermode::kSrc_Mode); + // If this picture has opaque contents, it is guaranteeing that it will + // draw an opaque rect the size of the layer. If it is not, then we must + // clear this canvas ourselves. + if (!contents_opaque_) { + // Clearing is about ~4x faster than drawing a rect even if the content + // isn't covering a majority of the canvas. + canvas->clear(SK_ColorTRANSPARENT); + } else { + // Even if it is opaque, on any rasterizations that touch the edge of the + // layer, we also need to raster the background color underneath the last + // texel (since the recording won't cover it) and outside the last texel + // (due to linear filtering when using this texture). + gfx::SizeF total_content_size = gfx::ScaleSize(tiling_.total_size(), + contents_scale); + gfx::Rect content_rect(gfx::ToCeiledSize(total_content_size)); + gfx::Rect deflated_content_rect = content_rect; + content_rect.Intersect(canvas_rect); + + // The final texel of content may only be partially covered by a + // rasterization; this rect represents the content rect that is fully + // covered by content. + deflated_content_rect.Inset(0, 0, 1, 1); + deflated_content_rect.Intersect(canvas_rect); + if (!deflated_content_rect.Contains(canvas_rect)) { + // Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X + // faster than clearing, so special case this. + canvas->save(); + gfx::Rect inflated_content_rect = content_rect; + inflated_content_rect.Inset(0, 0, -1, -1); + canvas->clipRect(gfx::RectToSkRect(inflated_content_rect), + SkRegion::kReplace_Op); + canvas->clipRect(gfx::RectToSkRect(deflated_content_rect), + SkRegion::kDifference_Op); + canvas->drawColor(background_color_, SkXfermode::kSrc_Mode); + canvas->restore(); + } } - canvas->restore(); - RasterCommon(canvas, NULL, canvas_rect, contents_scale, raster_stats); } diff --git a/cc/resources/picture_pile_impl_unittest.cc b/cc/resources/picture_pile_impl_unittest.cc index d0e78b9..336dc7a 100644 --- a/cc/resources/picture_pile_impl_unittest.cc +++ b/cc/resources/picture_pile_impl_unittest.cc @@ -11,6 +11,7 @@ #include "third_party/skia/include/core/SkPixelRef.h" #include "third_party/skia/include/core/SkShader.h" #include "ui/gfx/rect.h" +#include "ui/gfx/size_conversions.h" namespace cc { namespace { @@ -712,5 +713,86 @@ TEST(PicturePileImplTest, PixelRefIteratorMultiplePictures) { } } +TEST(PicturePileImpl, RasterContentsOpaque) { + gfx::Size tile_size(1000, 1000); + gfx::Size layer_bounds(3, 5); + float contents_scale = 1.5f; + + scoped_refptr pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + // Because the caller sets content opaque, it also promises that it + // has at least filled in layer_bounds opaquely. + SkPaint red_paint; + red_paint.setColor(SK_ColorRED); + pile->add_draw_rect_with_paint(gfx::Rect(layer_bounds), red_paint); + + pile->SetMinContentsScale(contents_scale); + pile->set_background_color(SK_ColorRED); + pile->set_contents_opaque(true); + pile->RerecordPile(); + + gfx::Size content_bounds( + gfx::ToCeiledSize(gfx::ScaleSize(layer_bounds, contents_scale))); + + // Simulate a canvas rect larger than the content bounds. Every pixel + // up to one pixel outside the content bounds is guaranteed to be opaque. + // Outside of that is undefined. + gfx::Rect canvas_rect(content_bounds); + canvas_rect.Inset(0, 0, -1, -1); + + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, + canvas_rect.width(), + canvas_rect.height()); + bitmap.allocPixels(); + SkCanvas canvas(bitmap); + + PicturePileImpl::RasterStats raster_stats; + pile->RasterToBitmap( + &canvas, canvas_rect, contents_scale, &raster_stats); + + SkColor* pixels = reinterpret_cast(bitmap.getPixels()); + int num_pixels = bitmap.width() * bitmap.height(); + for (int i = 0; i < num_pixels; ++i) { + EXPECT_EQ(SkColorGetA(pixels[i]), 255u); + } +} + +TEST(PicturePileImpl, RasterContentsTransparent) { + gfx::Size tile_size(1000, 1000); + gfx::Size layer_bounds(5, 3); + float contents_scale = 0.5f; + + scoped_refptr pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + pile->set_background_color(SK_ColorTRANSPARENT); + pile->set_contents_opaque(false); + pile->SetMinContentsScale(contents_scale); + pile->RerecordPile(); + + gfx::Size content_bounds( + gfx::ToCeiledSize(gfx::ScaleSize(layer_bounds, contents_scale))); + + gfx::Rect canvas_rect(content_bounds); + canvas_rect.Inset(0, 0, -1, -1); + + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, + canvas_rect.width(), + canvas_rect.height()); + bitmap.allocPixels(); + SkCanvas canvas(bitmap); + + PicturePileImpl::RasterStats raster_stats; + pile->RasterToBitmap( + &canvas, canvas_rect, contents_scale, &raster_stats); + + SkColor* pixels = reinterpret_cast(bitmap.getPixels()); + int num_pixels = bitmap.width() * bitmap.height(); + for (int i = 0; i < num_pixels; ++i) { + EXPECT_EQ(SkColorGetA(pixels[i]), 0u); + } +} + } // namespace } // namespace cc diff --git a/cc/resources/picture_pile_unittest.cc b/cc/resources/picture_pile_unittest.cc index 7f75cee..1d2800b 100644 --- a/cc/resources/picture_pile_unittest.cc +++ b/cc/resources/picture_pile_unittest.cc @@ -41,6 +41,7 @@ TEST(PicturePileTest, SmallInvalidateInflated) { // Update the whole layer. pile->Update(&client, background_color, + false, gfx::Rect(layer_size), gfx::Rect(layer_size), NULL); @@ -49,6 +50,7 @@ TEST(PicturePileTest, SmallInvalidateInflated) { gfx::Rect invalidate_rect(50, 50, 1, 1); pile->Update(&client, background_color, + false, invalidate_rect, gfx::Rect(layer_size), NULL); @@ -90,6 +92,7 @@ TEST(PicturePileTest, LargeInvalidateInflated) { // Update the whole layer. pile->Update(&client, background_color, + false, gfx::Rect(layer_size), gfx::Rect(layer_size), NULL); @@ -98,6 +101,7 @@ TEST(PicturePileTest, LargeInvalidateInflated) { gfx::Rect invalidate_rect(50, 50, 100, 100); pile->Update(&client, background_color, + false, invalidate_rect, gfx::Rect(layer_size), NULL); @@ -150,6 +154,7 @@ TEST(PicturePileTest, InvalidateOnTileBoundaryInflated) { // Update the whole layer. pile->Update(&client, background_color, + false, gfx::Rect(layer_size), gfx::Rect(layer_size), NULL); @@ -163,6 +168,7 @@ TEST(PicturePileTest, InvalidateOnTileBoundaryInflated) { 50); pile->Update(&client, background_color, + false, invalidate_rect, gfx::Rect(layer_size), NULL); diff --git a/cc/test/fake_picture_pile_impl.cc b/cc/test/fake_picture_pile_impl.cc index 0530cfb..b14a6e9 100644 --- a/cc/test/fake_picture_pile_impl.cc +++ b/cc/test/fake_picture_pile_impl.cc @@ -51,6 +51,8 @@ void FakePicturePileImpl::AddRecordingAt(int x, int y) { if (HasRecordingAt(x, y)) return; gfx::Rect bounds(tiling().TileBounds(x, y)); + bounds.Inset(-buffer_pixels(), -buffer_pixels()); + scoped_refptr picture(Picture::Create(bounds)); picture->Record(&client_, tile_grid_info_, NULL); picture->GatherPixelRefs(tile_grid_info_, NULL); diff --git a/cc/test/fake_picture_pile_impl.h b/cc/test/fake_picture_pile_impl.h index bedc6ce..a606934 100644 --- a/cc/test/fake_picture_pile_impl.h +++ b/cc/test/fake_picture_pile_impl.h @@ -48,6 +48,14 @@ class FakePicturePileImpl : public PicturePileImpl { default_paint_ = paint; } + void set_background_color(SkColor color) { + background_color_ = color; + } + + void set_contents_opaque(bool contents_opaque) { + contents_opaque_ = contents_opaque; + } + protected: FakePicturePileImpl(); virtual ~FakePicturePileImpl(); -- cgit v1.1