summaryrefslogtreecommitdiffstats
path: root/cc/layers
diff options
context:
space:
mode:
Diffstat (limited to 'cc/layers')
-rw-r--r--cc/layers/append_quads_data.h41
-rw-r--r--cc/layers/content_layer.cc114
-rw-r--r--cc/layers/content_layer.h69
-rw-r--r--cc/layers/content_layer_client.h31
-rw-r--r--cc/layers/content_layer_unittest.cc58
-rw-r--r--cc/layers/contents_scaling_layer.cc50
-rw-r--r--cc/layers/contents_scaling_layer.h42
-rw-r--r--cc/layers/contents_scaling_layer_unittest.cc88
-rw-r--r--cc/layers/delegated_renderer_layer.cc103
-rw-r--r--cc/layers/delegated_renderer_layer.h49
-rw-r--r--cc/layers/delegated_renderer_layer_impl.cc350
-rw-r--r--cc/layers/delegated_renderer_layer_impl.h89
-rw-r--r--cc/layers/delegated_renderer_layer_impl_unittest.cc1255
-rw-r--r--cc/layers/draw_properties.h99
-rw-r--r--cc/layers/heads_up_display_layer.cc64
-rw-r--r--cc/layers/heads_up_display_layer.h35
-rw-r--r--cc/layers/heads_up_display_layer_impl.cc697
-rw-r--r--cc/layers/heads_up_display_layer_impl.h126
-rw-r--r--cc/layers/heads_up_display_unittest.cc110
-rw-r--r--cc/layers/image_layer.cc96
-rw-r--r--cc/layers/image_layer.h56
-rw-r--r--cc/layers/io_surface_layer.cc43
-rw-r--r--cc/layers/io_surface_layer.h35
-rw-r--r--cc/layers/io_surface_layer_impl.cc145
-rw-r--r--cc/layers/io_surface_layer_impl.h51
-rw-r--r--cc/layers/layer.cc817
-rw-r--r--cc/layers/layer.h488
-rw-r--r--cc/layers/layer_impl.cc966
-rw-r--r--cc/layers/layer_impl.h528
-rw-r--r--cc/layers/layer_impl_unittest.cc255
-rw-r--r--cc/layers/layer_iterator.cc147
-rw-r--r--cc/layers/layer_iterator.h208
-rw-r--r--cc/layers/layer_iterator_unittest.cc254
-rw-r--r--cc/layers/layer_unittest.cc977
-rw-r--r--cc/layers/nine_patch_layer.cc116
-rw-r--r--cc/layers/nine_patch_layer.h61
-rw-r--r--cc/layers/nine_patch_layer_impl.cc300
-rw-r--r--cc/layers/nine_patch_layer_impl.h63
-rw-r--r--cc/layers/nine_patch_layer_impl_unittest.cc150
-rw-r--r--cc/layers/nine_patch_layer_unittest.cc148
-rw-r--r--cc/layers/picture_image_layer.cc58
-rw-r--r--cc/layers/picture_image_layer.h42
-rw-r--r--cc/layers/picture_image_layer_impl.cc45
-rw-r--r--cc/layers/picture_image_layer_impl.h39
-rw-r--r--cc/layers/picture_layer.cc97
-rw-r--r--cc/layers/picture_layer.h57
-rw-r--r--cc/layers/picture_layer_impl.cc828
-rw-r--r--cc/layers/picture_layer_impl.h116
-rw-r--r--cc/layers/picture_layer_impl_unittest.cc708
-rw-r--r--cc/layers/quad_sink.h36
-rw-r--r--cc/layers/render_pass_sink.h21
-rw-r--r--cc/layers/render_surface.cc33
-rw-r--r--cc/layers/render_surface.h139
-rw-r--r--cc/layers/render_surface_impl.cc290
-rw-r--r--cc/layers/render_surface_impl.h171
-rw-r--r--cc/layers/render_surface_unittest.cc159
-rw-r--r--cc/layers/scrollbar_geometry_fixed_thumb.cc83
-rw-r--r--cc/layers/scrollbar_geometry_fixed_thumb.h39
-rw-r--r--cc/layers/scrollbar_geometry_stub.cc109
-rw-r--r--cc/layers/scrollbar_geometry_stub.h54
-rw-r--r--cc/layers/scrollbar_layer.cc441
-rw-r--r--cc/layers/scrollbar_layer.h99
-rw-r--r--cc/layers/scrollbar_layer_impl.cc342
-rw-r--r--cc/layers/scrollbar_layer_impl.h150
-rw-r--r--cc/layers/scrollbar_layer_impl_base.h30
-rw-r--r--cc/layers/scrollbar_layer_unittest.cc411
-rw-r--r--cc/layers/scrollbar_theme_painter.h36
-rw-r--r--cc/layers/solid_color_layer.cc30
-rw-r--r--cc/layers/solid_color_layer.h33
-rw-r--r--cc/layers/solid_color_layer_impl.cc50
-rw-r--r--cc/layers/solid_color_layer_impl.h39
-rw-r--r--cc/layers/solid_color_layer_impl_unittest.cc163
-rw-r--r--cc/layers/texture_layer.cc204
-rw-r--r--cc/layers/texture_layer.h106
-rw-r--r--cc/layers/texture_layer_client.h31
-rw-r--r--cc/layers/texture_layer_impl.cc163
-rw-r--r--cc/layers/texture_layer_impl.h87
-rw-r--r--cc/layers/texture_layer_unittest.cc471
-rw-r--r--cc/layers/tiled_layer.cc888
-rw-r--r--cc/layers/tiled_layer.h143
-rw-r--r--cc/layers/tiled_layer_impl.cc307
-rw-r--r--cc/layers/tiled_layer_impl.h70
-rw-r--r--cc/layers/tiled_layer_impl_unittest.cc265
-rw-r--r--cc/layers/tiled_layer_unittest.cc1672
-rw-r--r--cc/layers/video_frame_provider.h60
-rw-r--r--cc/layers/video_frame_provider_client_impl.cc87
-rw-r--r--cc/layers/video_frame_provider_client_impl.h63
-rw-r--r--cc/layers/video_layer.cc25
-rw-r--r--cc/layers/video_layer.h38
-rw-r--r--cc/layers/video_layer_impl.cc482
-rw-r--r--cc/layers/video_layer_impl.h88
91 files changed, 19572 insertions, 0 deletions
diff --git a/cc/layers/append_quads_data.h b/cc/layers/append_quads_data.h
new file mode 100644
index 0000000..18580a9
--- /dev/null
+++ b/cc/layers/append_quads_data.h
@@ -0,0 +1,41 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_APPEND_QUADS_DATA_H_
+#define CC_LAYERS_APPEND_QUADS_DATA_H_
+
+#include "base/basictypes.h"
+#include "cc/quads/render_pass.h"
+
+namespace cc {
+
+struct AppendQuadsData {
+ AppendQuadsData()
+ : hadOcclusionFromOutsideTargetSurface(false)
+ , hadIncompleteTile(false)
+ , numMissingTiles(0)
+ , renderPassId(0, 0)
+ {
+ }
+
+ explicit AppendQuadsData(RenderPass::Id renderPassId)
+ : hadOcclusionFromOutsideTargetSurface(false)
+ , hadIncompleteTile(false)
+ , numMissingTiles(0)
+ , renderPassId(renderPassId)
+ {
+ }
+
+ // Set by the QuadCuller.
+ bool hadOcclusionFromOutsideTargetSurface;
+ // Set by the layer appending quads.
+ bool hadIncompleteTile;
+ // Set by the layer appending quads.
+ int64 numMissingTiles;
+ // Given to the layer appending quads.
+ const RenderPass::Id renderPassId;
+};
+
+}
+#endif // CC_LAYERS_APPEND_QUADS_DATA_H_
diff --git a/cc/layers/content_layer.cc b/cc/layers/content_layer.cc
new file mode 100644
index 0000000..b6c8d7b
--- /dev/null
+++ b/cc/layers/content_layer.cc
@@ -0,0 +1,114 @@
+// Copyright 2010 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 "cc/layers/content_layer.h"
+
+#include "base/auto_reset.h"
+#include "base/metrics/histogram.h"
+#include "base/time.h"
+#include "cc/layers/content_layer_client.h"
+#include "cc/resources/bitmap_content_layer_updater.h"
+#include "cc/resources/bitmap_skpicture_content_layer_updater.h"
+#include "cc/resources/layer_painter.h"
+#include "cc/trees/layer_tree_host.h"
+
+namespace cc {
+
+ContentLayerPainter::ContentLayerPainter(ContentLayerClient* client)
+ : client_(client) {}
+
+scoped_ptr<ContentLayerPainter> ContentLayerPainter::Create(
+ ContentLayerClient* client) {
+ return make_scoped_ptr(new ContentLayerPainter(client));
+}
+
+void ContentLayerPainter::Paint(SkCanvas* canvas,
+ gfx::Rect content_rect,
+ gfx::RectF* opaque) {
+ base::TimeTicks paint_start = base::TimeTicks::HighResNow();
+ client_->PaintContents(canvas, content_rect, opaque);
+ base::TimeTicks paint_end = base::TimeTicks::HighResNow();
+ double pixels_per_sec = (content_rect.width() * content_rect.height()) /
+ (paint_end - paint_start).InSecondsF();
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.AccelContentPaintDurationMS",
+ (paint_end - paint_start).InMilliseconds(),
+ 0,
+ 120,
+ 30);
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.AccelContentPaintMegapixPerSecond",
+ pixels_per_sec / 1000000,
+ 10,
+ 210,
+ 30);
+}
+
+scoped_refptr<ContentLayer> ContentLayer::Create(ContentLayerClient* client) {
+ return make_scoped_refptr(new ContentLayer(client));
+}
+
+ContentLayer::ContentLayer(ContentLayerClient* client)
+ : TiledLayer(),
+ client_(client) {}
+
+ContentLayer::~ContentLayer() {}
+
+bool ContentLayer::DrawsContent() const {
+ return TiledLayer::DrawsContent() && client_;
+}
+
+void ContentLayer::SetTexturePriorities(
+ const PriorityCalculator& priority_calc) {
+ // Update the tile data before creating all the layer's tiles.
+ UpdateTileSizeAndTilingOption();
+
+ TiledLayer::SetTexturePriorities(priority_calc);
+}
+
+void ContentLayer::Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) {
+ {
+ base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_,
+ true);
+
+ CreateUpdaterIfNeeded();
+ }
+
+ TiledLayer::Update(queue, occlusion, stats);
+ needs_display_ = false;
+}
+
+bool ContentLayer::NeedMoreUpdates() {
+ return NeedsIdlePaint();
+}
+
+LayerUpdater* ContentLayer::Updater() const {
+ return updater_.get();
+}
+
+void ContentLayer::CreateUpdaterIfNeeded() {
+ if (updater_)
+ return;
+ scoped_ptr<LayerPainter> painter =
+ ContentLayerPainter::Create(client_).PassAs<LayerPainter>();
+ if (layer_tree_host()->settings().acceleratePainting)
+ updater_ = SkPictureContentLayerUpdater::Create(painter.Pass());
+ else if (layer_tree_host()->settings().perTilePaintingEnabled)
+ updater_ = BitmapSkPictureContentLayerUpdater::Create(painter.Pass());
+ else
+ updater_ = BitmapContentLayerUpdater::Create(painter.Pass());
+ updater_->SetOpaque(contents_opaque());
+
+ unsigned texture_format =
+ layer_tree_host()->GetRendererCapabilities().best_texture_format;
+ SetTextureFormat(texture_format);
+}
+
+void ContentLayer::SetContentsOpaque(bool opaque) {
+ Layer::SetContentsOpaque(opaque);
+ if (updater_)
+ updater_->SetOpaque(opaque);
+}
+
+} // namespace cc
diff --git a/cc/layers/content_layer.h b/cc/layers/content_layer.h
new file mode 100644
index 0000000..94db0bf
--- /dev/null
+++ b/cc/layers/content_layer.h
@@ -0,0 +1,69 @@
+// Copyright 2010 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.
+
+#ifndef CC_LAYERS_CONTENT_LAYER_H_
+#define CC_LAYERS_CONTENT_LAYER_H_
+
+#include "base/basictypes.h"
+#include "cc/base/cc_export.h"
+#include "cc/layers/tiled_layer.h"
+#include "cc/resources/layer_painter.h"
+
+class SkCanvas;
+
+namespace cc {
+
+class ContentLayerClient;
+class LayerUpdater;
+
+class CC_EXPORT ContentLayerPainter : public LayerPainter {
+ public:
+ static scoped_ptr<ContentLayerPainter> Create(ContentLayerClient* client);
+
+ virtual void Paint(SkCanvas* canvas,
+ gfx::Rect content_rect,
+ gfx::RectF* opaque) OVERRIDE;
+
+ private:
+ explicit ContentLayerPainter(ContentLayerClient* client);
+
+ ContentLayerClient* client_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContentLayerPainter);
+};
+
+// A layer that renders its contents into an SkCanvas.
+class CC_EXPORT ContentLayer : public TiledLayer {
+ public:
+ static scoped_refptr<ContentLayer> Create(ContentLayerClient* client);
+
+ void ClearClient() { client_ = NULL; }
+
+ virtual bool DrawsContent() const OVERRIDE;
+ virtual void SetTexturePriorities(const PriorityCalculator& priority_calc)
+ OVERRIDE;
+ virtual void Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) OVERRIDE;
+ virtual bool NeedMoreUpdates() OVERRIDE;
+
+ virtual void SetContentsOpaque(bool contents_opaque) OVERRIDE;
+
+ protected:
+ explicit ContentLayer(ContentLayerClient* client);
+ virtual ~ContentLayer();
+
+ private:
+ // TiledLayer implementation.
+ virtual LayerUpdater* Updater() const OVERRIDE;
+ virtual void CreateUpdaterIfNeeded() OVERRIDE;
+
+ ContentLayerClient* client_;
+ scoped_refptr<LayerUpdater> updater_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContentLayer);
+};
+
+}
+#endif // CC_LAYERS_CONTENT_LAYER_H_
diff --git a/cc/layers/content_layer_client.h b/cc/layers/content_layer_client.h
new file mode 100644
index 0000000..7d51fd9
--- /dev/null
+++ b/cc/layers/content_layer_client.h
@@ -0,0 +1,31 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_CONTENT_LAYER_CLIENT_H_
+#define CC_LAYERS_CONTENT_LAYER_CLIENT_H_
+
+#include "cc/base/cc_export.h"
+
+class SkCanvas;
+
+namespace gfx {
+class Rect;
+class RectF;
+}
+
+namespace cc {
+
+class CC_EXPORT ContentLayerClient {
+ public:
+ virtual void PaintContents(SkCanvas* canvas,
+ gfx::Rect clip,
+ gfx::RectF* opaque) = 0;
+
+ protected:
+ virtual ~ContentLayerClient() {}
+};
+
+}
+
+#endif // CC_LAYERS_CONTENT_LAYER_CLIENT_H_
diff --git a/cc/layers/content_layer_unittest.cc b/cc/layers/content_layer_unittest.cc
new file mode 100644
index 0000000..259e44f
--- /dev/null
+++ b/cc/layers/content_layer_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright 2012 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 "cc/layers/content_layer.h"
+
+#include "cc/layers/content_layer_client.h"
+#include "cc/resources/bitmap_content_layer_updater.h"
+#include "cc/test/geometry_test_utils.h"
+#include "skia/ext/platform_canvas.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/rect_conversions.h"
+
+using namespace WebKit;
+
+namespace cc {
+namespace {
+
+class MockContentLayerClient : public ContentLayerClient {
+ public:
+ explicit MockContentLayerClient(gfx::Rect opaque_layer_rect)
+ : opaque_layer_rect_(opaque_layer_rect) {}
+
+ virtual void PaintContents(SkCanvas* canvas,
+ gfx::Rect clip,
+ gfx::RectF* opaque) OVERRIDE {
+ *opaque = gfx::RectF(opaque_layer_rect_);
+ }
+
+ private:
+ gfx::Rect opaque_layer_rect_;
+};
+
+TEST(ContentLayerTest, ContentLayerPainterWithDeviceScale) {
+ float contents_scale = 2.f;
+ gfx::Rect content_rect(10, 10, 100, 100);
+ gfx::Rect opaque_rect_in_layer_space(5, 5, 20, 20);
+ gfx::RectF opaque_rect_in_content_space = gfx::ScaleRect(
+ opaque_rect_in_layer_space, contents_scale, contents_scale);
+ MockContentLayerClient client(opaque_rect_in_layer_space);
+ scoped_refptr<BitmapContentLayerUpdater> updater =
+ BitmapContentLayerUpdater::Create(ContentLayerPainter::Create(&client).
+ PassAs<LayerPainter>());
+
+ gfx::Rect resulting_opaque_rect;
+ updater->PrepareToUpdate(content_rect,
+ gfx::Size(256, 256),
+ contents_scale,
+ contents_scale,
+ &resulting_opaque_rect,
+ NULL);
+
+ EXPECT_RECT_EQ(gfx::ToEnclosingRect(opaque_rect_in_content_space),
+ resulting_opaque_rect);
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/contents_scaling_layer.cc b/cc/layers/contents_scaling_layer.cc
new file mode 100644
index 0000000..d602e88
--- /dev/null
+++ b/cc/layers/contents_scaling_layer.cc
@@ -0,0 +1,50 @@
+// Copyright 2012 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 "cc/layers/contents_scaling_layer.h"
+#include "ui/gfx/size_conversions.h"
+
+namespace cc {
+
+gfx::Size ContentsScalingLayer::ComputeContentBoundsForScale(
+ float scale_x,
+ float scale_y) const {
+ return gfx::ToCeiledSize(gfx::ScaleSize(bounds(), scale_x, scale_y));
+}
+
+ContentsScalingLayer::ContentsScalingLayer()
+ : last_update_contents_scale_x_(0.f),
+ last_update_contents_scale_y_(0.f) {}
+
+ContentsScalingLayer::~ContentsScalingLayer() {
+}
+
+void ContentsScalingLayer::CalculateContentsScale(
+ float ideal_contents_scale,
+ bool animating_transform_to_screen,
+ float* contents_scale_x,
+ float* contents_scale_y,
+ gfx::Size* content_bounds) {
+ *contents_scale_x = ideal_contents_scale;
+ *contents_scale_y = ideal_contents_scale;
+ *content_bounds = ComputeContentBoundsForScale(
+ ideal_contents_scale,
+ ideal_contents_scale);
+}
+
+void ContentsScalingLayer::Update(
+ ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) {
+ if (draw_properties().contents_scale_x == last_update_contents_scale_x_ &&
+ draw_properties().contents_scale_y == last_update_contents_scale_y_)
+ return;
+
+ last_update_contents_scale_x_ = draw_properties().contents_scale_x;
+ last_update_contents_scale_y_ = draw_properties().contents_scale_y;
+ // Invalidate the whole layer if scale changed.
+ SetNeedsDisplayRect(gfx::Rect(bounds()));
+}
+
+} // namespace cc
diff --git a/cc/layers/contents_scaling_layer.h b/cc/layers/contents_scaling_layer.h
new file mode 100644
index 0000000..a550c26
--- /dev/null
+++ b/cc/layers/contents_scaling_layer.h
@@ -0,0 +1,42 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_CONTENTS_SCALING_LAYER_H_
+#define CC_LAYERS_CONTENTS_SCALING_LAYER_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer.h"
+
+namespace cc {
+
+// Base class for layers that need contents scale.
+// The content bounds are determined by bounds and scale of the contents.
+class CC_EXPORT ContentsScalingLayer : public Layer {
+ public:
+ virtual void CalculateContentsScale(
+ float ideal_contents_scale,
+ bool animating_transform_to_screen,
+ float* contents_scale_x,
+ float* contents_scale_y,
+ gfx::Size* content_bounds) OVERRIDE;
+
+ virtual void Update(
+ ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) OVERRIDE;
+
+ protected:
+ ContentsScalingLayer();
+ virtual ~ContentsScalingLayer();
+
+ gfx::Size ComputeContentBoundsForScale(float scale_x, float scale_y) const;
+
+ private:
+ float last_update_contents_scale_x_;
+ float last_update_contents_scale_y_;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_CONTENTS_SCALING_LAYER_H__
diff --git a/cc/layers/contents_scaling_layer_unittest.cc b/cc/layers/contents_scaling_layer_unittest.cc
new file mode 100644
index 0000000..69710d8
--- /dev/null
+++ b/cc/layers/contents_scaling_layer_unittest.cc
@@ -0,0 +1,88 @@
+// Copyright 2012 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 "cc/layers/contents_scaling_layer.h"
+
+#include "cc/test/geometry_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class MockContentsScalingLayer : public ContentsScalingLayer {
+ public:
+ MockContentsScalingLayer()
+ : ContentsScalingLayer() {}
+
+ virtual void SetNeedsDisplayRect(const gfx::RectF& dirty_rect) OVERRIDE {
+ last_needs_display_rect_ = dirty_rect;
+ ContentsScalingLayer::SetNeedsDisplayRect(dirty_rect);
+ }
+
+ void ResetNeedsDisplay() {
+ needs_display_ = false;
+ }
+
+ const gfx::RectF& LastNeedsDisplayRect() const {
+ return last_needs_display_rect_;
+ }
+
+ void UpdateContentsScale(float contents_scale) {
+ // Simulate CalcDrawProperties.
+ CalculateContentsScale(
+ contents_scale,
+ false, // animating_transform_to_screen
+ &draw_properties().contents_scale_x,
+ &draw_properties().contents_scale_y,
+ &draw_properties().content_bounds);
+ }
+
+ private:
+ virtual ~MockContentsScalingLayer() {}
+
+ gfx::RectF last_needs_display_rect_;
+};
+
+void CalcDrawProps(Layer* root, float device_scale) {
+ std::vector<scoped_refptr<Layer> > render_surface_layer_list;
+ LayerTreeHostCommon::calculateDrawProperties(
+ root,
+ gfx::Size(500, 500),
+ device_scale,
+ 1.f,
+ 1024,
+ false,
+ render_surface_layer_list);
+}
+
+TEST(ContentsScalingLayerTest, CheckContentsBounds) {
+ scoped_refptr<MockContentsScalingLayer> test_layer =
+ make_scoped_refptr(new MockContentsScalingLayer());
+
+ scoped_refptr<Layer> root = Layer::Create();
+ root->AddChild(test_layer);
+
+ test_layer->SetBounds(gfx::Size(320, 240));
+ CalcDrawProps(root, 1.f);
+ EXPECT_FLOAT_EQ(1.f, test_layer->contents_scale_x());
+ EXPECT_FLOAT_EQ(1.f, test_layer->contents_scale_y());
+ EXPECT_EQ(320, test_layer->content_bounds().width());
+ EXPECT_EQ(240, test_layer->content_bounds().height());
+
+ CalcDrawProps(root, 2.f);
+ EXPECT_EQ(640, test_layer->content_bounds().width());
+ EXPECT_EQ(480, test_layer->content_bounds().height());
+
+ test_layer->SetBounds(gfx::Size(10, 20));
+ CalcDrawProps(root, 2.f);
+ EXPECT_EQ(20, test_layer->content_bounds().width());
+ EXPECT_EQ(40, test_layer->content_bounds().height());
+
+ CalcDrawProps(root, 1.33f);
+ EXPECT_EQ(14, test_layer->content_bounds().width());
+ EXPECT_EQ(27, test_layer->content_bounds().height());
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/delegated_renderer_layer.cc b/cc/layers/delegated_renderer_layer.cc
new file mode 100644
index 0000000..37da92a
--- /dev/null
+++ b/cc/layers/delegated_renderer_layer.cc
@@ -0,0 +1,103 @@
+// Copyright 2012 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 "cc/layers/delegated_renderer_layer.h"
+
+#include "cc/layers/delegated_renderer_layer_impl.h"
+#include "cc/output/delegated_frame_data.h"
+
+namespace cc {
+
+scoped_refptr<DelegatedRendererLayer> DelegatedRendererLayer::Create() {
+ return scoped_refptr<DelegatedRendererLayer>(new DelegatedRendererLayer());
+}
+
+DelegatedRendererLayer::DelegatedRendererLayer()
+ : Layer() {
+}
+
+DelegatedRendererLayer::~DelegatedRendererLayer() {}
+
+scoped_ptr<LayerImpl> DelegatedRendererLayer::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return DelegatedRendererLayerImpl::Create(
+ tree_impl, layer_id_).PassAs<LayerImpl>();
+}
+
+bool DelegatedRendererLayer::DrawsContent() const {
+ return Layer::DrawsContent() && !frame_size_.IsEmpty();
+}
+
+void DelegatedRendererLayer::PushPropertiesTo(LayerImpl* impl) {
+ Layer::PushPropertiesTo(impl);
+
+ DelegatedRendererLayerImpl* delegated_impl =
+ static_cast<DelegatedRendererLayerImpl*>(impl);
+
+ delegated_impl->SetDisplaySize(display_size_);
+
+ if (!frame_data_) {
+ delegated_impl->SetFrameData(scoped_ptr<DelegatedFrameData>(),
+ gfx::Rect(),
+ &unused_resources_for_child_compositor_);
+ } else if (frame_size_.IsEmpty()) {
+ scoped_ptr<DelegatedFrameData> empty_frame(new DelegatedFrameData);
+ delegated_impl->SetFrameData(empty_frame.Pass(),
+ gfx::Rect(),
+ &unused_resources_for_child_compositor_);
+ } else {
+ delegated_impl->SetFrameData(frame_data_.Pass(),
+ damage_in_frame_,
+ &unused_resources_for_child_compositor_);
+ }
+ frame_data_.reset();
+ damage_in_frame_ = gfx::RectF();
+}
+
+void DelegatedRendererLayer::SetDisplaySize(gfx::Size size) {
+ if (display_size_ == size)
+ return;
+ display_size_ = size;
+ SetNeedsCommit();
+}
+
+void DelegatedRendererLayer::SetFrameData(
+ scoped_ptr<DelegatedFrameData> new_frame_data) {
+ if (frame_data_) {
+ // Copy the resources from the last provided frame into the new frame, as
+ // it may use resources that were transferred in the last frame.
+ new_frame_data->resource_list.insert(new_frame_data->resource_list.end(),
+ frame_data_->resource_list.begin(),
+ frame_data_->resource_list.end());
+ }
+ frame_data_ = new_frame_data.Pass();
+ if (!frame_data_->render_pass_list.empty()) {
+ RenderPass* root_pass = frame_data_->render_pass_list.back();
+ damage_in_frame_.Union(root_pass->damage_rect);
+ frame_size_ = root_pass->output_rect.size();
+
+ // TODO(danakj): This could be optimized to only add resources to the
+ // frame_data_ if they are actually used in the frame. For now, it will
+ // cause the parent (this layer) to hold onto some resources it doesn't
+ // need to for an extra frame.
+ for (size_t i = 0; i < unused_resources_for_child_compositor_.size(); ++i) {
+ frame_data_->resource_list.push_back(
+ unused_resources_for_child_compositor_[i]);
+ }
+ unused_resources_for_child_compositor_.clear();
+ } else {
+ frame_size_ = gfx::Size();
+ }
+ SetNeedsCommit();
+}
+
+void DelegatedRendererLayer::TakeUnusedResourcesForChildCompositor(
+ TransferableResourceArray* array) {
+ DCHECK(array->empty());
+ array->clear();
+
+ array->swap(unused_resources_for_child_compositor_);
+}
+
+} // namespace cc
diff --git a/cc/layers/delegated_renderer_layer.h b/cc/layers/delegated_renderer_layer.h
new file mode 100644
index 0000000..a789317
--- /dev/null
+++ b/cc/layers/delegated_renderer_layer.h
@@ -0,0 +1,49 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_DELEGATED_RENDERER_LAYER_H_
+#define CC_LAYERS_DELEGATED_RENDERER_LAYER_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer.h"
+#include "cc/resources/transferable_resource.h"
+
+namespace cc {
+class DelegatedFrameData;
+
+class CC_EXPORT DelegatedRendererLayer : public Layer {
+ public:
+ static scoped_refptr<DelegatedRendererLayer> Create();
+
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* impl) OVERRIDE;
+ virtual bool DrawsContent() const OVERRIDE;
+
+ // Set the size at which the frame should be displayed, with the origin at the
+ // layer's origin. This must always contain at least the layer's bounds. A
+ // value of (0, 0) implies that the frame should be displayed to fit exactly
+ // in the layer's bounds.
+ void SetDisplaySize(gfx::Size size);
+
+ void SetFrameData(scoped_ptr<DelegatedFrameData> frame_data);
+
+ // Passes ownership of any unused resources that had been given by the child
+ // compositor to the given array, so they can be given back to the child.
+ void TakeUnusedResourcesForChildCompositor(TransferableResourceArray* array);
+
+ protected:
+ DelegatedRendererLayer();
+ virtual ~DelegatedRendererLayer();
+
+ private:
+ scoped_ptr<DelegatedFrameData> frame_data_;
+ gfx::RectF damage_in_frame_;
+ gfx::Size frame_size_;
+ gfx::Size display_size_;
+ TransferableResourceArray unused_resources_for_child_compositor_;
+};
+
+}
+#endif // CC_LAYERS_DELEGATED_RENDERER_LAYER_H_
diff --git a/cc/layers/delegated_renderer_layer_impl.cc b/cc/layers/delegated_renderer_layer_impl.cc
new file mode 100644
index 0000000..cfd4897
--- /dev/null
+++ b/cc/layers/delegated_renderer_layer_impl.cc
@@ -0,0 +1,350 @@
+// Copyright 2012 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 "cc/layers/delegated_renderer_layer_impl.h"
+
+#include "base/bind.h"
+#include "cc/base/math_util.h"
+#include "cc/layers/append_quads_data.h"
+#include "cc/layers/quad_sink.h"
+#include "cc/layers/render_pass_sink.h"
+#include "cc/output/delegated_frame_data.h"
+#include "cc/quads/render_pass_draw_quad.h"
+#include "cc/trees/layer_tree_impl.h"
+
+namespace cc {
+
+DelegatedRendererLayerImpl::DelegatedRendererLayerImpl(
+ LayerTreeImpl* tree_impl, int id)
+ : LayerImpl(tree_impl, id),
+ child_id_(0) {
+}
+
+DelegatedRendererLayerImpl::~DelegatedRendererLayerImpl() {
+ ClearRenderPasses();
+ ClearChildId();
+}
+
+bool DelegatedRendererLayerImpl::HasDelegatedContent() const {
+ return !render_passes_in_draw_order_.empty();
+}
+
+bool DelegatedRendererLayerImpl::HasContributingDelegatedRenderPasses() const {
+ // The root RenderPass for the layer is merged with its target
+ // RenderPass in each frame. So we only have extra RenderPasses
+ // to merge when we have a non-root RenderPass present.
+ return render_passes_in_draw_order_.size() > 1;
+}
+
+static ResourceProvider::ResourceId ResourceRemapHelper(
+ bool* invalid_frame,
+ const ResourceProvider::ResourceIdMap& child_to_parent_map,
+ ResourceProvider::ResourceIdSet *remapped_resources,
+ ResourceProvider::ResourceId id) {
+
+ ResourceProvider::ResourceIdMap::const_iterator it =
+ child_to_parent_map.find(id);
+ if (it == child_to_parent_map.end()) {
+ *invalid_frame = true;
+ return 0;
+ }
+
+ DCHECK(it->first == id);
+ ResourceProvider::ResourceId remapped_id = it->second;
+ remapped_resources->insert(remapped_id);
+ return remapped_id;
+}
+
+void DelegatedRendererLayerImpl::SetFrameData(
+ scoped_ptr<DelegatedFrameData> frame_data,
+ gfx::RectF damage_in_frame,
+ TransferableResourceArray* resources_for_ack) {
+ CreateChildIdIfNeeded();
+ DCHECK(child_id_);
+
+ ResourceProvider* resource_provider = layer_tree_impl()->resource_provider();
+ const ResourceProvider::ResourceIdMap& resource_map =
+ resource_provider->GetChildToParentMap(child_id_);
+
+ if (frame_data) {
+ // A frame with an empty root render pass is invalid.
+ DCHECK(frame_data->render_pass_list.empty() ||
+ !frame_data->render_pass_list.back()->output_rect.IsEmpty());
+
+ // Display size is already set so we can compute what the damage rect
+ // will be in layer space.
+ if (!frame_data->render_pass_list.empty()) {
+ RenderPass* new_root_pass = frame_data->render_pass_list.back();
+ gfx::RectF damage_in_layer = MathUtil::MapClippedRect(
+ DelegatedFrameToLayerSpaceTransform(
+ new_root_pass->output_rect.size()),
+ damage_in_frame);
+ set_update_rect(gfx::UnionRects(update_rect(), damage_in_layer));
+ }
+
+ resource_provider->ReceiveFromChild(child_id_, frame_data->resource_list);
+
+ bool invalid_frame = false;
+ ResourceProvider::ResourceIdSet used_resources;
+ DrawQuad::ResourceIteratorCallback remap_resources_to_parent_callback =
+ base::Bind(&ResourceRemapHelper,
+ &invalid_frame,
+ resource_map,
+ &used_resources);
+ for (size_t i = 0; i < frame_data->render_pass_list.size(); ++i) {
+ RenderPass* pass = frame_data->render_pass_list[i];
+ for (size_t j = 0; j < pass->quad_list.size(); ++j) {
+ DrawQuad* quad = pass->quad_list[j];
+ quad->IterateResources(remap_resources_to_parent_callback);
+ }
+ }
+
+ if (!invalid_frame) {
+ // Save the remapped quads on the layer. This steals the quads and render
+ // passes from the frame_data.
+ SetRenderPasses(&frame_data->render_pass_list);
+ resources_.swap(used_resources);
+ }
+ }
+
+ ResourceProvider::ResourceIdArray unused_resources;
+ for (ResourceProvider::ResourceIdMap::const_iterator it =
+ resource_map.begin();
+ it != resource_map.end();
+ ++it) {
+ bool resource_is_in_current_frame = resources_.count(it->second);
+ bool resource_is_in_use = resource_provider->InUseByConsumer(it->second);
+ if (!resource_is_in_current_frame && !resource_is_in_use)
+ unused_resources.push_back(it->second);
+ }
+ resource_provider->PrepareSendToChild(
+ child_id_, unused_resources, resources_for_ack);
+}
+
+void DelegatedRendererLayerImpl::SetDisplaySize(gfx::Size size) {
+ if (display_size_ == size)
+ return;
+ display_size_ = size;
+ NoteLayerPropertyChanged();
+}
+
+void DelegatedRendererLayerImpl::SetRenderPasses(
+ ScopedPtrVector<RenderPass>* render_passes_in_draw_order) {
+ gfx::RectF old_root_damage;
+ if (!render_passes_in_draw_order_.empty())
+ old_root_damage = render_passes_in_draw_order_.back()->damage_rect;
+
+ ClearRenderPasses();
+
+ for (size_t i = 0; i < render_passes_in_draw_order->size(); ++i) {
+ ScopedPtrVector<RenderPass>::iterator to_take =
+ render_passes_in_draw_order->begin() + i;
+ render_passes_index_by_id_.insert(
+ std::pair<RenderPass::Id, int>((*to_take)->id, i));
+ scoped_ptr<RenderPass> taken_render_pass =
+ render_passes_in_draw_order->take(to_take);
+ render_passes_in_draw_order_.push_back(taken_render_pass.Pass());
+ }
+
+ if (!render_passes_in_draw_order_.empty())
+ render_passes_in_draw_order_.back()->damage_rect.Union(old_root_damage);
+}
+
+void DelegatedRendererLayerImpl::ClearRenderPasses() {
+ // FIXME: Release the resources back to the nested compositor.
+ render_passes_index_by_id_.clear();
+ render_passes_in_draw_order_.clear();
+}
+
+scoped_ptr<LayerImpl> DelegatedRendererLayerImpl::CreateLayerImpl(
+ LayerTreeImpl* treeImpl) {
+ return DelegatedRendererLayerImpl::Create(treeImpl, id()).PassAs<LayerImpl>();
+}
+
+void DelegatedRendererLayerImpl::DidLoseOutputSurface() {
+ ClearRenderPasses();
+ ClearChildId();
+}
+
+gfx::Transform DelegatedRendererLayerImpl::DelegatedFrameToLayerSpaceTransform(
+ gfx::Size frame_size) const {
+ gfx::Size display_size = display_size_.IsEmpty() ? bounds() : display_size_;
+
+ gfx::Transform delegated_frame_to_layer_space_transform;
+ delegated_frame_to_layer_space_transform.Scale(
+ static_cast<double>(display_size.width()) / frame_size.width(),
+ static_cast<double>(display_size.height()) / frame_size.height());
+ return delegated_frame_to_layer_space_transform;
+}
+
+static inline int IndexToId(int index) { return index + 1; }
+static inline int IdToIndex(int id) { return id - 1; }
+
+RenderPass::Id DelegatedRendererLayerImpl::FirstContributingRenderPassId()
+ const {
+ return RenderPass::Id(id(), IndexToId(0));
+}
+
+RenderPass::Id DelegatedRendererLayerImpl::NextContributingRenderPassId(
+ RenderPass::Id previous) const {
+ return RenderPass::Id(previous.layer_id, previous.index + 1);
+}
+
+RenderPass::Id DelegatedRendererLayerImpl::ConvertDelegatedRenderPassId(
+ RenderPass::Id delegated_render_pass_id) const {
+ base::hash_map<RenderPass::Id, int>::const_iterator found =
+ render_passes_index_by_id_.find(delegated_render_pass_id);
+ DCHECK(found != render_passes_index_by_id_.end());
+ unsigned delegatedRenderPassIndex = found->second;
+ return RenderPass::Id(id(), IndexToId(delegatedRenderPassIndex));
+}
+
+void DelegatedRendererLayerImpl::AppendContributingRenderPasses(
+ RenderPassSink* render_pass_sink) {
+ DCHECK(HasContributingDelegatedRenderPasses());
+
+ for (size_t i = 0; i < render_passes_in_draw_order_.size() - 1; ++i) {
+ RenderPass::Id output_render_pass_id =
+ ConvertDelegatedRenderPassId(render_passes_in_draw_order_[i]->id);
+
+ // Don't clash with the RenderPass we generate if we own a RenderSurface.
+ DCHECK(output_render_pass_id.index > 0);
+
+ render_pass_sink->AppendRenderPass(
+ render_passes_in_draw_order_[i]->Copy(output_render_pass_id));
+ }
+}
+
+void DelegatedRendererLayerImpl::AppendQuads(
+ QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) {
+ if (render_passes_in_draw_order_.empty())
+ return;
+
+ RenderPass::Id target_render_pass_id = append_quads_data->renderPassId;
+
+ const RenderPass* root_delegated_render_pass =
+ render_passes_in_draw_order_.back();
+
+ DCHECK(root_delegated_render_pass->output_rect.origin().IsOrigin());
+ gfx::Size frame_size = root_delegated_render_pass->output_rect.size();
+
+ // If the index of the renderPassId is 0, then it is a renderPass generated
+ // for a layer in this compositor, not the delegated renderer. Then we want to
+ // merge our root renderPass with the target renderPass. Otherwise, it is some
+ // renderPass which we added from the delegated renderer.
+ bool should_merge_root_render_pass_with_target = !target_render_pass_id.index;
+ if (should_merge_root_render_pass_with_target) {
+ // Verify that the renderPass we are appending to is created our
+ // renderTarget.
+ DCHECK(target_render_pass_id.layer_id == render_target()->id());
+
+ AppendRenderPassQuads(
+ quad_sink, append_quads_data, root_delegated_render_pass, frame_size);
+ } else {
+ // Verify that the renderPass we are appending to was created by us.
+ DCHECK(target_render_pass_id.layer_id == id());
+
+ int render_pass_index = IdToIndex(target_render_pass_id.index);
+ const RenderPass* delegated_render_pass =
+ render_passes_in_draw_order_[render_pass_index];
+ AppendRenderPassQuads(
+ quad_sink, append_quads_data, delegated_render_pass, frame_size);
+ }
+}
+
+void DelegatedRendererLayerImpl::AppendRenderPassQuads(
+ QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data,
+ const RenderPass* delegated_render_pass,
+ gfx::Size frame_size) const {
+
+ const SharedQuadState* delegated_shared_quad_state = NULL;
+ SharedQuadState* output_shared_quad_state = NULL;
+
+ for (size_t i = 0; i < delegated_render_pass->quad_list.size(); ++i) {
+ const DrawQuad* delegated_quad = delegated_render_pass->quad_list[i];
+
+ if (delegated_quad->shared_quad_state != delegated_shared_quad_state) {
+ delegated_shared_quad_state = delegated_quad->shared_quad_state;
+ output_shared_quad_state = quad_sink->UseSharedQuadState(
+ delegated_shared_quad_state->Copy());
+
+ bool is_root_delegated_render_pass =
+ delegated_render_pass == render_passes_in_draw_order_.back();
+ if (is_root_delegated_render_pass) {
+ // Don't allow areas inside the bounds that are empty.
+ DCHECK(display_size_.IsEmpty() ||
+ gfx::Rect(display_size_).Contains(gfx::Rect(bounds())));
+ gfx::Transform delegated_frame_to_target_transform =
+ draw_transform() * DelegatedFrameToLayerSpaceTransform(frame_size);
+
+ output_shared_quad_state->content_to_target_transform.ConcatTransform(
+ delegated_frame_to_target_transform);
+
+ if (render_target() == this) {
+ DCHECK(!is_clipped());
+ DCHECK(render_surface());
+ output_shared_quad_state->clip_rect = MathUtil::MapClippedRect(
+ delegated_frame_to_target_transform,
+ output_shared_quad_state->clip_rect);
+ } else {
+ gfx::Rect clip_rect = drawable_content_rect();
+ if (output_shared_quad_state->is_clipped) {
+ clip_rect.Intersect(MathUtil::MapClippedRect(
+ delegated_frame_to_target_transform,
+ output_shared_quad_state->clip_rect));
+ }
+ output_shared_quad_state->clip_rect = clip_rect;
+ output_shared_quad_state->is_clipped = true;
+ }
+
+ output_shared_quad_state->opacity *= draw_opacity();
+ }
+ }
+ DCHECK(output_shared_quad_state);
+
+ scoped_ptr<DrawQuad> output_quad;
+ if (delegated_quad->material != DrawQuad::RENDER_PASS) {
+ output_quad = delegated_quad->Copy(output_shared_quad_state);
+ } else {
+ RenderPass::Id delegated_contributing_render_pass_id =
+ RenderPassDrawQuad::MaterialCast(delegated_quad)->render_pass_id;
+ RenderPass::Id output_contributing_render_pass_id =
+ ConvertDelegatedRenderPassId(delegated_contributing_render_pass_id);
+ DCHECK(output_contributing_render_pass_id !=
+ append_quads_data->renderPassId);
+
+ output_quad = RenderPassDrawQuad::MaterialCast(delegated_quad)->Copy(
+ output_shared_quad_state,
+ output_contributing_render_pass_id).PassAs<DrawQuad>();
+ }
+ DCHECK(output_quad.get());
+
+ quad_sink->Append(output_quad.Pass(), append_quads_data);
+ }
+}
+
+const char* DelegatedRendererLayerImpl::LayerTypeAsString() const {
+ return "DelegatedRendererLayer";
+}
+
+void DelegatedRendererLayerImpl::CreateChildIdIfNeeded() {
+ if (child_id_)
+ return;
+
+ ResourceProvider* resource_provider = layer_tree_impl()->resource_provider();
+ child_id_ = resource_provider->CreateChild();
+}
+
+void DelegatedRendererLayerImpl::ClearChildId() {
+ if (!child_id_)
+ return;
+
+ ResourceProvider* resource_provider = layer_tree_impl()->resource_provider();
+ resource_provider->DestroyChild(child_id_);
+ child_id_ = 0;
+}
+
+} // namespace cc
diff --git a/cc/layers/delegated_renderer_layer_impl.h b/cc/layers/delegated_renderer_layer_impl.h
new file mode 100644
index 0000000..1f6e214
--- /dev/null
+++ b/cc/layers/delegated_renderer_layer_impl.h
@@ -0,0 +1,89 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_DELEGATED_RENDERER_LAYER_IMPL_H_
+#define CC_LAYERS_DELEGATED_RENDERER_LAYER_IMPL_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/base/scoped_ptr_vector.h"
+#include "cc/layers/layer_impl.h"
+
+namespace cc {
+class DelegatedFrameData;
+
+class CC_EXPORT DelegatedRendererLayerImpl : public LayerImpl {
+ public:
+ static scoped_ptr<DelegatedRendererLayerImpl> Create(
+ LayerTreeImpl* tree_impl, int id) {
+ return make_scoped_ptr(new DelegatedRendererLayerImpl(tree_impl, id));
+ }
+ virtual ~DelegatedRendererLayerImpl();
+
+ // LayerImpl overrides.
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl*) OVERRIDE;
+ virtual bool HasDelegatedContent() const OVERRIDE;
+ virtual bool HasContributingDelegatedRenderPasses() const OVERRIDE;
+ virtual RenderPass::Id FirstContributingRenderPassId() const OVERRIDE;
+ virtual RenderPass::Id NextContributingRenderPassId(
+ RenderPass::Id previous) const OVERRIDE;
+ virtual void DidLoseOutputSurface() OVERRIDE;
+ virtual void AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) OVERRIDE;
+
+ void AppendContributingRenderPasses(RenderPassSink* render_pass_sink);
+
+ void SetFrameData(scoped_ptr<DelegatedFrameData> frame_data,
+ gfx::RectF damage_in_frame,
+ TransferableResourceArray* resources_for_ack);
+
+ void SetDisplaySize(gfx::Size size);
+
+ protected:
+ DelegatedRendererLayerImpl(LayerTreeImpl* tree_impl, int id);
+
+ int ChildIdForTesting() const { return child_id_; }
+ const ScopedPtrVector<RenderPass>& RenderPassesInDrawOrderForTesting() const {
+ return render_passes_in_draw_order_;
+ }
+ const ResourceProvider::ResourceIdSet& ResourcesForTesting() const {
+ return resources_;
+ }
+
+ private:
+ // Creates an ID with the resource provider for the child renderer
+ // that will be sending quads to the layer.
+ void CreateChildIdIfNeeded();
+ void ClearChildId();
+
+ void SetRenderPasses(
+ ScopedPtrVector<RenderPass>* render_passes_in_draw_order);
+ void ClearRenderPasses();
+
+ RenderPass::Id ConvertDelegatedRenderPassId(
+ RenderPass::Id delegated_render_pass_id) const;
+
+ gfx::Transform DelegatedFrameToLayerSpaceTransform(gfx::Size frame_size)
+ const;
+
+ void AppendRenderPassQuads(
+ QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data,
+ const RenderPass* delegated_render_pass,
+ gfx::Size frame_size) const;
+
+ // LayerImpl overrides.
+ virtual const char* LayerTypeAsString() const OVERRIDE;
+
+ ScopedPtrVector<RenderPass> render_passes_in_draw_order_;
+ base::hash_map<RenderPass::Id, int> render_passes_index_by_id_;
+ ResourceProvider::ResourceIdSet resources_;
+
+ gfx::Size display_size_;
+ int child_id_;
+};
+
+}
+
+#endif // CC_LAYERS_DELEGATED_RENDERER_LAYER_IMPL_H_
diff --git a/cc/layers/delegated_renderer_layer_impl_unittest.cc b/cc/layers/delegated_renderer_layer_impl_unittest.cc
new file mode 100644
index 0000000..ca3f833
--- /dev/null
+++ b/cc/layers/delegated_renderer_layer_impl_unittest.cc
@@ -0,0 +1,1255 @@
+// Copyright 2012 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 "cc/layers/delegated_renderer_layer_impl.h"
+
+#include "cc/base/scoped_ptr_vector.h"
+#include "cc/layers/append_quads_data.h"
+#include "cc/layers/quad_sink.h"
+#include "cc/layers/solid_color_layer_impl.h"
+#include "cc/quads/render_pass_draw_quad.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/test/fake_delegated_renderer_layer_impl.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/fake_layer_tree_host_impl_client.h"
+#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_proxy.h"
+#include "cc/test/geometry_test_utils.h"
+#include "cc/test/mock_quad_culler.h"
+#include "cc/test/render_pass_test_common.h"
+#include "cc/test/render_pass_test_utils.h"
+#include "cc/test/test_web_graphics_context_3d.h"
+#include "cc/trees/layer_tree_host_impl.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "cc/trees/single_thread_proxy.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+namespace {
+
+class DelegatedRendererLayerImplTest : public testing::Test {
+ public:
+ DelegatedRendererLayerImplTest()
+ : proxy_(scoped_ptr<Thread>(NULL))
+ , always_impl_thread_and_main_thread_blocked_(&proxy_) {
+ LayerTreeSettings settings;
+ settings.minimumOcclusionTrackingSize = gfx::Size();
+
+ host_impl_ = LayerTreeHostImpl::Create(settings, &client_, &proxy_);
+ host_impl_->InitializeRenderer(createFakeOutputSurface());
+ host_impl_->SetViewportSize(gfx::Size(10, 10), gfx::Size(10, 10));
+ }
+
+ protected:
+ FakeProxy proxy_;
+ FakeLayerTreeHostImplClient client_;
+ DebugScopedSetImplThreadAndMainThreadBlocked
+ always_impl_thread_and_main_thread_blocked_;
+ scoped_ptr<LayerTreeHostImpl> host_impl_;
+};
+
+class DelegatedRendererLayerImplTestSimple
+ : public DelegatedRendererLayerImplTest {
+ public:
+ DelegatedRendererLayerImplTestSimple()
+ : DelegatedRendererLayerImplTest() {
+ scoped_ptr<LayerImpl> root_layer = SolidColorLayerImpl::Create(
+ host_impl_->active_tree(), 1).PassAs<LayerImpl>();
+ scoped_ptr<LayerImpl> layer_before = SolidColorLayerImpl::Create(
+ host_impl_->active_tree(), 2).PassAs<LayerImpl>();
+ scoped_ptr<LayerImpl> layer_after = SolidColorLayerImpl::Create(
+ host_impl_->active_tree(), 3).PassAs<LayerImpl>();
+ scoped_ptr<FakeDelegatedRendererLayerImpl> delegated_renderer_layer =
+ FakeDelegatedRendererLayerImpl::Create(host_impl_->active_tree(), 4);
+
+ host_impl_->SetViewportSize(gfx::Size(100, 100), gfx::Size(100, 100));
+ root_layer->SetBounds(gfx::Size(100, 100));
+
+ layer_before->SetPosition(gfx::Point(20, 20));
+ layer_before->SetBounds(gfx::Size(14, 14));
+ layer_before->SetContentBounds(gfx::Size(14, 14));
+ layer_before->SetDrawsContent(true);
+ layer_before->SetForceRenderSurface(true);
+
+ layer_after->SetPosition(gfx::Point(5, 5));
+ layer_after->SetBounds(gfx::Size(15, 15));
+ layer_after->SetContentBounds(gfx::Size(15, 15));
+ layer_after->SetDrawsContent(true);
+ layer_after->SetForceRenderSurface(true);
+
+ delegated_renderer_layer->SetPosition(gfx::Point(3, 3));
+ delegated_renderer_layer->SetBounds(gfx::Size(10, 10));
+ delegated_renderer_layer->SetContentBounds(gfx::Size(10, 10));
+ delegated_renderer_layer->SetDrawsContent(true);
+ gfx::Transform transform;
+ transform.Translate(1.0, 1.0);
+ delegated_renderer_layer->SetTransform(transform);
+
+ ScopedPtrVector<RenderPass> delegated_render_passes;
+ TestRenderPass* pass1 = addRenderPass(
+ delegated_render_passes,
+ RenderPass::Id(9, 6),
+ gfx::Rect(6, 6, 6, 6),
+ gfx::Transform());
+ addQuad(pass1, gfx::Rect(0, 0, 6, 6), 33u);
+ TestRenderPass* pass2 = addRenderPass(
+ delegated_render_passes,
+ RenderPass::Id(9, 7),
+ gfx::Rect(7, 7, 7, 7),
+ gfx::Transform());
+ addQuad(pass2, gfx::Rect(0, 0, 7, 7), 22u);
+ addRenderPassQuad(pass2, pass1);
+ TestRenderPass* pass3 = addRenderPass(
+ delegated_render_passes,
+ RenderPass::Id(9, 8),
+ gfx::Rect(0, 0, 8, 8),
+ gfx::Transform());
+ addRenderPassQuad(pass3, pass2);
+ delegated_renderer_layer->SetFrameDataForRenderPasses(
+ &delegated_render_passes);
+
+ // The RenderPasses should be taken by the layer.
+ EXPECT_EQ(0u, delegated_render_passes.size());
+
+ root_layer_ = root_layer.get();
+ layer_before_ = layer_before.get();
+ layer_after_ = layer_after.get();
+ delegated_renderer_layer_ = delegated_renderer_layer.get();
+
+ // Force the delegated RenderPasses to come before the RenderPass from
+ // layer_after.
+ layer_after->AddChild(delegated_renderer_layer.PassAs<LayerImpl>());
+ root_layer->AddChild(layer_after.Pass());
+
+ // Get the RenderPass generated by layer_before to come before the delegated
+ // RenderPasses.
+ root_layer->AddChild(layer_before.Pass());
+ host_impl_->active_tree()->SetRootLayer(root_layer.Pass());
+ }
+
+ protected:
+ LayerImpl* root_layer_;
+ LayerImpl* layer_before_;
+ LayerImpl* layer_after_;
+ DelegatedRendererLayerImpl* delegated_renderer_layer_;
+};
+
+TEST_F(DelegatedRendererLayerImplTestSimple, AddsContributingRenderPasses) {
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ // Each non-DelegatedRendererLayer added one RenderPass. The
+ // DelegatedRendererLayer added two contributing passes.
+ ASSERT_EQ(5u, frame.render_passes.size());
+
+ // The DelegatedRendererLayer should have added its contributing RenderPasses
+ // to the frame.
+ EXPECT_EQ(4, frame.render_passes[1]->id.layer_id);
+ EXPECT_EQ(1, frame.render_passes[1]->id.index);
+ EXPECT_EQ(4, frame.render_passes[2]->id.layer_id);
+ EXPECT_EQ(2, frame.render_passes[2]->id.index);
+ // And all other RenderPasses should be non-delegated.
+ EXPECT_NE(4, frame.render_passes[0]->id.layer_id);
+ EXPECT_EQ(0, frame.render_passes[0]->id.index);
+ EXPECT_NE(4, frame.render_passes[3]->id.layer_id);
+ EXPECT_EQ(0, frame.render_passes[3]->id.index);
+ EXPECT_NE(4, frame.render_passes[4]->id.layer_id);
+ EXPECT_EQ(0, frame.render_passes[4]->id.index);
+
+ // The DelegatedRendererLayer should have added its RenderPasses to the frame
+ // in order.
+ EXPECT_EQ(gfx::Rect(6, 6, 6, 6).ToString(),
+ frame.render_passes[1]->output_rect.ToString());
+ EXPECT_EQ(gfx::Rect(7, 7, 7, 7).ToString(),
+ frame.render_passes[2]->output_rect.ToString());
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestSimple,
+ AddsQuadsToContributingRenderPasses) {
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ // Each non-DelegatedRendererLayer added one RenderPass. The
+ // DelegatedRendererLayer added two contributing passes.
+ ASSERT_EQ(5u, frame.render_passes.size());
+
+ // The DelegatedRendererLayer should have added its contributing RenderPasses
+ // to the frame.
+ EXPECT_EQ(4, frame.render_passes[1]->id.layer_id);
+ EXPECT_EQ(1, frame.render_passes[1]->id.index);
+ EXPECT_EQ(4, frame.render_passes[2]->id.layer_id);
+ EXPECT_EQ(2, frame.render_passes[2]->id.index);
+
+ // The DelegatedRendererLayer should have added copies of its quads to
+ // contributing RenderPasses.
+ ASSERT_EQ(1u, frame.render_passes[1]->quad_list.size());
+ EXPECT_EQ(gfx::Rect(0, 0, 6, 6).ToString(),
+ frame.render_passes[1]->quad_list[0]->rect.ToString());
+
+ // Verify it added the right quads.
+ ASSERT_EQ(2u, frame.render_passes[2]->quad_list.size());
+ EXPECT_EQ(gfx::Rect(0, 0, 7, 7).ToString(),
+ frame.render_passes[2]->quad_list[0]->rect.ToString());
+ EXPECT_EQ(gfx::Rect(6, 6, 6, 6).ToString(),
+ frame.render_passes[2]->quad_list[1]->rect.ToString());
+ ASSERT_EQ(1u, frame.render_passes[1]->quad_list.size());
+ EXPECT_EQ(gfx::Rect(0, 0, 6, 6).ToString(),
+ frame.render_passes[1]->quad_list[0]->rect.ToString());
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestSimple, AddsQuadsToTargetRenderPass) {
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ // Each non-DelegatedRendererLayer added one RenderPass. The
+ // DelegatedRendererLayer added two contributing passes.
+ ASSERT_EQ(5u, frame.render_passes.size());
+
+ // The layer's target is the RenderPass from m_layer_after.
+ EXPECT_EQ(RenderPass::Id(3, 0), frame.render_passes[3]->id);
+
+ // The DelegatedRendererLayer should have added copies of quads in its root
+ // RenderPass to its target RenderPass. The m_layer_after also adds one quad.
+ ASSERT_EQ(2u, frame.render_passes[3]->quad_list.size());
+
+ // Verify it added the right quads.
+ EXPECT_EQ(gfx::Rect(7, 7, 7, 7).ToString(),
+ frame.render_passes[3]->quad_list[0]->rect.ToString());
+
+ // Its target layer should have a quad as well.
+ EXPECT_EQ(gfx::Rect(0, 0, 15, 15).ToString(),
+ frame.render_passes[3]->quad_list[1]->rect.ToString());
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestSimple,
+ QuadsFromRootRenderPassAreModifiedForTheTarget) {
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ // Each non-DelegatedRendererLayer added one RenderPass. The
+ // DelegatedRendererLayer added two contributing passes.
+ ASSERT_EQ(5u, frame.render_passes.size());
+
+ // The DelegatedRendererLayer is at position 3,3 compared to its target, and
+ // has a translation transform of 1,1. So its root RenderPass' quads should
+ // all be transformed by that combined amount.
+ // The DelegatedRendererLayer has a size of 10x10, but the root delegated
+ // RenderPass has a size of 8x8, so any quads should be scaled by 10/8.
+ gfx::Transform transform;
+ transform.Translate(4.0, 4.0);
+ transform.Scale(10.0 / 8.0, 10.0 / 8.0);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ transform, frame.render_passes[3]->quad_list[0]->quadTransform());
+
+ // Quads from non-root RenderPasses should not be shifted though.
+ ASSERT_EQ(2u, frame.render_passes[2]->quad_list.size());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ gfx::Transform(), frame.render_passes[2]->quad_list[0]->quadTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ gfx::Transform(), frame.render_passes[2]->quad_list[1]->quadTransform());
+ ASSERT_EQ(1u, frame.render_passes[1]->quad_list.size());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ gfx::Transform(), frame.render_passes[1]->quad_list[0]->quadTransform());
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestSimple, DoesNotOwnARenderSurface) {
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ // If the DelegatedRendererLayer is axis aligned and has opacity 1, then it
+ // has no need to be a renderSurface for the quads it carries.
+ EXPECT_FALSE(delegated_renderer_layer_->render_surface());
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestSimple, DoesOwnARenderSurfaceForOpacity) {
+ delegated_renderer_layer_->SetOpacity(0.5f);
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ // This test case has quads from multiple layers in the delegated renderer, so
+ // if the DelegatedRendererLayer has opacity < 1, it should end up with a
+ // render surface.
+ EXPECT_TRUE(delegated_renderer_layer_->render_surface());
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestSimple,
+ DoesOwnARenderSurfaceForTransform) {
+ gfx::Transform rotation;
+ rotation.RotateAboutZAxis(30.0);
+ delegated_renderer_layer_->SetTransform(rotation);
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ // This test case has quads from multiple layers in the delegated renderer, so
+ // if the DelegatedRendererLayer has opacity < 1, it should end up with a
+ // render surface.
+ EXPECT_TRUE(delegated_renderer_layer_->render_surface());
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+class DelegatedRendererLayerImplTestOwnSurface
+ : public DelegatedRendererLayerImplTestSimple {
+ public:
+ DelegatedRendererLayerImplTestOwnSurface()
+ : DelegatedRendererLayerImplTestSimple() {
+ delegated_renderer_layer_->SetForceRenderSurface(true);
+ }
+};
+
+TEST_F(DelegatedRendererLayerImplTestOwnSurface, AddsRenderPasses) {
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ // Each non-DelegatedRendererLayer added one RenderPass. The
+ // DelegatedRendererLayer added two contributing passes and its owned surface
+ // added one pass.
+ ASSERT_EQ(6u, frame.render_passes.size());
+
+ // The DelegatedRendererLayer should have added its contributing RenderPasses
+ // to the frame.
+ EXPECT_EQ(4, frame.render_passes[1]->id.layer_id);
+ EXPECT_EQ(1, frame.render_passes[1]->id.index);
+ EXPECT_EQ(4, frame.render_passes[2]->id.layer_id);
+ EXPECT_EQ(2, frame.render_passes[2]->id.index);
+ // The DelegatedRendererLayer should have added a RenderPass for its surface
+ // to the frame.
+ EXPECT_EQ(4, frame.render_passes[1]->id.layer_id);
+ EXPECT_EQ(0, frame.render_passes[3]->id.index);
+ // And all other RenderPasses should be non-delegated.
+ EXPECT_NE(4, frame.render_passes[0]->id.layer_id);
+ EXPECT_EQ(0, frame.render_passes[0]->id.index);
+ EXPECT_NE(4, frame.render_passes[4]->id.layer_id);
+ EXPECT_EQ(0, frame.render_passes[4]->id.index);
+ EXPECT_NE(4, frame.render_passes[5]->id.layer_id);
+ EXPECT_EQ(0, frame.render_passes[5]->id.index);
+
+ // The DelegatedRendererLayer should have added its RenderPasses to the frame
+ // in order.
+ EXPECT_EQ(gfx::Rect(6, 6, 6, 6).ToString(),
+ frame.render_passes[1]->output_rect.ToString());
+ EXPECT_EQ(gfx::Rect(7, 7, 7, 7).ToString(),
+ frame.render_passes[2]->output_rect.ToString());
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestOwnSurface,
+ AddsQuadsToContributingRenderPasses) {
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ // Each non-DelegatedRendererLayer added one RenderPass. The
+ // DelegatedRendererLayer added two contributing passes and its owned surface
+ // added one pass.
+ ASSERT_EQ(6u, frame.render_passes.size());
+
+ // The DelegatedRendererLayer should have added its contributing RenderPasses
+ // to the frame.
+ EXPECT_EQ(4, frame.render_passes[1]->id.layer_id);
+ EXPECT_EQ(1, frame.render_passes[1]->id.index);
+ EXPECT_EQ(4, frame.render_passes[2]->id.layer_id);
+ EXPECT_EQ(2, frame.render_passes[2]->id.index);
+
+ // The DelegatedRendererLayer should have added copies of its quads to
+ // contributing RenderPasses.
+ ASSERT_EQ(1u, frame.render_passes[1]->quad_list.size());
+ EXPECT_EQ(gfx::Rect(0, 0, 6, 6).ToString(),
+ frame.render_passes[1]->quad_list[0]->rect.ToString());
+
+ // Verify it added the right quads.
+ ASSERT_EQ(2u, frame.render_passes[2]->quad_list.size());
+ EXPECT_EQ(gfx::Rect(0, 0, 7, 7).ToString(),
+ frame.render_passes[2]->quad_list[0]->rect.ToString());
+ EXPECT_EQ(gfx::Rect(6, 6, 6, 6).ToString(),
+ frame.render_passes[2]->quad_list[1]->rect.ToString());
+ ASSERT_EQ(1u, frame.render_passes[1]->quad_list.size());
+ EXPECT_EQ(gfx::Rect(0, 0, 6, 6).ToString(),
+ frame.render_passes[1]->quad_list[0]->rect.ToString());
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestOwnSurface, AddsQuadsToTargetRenderPass) {
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ // Each non-DelegatedRendererLayer added one RenderPass. The
+ // DelegatedRendererLayer added two contributing passes and its owned surface
+ // added one pass.
+ ASSERT_EQ(6u, frame.render_passes.size());
+
+ // The layer's target is the RenderPass owned by itself.
+ EXPECT_EQ(RenderPass::Id(4, 0), frame.render_passes[3]->id);
+
+ // The DelegatedRendererLayer should have added copies of quads in its root
+ // RenderPass to its target RenderPass.
+ // The m_layer_after also adds one quad.
+ ASSERT_EQ(1u, frame.render_passes[3]->quad_list.size());
+
+ // Verify it added the right quads.
+ EXPECT_EQ(gfx::Rect(7, 7, 7, 7).ToString(),
+ frame.render_passes[3]->quad_list[0]->rect.ToString());
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestOwnSurface,
+ QuadsFromRootRenderPassAreNotModifiedForTheTarget) {
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ // Each non-DelegatedRendererLayer added one RenderPass. The
+ // DelegatedRendererLayer added two contributing passes and its owned surface
+ // added one pass.
+ ASSERT_EQ(6u, frame.render_passes.size());
+
+ // Because the DelegatedRendererLayer owns a RenderSurfaceImpl, its root
+ // RenderPass' quads do not need to be translated at all. However, they are
+ // scaled from the frame's size (8x8) to the layer's bounds (10x10).
+ gfx::Transform transform;
+ transform.Scale(10.0 / 8.0, 10.0 / 8.0);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ transform, frame.render_passes[3]->quad_list[0]->quadTransform());
+
+ // Quads from non-root RenderPasses should not be shifted either.
+ ASSERT_EQ(2u, frame.render_passes[2]->quad_list.size());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ gfx::Transform(), frame.render_passes[2]->quad_list[0]->quadTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ gfx::Transform(), frame.render_passes[2]->quad_list[1]->quadTransform());
+ ASSERT_EQ(1u, frame.render_passes[1]->quad_list.size());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ gfx::Transform(), frame.render_passes[1]->quad_list[0]->quadTransform());
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+class DelegatedRendererLayerImplTestTransform
+ : public DelegatedRendererLayerImplTest {
+ public:
+ void SetUpTest() {
+ scoped_ptr<LayerImpl> root_layer = LayerImpl::Create(
+ host_impl_->active_tree(), 1);
+ scoped_ptr<FakeDelegatedRendererLayerImpl> delegated_renderer_layer =
+ FakeDelegatedRendererLayerImpl::Create(host_impl_->active_tree(), 2);
+
+ host_impl_->SetViewportSize(gfx::Size(100, 100), gfx::Size(100, 100));
+ root_layer->SetBounds(gfx::Size(100, 100));
+
+ delegated_renderer_layer->SetPosition(gfx::Point(20, 20));
+ delegated_renderer_layer->SetBounds(gfx::Size(30, 30));
+ delegated_renderer_layer->SetContentBounds(gfx::Size(30, 30));
+ delegated_renderer_layer->SetDrawsContent(true);
+ gfx::Transform transform;
+ transform.Scale(2.0, 2.0);
+ transform.Translate(8.0, 8.0);
+ delegated_renderer_layer->SetTransform(transform);
+
+ ScopedPtrVector<RenderPass> delegated_render_passes;
+
+ gfx::Size child_pass_content_bounds(7, 7);
+ gfx::Rect child_pass_rect(20, 20, 7, 7);
+ gfx::Transform child_pass_transform;
+ child_pass_transform.Scale(0.8, 0.8);
+ child_pass_transform.Translate(9.0, 9.0);
+ gfx::Rect child_pass_clip_rect(21, 21, 3, 3);
+ bool child_pass_clipped = false;
+
+ {
+ TestRenderPass* pass = addRenderPass(
+ delegated_render_passes,
+ RenderPass::Id(10, 7),
+ child_pass_rect,
+ gfx::Transform());
+ MockQuadCuller quad_sink(pass->quad_list, pass->shared_quad_state_list);
+ AppendQuadsData data(pass->id);
+ SharedQuadState* shared_quad_state = quad_sink.UseSharedQuadState(
+ SharedQuadState::Create());
+ shared_quad_state->SetAll(
+ child_pass_transform,
+ child_pass_content_bounds,
+ child_pass_rect,
+ child_pass_clip_rect,
+ child_pass_clipped,
+ 1.f);
+
+ scoped_ptr<SolidColorDrawQuad> color_quad;
+ color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_quad_state, gfx::Rect(20, 20, 3, 7), 1u);
+ quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data);
+
+ color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_quad_state, gfx::Rect(23, 20, 4, 7), 1u);
+ quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data);
+ }
+
+ gfx::Size root_pass_content_bounds(50, 50);
+ gfx::Rect root_pass_rect(0, 0, 50, 50);
+ gfx::Transform root_pass_transform;
+ root_pass_transform.Scale(1.5, 1.5);
+ root_pass_transform.Translate(7.0, 7.0);
+ gfx::Rect root_pass_clip_rect(10, 10, 35, 35);
+ bool root_pass_clipped = root_delegated_render_pass_is_clipped_;
+
+ TestRenderPass* pass = addRenderPass(
+ delegated_render_passes,
+ RenderPass::Id(9, 6),
+ root_pass_rect,
+ gfx::Transform());
+ MockQuadCuller quad_sink(pass->quad_list, pass->shared_quad_state_list);
+ AppendQuadsData data(pass->id);
+ SharedQuadState* shared_quad_state =
+ quad_sink.UseSharedQuadState(SharedQuadState::Create());
+ shared_quad_state->SetAll(
+ root_pass_transform,
+ root_pass_content_bounds,
+ root_pass_rect,
+ root_pass_clip_rect,
+ root_pass_clipped,
+ 1.f);
+
+ scoped_ptr<RenderPassDrawQuad> render_pass_quad =
+ RenderPassDrawQuad::Create();
+ render_pass_quad->SetNew(
+ shared_quad_state,
+ gfx::Rect(5, 5, 7, 7), // rect
+ RenderPass::Id(10, 7), // render_pass_id
+ false, // is_replica
+ 0, // mask_resource_id
+ child_pass_rect, // contents_changed_since_last_frame
+ gfx::RectF(), // mask_uv_rect
+ WebKit::WebFilterOperations(), // filters
+ skia::RefPtr<SkImageFilter>(), // filter
+ WebKit::WebFilterOperations()); // background_filters
+ quad_sink.Append(render_pass_quad.PassAs<DrawQuad>(), &data);
+
+ scoped_ptr<SolidColorDrawQuad> color_quad;
+ color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_quad_state, gfx::Rect(0, 0, 10, 10), 1u);
+ quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data);
+
+ color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_quad_state, gfx::Rect(0, 10, 10, 10), 2u);
+ quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data);
+
+ color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_quad_state, gfx::Rect(10, 0, 10, 10), 3u);
+ quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data);
+
+ color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_quad_state, gfx::Rect(10, 10, 10, 10), 4u);
+ quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data);
+
+ delegated_renderer_layer->SetFrameDataForRenderPasses(
+ &delegated_render_passes);
+
+ // The RenderPasses should be taken by the layer.
+ EXPECT_EQ(0u, delegated_render_passes.size());
+
+ root_layer_ = root_layer.get();
+ delegated_renderer_layer_ = delegated_renderer_layer.get();
+
+ root_layer->AddChild(delegated_renderer_layer.PassAs<LayerImpl>());
+ host_impl_->active_tree()->SetRootLayer(root_layer.Pass());
+ }
+
+ void VerifyRenderPasses(
+ const LayerTreeHostImpl::FrameData& frame,
+ size_t num_render_passes,
+ const SharedQuadState** root_delegated_shared_quad_state,
+ const SharedQuadState** contrib_delegated_shared_quad_state) {
+ ASSERT_EQ(num_render_passes, frame.render_passes.size());
+ // The contributing render pass in the DelegatedRendererLayer.
+ EXPECT_EQ(2, frame.render_passes[0]->id.layer_id);
+ EXPECT_EQ(1, frame.render_passes[0]->id.index);
+ // The root render pass.
+ EXPECT_EQ(1, frame.render_passes.back()->id.layer_id);
+ EXPECT_EQ(0, frame.render_passes.back()->id.index);
+
+ const QuadList& contrib_delegated_quad_list =
+ frame.render_passes[0]->quad_list;
+ ASSERT_EQ(2u, contrib_delegated_quad_list.size());
+
+ const QuadList& root_delegated_quad_list =
+ frame.render_passes[1]->quad_list;
+ ASSERT_EQ(5u, root_delegated_quad_list.size());
+
+ // All quads in a render pass should share the same state.
+ *contrib_delegated_shared_quad_state =
+ contrib_delegated_quad_list[0]->shared_quad_state;
+ EXPECT_EQ(*contrib_delegated_shared_quad_state,
+ contrib_delegated_quad_list[1]->shared_quad_state);
+
+ *root_delegated_shared_quad_state =
+ root_delegated_quad_list[0]->shared_quad_state;
+ EXPECT_EQ(*root_delegated_shared_quad_state,
+ root_delegated_quad_list[1]->shared_quad_state);
+ EXPECT_EQ(*root_delegated_shared_quad_state,
+ root_delegated_quad_list[2]->shared_quad_state);
+ EXPECT_EQ(*root_delegated_shared_quad_state,
+ root_delegated_quad_list[3]->shared_quad_state);
+ EXPECT_EQ(*root_delegated_shared_quad_state,
+ root_delegated_quad_list[4]->shared_quad_state);
+
+ EXPECT_NE(*contrib_delegated_shared_quad_state,
+ *root_delegated_shared_quad_state);
+ }
+
+ protected:
+ LayerImpl* root_layer_;
+ DelegatedRendererLayerImpl* delegated_renderer_layer_;
+ bool root_delegated_render_pass_is_clipped_;
+};
+
+TEST_F(DelegatedRendererLayerImplTestTransform, QuadsUnclipped_NoSurface) {
+ root_delegated_render_pass_is_clipped_ = false;
+ SetUpTest();
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ const SharedQuadState* root_delegated_shared_quad_state = NULL;
+ const SharedQuadState* contrib_delegated_shared_quad_state = NULL;
+ VerifyRenderPasses(
+ frame,
+ 2,
+ &root_delegated_shared_quad_state,
+ &contrib_delegated_shared_quad_state);
+
+ // When the quads don't have a clip of their own, the clip rect is set to
+ // the drawableContentRect of the delegated renderer layer.
+ EXPECT_EQ(gfx::Rect(21, 21, 60, 60).ToString(),
+ root_delegated_shared_quad_state->clip_rect.ToString());
+
+ // Even though the quads in the root pass have no clip of their own, they
+ // inherit the clip rect from the delegated renderer layer if it does not
+ // own a surface.
+ EXPECT_TRUE(root_delegated_shared_quad_state->is_clipped);
+
+ gfx::Transform expected;
+ // This is the transform from the layer's space to its target.
+ // The position (20) - the width / scale (30 / 2) = 20 - 15 = 5
+ expected.Translate(5.0, 5.0);
+ expected.Scale(2.0, 2.0);
+ expected.Translate(8.0, 8.0);
+ // The frame has size 50x50 but the layer's bounds are 30x30.
+ expected.Scale(30.0 / 50.0, 30.0 / 50.0);
+ // This is the transform within the source frame.
+ expected.Scale(1.5, 1.5);
+ expected.Translate(7.0, 7.0);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ expected, root_delegated_shared_quad_state->content_to_target_transform);
+
+ // The contributing render pass should not be transformed from its input.
+ EXPECT_EQ(gfx::Rect(21, 21, 3, 3).ToString(),
+ contrib_delegated_shared_quad_state->clip_rect.ToString());
+ EXPECT_FALSE(contrib_delegated_shared_quad_state->is_clipped);
+ expected.MakeIdentity();
+ expected.Scale(0.8, 0.8);
+ expected.Translate(9.0, 9.0);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ expected,
+ contrib_delegated_shared_quad_state->content_to_target_transform);
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestTransform, QuadsClipped_NoSurface) {
+ root_delegated_render_pass_is_clipped_ = true;
+ SetUpTest();
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ const SharedQuadState* root_delegated_shared_quad_state = NULL;
+ const SharedQuadState* contrib_delegated_shared_quad_state = NULL;
+ VerifyRenderPasses(
+ frame,
+ 2,
+ &root_delegated_shared_quad_state,
+ &contrib_delegated_shared_quad_state);
+
+ // Since the quads have a clip_rect it should be modified by delegated
+ // renderer layer's drawTransform.
+ // The position of the resulting clip_rect is:
+ // (clip rect position (10) * scale to layer (30/50) + translate (8)) *
+ // layer scale (2) + layer position (20) = 48
+ // But the layer is centered, so: 48 - (width / 2) = 48 - 30 / 2 = 33
+ //
+ // The size is 35x35 scaled to fit inside the layer's bounds at 30x30 from
+ // a frame at 50x50: 35 * 2 (layer's scale) * 30 / 50 = 42.
+ EXPECT_EQ(gfx::Rect(33, 33, 42, 42).ToString(),
+ root_delegated_shared_quad_state->clip_rect.ToString());
+
+ // The quads had a clip and it should be preserved.
+ EXPECT_TRUE(root_delegated_shared_quad_state->is_clipped);
+
+ gfx::Transform expected;
+ // This is the transform from the layer's space to its target.
+ // The position (20) - the width / scale (30 / 2) = 20 - 15 = 5
+ expected.Translate(5.0, 5.0);
+ expected.Scale(2.0, 2.0);
+ expected.Translate(8.0, 8.0);
+ // The frame has size 50x50 but the layer's bounds are 30x30.
+ expected.Scale(30.0 / 50.0, 30.0 / 50.0);
+ // This is the transform within the source frame.
+ expected.Scale(1.5, 1.5);
+ expected.Translate(7.0, 7.0);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ expected, root_delegated_shared_quad_state->content_to_target_transform);
+
+ // The contributing render pass should not be transformed from its input.
+ EXPECT_EQ(gfx::Rect(21, 21, 3, 3).ToString(),
+ contrib_delegated_shared_quad_state->clip_rect.ToString());
+ EXPECT_FALSE(contrib_delegated_shared_quad_state->is_clipped);
+ expected.MakeIdentity();
+ expected.Scale(0.8, 0.8);
+ expected.Translate(9.0, 9.0);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ expected,
+ contrib_delegated_shared_quad_state->content_to_target_transform);
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestTransform, QuadsUnclipped_Surface) {
+ root_delegated_render_pass_is_clipped_ = false;
+ SetUpTest();
+
+ delegated_renderer_layer_->SetForceRenderSurface(true);
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ const SharedQuadState* root_delegated_shared_quad_state = NULL;
+ const SharedQuadState* contrib_delegated_shared_quad_state = NULL;
+ VerifyRenderPasses(
+ frame,
+ 3,
+ &root_delegated_shared_quad_state,
+ &contrib_delegated_shared_quad_state);
+
+ // When the layer owns a surface, then its position and translation are not
+ // a part of its draw transform.
+ // The position of the resulting clip_rect is:
+ // (clip rect position (10) * scale to layer (30/50)) * layer scale (2) = 12
+ // The size is 35x35 scaled to fit inside the layer's bounds at 30x30 from
+ // a frame at 50x50: 35 * 2 (layer's scale) * 30 / 50 = 42.
+ EXPECT_EQ(gfx::Rect(12, 12, 42, 42).ToString(),
+ root_delegated_shared_quad_state->clip_rect.ToString());
+
+ // Since the layer owns a surface it doesn't need to clip its quads, so
+ // unclipped quads remain unclipped.
+ EXPECT_FALSE(root_delegated_shared_quad_state->is_clipped);
+
+ gfx::Transform expected;
+ expected.Scale(2.0, 2.0);
+ // The frame has size 50x50 but the layer's bounds are 30x30.
+ expected.Scale(30.0 / 50.0, 30.0 / 50.0);
+ // This is the transform within the source frame.
+ expected.Scale(1.5, 1.5);
+ expected.Translate(7.0, 7.0);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ expected, root_delegated_shared_quad_state->content_to_target_transform);
+
+ // The contributing render pass should not be transformed from its input.
+ EXPECT_EQ(gfx::Rect(21, 21, 3, 3).ToString(),
+ contrib_delegated_shared_quad_state->clip_rect.ToString());
+ EXPECT_FALSE(contrib_delegated_shared_quad_state->is_clipped);
+ expected.MakeIdentity();
+ expected.Scale(0.8, 0.8);
+ expected.Translate(9.0, 9.0);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ expected,
+ contrib_delegated_shared_quad_state->content_to_target_transform);
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestTransform, QuadsClipped_Surface) {
+ root_delegated_render_pass_is_clipped_ = true;
+ SetUpTest();
+
+ delegated_renderer_layer_->SetForceRenderSurface(true);
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ const SharedQuadState* root_delegated_shared_quad_state = NULL;
+ const SharedQuadState* contrib_delegated_shared_quad_state = NULL;
+ VerifyRenderPasses(
+ frame,
+ 3,
+ &root_delegated_shared_quad_state,
+ &contrib_delegated_shared_quad_state);
+
+ // When the layer owns a surface, then its position and translation are not
+ // a part of its draw transform.
+ // The position of the resulting clip_rect is:
+ // (clip rect position (10) * scale to layer (30/50)) * layer scale (2) = 12
+ // The size is 35x35 scaled to fit inside the layer's bounds at 30x30 from
+ // a frame at 50x50: 35 * 2 (layer's scale) * 30 / 50 = 42.
+ EXPECT_EQ(gfx::Rect(12, 12, 42, 42).ToString(),
+ root_delegated_shared_quad_state->clip_rect.ToString());
+
+ // The quads had a clip and it should be preserved.
+ EXPECT_TRUE(root_delegated_shared_quad_state->is_clipped);
+
+ gfx::Transform expected;
+ expected.Scale(2.0, 2.0);
+ // The frame has size 50x50 but the layer's bounds are 30x30.
+ expected.Scale(30.0 / 50.0, 30.0 / 50.0);
+ // This is the transform within the source frame.
+ expected.Scale(1.5, 1.5);
+ expected.Translate(7.0, 7.0);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ expected, root_delegated_shared_quad_state->content_to_target_transform);
+
+ // The contributing render pass should not be transformed from its input.
+ EXPECT_EQ(gfx::Rect(21, 21, 3, 3).ToString(),
+ contrib_delegated_shared_quad_state->clip_rect.ToString());
+ EXPECT_FALSE(contrib_delegated_shared_quad_state->is_clipped);
+ expected.MakeIdentity();
+ expected.Scale(0.8, 0.8);
+ expected.Translate(9.0, 9.0);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ expected,
+ contrib_delegated_shared_quad_state->content_to_target_transform);
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+class DelegatedRendererLayerImplTestClip
+ : public DelegatedRendererLayerImplTest {
+ public:
+ void SetUpTest() {
+ scoped_ptr<LayerImpl> root_layer =
+ LayerImpl::Create(host_impl_->active_tree(), 1);
+ scoped_ptr<FakeDelegatedRendererLayerImpl> delegated_renderer_layer =
+ FakeDelegatedRendererLayerImpl::Create(host_impl_->active_tree(), 2);
+ scoped_ptr<LayerImpl> clip_layer =
+ LayerImpl::Create(host_impl_->active_tree(), 3);
+ scoped_ptr<LayerImpl> origin_layer =
+ LayerImpl::Create(host_impl_->active_tree(), 4);
+
+ host_impl_->SetViewportSize(gfx::Size(100, 100), gfx::Size(100, 100));
+ root_layer->SetBounds(gfx::Size(100, 100));
+
+ delegated_renderer_layer->SetPosition(gfx::Point(20, 20));
+ delegated_renderer_layer->SetBounds(gfx::Size(50, 50));
+ delegated_renderer_layer->SetContentBounds(gfx::Size(50, 50));
+ delegated_renderer_layer->SetDrawsContent(true);
+
+ ScopedPtrVector<RenderPass> delegated_render_passes;
+
+ gfx::Size child_pass_content_bounds(7, 7);
+ gfx::Rect child_pass_rect(20, 20, 7, 7);
+ gfx::Transform child_pass_transform;
+ gfx::Rect child_pass_clip_rect(21, 21, 3, 3);
+ bool child_pass_clipped = false;
+
+ {
+ TestRenderPass* pass = addRenderPass(
+ delegated_render_passes,
+ RenderPass::Id(10, 7),
+ child_pass_rect,
+ gfx::Transform());
+ MockQuadCuller quad_sink(pass->quad_list, pass->shared_quad_state_list);
+ AppendQuadsData data(pass->id);
+ SharedQuadState* shared_quad_state =
+ quad_sink.UseSharedQuadState(SharedQuadState::Create());
+ shared_quad_state->SetAll(
+ child_pass_transform,
+ child_pass_content_bounds,
+ child_pass_rect,
+ child_pass_clip_rect,
+ child_pass_clipped,
+ 1.f);
+
+ scoped_ptr<SolidColorDrawQuad> color_quad;
+ color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_quad_state, gfx::Rect(20, 20, 3, 7), 1u);
+ quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data);
+
+ color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_quad_state, gfx::Rect(23, 20, 4, 7), 1u);
+ quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data);
+ }
+
+ gfx::Size root_pass_content_bounds(50, 50);
+ gfx::Rect root_pass_rect(0, 0, 50, 50);
+ gfx::Transform root_pass_transform;
+ gfx::Rect root_pass_clip_rect(5, 5, 40, 40);
+ bool root_pass_clipped = root_delegated_render_pass_is_clipped_;
+
+ TestRenderPass* pass = addRenderPass(
+ delegated_render_passes,
+ RenderPass::Id(9, 6),
+ root_pass_rect,
+ gfx::Transform());
+ MockQuadCuller quad_sink(pass->quad_list, pass->shared_quad_state_list);
+ AppendQuadsData data(pass->id);
+ SharedQuadState* shared_quad_state =
+ quad_sink.UseSharedQuadState(SharedQuadState::Create());
+ shared_quad_state->SetAll(root_pass_transform,
+ root_pass_content_bounds,
+ root_pass_rect,
+ root_pass_clip_rect,
+ root_pass_clipped,
+ 1.f);
+
+ scoped_ptr<RenderPassDrawQuad> render_pass_quad =
+ RenderPassDrawQuad::Create();
+ render_pass_quad->SetNew(
+ shared_quad_state,
+ gfx::Rect(5, 5, 7, 7), // rect
+ RenderPass::Id(10, 7), // render_pass_id
+ false, // is_replica
+ 0, // mask_resource_id
+ child_pass_rect, // contents_changed_since_last_frame
+ gfx::RectF(), // mask_uv_rect
+ WebKit::WebFilterOperations(), // filters
+ skia::RefPtr<SkImageFilter>(), // filter
+ WebKit::WebFilterOperations()); // background_filters
+ quad_sink.Append(render_pass_quad.PassAs<DrawQuad>(), &data);
+
+ scoped_ptr<SolidColorDrawQuad> color_quad;
+ color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_quad_state, gfx::Rect(0, 0, 10, 10), 1u);
+ quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data);
+
+ color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_quad_state, gfx::Rect(0, 10, 10, 10), 2u);
+ quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data);
+
+ color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_quad_state, gfx::Rect(10, 0, 10, 10), 3u);
+ quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data);
+
+ color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_quad_state, gfx::Rect(10, 10, 10, 10), 4u);
+ quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data);
+
+ delegated_renderer_layer->SetFrameDataForRenderPasses(
+ &delegated_render_passes);
+
+ // The RenderPasses should be taken by the layer.
+ EXPECT_EQ(0u, delegated_render_passes.size());
+
+ root_layer_ = root_layer.get();
+ delegated_renderer_layer_ = delegated_renderer_layer.get();
+
+ if (clip_delegated_renderer_layer_) {
+ gfx::Rect clip_rect(21, 27, 23, 21);
+
+ clip_layer->SetPosition(clip_rect.origin());
+ clip_layer->SetBounds(clip_rect.size());
+ clip_layer->SetContentBounds(clip_rect.size());
+ clip_layer->SetMasksToBounds(true);
+
+ origin_layer->SetPosition(
+ gfx::PointAtOffsetFromOrigin(-clip_rect.OffsetFromOrigin()));
+
+ origin_layer->AddChild(delegated_renderer_layer.PassAs<LayerImpl>());
+ clip_layer->AddChild(origin_layer.Pass());
+ root_layer->AddChild(clip_layer.Pass());
+ } else {
+ root_layer->AddChild(delegated_renderer_layer.PassAs<LayerImpl>());
+ }
+
+ host_impl_->active_tree()->SetRootLayer(root_layer.Pass());
+ }
+
+ protected:
+ LayerImpl* root_layer_;
+ DelegatedRendererLayerImpl* delegated_renderer_layer_;
+ bool root_delegated_render_pass_is_clipped_;
+ bool clip_delegated_renderer_layer_;
+};
+
+TEST_F(DelegatedRendererLayerImplTestClip,
+ QuadsUnclipped_LayerUnclipped_NoSurface) {
+ root_delegated_render_pass_is_clipped_ = false;
+ clip_delegated_renderer_layer_ = false;
+ SetUpTest();
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ ASSERT_EQ(2u, frame.render_passes.size());
+ const QuadList& contrib_delegated_quad_list =
+ frame.render_passes[0]->quad_list;
+ ASSERT_EQ(2u, contrib_delegated_quad_list.size());
+ const QuadList& root_delegated_quad_list = frame.render_passes[1]->quad_list;
+ ASSERT_EQ(5u, root_delegated_quad_list.size());
+ const SharedQuadState* root_delegated_shared_quad_state =
+ root_delegated_quad_list[0]->shared_quad_state;
+ const SharedQuadState* contrib_delegated_shared_quad_state =
+ contrib_delegated_quad_list[0]->shared_quad_state;
+
+ // When the quads don't have a clip of their own, the clip rect is set to
+ // the drawableContentRect of the delegated renderer layer.
+ EXPECT_EQ(gfx::Rect(20, 20, 50, 50).ToString(),
+ root_delegated_shared_quad_state->clip_rect.ToString());
+ // Quads are clipped to the delegated renderer layer.
+ EXPECT_TRUE(root_delegated_shared_quad_state->is_clipped);
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestClip,
+ QuadsClipped_LayerUnclipped_NoSurface) {
+ root_delegated_render_pass_is_clipped_ = true;
+ clip_delegated_renderer_layer_ = false;
+ SetUpTest();
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ ASSERT_EQ(2u, frame.render_passes.size());
+ const QuadList& contrib_delegated_quad_list =
+ frame.render_passes[0]->quad_list;
+ ASSERT_EQ(2u, contrib_delegated_quad_list.size());
+ const QuadList& root_delegated_quad_list =
+ frame.render_passes[1]->quad_list;
+ ASSERT_EQ(5u, root_delegated_quad_list.size());
+ const SharedQuadState* root_delegated_shared_quad_state =
+ root_delegated_quad_list[0]->shared_quad_state;
+ const SharedQuadState* contrib_delegated_shared_quad_state =
+ contrib_delegated_quad_list[0]->shared_quad_state;
+
+ // When the quads have a clip of their own, it is used.
+ EXPECT_EQ(gfx::Rect(25, 25, 40, 40).ToString(),
+ root_delegated_shared_quad_state->clip_rect.ToString());
+ // Quads came with a clip rect.
+ EXPECT_TRUE(root_delegated_shared_quad_state->is_clipped);
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestClip,
+ QuadsUnclipped_LayerClipped_NoSurface) {
+ root_delegated_render_pass_is_clipped_ = false;
+ clip_delegated_renderer_layer_ = true;
+ SetUpTest();
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ ASSERT_EQ(2u, frame.render_passes.size());
+ const QuadList& contrib_delegated_quad_list =
+ frame.render_passes[0]->quad_list;
+ ASSERT_EQ(2u, contrib_delegated_quad_list.size());
+ const QuadList& root_delegated_quad_list = frame.render_passes[1]->quad_list;
+ ASSERT_EQ(5u, root_delegated_quad_list.size());
+ const SharedQuadState* root_delegated_shared_quad_state =
+ root_delegated_quad_list[0]->shared_quad_state;
+ const SharedQuadState* contrib_delegated_shared_quad_state =
+ contrib_delegated_quad_list[0]->shared_quad_state;
+
+ // When the quads don't have a clip of their own, the clip rect is set to
+ // the drawableContentRect of the delegated renderer layer. When the layer
+ // is clipped, that should be seen in the quads' clip_rect.
+ EXPECT_EQ(gfx::Rect(21, 27, 23, 21).ToString(),
+ root_delegated_shared_quad_state->clip_rect.ToString());
+ // Quads are clipped to the delegated renderer layer.
+ EXPECT_TRUE(root_delegated_shared_quad_state->is_clipped);
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestClip,
+ QuadsClipped_LayerClipped_NoSurface) {
+ root_delegated_render_pass_is_clipped_ = true;
+ clip_delegated_renderer_layer_ = true;
+ SetUpTest();
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ ASSERT_EQ(2u, frame.render_passes.size());
+ const QuadList& contrib_delegated_quad_list =
+ frame.render_passes[0]->quad_list;
+ ASSERT_EQ(2u, contrib_delegated_quad_list.size());
+ const QuadList& root_delegated_quad_list = frame.render_passes[1]->quad_list;
+ ASSERT_EQ(5u, root_delegated_quad_list.size());
+ const SharedQuadState* root_delegated_shared_quad_state =
+ root_delegated_quad_list[0]->shared_quad_state;
+ const SharedQuadState* contrib_delegated_shared_quad_state =
+ contrib_delegated_quad_list[0]->shared_quad_state;
+
+ // When the quads have a clip of their own, it is used, but it is
+ // combined with the clip rect of the delegated renderer layer.
+ EXPECT_EQ(gfx::Rect(25, 27, 19, 21).ToString(),
+ root_delegated_shared_quad_state->clip_rect.ToString());
+ // Quads came with a clip rect.
+ EXPECT_TRUE(root_delegated_shared_quad_state->is_clipped);
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestClip,
+ QuadsUnclipped_LayerUnclipped_Surface) {
+ root_delegated_render_pass_is_clipped_ = false;
+ clip_delegated_renderer_layer_ = false;
+ SetUpTest();
+
+ delegated_renderer_layer_->SetForceRenderSurface(true);
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ ASSERT_EQ(3u, frame.render_passes.size());
+ const QuadList& contrib_delegated_quad_list =
+ frame.render_passes[0]->quad_list;
+ ASSERT_EQ(2u, contrib_delegated_quad_list.size());
+ const QuadList& root_delegated_quad_list = frame.render_passes[1]->quad_list;
+ ASSERT_EQ(5u, root_delegated_quad_list.size());
+ const SharedQuadState* root_delegated_shared_quad_state =
+ root_delegated_quad_list[0]->shared_quad_state;
+ const SharedQuadState* contrib_delegated_shared_quad_state =
+ contrib_delegated_quad_list[0]->shared_quad_state;
+
+ // When the layer owns a surface, the quads don't need to be clipped
+ // further than they already specify. If they aren't clipped, then their
+ // clip rect is ignored, and they are not set as clipped.
+ EXPECT_FALSE(root_delegated_shared_quad_state->is_clipped);
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestClip,
+ QuadsClipped_LayerUnclipped_Surface) {
+ root_delegated_render_pass_is_clipped_ = true;
+ clip_delegated_renderer_layer_ = false;
+ SetUpTest();
+
+ delegated_renderer_layer_->SetForceRenderSurface(true);
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ ASSERT_EQ(3u, frame.render_passes.size());
+ const QuadList& contrib_delegated_quad_list =
+ frame.render_passes[0]->quad_list;
+ ASSERT_EQ(2u, contrib_delegated_quad_list.size());
+ const QuadList& root_delegated_quad_list = frame.render_passes[1]->quad_list;
+ ASSERT_EQ(5u, root_delegated_quad_list.size());
+ const SharedQuadState* root_delegated_shared_quad_state =
+ root_delegated_quad_list[0]->shared_quad_state;
+ const SharedQuadState* contrib_delegated_shared_quad_state =
+ contrib_delegated_quad_list[0]->shared_quad_state;
+
+ // When the quads have a clip of their own, it is used.
+ EXPECT_EQ(gfx::Rect(5, 5, 40, 40).ToString(),
+ root_delegated_shared_quad_state->clip_rect.ToString());
+ // Quads came with a clip rect.
+ EXPECT_TRUE(root_delegated_shared_quad_state->is_clipped);
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestClip,
+ QuadsUnclipped_LayerClipped_Surface) {
+ root_delegated_render_pass_is_clipped_ = false;
+ clip_delegated_renderer_layer_ = true;
+ SetUpTest();
+
+ delegated_renderer_layer_->SetForceRenderSurface(true);
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ ASSERT_EQ(3u, frame.render_passes.size());
+ const QuadList& contrib_delegated_quad_list =
+ frame.render_passes[0]->quad_list;
+ ASSERT_EQ(2u, contrib_delegated_quad_list.size());
+ const QuadList& root_delegated_quad_list = frame.render_passes[1]->quad_list;
+ ASSERT_EQ(5u, root_delegated_quad_list.size());
+ const SharedQuadState* root_delegated_shared_quad_state =
+ root_delegated_quad_list[0]->shared_quad_state;
+ const SharedQuadState* contrib_delegated_shared_quad_state =
+ contrib_delegated_quad_list[0]->shared_quad_state;
+
+ // When the layer owns a surface, the quads don't need to be clipped
+ // further than they already specify. If they aren't clipped, then their
+ // clip rect is ignored, and they are not set as clipped.
+ EXPECT_FALSE(root_delegated_shared_quad_state->is_clipped);
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+TEST_F(DelegatedRendererLayerImplTestClip, QuadsClipped_LayerClipped_Surface) {
+ root_delegated_render_pass_is_clipped_ = true;
+ clip_delegated_renderer_layer_ = true;
+ SetUpTest();
+
+ delegated_renderer_layer_->SetForceRenderSurface(true);
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame));
+
+ ASSERT_EQ(3u, frame.render_passes.size());
+ const QuadList& contrib_delegated_quad_list =
+ frame.render_passes[0]->quad_list;
+ ASSERT_EQ(2u, contrib_delegated_quad_list.size());
+ const QuadList& root_delegated_quad_list = frame.render_passes[1]->quad_list;
+ ASSERT_EQ(5u, root_delegated_quad_list.size());
+ const SharedQuadState* root_delegated_shared_quad_state =
+ root_delegated_quad_list[0]->shared_quad_state;
+ const SharedQuadState* contrib_delegated_shared_quad_state =
+ contrib_delegated_quad_list[0]->shared_quad_state;
+
+ // When the quads have a clip of their own, it is used, but it is
+ // combined with the clip rect of the delegated renderer layer. If the
+ // layer owns a surface, then it does not have a clip rect of its own.
+ EXPECT_EQ(gfx::Rect(5, 5, 40, 40).ToString(),
+ root_delegated_shared_quad_state->clip_rect.ToString());
+ // Quads came with a clip rect.
+ EXPECT_TRUE(root_delegated_shared_quad_state->is_clipped);
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/draw_properties.h b/cc/layers/draw_properties.h
new file mode 100644
index 0000000..23db23c
--- /dev/null
+++ b/cc/layers/draw_properties.h
@@ -0,0 +1,99 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_DRAW_PROPERTIES_H_
+#define CC_LAYERS_DRAW_PROPERTIES_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+
+// Container for properties that layers need to compute before they can be
+// drawn.
+template<typename LayerType, typename RenderSurfaceType>
+struct CC_EXPORT DrawProperties {
+ DrawProperties()
+ : opacity(0)
+ , opacity_is_animating(false)
+ , screen_space_opacity_is_animating(false)
+ , target_space_transform_is_animating(false)
+ , screen_space_transform_is_animating(false)
+ , can_use_lcd_text(false)
+ , is_clipped(false)
+ , render_target(0)
+ , contents_scale_x(1)
+ , contents_scale_y(1)
+ , num_descendants_that_draw_content(0)
+ , descendants_can_clip_selves(false)
+ {
+ }
+
+ // Transforms objects from content space to target surface space, where
+ // this layer would be drawn.
+ gfx::Transform target_space_transform;
+
+ // Transforms objects from content space to screen space (viewport space).
+ gfx::Transform screen_space_transform;
+
+ // DrawProperties::opacity may be different than LayerType::opacity,
+ // particularly in the case when a renderSurface re-parents the layer's
+ // opacity, or when opacity is compounded by the hierarchy.
+ float opacity;
+
+ // XXXIsAnimating flags are used to indicate whether the drawProperties
+ // are actually meaningful on the main thread. When the properties are
+ // animating, the main thread may not have the same values that are used
+ // to draw.
+ bool opacity_is_animating;
+ bool screen_space_opacity_is_animating;
+ bool target_space_transform_is_animating;
+ bool screen_space_transform_is_animating;
+
+ // True if the layer can use LCD text.
+ bool can_use_lcd_text;
+
+ // True if the layer needs to be clipped by clipRect.
+ bool is_clipped;
+
+ // The layer whose coordinate space this layer draws into. This can be
+ // either the same layer (m_drawProperties.render_target == this) or an
+ // ancestor of this layer.
+ LayerType* render_target;
+
+ // The surface that this layer and its subtree would contribute to.
+ scoped_ptr<RenderSurfaceType> render_surface;
+
+ // This rect is in the layer's content space.
+ gfx::Rect visible_content_rect;
+
+ // In target surface space, the rect that encloses the clipped, drawable
+ // content of the layer.
+ gfx::Rect drawable_content_rect;
+
+ // In target surface space, the original rect that clipped this
+ // layer. This value is used to avoid unnecessarily changing GL scissor
+ // state.
+ gfx::Rect clip_rect;
+
+ // The scale used to move between layer space and content space, and bounds
+ // of the space. One is always a function of the other, but which one
+ // depends on the layer type. For picture layers, this is an ideal scale,
+ // and not always the one used.
+ float contents_scale_x;
+ float contents_scale_y;
+ gfx::Size content_bounds;
+
+ // Does not include this layer itself, only its children and descendants.
+ int num_descendants_that_draw_content;
+
+ // If true, every descendant in the sub-tree can clip itself without the
+ // need to use hardware sissoring or a new render target.
+ bool descendants_can_clip_selves;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_DRAW_PROPERTIES_H_
diff --git a/cc/layers/heads_up_display_layer.cc b/cc/layers/heads_up_display_layer.cc
new file mode 100644
index 0000000..56d7c96
--- /dev/null
+++ b/cc/layers/heads_up_display_layer.cc
@@ -0,0 +1,64 @@
+// Copyright 2012 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 "cc/layers/heads_up_display_layer.h"
+
+#include "base/debug/trace_event.h"
+#include "cc/layers/heads_up_display_layer_impl.h"
+#include "cc/trees/layer_tree_host.h"
+
+namespace cc {
+
+scoped_refptr<HeadsUpDisplayLayer> HeadsUpDisplayLayer::Create() {
+ return make_scoped_refptr(new HeadsUpDisplayLayer());
+}
+
+HeadsUpDisplayLayer::HeadsUpDisplayLayer() : Layer() {
+ SetBounds(gfx::Size(256, 256));
+}
+
+HeadsUpDisplayLayer::~HeadsUpDisplayLayer() {}
+
+void HeadsUpDisplayLayer::Update(ResourceUpdateQueue*,
+ const OcclusionTracker*,
+ RenderingStats*) {
+ const LayerTreeDebugState& debug_state = layer_tree_host()->debug_state();
+ int max_texture_size =
+ layer_tree_host()->GetRendererCapabilities().max_texture_size;
+
+ int device_viewport_in_layout_pixels_width =
+ layer_tree_host()->device_viewport_size().width() /
+ layer_tree_host()->device_scale_factor();
+ int device_viewport_in_layout_pixels_height =
+ layer_tree_host()->device_viewport_size().height() /
+ layer_tree_host()->device_scale_factor();
+
+ gfx::Size bounds;
+ gfx::Transform matrix;
+ matrix.MakeIdentity();
+
+ if (debug_state.showPlatformLayerTree || debug_state.showHudRects()) {
+ int width =
+ std::min(max_texture_size, device_viewport_in_layout_pixels_width);
+ int height =
+ std::min(max_texture_size, device_viewport_in_layout_pixels_height);
+ bounds = gfx::Size(width, height);
+ } else {
+ bounds = gfx::Size(256, 256);
+ matrix.Translate(device_viewport_in_layout_pixels_width - 256.0, 0.0);
+ }
+
+ SetBounds(bounds);
+ SetTransform(matrix);
+}
+
+bool HeadsUpDisplayLayer::DrawsContent() const { return true; }
+
+scoped_ptr<LayerImpl> HeadsUpDisplayLayer::CreateLayerImpl(
+ LayerTreeImpl* treeImpl) {
+ return HeadsUpDisplayLayerImpl::Create(treeImpl, layer_id_).
+ PassAs<LayerImpl>();
+}
+
+} // namespace cc
diff --git a/cc/layers/heads_up_display_layer.h b/cc/layers/heads_up_display_layer.h
new file mode 100644
index 0000000..d7656c5
--- /dev/null
+++ b/cc/layers/heads_up_display_layer.h
@@ -0,0 +1,35 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_HEADS_UP_DISPLAY_LAYER_H_
+#define CC_LAYERS_HEADS_UP_DISPLAY_LAYER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer.h"
+
+namespace cc {
+
+class CC_EXPORT HeadsUpDisplayLayer : public Layer {
+ public:
+ static scoped_refptr<HeadsUpDisplayLayer> Create();
+
+ virtual void Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* tracker,
+ RenderingStats* stats) OVERRIDE;
+ virtual bool DrawsContent() const OVERRIDE;
+
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+
+ protected:
+ HeadsUpDisplayLayer();
+
+ private:
+ virtual ~HeadsUpDisplayLayer();
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_HEADS_UP_DISPLAY_LAYER_H_
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
new file mode 100644
index 0000000..93c51e1
--- /dev/null
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -0,0 +1,697 @@
+// Copyright 2012 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 "cc/layers/heads_up_display_layer_impl.h"
+
+#include "base/stringprintf.h"
+#include "base/strings/string_split.h"
+#include "cc/debug/debug_colors.h"
+#include "cc/debug/debug_rect_history.h"
+#include "cc/debug/frame_rate_counter.h"
+#include "cc/debug/paint_time_counter.h"
+#include "cc/layers/quad_sink.h"
+#include "cc/output/renderer.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "cc/resources/memory_history.h"
+#include "cc/resources/tile_manager.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "skia/ext/platform_canvas.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+#include "third_party/skia/include/effects/SkColorMatrixFilter.h"
+#include "ui/gfx/point.h"
+#include "ui/gfx/size.h"
+
+namespace cc {
+
+static inline SkPaint CreatePaint() {
+ SkPaint paint;
+#if (SK_R32_SHIFT || SK_B32_SHIFT != 16)
+ // The SkCanvas is in RGBA but the shader is expecting BGRA, so we need to
+ // swizzle our colors when drawing to the SkCanvas.
+ SkColorMatrix swizzle_matrix;
+ for (int i = 0; i < 20; ++i)
+ swizzle_matrix.fMat[i] = 0;
+ swizzle_matrix.fMat[0 + 5 * 2] = 1;
+ swizzle_matrix.fMat[1 + 5 * 1] = 1;
+ swizzle_matrix.fMat[2 + 5 * 0] = 1;
+ swizzle_matrix.fMat[3 + 5 * 3] = 1;
+
+ skia::RefPtr<SkColorMatrixFilter> filter =
+ skia::AdoptRef(new SkColorMatrixFilter(swizzle_matrix));
+ paint.setColorFilter(filter.get());
+#endif
+ return paint;
+}
+
+HeadsUpDisplayLayerImpl::Graph::Graph(double indicator_value,
+ double start_upper_bound)
+ : value(0.0),
+ min(0.0),
+ max(0.0),
+ current_upper_bound(start_upper_bound),
+ default_upper_bound(start_upper_bound),
+ indicator(indicator_value) {}
+
+double HeadsUpDisplayLayerImpl::Graph::UpdateUpperBound() {
+ double target_upper_bound = std::max(max, default_upper_bound);
+ current_upper_bound += (target_upper_bound - current_upper_bound) * 0.5;
+ return current_upper_bound;
+}
+
+HeadsUpDisplayLayerImpl::HeadsUpDisplayLayerImpl(LayerTreeImpl* tree_impl,
+ int id)
+ : LayerImpl(tree_impl, id),
+ fps_graph_(60.0, 80.0),
+ paint_time_graph_(16.0, 48.0),
+ typeface_(skia::AdoptRef(
+ SkTypeface::CreateFromName("monospace", SkTypeface::kBold))) {}
+
+HeadsUpDisplayLayerImpl::~HeadsUpDisplayLayerImpl() {}
+
+scoped_ptr<LayerImpl> HeadsUpDisplayLayerImpl::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return HeadsUpDisplayLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
+}
+
+void HeadsUpDisplayLayerImpl::WillDraw(ResourceProvider* resource_provider) {
+ LayerImpl::WillDraw(resource_provider);
+
+ if (!hud_texture_)
+ hud_texture_ = ScopedResource::create(resource_provider);
+
+ // TODO(danakj): Scale the HUD by deviceScale to make it more friendly under
+ // high DPI.
+
+ // TODO(danakj): The HUD could swap between two textures instead of creating a
+ // texture every frame in ubercompositor.
+ if (hud_texture_->size() != bounds() ||
+ resource_provider->InUseByConsumer(hud_texture_->id()))
+ hud_texture_->Free();
+
+ if (!hud_texture_->id()) {
+ hud_texture_->Allocate(
+ bounds(), GL_RGBA, ResourceProvider::TextureUsageAny);
+ // TODO(epenner): This texture was being used before SetPixels was called,
+ // which is now not allowed (it's an uninitialized read). This should be
+ // fixed and this allocateForTesting() removed.
+ // http://crbug.com/166784
+ resource_provider->AllocateForTesting(hud_texture_->id());
+ }
+}
+
+void HeadsUpDisplayLayerImpl::AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) {
+ if (!hud_texture_->id())
+ return;
+
+ SharedQuadState* shared_quad_state =
+ quad_sink->UseSharedQuadState(CreateSharedQuadState());
+
+ gfx::Rect quad_rect(gfx::Point(), bounds());
+ gfx::Rect opaque_rect(contents_opaque() ? quad_rect : gfx::Rect());
+ bool premultiplied_alpha = true;
+ gfx::PointF uv_top_left(0.f, 0.f);
+ gfx::PointF uv_bottom_right(1.f, 1.f);
+ const float vertex_opacity[] = { 1.f, 1.f, 1.f, 1.f };
+ bool flipped = false;
+ scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ quad_rect,
+ opaque_rect,
+ hud_texture_->id(),
+ premultiplied_alpha,
+ uv_top_left,
+ uv_bottom_right,
+ vertex_opacity,
+ flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+}
+
+void HeadsUpDisplayLayerImpl::UpdateHudTexture(
+ ResourceProvider* resource_provider) {
+ if (!hud_texture_->id())
+ return;
+
+ SkISize canvas_size;
+ if (hud_canvas_)
+ canvas_size = hud_canvas_->getDeviceSize();
+ else
+ canvas_size.set(0, 0);
+
+ if (canvas_size.fWidth != bounds().width() ||
+ canvas_size.fHeight != bounds().height() || !hud_canvas_) {
+ bool opaque = false;
+ hud_canvas_ = make_scoped_ptr(
+ skia::CreateBitmapCanvas(bounds().width(), bounds().height(), opaque));
+ }
+
+ UpdateHudContents();
+
+ hud_canvas_->clear(SkColorSetARGB(0, 0, 0, 0));
+ DrawHudContents(hud_canvas_.get());
+
+ const SkBitmap* bitmap = &hud_canvas_->getDevice()->accessBitmap(false);
+ SkAutoLockPixels locker(*bitmap);
+
+ gfx::Rect layer_rect(bounds());
+ DCHECK(bitmap->config() == SkBitmap::kARGB_8888_Config);
+ resource_provider->SetPixels(hud_texture_->id(),
+ static_cast<const uint8_t*>(bitmap->getPixels()),
+ layer_rect,
+ layer_rect,
+ gfx::Vector2d());
+}
+
+void HeadsUpDisplayLayerImpl::DidDraw(ResourceProvider* resource_provider) {
+ LayerImpl::DidDraw(resource_provider);
+
+ if (!hud_texture_->id())
+ return;
+
+ // FIXME: the following assert will not be true when sending resources to a
+ // parent compositor. We will probably need to hold on to hud_texture_ for
+ // longer, and have several HUD textures in the pipeline.
+ DCHECK(!resource_provider->InUseByConsumer(hud_texture_->id()));
+}
+
+void HeadsUpDisplayLayerImpl::DidLoseOutputSurface() { hud_texture_.reset(); }
+
+bool HeadsUpDisplayLayerImpl::LayerIsAlwaysDamaged() const { return true; }
+
+void HeadsUpDisplayLayerImpl::UpdateHudContents() {
+ const LayerTreeDebugState& debug_state = layer_tree_impl()->debug_state();
+
+ // Don't update numbers every frame so text is readable.
+ base::TimeTicks now = layer_tree_impl()->CurrentFrameTime();
+ if (base::TimeDelta(now - time_of_last_graph_update_).InSecondsF() > 0.25f) {
+ time_of_last_graph_update_ = now;
+
+ if (debug_state.showFPSCounter) {
+ FrameRateCounter* fps_counter = layer_tree_impl()->frame_rate_counter();
+ fps_graph_.value = fps_counter->GetAverageFPS();
+ fps_counter->GetMinAndMaxFPS(&fps_graph_.min, &fps_graph_.max);
+ }
+
+ if (debug_state.continuousPainting) {
+ PaintTimeCounter* paint_time_counter =
+ layer_tree_impl()->paint_time_counter();
+ base::TimeDelta latest, min, max;
+
+ if (paint_time_counter->End())
+ latest = paint_time_counter->End()->total_time();
+ paint_time_counter->GetMinAndMaxPaintTime(&min, &max);
+
+ paint_time_graph_.value = latest.InMillisecondsF();
+ paint_time_graph_.min = min.InMillisecondsF();
+ paint_time_graph_.max = max.InMillisecondsF();
+ }
+
+ if (debug_state.showMemoryStats()) {
+ MemoryHistory* memory_history = layer_tree_impl()->memory_history();
+ if (memory_history->End())
+ memory_entry_ = **memory_history->End();
+ else
+ memory_entry_ = MemoryHistory::Entry();
+ }
+ }
+
+ fps_graph_.UpdateUpperBound();
+ paint_time_graph_.UpdateUpperBound();
+}
+
+void HeadsUpDisplayLayerImpl::DrawHudContents(SkCanvas* canvas) const {
+ const LayerTreeDebugState& debug_state = layer_tree_impl()->debug_state();
+
+ if (debug_state.showHudRects())
+ DrawDebugRects(canvas, layer_tree_impl()->debug_rect_history());
+
+ if (debug_state.showPlatformLayerTree)
+ DrawPlatformLayerTree(canvas);
+
+ SkRect area = SkRect::MakeEmpty();
+ if (debug_state.continuousPainting) {
+ // Don't show the FPS display when continuous painting is enabled, because
+ // it would show misleading numbers.
+ area = DrawPaintTimeDisplay(
+ canvas, layer_tree_impl()->paint_time_counter(), 0, 0);
+ } else if (debug_state.showFPSCounter) {
+ area =
+ DrawFPSDisplay(canvas, layer_tree_impl()->frame_rate_counter(), 0, 0);
+ }
+
+ if (debug_state.showMemoryStats())
+ DrawMemoryDisplay(canvas, 0, area.bottom(), SkMaxScalar(area.width(), 150));
+}
+
+void HeadsUpDisplayLayerImpl::DrawText(SkCanvas* canvas,
+ SkPaint* paint,
+ const std::string& text,
+ SkPaint::Align align,
+ int size,
+ int x,
+ int y) const {
+ const bool anti_alias = paint->isAntiAlias();
+ paint->setAntiAlias(true);
+
+ paint->setTextSize(size);
+ paint->setTextAlign(align);
+ paint->setTypeface(typeface_.get());
+ canvas->drawText(text.c_str(), text.length(), x, y, *paint);
+
+ paint->setAntiAlias(anti_alias);
+}
+
+void HeadsUpDisplayLayerImpl::DrawText(SkCanvas* canvas,
+ SkPaint* paint,
+ const std::string& text,
+ SkPaint::Align align,
+ int size,
+ const SkPoint& pos) const {
+ DrawText(canvas, paint, text, align, size, pos.x(), pos.y());
+}
+
+void HeadsUpDisplayLayerImpl::DrawGraphBackground(SkCanvas* canvas,
+ SkPaint* paint,
+ const SkRect& bounds) const {
+ paint->setColor(DebugColors::HUDBackgroundColor());
+ canvas->drawRect(bounds, *paint);
+}
+
+void HeadsUpDisplayLayerImpl::DrawGraphLines(SkCanvas* canvas,
+ SkPaint* paint,
+ const SkRect& bounds,
+ const Graph& graph) const {
+ // Draw top and bottom line.
+ paint->setColor(DebugColors::HUDSeparatorLineColor());
+ canvas->drawLine(bounds.left(),
+ bounds.top() - 1,
+ bounds.right(),
+ bounds.top() - 1,
+ *paint);
+ canvas->drawLine(
+ bounds.left(), bounds.bottom(), bounds.right(), bounds.bottom(), *paint);
+
+ // Draw indicator line (additive blend mode to increase contrast when drawn on
+ // top of graph).
+ paint->setColor(DebugColors::HUDIndicatorLineColor());
+ paint->setXfermodeMode(SkXfermode::kPlus_Mode);
+ const double indicator_top =
+ bounds.height() * (1.0 - graph.indicator / graph.current_upper_bound) -
+ 1.0;
+ canvas->drawLine(bounds.left(),
+ bounds.top() + indicator_top,
+ bounds.right(),
+ bounds.top() + indicator_top,
+ *paint);
+ paint->setXfermode(NULL);
+}
+
+void HeadsUpDisplayLayerImpl::DrawPlatformLayerTree(SkCanvas* canvas) const {
+ const int kFontHeight = 14;
+ SkPaint paint = CreatePaint();
+ DrawGraphBackground(
+ canvas,
+ &paint,
+ SkRect::MakeXYWH(0, 0, bounds().width(), bounds().height()));
+
+ std::string layer_tree = layer_tree_impl()->layer_tree_as_text();
+ std::vector<std::string> lines;
+ base::SplitString(layer_tree, '\n', &lines);
+
+ paint.setColor(DebugColors::PlatformLayerTreeTextColor());
+ for (size_t i = 0;
+ i < lines.size() &&
+ static_cast<int>(2 + i * kFontHeight) < bounds().height();
+ ++i) {
+ DrawText(canvas,
+ &paint,
+ lines[i],
+ SkPaint::kLeft_Align,
+ kFontHeight,
+ 2,
+ 2 + (i + 1) * kFontHeight);
+ }
+}
+
+SkRect HeadsUpDisplayLayerImpl::DrawFPSDisplay(
+ SkCanvas* canvas,
+ const FrameRateCounter* fps_counter,
+ int right,
+ int top) const {
+ const int kPadding = 4;
+ const int kGap = 6;
+
+ const int kFontHeight = 15;
+
+ const int kGraphWidth = fps_counter->time_stamp_history_size() - 2;
+ const int kGraphHeight = 40;
+
+ const int kHistogramWidth = 37;
+
+ int width = kGraphWidth + kHistogramWidth + 4 * kPadding;
+ int height = kFontHeight + kGraphHeight + 4 * kPadding + 2;
+ int left = bounds().width() - width - right;
+ SkRect area = SkRect::MakeXYWH(left, top, width, height);
+
+ SkPaint paint = CreatePaint();
+ DrawGraphBackground(canvas, &paint, area);
+
+ SkRect text_bounds =
+ SkRect::MakeXYWH(left + kPadding,
+ top + kPadding,
+ kGraphWidth + kHistogramWidth + kGap + 2,
+ kFontHeight);
+ SkRect graph_bounds = SkRect::MakeXYWH(left + kPadding,
+ text_bounds.bottom() + 2 * kPadding,
+ kGraphWidth,
+ kGraphHeight);
+ SkRect histogram_bounds = SkRect::MakeXYWH(graph_bounds.right() + kGap,
+ graph_bounds.top(),
+ kHistogramWidth,
+ kGraphHeight);
+
+ const std::string value_text =
+ base::StringPrintf("FPS:%5.1f", fps_graph_.value);
+ const std::string min_max_text =
+ base::StringPrintf("%.0f-%.0f", fps_graph_.min, fps_graph_.max);
+
+ paint.setColor(DebugColors::FPSDisplayTextAndGraphColor());
+ DrawText(canvas,
+ &paint,
+ value_text,
+ SkPaint::kLeft_Align,
+ kFontHeight,
+ text_bounds.left(),
+ text_bounds.bottom());
+ DrawText(canvas,
+ &paint,
+ min_max_text,
+ SkPaint::kRight_Align,
+ kFontHeight,
+ text_bounds.right(),
+ text_bounds.bottom());
+
+ DrawGraphLines(canvas, &paint, graph_bounds, fps_graph_);
+
+ // Collect graph and histogram data.
+ SkPath path;
+
+ const int kHistogramSize = 20;
+ double histogram[kHistogramSize] = { 1.0 };
+ double max_bucket_value = 1.0;
+
+ for (FrameRateCounter::RingBufferType::Iterator it = --fps_counter->end(); it;
+ --it) {
+ base::TimeDelta delta = fps_counter->RecentFrameInterval(it.index() + 1);
+
+ // Skip this particular instantaneous frame rate if it is not likely to have
+ // been valid.
+ if (!fps_counter->IsBadFrameInterval(delta)) {
+ double fps = 1.0 / delta.InSecondsF();
+
+ // Clamp the FPS to the range we want to plot visually.
+ double p = fps / fps_graph_.current_upper_bound;
+ if (p > 1.0)
+ p = 1.0;
+
+ // Plot this data point.
+ SkPoint cur =
+ SkPoint::Make(graph_bounds.left() + it.index(),
+ graph_bounds.bottom() - p * graph_bounds.height());
+ if (path.isEmpty())
+ path.moveTo(cur);
+ else
+ path.lineTo(cur);
+
+ // Use the fps value to find the right bucket in the histogram.
+ int bucket_index = floor(p * (kHistogramSize - 1));
+
+ // Add the delta time to take the time spent at that fps rate into
+ // account.
+ histogram[bucket_index] += delta.InSecondsF();
+ max_bucket_value = std::max(histogram[bucket_index], max_bucket_value);
+ }
+ }
+
+ // Draw FPS histogram.
+ paint.setColor(DebugColors::HUDSeparatorLineColor());
+ canvas->drawLine(histogram_bounds.left() - 1,
+ histogram_bounds.top() - 1,
+ histogram_bounds.left() - 1,
+ histogram_bounds.bottom() + 1,
+ paint);
+ canvas->drawLine(histogram_bounds.right() + 1,
+ histogram_bounds.top() - 1,
+ histogram_bounds.right() + 1,
+ histogram_bounds.bottom() + 1,
+ paint);
+
+ paint.setColor(DebugColors::FPSDisplayTextAndGraphColor());
+ const double bar_height = histogram_bounds.height() / kHistogramSize;
+
+ for (int i = kHistogramSize - 1; i >= 0; --i) {
+ if (histogram[i] > 0) {
+ double bar_width =
+ histogram[i] / max_bucket_value * histogram_bounds.width();
+ canvas->drawRect(
+ SkRect::MakeXYWH(histogram_bounds.left(),
+ histogram_bounds.bottom() - (i + 1) * bar_height,
+ bar_width,
+ 1),
+ paint);
+ }
+ }
+
+ // Draw FPS graph.
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(1);
+ canvas->drawPath(path, paint);
+
+ return area;
+}
+
+SkRect HeadsUpDisplayLayerImpl::DrawMemoryDisplay(SkCanvas* canvas,
+ int right,
+ int top,
+ int width) const {
+ if (!memory_entry_.bytes_total())
+ return SkRect::MakeEmpty();
+
+ const int kPadding = 4;
+ const int kFontHeight = 13;
+
+ const int height = 3 * kFontHeight + 4 * kPadding;
+ const int left = bounds().width() - width - right;
+ const SkRect area = SkRect::MakeXYWH(left, top, width, height);
+
+ const double megabyte = 1024.0 * 1024.0;
+
+ SkPaint paint = CreatePaint();
+ DrawGraphBackground(canvas, &paint, area);
+
+ SkPoint title_pos = SkPoint::Make(left + kPadding, top + kFontHeight);
+ SkPoint stat1_pos = SkPoint::Make(left + width - kPadding - 1,
+ top + kPadding + 2 * kFontHeight);
+ SkPoint stat2_pos = SkPoint::Make(left + width - kPadding - 1,
+ top + 2 * kPadding + 3 * kFontHeight);
+
+ paint.setColor(DebugColors::MemoryDisplayTextColor());
+ DrawText(canvas,
+ &paint,
+ "GPU memory",
+ SkPaint::kLeft_Align,
+ kFontHeight,
+ title_pos);
+
+ std::string text =
+ base::StringPrintf("%6.1f MB used",
+ (memory_entry_.bytes_unreleasable +
+ memory_entry_.bytes_allocated) / megabyte);
+ DrawText(canvas, &paint, text, SkPaint::kRight_Align, kFontHeight, stat1_pos);
+
+ if (memory_entry_.bytes_over) {
+ paint.setColor(SK_ColorRED);
+ text = base::StringPrintf("%6.1f MB over",
+ memory_entry_.bytes_over / megabyte);
+ } else {
+ text = base::StringPrintf("%6.1f MB max ",
+ memory_entry_.total_budget_in_bytes / megabyte);
+ }
+ DrawText(canvas, &paint, text, SkPaint::kRight_Align, kFontHeight, stat2_pos);
+
+ return area;
+}
+
+SkRect HeadsUpDisplayLayerImpl::DrawPaintTimeDisplay(
+ SkCanvas* canvas,
+ const PaintTimeCounter* paint_time_counter,
+ int right,
+ int top) const {
+ const int kPadding = 4;
+ const int kFontHeight = 15;
+
+ const int kGraphWidth = paint_time_counter->HistorySize();
+ const int kGraphHeight = 40;
+
+ const int width = kGraphWidth + 2 * kPadding;
+ const int height =
+ kFontHeight + kGraphHeight + 4 * kPadding + 2 + kFontHeight + kPadding;
+ const int left = bounds().width() - width - right;
+
+ const SkRect area = SkRect::MakeXYWH(left, top, width, height);
+
+ SkPaint paint = CreatePaint();
+ DrawGraphBackground(canvas, &paint, area);
+
+ SkRect text_bounds = SkRect::MakeXYWH(
+ left + kPadding, top + kPadding, kGraphWidth, kFontHeight);
+ SkRect text_bounds2 = SkRect::MakeXYWH(left + kPadding,
+ text_bounds.bottom() + kPadding,
+ kGraphWidth,
+ kFontHeight);
+ SkRect graph_bounds = SkRect::MakeXYWH(left + kPadding,
+ text_bounds2.bottom() + 2 * kPadding,
+ kGraphWidth,
+ kGraphHeight);
+
+ const std::string value_text =
+ base::StringPrintf("%.1f", paint_time_graph_.value);
+ const std::string min_max_text = base::StringPrintf(
+ "%.1f-%.1f", paint_time_graph_.min, paint_time_graph_.max);
+
+ paint.setColor(DebugColors::PaintTimeDisplayTextAndGraphColor());
+ DrawText(canvas,
+ &paint,
+ "Page paint time (ms)",
+ SkPaint::kLeft_Align,
+ kFontHeight,
+ text_bounds.left(),
+ text_bounds.bottom());
+ DrawText(canvas,
+ &paint,
+ value_text,
+ SkPaint::kLeft_Align,
+ kFontHeight,
+ text_bounds2.left(),
+ text_bounds2.bottom());
+ DrawText(canvas,
+ &paint,
+ min_max_text,
+ SkPaint::kRight_Align,
+ kFontHeight,
+ text_bounds2.right(),
+ text_bounds2.bottom());
+
+ paint.setColor(DebugColors::PaintTimeDisplayTextAndGraphColor());
+ for (PaintTimeCounter::RingBufferType::Iterator it =
+ paint_time_counter->End();
+ it;
+ --it) {
+ double pt = it->total_time().InMillisecondsF();
+
+ if (pt == 0.0)
+ continue;
+
+ double p = pt / paint_time_graph_.current_upper_bound;
+ if (p > 1.0)
+ p = 1.0;
+
+ canvas->drawRect(
+ SkRect::MakeXYWH(graph_bounds.left() + it.index(),
+ graph_bounds.bottom() - p * graph_bounds.height(),
+ 1,
+ p * graph_bounds.height()),
+ paint);
+ }
+
+ DrawGraphLines(canvas, &paint, graph_bounds, paint_time_graph_);
+
+ return area;
+}
+
+void HeadsUpDisplayLayerImpl::DrawDebugRects(
+ SkCanvas* canvas,
+ DebugRectHistory* debug_rect_history) const {
+ const std::vector<DebugRect>& debug_rects = debug_rect_history->debug_rects();
+ float rect_scale = 1.f / layer_tree_impl()->device_scale_factor();
+ SkPaint paint = CreatePaint();
+
+ canvas->save();
+ canvas->scale(rect_scale, rect_scale);
+
+ for (size_t i = 0; i < debug_rects.size(); ++i) {
+ SkColor stroke_color = 0;
+ SkColor fill_color = 0;
+ float stroke_width = 0.f;
+
+ switch (debug_rects[i].type) {
+ case PAINT_RECT_TYPE:
+ stroke_color = DebugColors::PaintRectBorderColor();
+ fill_color = DebugColors::PaintRectFillColor();
+ stroke_width = DebugColors::PaintRectBorderWidth(layer_tree_impl());
+ break;
+ case PROPERTY_CHANGED_RECT_TYPE:
+ stroke_color = DebugColors::PropertyChangedRectBorderColor();
+ fill_color = DebugColors::PropertyChangedRectFillColor();
+ stroke_width =
+ DebugColors::PropertyChangedRectBorderWidth(layer_tree_impl());
+ break;
+ case SURFACE_DAMAGE_RECT_TYPE:
+ stroke_color = DebugColors::SurfaceDamageRectBorderColor();
+ fill_color = DebugColors::SurfaceDamageRectFillColor();
+ stroke_width =
+ DebugColors::SurfaceDamageRectBorderWidth(layer_tree_impl());
+ break;
+ case REPLICA_SCREEN_SPACE_RECT_TYPE:
+ stroke_color = DebugColors::ScreenSpaceSurfaceReplicaRectBorderColor();
+ fill_color = DebugColors::ScreenSpaceSurfaceReplicaRectFillColor();
+ stroke_width = DebugColors::ScreenSpaceSurfaceReplicaRectBorderWidth(
+ layer_tree_impl());
+ break;
+ case SCREEN_SPACE_RECT_TYPE:
+ stroke_color = DebugColors::ScreenSpaceLayerRectBorderColor();
+ fill_color = DebugColors::ScreenSpaceLayerRectFillColor();
+ stroke_width =
+ DebugColors::ScreenSpaceLayerRectBorderWidth(layer_tree_impl());
+ break;
+ case OCCLUDING_RECT_TYPE:
+ stroke_color = DebugColors::OccludingRectBorderColor();
+ fill_color = DebugColors::OccludingRectFillColor();
+ stroke_width = DebugColors::OccludingRectBorderWidth(layer_tree_impl());
+ break;
+ case NONOCCLUDING_RECT_TYPE:
+ stroke_color = DebugColors::NonOccludingRectBorderColor();
+ fill_color = DebugColors::NonOccludingRectFillColor();
+ stroke_width =
+ DebugColors::NonOccludingRectBorderWidth(layer_tree_impl());
+ break;
+ }
+
+ const gfx::RectF& rect = debug_rects[i].rect;
+ SkRect sk_rect =
+ SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height());
+ paint.setColor(fill_color);
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawRect(sk_rect, paint);
+
+ paint.setColor(stroke_color);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkFloatToScalar(stroke_width));
+ canvas->drawRect(sk_rect, paint);
+ }
+
+ canvas->restore();
+}
+
+const char* HeadsUpDisplayLayerImpl::LayerTypeAsString() const {
+ return "HeadsUpDisplayLayer";
+}
+
+} // namespace cc
diff --git a/cc/layers/heads_up_display_layer_impl.h b/cc/layers/heads_up_display_layer_impl.h
new file mode 100644
index 0000000..3bc3cf2
--- /dev/null
+++ b/cc/layers/heads_up_display_layer_impl.h
@@ -0,0 +1,126 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_HEADS_UP_DISPLAY_LAYER_IMPL_H_
+#define CC_LAYERS_HEADS_UP_DISPLAY_LAYER_IMPL_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/time.h"
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer_impl.h"
+#include "cc/resources/memory_history.h"
+#include "cc/resources/scoped_resource.h"
+
+class SkCanvas;
+class SkPaint;
+class SkTypeface;
+struct SkRect;
+
+namespace cc {
+
+class DebugRectHistory;
+class FrameRateCounter;
+class PaintTimeCounter;
+
+class CC_EXPORT HeadsUpDisplayLayerImpl : public LayerImpl {
+ public:
+ static scoped_ptr<HeadsUpDisplayLayerImpl> Create(LayerTreeImpl* tree_impl,
+ int id) {
+ return make_scoped_ptr(new HeadsUpDisplayLayerImpl(tree_impl, id));
+ }
+ virtual ~HeadsUpDisplayLayerImpl();
+
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+
+ virtual void WillDraw(ResourceProvider* resource_provider) OVERRIDE;
+ virtual void AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) OVERRIDE;
+ void UpdateHudTexture(ResourceProvider* resource_provider);
+ virtual void DidDraw(ResourceProvider* resource_provider) OVERRIDE;
+
+ virtual void DidLoseOutputSurface() OVERRIDE;
+
+ virtual bool LayerIsAlwaysDamaged() const OVERRIDE;
+
+ private:
+ class Graph {
+ public:
+ Graph(double indicator_value, double start_upper_bound);
+
+ // Eases the upper bound, which limits what is currently visible in the
+ // graph, so that the graph always scales to either it's max or
+ // default_upper_bound.
+ double UpdateUpperBound();
+
+ double value;
+ double min;
+ double max;
+
+ double current_upper_bound;
+ const double default_upper_bound;
+ const double indicator;
+ };
+
+ HeadsUpDisplayLayerImpl(LayerTreeImpl* tree_impl, int id);
+
+ virtual const char* LayerTypeAsString() const OVERRIDE;
+
+ void UpdateHudContents();
+ void DrawHudContents(SkCanvas* canvas) const;
+
+ void DrawText(SkCanvas* canvas,
+ SkPaint* paint,
+ const std::string& text,
+ SkPaint::Align align,
+ int size,
+ int x,
+ int y) const;
+ void DrawText(SkCanvas* canvas,
+ SkPaint* paint,
+ const std::string& text,
+ SkPaint::Align align,
+ int size,
+ const SkPoint& pos) const;
+ void DrawGraphBackground(SkCanvas* canvas,
+ SkPaint* paint,
+ const SkRect& bounds) const;
+ void DrawGraphLines(SkCanvas* canvas,
+ SkPaint* paint,
+ const SkRect& bounds,
+ const Graph& graph) const;
+
+ void DrawPlatformLayerTree(SkCanvas* canvas) const;
+ SkRect DrawFPSDisplay(SkCanvas* canvas,
+ const FrameRateCounter* fps_counter,
+ int right,
+ int top) const;
+ SkRect DrawMemoryDisplay(SkCanvas* canvas,
+ int top,
+ int right,
+ int width) const;
+ SkRect DrawPaintTimeDisplay(SkCanvas* canvas,
+ const PaintTimeCounter* paint_time_counter,
+ int top,
+ int right) const;
+ void DrawDebugRects(SkCanvas* canvas,
+ DebugRectHistory* debug_rect_history) const;
+
+ scoped_ptr<ScopedResource> hud_texture_;
+ scoped_ptr<SkCanvas> hud_canvas_;
+
+ skia::RefPtr<SkTypeface> typeface_;
+
+ Graph fps_graph_;
+ Graph paint_time_graph_;
+ MemoryHistory::Entry memory_entry_;
+
+ base::TimeTicks time_of_last_graph_update_;
+
+ DISALLOW_COPY_AND_ASSIGN(HeadsUpDisplayLayerImpl);
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_HEADS_UP_DISPLAY_LAYER_IMPL_H_
diff --git a/cc/layers/heads_up_display_unittest.cc b/cc/layers/heads_up_display_unittest.cc
new file mode 100644
index 0000000..1a4213e
--- /dev/null
+++ b/cc/layers/heads_up_display_unittest.cc
@@ -0,0 +1,110 @@
+// Copyright 2012 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 "cc/layers/heads_up_display_layer.h"
+#include "cc/layers/layer.h"
+#include "cc/test/layer_tree_test_common.h"
+#include "cc/trees/layer_tree_host.h"
+
+namespace cc {
+namespace {
+
+class HeadsUpDisplayTest : public ThreadedTest {
+protected:
+ virtual void initializeSettings(LayerTreeSettings& settings) OVERRIDE
+ {
+ // Enable the HUD without requiring text.
+ settings.initialDebugState.showPropertyChangedRects = true;
+ }
+};
+
+class DrawsContentLayer : public Layer {
+public:
+ static scoped_refptr<DrawsContentLayer> Create() { return make_scoped_refptr(new DrawsContentLayer()); }
+ virtual bool DrawsContent() const OVERRIDE { return true; }
+
+private:
+ DrawsContentLayer() : Layer() { }
+ virtual ~DrawsContentLayer()
+ {
+ }
+};
+
+class HudWithRootLayerChange : public HeadsUpDisplayTest {
+public:
+ HudWithRootLayerChange()
+ : m_rootLayer1(DrawsContentLayer::Create())
+ , m_rootLayer2(DrawsContentLayer::Create())
+ , m_numCommits(0)
+ {
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_rootLayer1->SetBounds(gfx::Size(30, 30));
+ m_rootLayer2->SetBounds(gfx::Size(30, 30));
+
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void didCommit() OVERRIDE
+ {
+ ++m_numCommits;
+
+ ASSERT_TRUE(m_layerTreeHost->hud_layer());
+
+ switch (m_numCommits) {
+ case 1:
+ // Change directly to a new root layer.
+ m_layerTreeHost->SetRootLayer(m_rootLayer1);
+ break;
+ case 2:
+ EXPECT_EQ(m_rootLayer1.get(), m_layerTreeHost->hud_layer()->parent());
+ // Unset the root layer.
+ m_layerTreeHost->SetRootLayer(NULL);
+ break;
+ case 3:
+ EXPECT_EQ(0, m_layerTreeHost->hud_layer()->parent());
+ // Change back to the previous root layer.
+ m_layerTreeHost->SetRootLayer(m_rootLayer1);
+ break;
+ case 4:
+ EXPECT_EQ(m_rootLayer1.get(), m_layerTreeHost->hud_layer()->parent());
+ // Unset the root layer.
+ m_layerTreeHost->SetRootLayer(NULL);
+ break;
+ case 5:
+ EXPECT_EQ(0, m_layerTreeHost->hud_layer()->parent());
+ // Change to a new root layer from a null root.
+ m_layerTreeHost->SetRootLayer(m_rootLayer2);
+ break;
+ case 6:
+ EXPECT_EQ(m_rootLayer2.get(), m_layerTreeHost->hud_layer()->parent());
+ // Change directly back to the last root layer/
+ m_layerTreeHost->SetRootLayer(m_rootLayer1);
+ break;
+ case 7:
+ EXPECT_EQ(m_rootLayer1.get(), m_layerTreeHost->hud_layer()->parent());
+ endTest();
+ break;
+ }
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ scoped_refptr<DrawsContentLayer> m_rootLayer1;
+ scoped_refptr<DrawsContentLayer> m_rootLayer2;
+ int m_numCommits;
+};
+
+TEST_F(HudWithRootLayerChange, runMultiThread)
+{
+ runTest(true);
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/image_layer.cc b/cc/layers/image_layer.cc
new file mode 100644
index 0000000..4f5e105
--- /dev/null
+++ b/cc/layers/image_layer.cc
@@ -0,0 +1,96 @@
+// Copyright 2010 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 "cc/layers/image_layer.h"
+
+#include "base/compiler_specific.h"
+#include "cc/resources/image_layer_updater.h"
+#include "cc/resources/layer_updater.h"
+#include "cc/resources/prioritized_resource.h"
+#include "cc/resources/resource_update_queue.h"
+#include "cc/trees/layer_tree_host.h"
+
+namespace cc {
+
+scoped_refptr<ImageLayer> ImageLayer::Create() {
+ return make_scoped_refptr(new ImageLayer());
+}
+
+ImageLayer::ImageLayer() : TiledLayer() {}
+
+ImageLayer::~ImageLayer() {}
+
+void ImageLayer::SetBitmap(const SkBitmap& bitmap) {
+ // SetBitmap() currently gets called whenever there is any
+ // style change that affects the layer even if that change doesn't
+ // affect the actual contents of the image (e.g. a CSS animation).
+ // With this check in place we avoid unecessary texture uploads.
+ if (bitmap.pixelRef() && bitmap.pixelRef() == bitmap_.pixelRef())
+ return;
+
+ bitmap_ = bitmap;
+ SetNeedsDisplay();
+}
+
+void ImageLayer::SetTexturePriorities(const PriorityCalculator& priority_calc) {
+ // Update the tile data before creating all the layer's tiles.
+ UpdateTileSizeAndTilingOption();
+
+ TiledLayer::SetTexturePriorities(priority_calc);
+}
+
+void ImageLayer::Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) {
+ CreateUpdaterIfNeeded();
+ if (needs_display_) {
+ updater_->set_bitmap(bitmap_);
+ UpdateTileSizeAndTilingOption();
+ InvalidateContentRect(gfx::Rect(content_bounds()));
+ needs_display_ = false;
+ }
+ TiledLayer::Update(queue, occlusion, stats);
+}
+
+void ImageLayer::CreateUpdaterIfNeeded() {
+ if (updater_)
+ return;
+
+ updater_ = ImageLayerUpdater::Create();
+ GLenum texture_format =
+ layer_tree_host()->GetRendererCapabilities().best_texture_format;
+ SetTextureFormat(texture_format);
+}
+
+LayerUpdater* ImageLayer::Updater() const {
+ return updater_.get();
+}
+
+void ImageLayer::CalculateContentsScale(float ideal_contents_scale,
+ bool animating_transform_to_screen,
+ float* contents_scale_x,
+ float* contents_scale_y,
+ gfx::Size* content_bounds) {
+ *contents_scale_x = ImageContentsScaleX();
+ *contents_scale_y = ImageContentsScaleY();
+ *content_bounds = gfx::Size(bitmap_.width(), bitmap_.height());
+}
+
+bool ImageLayer::DrawsContent() const {
+ return !bitmap_.isNull() && TiledLayer::DrawsContent();
+}
+
+float ImageLayer::ImageContentsScaleX() const {
+ if (bounds().IsEmpty() || bitmap_.width() == 0)
+ return 1;
+ return static_cast<float>(bitmap_.width()) / bounds().width();
+}
+
+float ImageLayer::ImageContentsScaleY() const {
+ if (bounds().IsEmpty() || bitmap_.height() == 0)
+ return 1;
+ return static_cast<float>(bitmap_.height()) / bounds().height();
+}
+
+} // namespace cc
diff --git a/cc/layers/image_layer.h b/cc/layers/image_layer.h
new file mode 100644
index 0000000..8ba5b78
--- /dev/null
+++ b/cc/layers/image_layer.h
@@ -0,0 +1,56 @@
+// Copyright 2010 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.
+
+#ifndef CC_LAYERS_IMAGE_LAYER_H_
+#define CC_LAYERS_IMAGE_LAYER_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/content_layer.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace cc {
+
+class ImageLayerUpdater;
+
+// A Layer that contains only an Image element.
+class CC_EXPORT ImageLayer : public TiledLayer {
+ public:
+ static scoped_refptr<ImageLayer> Create();
+
+ // Layer implementation.
+ virtual bool DrawsContent() const OVERRIDE;
+ virtual void SetTexturePriorities(const PriorityCalculator& priority_calc)
+ OVERRIDE;
+ virtual void Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) OVERRIDE;
+ virtual void CalculateContentsScale(float ideal_contents_scale,
+ bool animating_transform_to_screen,
+ float* contents_scale_x,
+ float* contents_scale_y,
+ gfx::Size* content_bounds) OVERRIDE;
+
+ void SetBitmap(const SkBitmap& image);
+
+ private:
+ ImageLayer();
+ virtual ~ImageLayer();
+
+ // TiledLayer Implementation.
+ virtual LayerUpdater* Updater() const OVERRIDE;
+ virtual void CreateUpdaterIfNeeded() OVERRIDE;
+
+ float ImageContentsScaleX() const;
+ float ImageContentsScaleY() const;
+
+ SkBitmap bitmap_;
+
+ scoped_refptr<ImageLayerUpdater> updater_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageLayer);
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_IMAGE_LAYER_H_
diff --git a/cc/layers/io_surface_layer.cc b/cc/layers/io_surface_layer.cc
new file mode 100644
index 0000000..3ccbdd9
--- /dev/null
+++ b/cc/layers/io_surface_layer.cc
@@ -0,0 +1,43 @@
+// Copyright 2012 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 "cc/layers/io_surface_layer.h"
+
+#include "cc/layers/io_surface_layer_impl.h"
+
+namespace cc {
+
+scoped_refptr<IOSurfaceLayer> IOSurfaceLayer::Create() {
+ return make_scoped_refptr(new IOSurfaceLayer());
+}
+
+IOSurfaceLayer::IOSurfaceLayer() : Layer(), io_surface_id_(0) {}
+
+IOSurfaceLayer::~IOSurfaceLayer() {}
+
+void IOSurfaceLayer::SetIOSurfaceProperties(uint32_t io_surface_id,
+ gfx::Size size) {
+ io_surface_id_ = io_surface_id;
+ io_surface_size_ = size;
+ SetNeedsCommit();
+}
+
+scoped_ptr<LayerImpl> IOSurfaceLayer::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return IOSurfaceLayerImpl::Create(tree_impl, layer_id_).PassAs<LayerImpl>();
+}
+
+bool IOSurfaceLayer::DrawsContent() const {
+ return io_surface_id_ && Layer::DrawsContent();
+}
+
+void IOSurfaceLayer::PushPropertiesTo(LayerImpl* layer) {
+ Layer::PushPropertiesTo(layer);
+
+ IOSurfaceLayerImpl* io_surface_layer =
+ static_cast<IOSurfaceLayerImpl*>(layer);
+ io_surface_layer->SetIOSurfaceProperties(io_surface_id_, io_surface_size_);
+}
+
+} // namespace cc
diff --git a/cc/layers/io_surface_layer.h b/cc/layers/io_surface_layer.h
new file mode 100644
index 0000000..4073a0d
--- /dev/null
+++ b/cc/layers/io_surface_layer.h
@@ -0,0 +1,35 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_IO_SURFACE_LAYER_H_
+#define CC_LAYERS_IO_SURFACE_LAYER_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer.h"
+
+namespace cc {
+
+class CC_EXPORT IOSurfaceLayer : public Layer {
+ public:
+ static scoped_refptr<IOSurfaceLayer> Create();
+
+ void SetIOSurfaceProperties(uint32_t io_surface_id, gfx::Size size);
+
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+ virtual bool DrawsContent() const OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+
+ protected:
+ IOSurfaceLayer();
+
+ private:
+ virtual ~IOSurfaceLayer();
+
+ uint32_t io_surface_id_;
+ gfx::Size io_surface_size_;
+};
+
+}
+#endif // CC_LAYERS_IO_SURFACE_LAYER_H_
diff --git a/cc/layers/io_surface_layer_impl.cc b/cc/layers/io_surface_layer_impl.cc
new file mode 100644
index 0000000..3bfc1de
--- /dev/null
+++ b/cc/layers/io_surface_layer_impl.cc
@@ -0,0 +1,145 @@
+// Copyright 2012 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 "cc/layers/io_surface_layer_impl.h"
+
+#include "base/stringprintf.h"
+#include "cc/layers/quad_sink.h"
+#include "cc/output/gl_renderer.h" // For the GLC() macro.
+#include "cc/output/output_surface.h"
+#include "cc/quads/io_surface_draw_quad.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+
+namespace cc {
+
+IOSurfaceLayerImpl::IOSurfaceLayerImpl(LayerTreeImpl* tree_impl, int id)
+ : LayerImpl(tree_impl, id),
+ io_surface_id_(0),
+ io_surface_changed_(false),
+ io_surface_texture_id_(0) {}
+
+IOSurfaceLayerImpl::~IOSurfaceLayerImpl() {
+ if (!io_surface_texture_id_)
+ return;
+
+ OutputSurface* output_surface = layer_tree_impl()->output_surface();
+ // FIXME: Implement this path for software compositing.
+ WebKit::WebGraphicsContext3D* context3d = output_surface->context3d();
+ if (context3d)
+ context3d->deleteTexture(io_surface_texture_id_);
+}
+
+scoped_ptr<LayerImpl> IOSurfaceLayerImpl::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return IOSurfaceLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
+}
+
+void IOSurfaceLayerImpl::PushPropertiesTo(LayerImpl* layer) {
+ LayerImpl::PushPropertiesTo(layer);
+
+ IOSurfaceLayerImpl* io_surface_layer =
+ static_cast<IOSurfaceLayerImpl*>(layer);
+ io_surface_layer->SetIOSurfaceProperties(io_surface_id_, io_surface_size_);
+}
+
+void IOSurfaceLayerImpl::WillDraw(ResourceProvider* resource_provider) {
+ LayerImpl::WillDraw(resource_provider);
+
+ if (io_surface_changed_) {
+ WebKit::WebGraphicsContext3D* context3d =
+ resource_provider->GraphicsContext3D();
+ if (!context3d) {
+ // FIXME: Implement this path for software compositing.
+ return;
+ }
+
+ // FIXME: Do this in a way that we can track memory usage.
+ if (!io_surface_texture_id_)
+ io_surface_texture_id_ = context3d->createTexture();
+
+ GLC(context3d, context3d->activeTexture(GL_TEXTURE0));
+ GLC(context3d,
+ context3d->bindTexture(GL_TEXTURE_RECTANGLE_ARB,
+ io_surface_texture_id_));
+ GLC(context3d,
+ context3d->texParameteri(
+ GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ GLC(context3d,
+ context3d->texParameteri(
+ GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ GLC(context3d,
+ context3d->texParameteri(
+ GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ GLC(context3d,
+ context3d->texParameteri(
+ GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+ context3d->texImageIOSurface2DCHROMIUM(GL_TEXTURE_RECTANGLE_ARB,
+ io_surface_size_.width(),
+ io_surface_size_.height(),
+ io_surface_id_,
+ 0);
+ // Do not check for error conditions. texImageIOSurface2DCHROMIUM is
+ // supposed to hold on to the last good IOSurface if the new one is already
+ // closed. This is only a possibility during live resizing of plugins.
+ // However, it seems that this is not sufficient to completely guard against
+ // garbage being drawn. If this is found to be a significant issue, it may
+ // be necessary to explicitly tell the embedder when to free the surfaces it
+ // has allocated.
+ io_surface_changed_ = false;
+ }
+}
+
+void IOSurfaceLayerImpl::AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) {
+ SharedQuadState* shared_quad_state =
+ quad_sink->UseSharedQuadState(CreateSharedQuadState());
+ AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
+
+ gfx::Rect quad_rect(gfx::Point(), content_bounds());
+ gfx::Rect opaque_rect(contents_opaque() ? quad_rect : gfx::Rect());
+ scoped_ptr<IOSurfaceDrawQuad> quad = IOSurfaceDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ quad_rect,
+ opaque_rect,
+ io_surface_size_,
+ io_surface_texture_id_,
+ IOSurfaceDrawQuad::FLIPPED);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+}
+
+void IOSurfaceLayerImpl::DumpLayerProperties(std::string* str,
+ int indent) const {
+ str->append(IndentString(indent));
+ base::StringAppendF(str,
+ "iosurface id: %u texture id: %u\n",
+ io_surface_id_,
+ io_surface_texture_id_);
+ LayerImpl::DumpLayerProperties(str, indent);
+}
+
+void IOSurfaceLayerImpl::DidLoseOutputSurface() {
+ // We don't have a valid texture ID in the new context; however,
+ // the IOSurface is still valid.
+ io_surface_texture_id_ = 0;
+ io_surface_changed_ = true;
+}
+
+void IOSurfaceLayerImpl::SetIOSurfaceProperties(unsigned io_surface_id,
+ gfx::Size size) {
+ if (io_surface_id_ != io_surface_id)
+ io_surface_changed_ = true;
+
+ io_surface_id_ = io_surface_id;
+ io_surface_size_ = size;
+}
+
+const char* IOSurfaceLayerImpl::LayerTypeAsString() const {
+ return "IOSurfaceLayer";
+}
+
+} // namespace cc
diff --git a/cc/layers/io_surface_layer_impl.h b/cc/layers/io_surface_layer_impl.h
new file mode 100644
index 0000000..2c04ddd
--- /dev/null
+++ b/cc/layers/io_surface_layer_impl.h
@@ -0,0 +1,51 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_IO_SURFACE_LAYER_IMPL_H_
+#define CC_LAYERS_IO_SURFACE_LAYER_IMPL_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer_impl.h"
+#include "ui/gfx/size.h"
+
+namespace cc {
+
+class CC_EXPORT IOSurfaceLayerImpl : public LayerImpl {
+ public:
+ static scoped_ptr<IOSurfaceLayerImpl> Create(LayerTreeImpl* tree_impl,
+ int id) {
+ return make_scoped_ptr(new IOSurfaceLayerImpl(tree_impl, id));
+ }
+ virtual ~IOSurfaceLayerImpl();
+
+ void SetIOSurfaceProperties(unsigned io_surface_id, gfx::Size size);
+
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer_tree_impl) OVERRIDE;
+
+ virtual void AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) OVERRIDE;
+
+ virtual void WillDraw(ResourceProvider* resource_provider) OVERRIDE;
+ virtual void DidLoseOutputSurface() OVERRIDE;
+
+ virtual void DumpLayerProperties(std::string* str, int indent) const OVERRIDE;
+
+ private:
+ IOSurfaceLayerImpl(LayerTreeImpl* tree_impl, int id);
+
+ virtual const char* LayerTypeAsString() const OVERRIDE;
+
+ unsigned io_surface_id_;
+ gfx::Size io_surface_size_;
+ bool io_surface_changed_;
+ unsigned io_surface_texture_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(IOSurfaceLayerImpl);
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_IO_SURFACE_LAYER_IMPL_H_
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
new file mode 100644
index 0000000..53a3390
--- /dev/null
+++ b/cc/layers/layer.cc
@@ -0,0 +1,817 @@
+// Copyright 2010 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 "cc/layers/layer.h"
+
+#include "cc/animation/animation.h"
+#include "cc/animation/animation_events.h"
+#include "cc/animation/layer_animation_controller.h"
+#include "cc/layers/layer_impl.h"
+#include "cc/trees/layer_tree_host.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebAnimationDelegate.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebLayerScrollClient.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h"
+#include "third_party/skia/include/core/SkImageFilter.h"
+#include "ui/gfx/rect_conversions.h"
+
+namespace cc {
+
+static int s_next_layer_id = 1;
+
+scoped_refptr<Layer> Layer::Create() {
+ return make_scoped_refptr(new Layer());
+}
+
+Layer::Layer()
+ : needs_display_(false),
+ stacking_order_changed_(false),
+ layer_id_(s_next_layer_id++),
+ ignore_set_needs_commit_(false),
+ parent_(NULL),
+ layer_tree_host_(NULL),
+ scrollable_(false),
+ should_scroll_on_main_thread_(false),
+ have_wheel_event_handlers_(false),
+ anchor_point_(0.5f, 0.5f),
+ background_color_(0),
+ opacity_(1.f),
+ anchor_point_z_(0.f),
+ is_container_for_fixed_position_layers_(false),
+ fixed_to_container_layer_(false),
+ is_drawable_(false),
+ masks_to_bounds_(false),
+ contents_opaque_(false),
+ double_sided_(true),
+ preserves_3d_(false),
+ use_parent_backface_visibility_(false),
+ draw_checkerboard_for_missing_tiles_(false),
+ force_render_surface_(false),
+ replica_layer_(NULL),
+ raster_scale_(1.f),
+ automatically_compute_raster_scale_(false),
+ bounds_contain_page_scale_(false),
+ layer_animation_delegate_(NULL),
+ layer_scroll_client_(NULL) {
+ if (layer_id_ < 0) {
+ s_next_layer_id = 1;
+ layer_id_ = s_next_layer_id++;
+ }
+
+ layer_animation_controller_ = LayerAnimationController::Create(layer_id_);
+ layer_animation_controller_->AddObserver(this);
+ AddLayerAnimationEventObserver(layer_animation_controller_.get());
+}
+
+Layer::~Layer() {
+ // Our parent should be holding a reference to us so there should be no
+ // way for us to be destroyed while we still have a parent.
+ DCHECK(!parent());
+
+ layer_animation_controller_->RemoveObserver(this);
+
+ // Remove the parent reference from all children and dependents.
+ RemoveAllChildren();
+ if (mask_layer_) {
+ DCHECK_EQ(this, mask_layer_->parent());
+ mask_layer_->RemoveFromParent();
+ }
+ if (replica_layer_) {
+ DCHECK_EQ(this, replica_layer_->parent());
+ replica_layer_->RemoveFromParent();
+ }
+}
+
+void Layer::SetLayerTreeHost(LayerTreeHost* host) {
+ if (layer_tree_host_ == host)
+ return;
+
+ layer_tree_host_ = host;
+
+ for (size_t i = 0; i < children_.size(); ++i)
+ children_[i]->SetLayerTreeHost(host);
+
+ if (mask_layer_)
+ mask_layer_->SetLayerTreeHost(host);
+ if (replica_layer_)
+ replica_layer_->SetLayerTreeHost(host);
+
+ layer_animation_controller_->SetAnimationRegistrar(
+ host ? host->animation_registrar() : NULL);
+
+ if (host && layer_animation_controller_->has_any_animation())
+ host->SetNeedsCommit();
+ if (host &&
+ (!filters_.isEmpty() || !background_filters_.isEmpty() || filter_))
+ layer_tree_host_->set_needs_filter_context();
+
+}
+
+void Layer::SetNeedsCommit() {
+ if (ignore_set_needs_commit_)
+ return;
+ if (layer_tree_host_)
+ layer_tree_host_->SetNeedsCommit();
+}
+
+void Layer::SetNeedsFullTreeSync() {
+ if (layer_tree_host_)
+ layer_tree_host_->SetNeedsFullTreeSync();
+}
+
+gfx::Rect Layer::LayerRectToContentRect(const gfx::RectF& layer_rect) const {
+ gfx::RectF content_rect =
+ gfx::ScaleRect(layer_rect, contents_scale_x(), contents_scale_y());
+ // Intersect with content rect to avoid the extra pixel because for some
+ // values x and y, ceil((x / y) * y) may be x + 1.
+ content_rect.Intersect(gfx::Rect(gfx::Point(), content_bounds()));
+ return gfx::ToEnclosingRect(content_rect);
+}
+
+bool Layer::BlocksPendingCommit() const {
+ return false;
+}
+
+bool Layer::CanClipSelf() const {
+ return false;
+}
+
+bool Layer::BlocksPendingCommitRecursive() const {
+ if (BlocksPendingCommit())
+ return true;
+ if (mask_layer() && mask_layer()->BlocksPendingCommitRecursive())
+ return true;
+ if (replica_layer() && replica_layer()->BlocksPendingCommitRecursive())
+ return true;
+ for (size_t i = 0; i < children_.size(); ++i) {
+ if (children_[i]->BlocksPendingCommitRecursive())
+ return true;
+ }
+ return false;
+}
+
+void Layer::SetParent(Layer* layer) {
+ DCHECK(!layer || !layer->HasAncestor(this));
+ parent_ = layer;
+ SetLayerTreeHost(parent_ ? parent_->layer_tree_host() : NULL);
+
+ ForceAutomaticRasterScaleToBeRecomputed();
+ if (mask_layer_)
+ mask_layer_->ForceAutomaticRasterScaleToBeRecomputed();
+ if (replica_layer_ && replica_layer_->mask_layer_)
+ replica_layer_->mask_layer_->ForceAutomaticRasterScaleToBeRecomputed();
+}
+
+bool Layer::HasAncestor(Layer* ancestor) const {
+ for (const Layer* layer = parent(); layer; layer = layer->parent()) {
+ if (layer == ancestor)
+ return true;
+ }
+ return false;
+}
+
+void Layer::AddChild(scoped_refptr<Layer> child) {
+ InsertChild(child, children_.size());
+}
+
+void Layer::InsertChild(scoped_refptr<Layer> child, size_t index) {
+ child->RemoveFromParent();
+ child->SetParent(this);
+ child->stacking_order_changed_ = true;
+
+ index = std::min(index, children_.size());
+ children_.insert(children_.begin() + index, child);
+ SetNeedsFullTreeSync();
+}
+
+void Layer::RemoveFromParent() {
+ if (parent_)
+ parent_->RemoveChildOrDependent(this);
+}
+
+void Layer::RemoveChildOrDependent(Layer* child) {
+ if (mask_layer_ == child) {
+ mask_layer_->SetParent(NULL);
+ mask_layer_ = NULL;
+ SetNeedsFullTreeSync();
+ return;
+ }
+ if (replica_layer_ == child) {
+ replica_layer_->SetParent(NULL);
+ replica_layer_ = NULL;
+ SetNeedsFullTreeSync();
+ return;
+ }
+
+ for (LayerList::iterator iter = children_.begin();
+ iter != children_.end();
+ ++iter) {
+ if (*iter != child)
+ continue;
+
+ child->SetParent(NULL);
+ children_.erase(iter);
+ SetNeedsFullTreeSync();
+ return;
+ }
+}
+
+void Layer::ReplaceChild(Layer* reference, scoped_refptr<Layer> new_layer) {
+ DCHECK(reference);
+ DCHECK_EQ(reference->parent(), this);
+
+ if (reference == new_layer)
+ return;
+
+ int reference_index = IndexOfChild(reference);
+ if (reference_index == -1) {
+ NOTREACHED();
+ return;
+ }
+
+ reference->RemoveFromParent();
+
+ if (new_layer) {
+ new_layer->RemoveFromParent();
+ InsertChild(new_layer, reference_index);
+ }
+}
+
+int Layer::IndexOfChild(const Layer* reference) {
+ for (size_t i = 0; i < children_.size(); ++i) {
+ if (children_[i] == reference)
+ return i;
+ }
+ return -1;
+}
+
+void Layer::SetBounds(gfx::Size size) {
+ if (bounds() == size)
+ return;
+
+ bool first_resize = bounds().IsEmpty() && !size.IsEmpty();
+
+ bounds_ = size;
+
+ if (first_resize)
+ SetNeedsDisplay();
+ else
+ SetNeedsCommit();
+}
+
+Layer* Layer::RootLayer() {
+ Layer* layer = this;
+ while (layer->parent())
+ layer = layer->parent();
+ return layer;
+}
+
+void Layer::RemoveAllChildren() {
+ while (children_.size()) {
+ Layer* layer = children_[0].get();
+ DCHECK_EQ(this, layer->parent());
+ layer->RemoveFromParent();
+ }
+}
+
+void Layer::SetChildren(const LayerList& children) {
+ if (children == children_)
+ return;
+
+ RemoveAllChildren();
+ for (size_t i = 0; i < children.size(); ++i)
+ AddChild(children[i]);
+}
+
+void Layer::SetAnchorPoint(gfx::PointF anchor_point) {
+ if (anchor_point_ == anchor_point)
+ return;
+ anchor_point_ = anchor_point;
+ SetNeedsCommit();
+}
+
+void Layer::SetAnchorPointZ(float anchor_point_z) {
+ if (anchor_point_z_ == anchor_point_z)
+ return;
+ anchor_point_z_ = anchor_point_z;
+ SetNeedsCommit();
+}
+
+void Layer::SetBackgroundColor(SkColor background_color) {
+ if (background_color_ == background_color)
+ return;
+ background_color_ = background_color;
+ SetNeedsCommit();
+}
+
+void Layer::CalculateContentsScale(
+ float ideal_contents_scale,
+ bool animating_transform_to_screen,
+ float* contents_scale_x,
+ float* contents_scale_y,
+ gfx::Size* contentBounds) {
+ *contents_scale_x = 1;
+ *contents_scale_y = 1;
+ *contentBounds = bounds();
+}
+
+void Layer::SetMasksToBounds(bool masks_to_bounds) {
+ if (masks_to_bounds_ == masks_to_bounds)
+ return;
+ masks_to_bounds_ = masks_to_bounds;
+ SetNeedsCommit();
+}
+
+void Layer::SetMaskLayer(Layer* mask_layer) {
+ if (mask_layer_ == mask_layer)
+ return;
+ if (mask_layer_) {
+ DCHECK_EQ(this, mask_layer_->parent());
+ mask_layer_->RemoveFromParent();
+ }
+ mask_layer_ = mask_layer;
+ if (mask_layer_) {
+ DCHECK(!mask_layer_->parent());
+ mask_layer_->RemoveFromParent();
+ mask_layer_->SetParent(this);
+ mask_layer_->SetIsMask(true);
+ }
+ SetNeedsFullTreeSync();
+}
+
+void Layer::SetReplicaLayer(Layer* layer) {
+ if (replica_layer_ == layer)
+ return;
+ if (replica_layer_) {
+ DCHECK_EQ(this, replica_layer_->parent());
+ replica_layer_->RemoveFromParent();
+ }
+ replica_layer_ = layer;
+ if (replica_layer_) {
+ DCHECK(!replica_layer_->parent());
+ replica_layer_->RemoveFromParent();
+ replica_layer_->SetParent(this);
+ }
+ SetNeedsFullTreeSync();
+}
+
+void Layer::SetFilters(const WebKit::WebFilterOperations& filters) {
+ if (filters_ == filters)
+ return;
+ DCHECK(!filter_);
+ filters_ = filters;
+ SetNeedsCommit();
+ if (!filters.isEmpty() && layer_tree_host_)
+ layer_tree_host_->set_needs_filter_context();
+}
+
+void Layer::SetFilter(const skia::RefPtr<SkImageFilter>& filter) {
+ if (filter_.get() == filter.get())
+ return;
+ DCHECK(filters_.isEmpty());
+ filter_ = filter;
+ SetNeedsCommit();
+ if (filter && layer_tree_host_)
+ layer_tree_host_->set_needs_filter_context();
+}
+
+void Layer::SetBackgroundFilters(const WebKit::WebFilterOperations& filters) {
+ if (background_filters_ == filters)
+ return;
+ background_filters_ = filters;
+ SetNeedsCommit();
+ if (!filters.isEmpty() && layer_tree_host_)
+ layer_tree_host_->set_needs_filter_context();
+}
+
+void Layer::SetOpacity(float opacity) {
+ if (opacity_ == opacity)
+ return;
+ opacity_ = opacity;
+ SetNeedsCommit();
+}
+
+bool Layer::OpacityIsAnimating() const {
+ return layer_animation_controller_->IsAnimatingProperty(Animation::Opacity);
+}
+
+bool Layer::OpacityCanAnimateOnImplThread() const {
+ return false;
+}
+
+void Layer::SetContentsOpaque(bool opaque) {
+ if (contents_opaque_ == opaque)
+ return;
+ contents_opaque_ = opaque;
+ SetNeedsCommit();
+}
+
+void Layer::SetPosition(gfx::PointF position) {
+ if (position_ == position)
+ return;
+ position_ = position;
+ SetNeedsCommit();
+}
+
+void Layer::SetSublayerTransform(const gfx::Transform& sublayer_transform) {
+ if (sublayer_transform_ == sublayer_transform)
+ return;
+ sublayer_transform_ = sublayer_transform;
+ SetNeedsCommit();
+}
+
+void Layer::SetTransform(const gfx::Transform& transform) {
+ if (transform_ == transform)
+ return;
+ transform_ = transform;
+ SetNeedsCommit();
+}
+
+bool Layer::TransformIsAnimating() const {
+ return layer_animation_controller_->IsAnimatingProperty(Animation::Transform);
+}
+
+void Layer::SetScrollOffset(gfx::Vector2d scroll_offset) {
+ if (scroll_offset_ == scroll_offset)
+ return;
+ scroll_offset_ = scroll_offset;
+ if (layer_scroll_client_)
+ layer_scroll_client_->didScroll();
+ SetNeedsCommit();
+}
+
+void Layer::SetMaxScrollOffset(gfx::Vector2d max_scroll_offset) {
+ if (max_scroll_offset_ == max_scroll_offset)
+ return;
+ max_scroll_offset_ = max_scroll_offset;
+ SetNeedsCommit();
+}
+
+void Layer::SetScrollable(bool scrollable) {
+ if (scrollable_ == scrollable)
+ return;
+ scrollable_ = scrollable;
+ SetNeedsCommit();
+}
+
+void Layer::SetShouldScrollOnMainThread(bool should_scroll_on_main_thread) {
+ if (should_scroll_on_main_thread_ == should_scroll_on_main_thread)
+ return;
+ should_scroll_on_main_thread_ = should_scroll_on_main_thread;
+ SetNeedsCommit();
+}
+
+void Layer::SetHaveWheelEventHandlers(bool have_wheel_event_handlers) {
+ if (have_wheel_event_handlers_ == have_wheel_event_handlers)
+ return;
+ have_wheel_event_handlers_ = have_wheel_event_handlers;
+ SetNeedsCommit();
+}
+
+void Layer::SetNonFastScrollableRegion(const Region& region) {
+ if (non_fast_scrollable_region_ == region)
+ return;
+ non_fast_scrollable_region_ = region;
+ SetNeedsCommit();
+}
+
+void Layer::SetTouchEventHandlerRegion(const Region& region) {
+ if (touch_event_handler_region_ == region)
+ return;
+ touch_event_handler_region_ = region;
+}
+
+void Layer::SetDrawCheckerboardForMissingTiles(bool checkerboard) {
+ if (draw_checkerboard_for_missing_tiles_ == checkerboard)
+ return;
+ draw_checkerboard_for_missing_tiles_ = checkerboard;
+ SetNeedsCommit();
+}
+
+void Layer::SetForceRenderSurface(bool force) {
+ if (force_render_surface_ == force)
+ return;
+ force_render_surface_ = force;
+ SetNeedsCommit();
+}
+
+void Layer::SetImplTransform(const gfx::Transform& transform) {
+ if (impl_transform_ == transform)
+ return;
+ impl_transform_ = transform;
+ SetNeedsCommit();
+}
+
+void Layer::SetDoubleSided(bool double_sided) {
+ if (double_sided_ == double_sided)
+ return;
+ double_sided_ = double_sided;
+ SetNeedsCommit();
+}
+
+void Layer::SetIsDrawable(bool is_drawable) {
+ if (is_drawable_ == is_drawable)
+ return;
+
+ is_drawable_ = is_drawable;
+ SetNeedsCommit();
+}
+
+void Layer::SetNeedsDisplayRect(const gfx::RectF& dirty_rect) {
+ update_rect_.Union(dirty_rect);
+ needs_display_ = true;
+
+ // Simply mark the contents as dirty. For non-root layers, the call to
+ // SetNeedsCommit will schedule a fresh compositing pass.
+ // For the root layer, SetNeedsCommit has no effect.
+ if (DrawsContent() && !update_rect_.IsEmpty())
+ SetNeedsCommit();
+}
+
+bool Layer::DescendantIsFixedToContainerLayer() const {
+ for (size_t i = 0; i < children_.size(); ++i) {
+ if (children_[i]->fixed_to_container_layer() ||
+ children_[i]->DescendantIsFixedToContainerLayer())
+ return true;
+ }
+ return false;
+}
+
+void Layer::SetIsContainerForFixedPositionLayers(bool container) {
+ if (is_container_for_fixed_position_layers_ == container)
+ return;
+ is_container_for_fixed_position_layers_ = container;
+
+ if (layer_tree_host_ && layer_tree_host_->CommitRequested())
+ return;
+
+ // Only request a commit if we have a fixed positioned descendant.
+ if (DescendantIsFixedToContainerLayer())
+ SetNeedsCommit();
+}
+
+void Layer::SetFixedToContainerLayer(bool fixed_to_container_layer) {
+ if (fixed_to_container_layer_ == fixed_to_container_layer)
+ return;
+ fixed_to_container_layer_ = fixed_to_container_layer;
+ SetNeedsCommit();
+}
+
+void Layer::PushPropertiesTo(LayerImpl* layer) {
+ layer->SetAnchorPoint(anchor_point_);
+ layer->SetAnchorPointZ(anchor_point_z_);
+ layer->SetBackgroundColor(background_color_);
+ layer->SetBounds(bounds_);
+ layer->SetContentBounds(content_bounds());
+ layer->SetContentsScale(contents_scale_x(), contents_scale_y());
+ layer->SetDebugName(debug_name_);
+ layer->SetDoubleSided(double_sided_);
+ layer->SetDrawCheckerboardForMissingTiles(
+ draw_checkerboard_for_missing_tiles_);
+ layer->SetForceRenderSurface(force_render_surface_);
+ layer->SetDrawsContent(DrawsContent());
+ layer->SetFilters(filters());
+ layer->SetFilter(filter());
+ layer->SetBackgroundFilters(background_filters());
+ layer->SetMasksToBounds(masks_to_bounds_);
+ layer->SetShouldScrollOnMainThread(should_scroll_on_main_thread_);
+ layer->SetHaveWheelEventHandlers(have_wheel_event_handlers_);
+ layer->SetNonFastScrollableRegion(non_fast_scrollable_region_);
+ layer->SetTouchEventHandlerRegion(touch_event_handler_region_);
+ layer->SetContentsOpaque(contents_opaque_);
+ if (!layer->OpacityIsAnimatingOnImplOnly())
+ layer->SetOpacity(opacity_);
+ DCHECK(!(OpacityIsAnimating() && layer->OpacityIsAnimatingOnImplOnly()));
+ layer->SetPosition(position_);
+ layer->SetIsContainerForFixedPositionLayers(
+ is_container_for_fixed_position_layers_);
+ layer->SetFixedToContainerLayer(fixed_to_container_layer_);
+ layer->SetPreserves3d(preserves_3d());
+ layer->SetUseParentBackfaceVisibility(use_parent_backface_visibility_);
+ layer->SetSublayerTransform(sublayer_transform_);
+ if (!layer->TransformIsAnimatingOnImplOnly())
+ layer->SetTransform(transform_);
+ DCHECK(!(TransformIsAnimating() && layer->TransformIsAnimatingOnImplOnly()));
+
+ layer->SetScrollable(scrollable_);
+ layer->SetScrollOffset(scroll_offset_);
+ layer->SetMaxScrollOffset(max_scroll_offset_);
+
+ // If the main thread commits multiple times before the impl thread actually
+ // draws, then damage tracking will become incorrect if we simply clobber the
+ // updateRect here. The LayerImpl's updateRect needs to accumulate (i.e.
+ // union) any update changes that have occurred on the main thread.
+ update_rect_.Union(layer->update_rect());
+ layer->set_update_rect(update_rect_);
+
+ if (layer->layer_tree_impl()->settings().implSidePainting) {
+ DCHECK(layer->layer_tree_impl()->IsPendingTree());
+ LayerImpl* active_twin =
+ layer->layer_tree_impl()->FindActiveTreeLayerById(id());
+ // Update the scroll delta from the active layer, which may have
+ // adjusted its scroll delta prior to this pending layer being created.
+ // This code is identical to that in LayerImpl::setScrollDelta.
+ if (active_twin) {
+ DCHECK(layer->sent_scroll_delta().IsZero());
+ layer->SetScrollDelta(active_twin->scroll_delta() -
+ active_twin->sent_scroll_delta());
+ }
+ } else {
+ layer->SetScrollDelta(layer->scroll_delta() - layer->sent_scroll_delta());
+ layer->SetSentScrollDelta(gfx::Vector2d());
+ }
+
+ layer->SetStackingOrderChanged(stacking_order_changed_);
+
+ layer_animation_controller_->PushAnimationUpdatesTo(
+ layer->layer_animation_controller());
+
+ // Reset any state that should be cleared for the next update.
+ stacking_order_changed_ = false;
+ update_rect_ = gfx::RectF();
+}
+
+scoped_ptr<LayerImpl> Layer::CreateLayerImpl(LayerTreeImpl* tree_impl) {
+ return LayerImpl::Create(tree_impl, layer_id_);
+}
+
+bool Layer::DrawsContent() const {
+ return is_drawable_;
+}
+
+bool Layer::NeedMoreUpdates() {
+ return false;
+}
+
+void Layer::SetDebugName(const std::string& debug_name) {
+ debug_name_ = debug_name;
+ SetNeedsCommit();
+}
+
+void Layer::SetRasterScale(float scale) {
+ if (raster_scale_ == scale)
+ return;
+ raster_scale_ = scale;
+
+ // When automatically computed, this acts like a draw property.
+ if (automatically_compute_raster_scale_)
+ return;
+ SetNeedsDisplay();
+}
+
+void Layer::SetAutomaticallyComputeRasterScale(bool automatic) {
+ if (automatically_compute_raster_scale_ == automatic)
+ return;
+ automatically_compute_raster_scale_ = automatic;
+
+ if (automatically_compute_raster_scale_)
+ ForceAutomaticRasterScaleToBeRecomputed();
+ else
+ SetRasterScale(1);
+}
+
+void Layer::ForceAutomaticRasterScaleToBeRecomputed() {
+ if (!automatically_compute_raster_scale_)
+ return;
+ if (!raster_scale_)
+ return;
+ raster_scale_ = 0.f;
+ SetNeedsCommit();
+}
+
+void Layer::SetBoundsContainPageScale(bool bounds_contain_page_scale) {
+ for (size_t i = 0; i < children_.size(); ++i)
+ children_[i]->SetBoundsContainPageScale(bounds_contain_page_scale);
+
+ if (bounds_contain_page_scale == bounds_contain_page_scale_)
+ return;
+
+ bounds_contain_page_scale_ = bounds_contain_page_scale;
+ SetNeedsDisplay();
+}
+
+void Layer::CreateRenderSurface() {
+ DCHECK(!draw_properties_.render_surface);
+ draw_properties_.render_surface = make_scoped_ptr(new RenderSurface(this));
+ draw_properties_.render_target = this;
+}
+
+void Layer::OnOpacityAnimated(float opacity) {
+ // This is called due to an ongoing accelerated animation. Since this
+ // animation is also being run on the impl thread, there is no need to request
+ // a commit to push this value over, so set the value directly rather than
+ // calling setOpacity.
+ opacity_ = opacity;
+}
+
+void Layer::OnTransformAnimated(const gfx::Transform& transform) {
+ // This is called due to an ongoing accelerated animation. Since this
+ // animation is also being run on the impl thread, there is no need to request
+ // a commit to push this value over, so set this value directly rather than
+ // calling setTransform.
+ transform_ = transform;
+}
+
+bool Layer::IsActive() const {
+ return true;
+}
+
+bool Layer::AddAnimation(scoped_ptr <Animation> animation) {
+ if (!layer_animation_controller_->animation_registrar())
+ return false;
+
+ layer_animation_controller_->AddAnimation(animation.Pass());
+ SetNeedsCommit();
+ return true;
+}
+
+void Layer::PauseAnimation(int animation_id, double time_offset) {
+ layer_animation_controller_->PauseAnimation(animation_id, time_offset);
+ SetNeedsCommit();
+}
+
+void Layer::RemoveAnimation(int animation_id) {
+ layer_animation_controller_->RemoveAnimation(animation_id);
+ SetNeedsCommit();
+}
+
+void Layer::SuspendAnimations(double monotonic_time) {
+ layer_animation_controller_->SuspendAnimations(monotonic_time);
+ SetNeedsCommit();
+}
+
+void Layer::ResumeAnimations(double monotonic_time) {
+ layer_animation_controller_->ResumeAnimations(monotonic_time);
+ SetNeedsCommit();
+}
+
+void Layer::SetLayerAnimationController(
+ scoped_refptr<LayerAnimationController> controller) {
+ RemoveLayerAnimationEventObserver(layer_animation_controller_.get());
+ layer_animation_controller_->RemoveObserver(this);
+ layer_animation_controller_ = controller;
+ layer_animation_controller_->set_force_sync();
+ layer_animation_controller_->AddObserver(this);
+ AddLayerAnimationEventObserver(layer_animation_controller_.get());
+ SetNeedsCommit();
+}
+
+scoped_refptr<LayerAnimationController>
+Layer::ReleaseLayerAnimationController() {
+ layer_animation_controller_->RemoveObserver(this);
+ scoped_refptr<LayerAnimationController> to_return =
+ layer_animation_controller_;
+ layer_animation_controller_ = LayerAnimationController::Create(id());
+ layer_animation_controller_->AddObserver(this);
+ layer_animation_controller_->SetAnimationRegistrar(
+ to_return->animation_registrar());
+ return to_return;
+}
+
+bool Layer::HasActiveAnimation() const {
+ return layer_animation_controller_->HasActiveAnimation();
+}
+
+void Layer::NotifyAnimationStarted(const AnimationEvent& event,
+ double wall_clock_time) {
+ FOR_EACH_OBSERVER(LayerAnimationEventObserver, layer_animation_observers_,
+ OnAnimationStarted(event));
+ if (layer_animation_delegate_)
+ layer_animation_delegate_->notifyAnimationStarted(wall_clock_time);
+}
+
+void Layer::NotifyAnimationFinished(double wall_clock_time) {
+ if (layer_animation_delegate_)
+ layer_animation_delegate_->notifyAnimationFinished(wall_clock_time);
+}
+
+void Layer::NotifyAnimationPropertyUpdate(const AnimationEvent& event) {
+ if (event.target_property == Animation::Opacity)
+ SetOpacity(event.opacity);
+ else
+ SetTransform(event.transform);
+}
+
+void Layer::AddLayerAnimationEventObserver(
+ LayerAnimationEventObserver* animation_observer) {
+ if (!layer_animation_observers_.HasObserver(animation_observer))
+ layer_animation_observers_.AddObserver(animation_observer);
+}
+
+void Layer::RemoveLayerAnimationEventObserver(
+ LayerAnimationEventObserver* animation_observer) {
+ layer_animation_observers_.RemoveObserver(animation_observer);
+}
+
+Region Layer::VisibleContentOpaqueRegion() const {
+ if (contents_opaque())
+ return visible_content_rect();
+ return Region();
+}
+
+ScrollbarLayer* Layer::ToScrollbarLayer() {
+ return NULL;
+}
+
+} // namespace cc
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
new file mode 100644
index 0000000..c0c34cc
--- /dev/null
+++ b/cc/layers/layer.h
@@ -0,0 +1,488 @@
+// Copyright 2010 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.
+
+#ifndef CC_LAYERS_LAYER_H_
+#define CC_LAYERS_LAYER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "cc/animation/layer_animation_controller.h"
+#include "cc/animation/layer_animation_event_observer.h"
+#include "cc/animation/layer_animation_value_observer.h"
+#include "cc/base/cc_export.h"
+#include "cc/base/region.h"
+#include "cc/layers/draw_properties.h"
+#include "cc/layers/render_surface.h"
+#include "cc/trees/occlusion_tracker.h"
+#include "skia/ext/refptr.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebFilterOperations.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkImageFilter.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/rect_f.h"
+#include "ui/gfx/transform.h"
+
+namespace WebKit {
+class WebAnimationDelegate;
+class WebLayerScrollClient;
+}
+
+namespace cc {
+
+class Animation;
+struct AnimationEvent;
+class LayerAnimationDelegate;
+class LayerImpl;
+class LayerTreeHost;
+class LayerTreeImpl;
+class PriorityCalculator;
+class ResourceUpdateQueue;
+class ScrollbarLayer;
+struct AnimationEvent;
+struct RenderingStats;
+
+// Base class for composited layers. Special layer types are derived from
+// this class.
+class CC_EXPORT Layer : public base::RefCounted<Layer>,
+ public LayerAnimationValueObserver {
+ public:
+ typedef std::vector<scoped_refptr<Layer> > LayerList;
+ enum LayerIdLabels {
+ PINCH_ZOOM_ROOT_SCROLL_LAYER_ID = -2,
+ INVALID_ID = -1,
+ };
+
+ static scoped_refptr<Layer> Create();
+
+ int id() const { return layer_id_; }
+
+ Layer* RootLayer();
+ Layer* parent() { return parent_; }
+ const Layer* parent() const { return parent_; }
+ void AddChild(scoped_refptr<Layer> child);
+ void InsertChild(scoped_refptr<Layer> child, size_t index);
+ void ReplaceChild(Layer* reference, scoped_refptr<Layer> new_layer);
+ void RemoveFromParent();
+ void RemoveAllChildren();
+ void SetChildren(const LayerList& children);
+
+ const LayerList& children() const { return children_; }
+ Layer* child_at(size_t index) { return children_[index].get(); }
+
+ void SetAnchorPoint(gfx::PointF anchor_point);
+ gfx::PointF anchor_point() const { return anchor_point_; }
+
+ void SetAnchorPointZ(float anchor_point_z);
+ float anchor_point_z() const { return anchor_point_z_; }
+
+ virtual void SetBackgroundColor(SkColor background_color);
+ SkColor background_color() const { return background_color_; }
+
+ // A layer's bounds are in logical, non-page-scaled pixels (however, the
+ // root layer's bounds are in physical pixels).
+ void SetBounds(gfx::Size bounds);
+ gfx::Size bounds() const { return bounds_; }
+
+ void SetMasksToBounds(bool masks_to_bounds);
+ bool masks_to_bounds() const { return masks_to_bounds_; }
+
+ void SetMaskLayer(Layer* mask_layer);
+ Layer* mask_layer() { return mask_layer_.get(); }
+ const Layer* mask_layer() const { return mask_layer_.get(); }
+
+ virtual void SetNeedsDisplayRect(const gfx::RectF& dirty_rect);
+ void SetNeedsDisplay() { SetNeedsDisplayRect(gfx::RectF(bounds())); }
+
+ void SetOpacity(float opacity);
+ float opacity() const { return opacity_; }
+ bool OpacityIsAnimating() const;
+ virtual bool OpacityCanAnimateOnImplThread() const;
+
+ void SetFilters(const WebKit::WebFilterOperations& filters);
+ const WebKit::WebFilterOperations& filters() const { return filters_; }
+
+ void SetFilter(const skia::RefPtr<SkImageFilter>& filter);
+ skia::RefPtr<SkImageFilter> filter() const { return filter_; }
+
+ // Background filters are filters applied to what is behind this layer, when
+ // they are viewed through non-opaque regions in this layer. They are used
+ // through the WebLayer interface, and are not exposed to HTML.
+ void SetBackgroundFilters(const WebKit::WebFilterOperations& filters);
+ const WebKit::WebFilterOperations& background_filters() const {
+ return background_filters_;
+ }
+
+ virtual void SetContentsOpaque(bool opaque);
+ bool contents_opaque() const { return contents_opaque_; }
+
+ void SetPosition(gfx::PointF position);
+ gfx::PointF position() const { return position_; }
+
+ void SetIsContainerForFixedPositionLayers(bool container);
+ bool is_container_for_fixed_position_layers() const {
+ return is_container_for_fixed_position_layers_;
+ }
+
+ void SetFixedToContainerLayer(bool fixed_to_container_layer);
+ bool fixed_to_container_layer() const { return fixed_to_container_layer_; }
+
+ void SetSublayerTransform(const gfx::Transform& sublayer_transform);
+ const gfx::Transform& sublayer_transform() const {
+ return sublayer_transform_;
+ }
+
+ void SetTransform(const gfx::Transform& transform);
+ const gfx::Transform& transform() const { return transform_; }
+ bool TransformIsAnimating() const;
+
+ DrawProperties<Layer, RenderSurface>& draw_properties() {
+ return draw_properties_;
+ }
+ const DrawProperties<Layer, RenderSurface>& draw_properties() const {
+ return draw_properties_;
+ }
+
+ // The following are shortcut accessors to get various information from
+ // draw_properties_
+ const gfx::Transform& draw_transform() const {
+ return draw_properties_.target_space_transform;
+ }
+ const gfx::Transform& screen_space_transform() const {
+ return draw_properties_.screen_space_transform;
+ }
+ float draw_opacity() const { return draw_properties_.opacity; }
+ bool draw_opacity_is_animating() const {
+ return draw_properties_.opacity_is_animating;
+ }
+ bool draw_transform_is_animating() const {
+ return draw_properties_.target_space_transform_is_animating;
+ }
+ bool screen_space_transform_is_animating() const {
+ return draw_properties_.screen_space_transform_is_animating;
+ }
+ bool screen_space_opacity_is_animating() const {
+ return draw_properties_.screen_space_opacity_is_animating;
+ }
+ bool can_use_lcd_text() const { return draw_properties_.can_use_lcd_text; }
+ bool is_clipped() const { return draw_properties_.is_clipped; }
+ gfx::Rect clip_rect() const { return draw_properties_.clip_rect; }
+ gfx::Rect drawable_content_rect() const {
+ return draw_properties_.drawable_content_rect;
+ }
+ gfx::Rect visible_content_rect() const {
+ return draw_properties_.visible_content_rect;
+ }
+ Layer* render_target() {
+ DCHECK(!draw_properties_.render_target ||
+ draw_properties_.render_target->render_surface());
+ return draw_properties_.render_target;
+ }
+ const Layer* render_target() const {
+ DCHECK(!draw_properties_.render_target ||
+ draw_properties_.render_target->render_surface());
+ return draw_properties_.render_target;
+ }
+ RenderSurface* render_surface() const {
+ return draw_properties_.render_surface.get();
+ }
+
+ void SetScrollOffset(gfx::Vector2d scroll_offset);
+ gfx::Vector2d scroll_offset() const { return scroll_offset_; }
+
+ void SetMaxScrollOffset(gfx::Vector2d max_scroll_offset);
+ gfx::Vector2d max_scroll_offset() const { return max_scroll_offset_; }
+
+ void SetScrollable(bool scrollable);
+ bool scrollable() const { return scrollable_; }
+
+ void SetShouldScrollOnMainThread(bool should_scroll_on_main_thread);
+ bool should_scroll_on_main_thread() const {
+ return should_scroll_on_main_thread_;
+ }
+
+ void SetHaveWheelEventHandlers(bool have_wheel_event_handlers);
+ bool have_wheel_event_handlers() const { return have_wheel_event_handlers_; }
+
+ void SetNonFastScrollableRegion(const Region& non_fast_scrollable_region);
+ const Region& non_fast_scrollable_region() const {
+ return non_fast_scrollable_region_;
+ }
+
+ void SetTouchEventHandlerRegion(const Region& touch_event_handler_region);
+ const Region& touch_event_handler_region() const {
+ return touch_event_handler_region_;
+ }
+
+ void set_layer_scroll_client(WebKit::WebLayerScrollClient* client) {
+ layer_scroll_client_ = client;
+ }
+
+ void SetDrawCheckerboardForMissingTiles(bool checkerboard);
+ bool DrawCheckerboardForMissingTiles() const {
+ return draw_checkerboard_for_missing_tiles_;
+ }
+
+ void SetForceRenderSurface(bool force_render_surface);
+ bool force_render_surface() const { return force_render_surface_; }
+
+ gfx::Vector2d scroll_delta() const { return gfx::Vector2d(); }
+
+ void SetImplTransform(const gfx::Transform& transform);
+ const gfx::Transform& impl_transform() const { return impl_transform_; }
+
+ void SetDoubleSided(bool double_sided);
+ bool double_sided() const { return double_sided_; }
+
+ void SetPreserves3d(bool preserves_3d) { preserves_3d_ = preserves_3d; }
+ bool preserves_3d() const { return preserves_3d_; }
+
+ void set_use_parent_backface_visibility(bool use) {
+ use_parent_backface_visibility_ = use;
+ }
+ bool use_parent_backface_visibility() const {
+ return use_parent_backface_visibility_;
+ }
+
+ virtual void SetLayerTreeHost(LayerTreeHost* host);
+
+ bool HasDelegatedContent() const { return false; }
+ bool HasContributingDelegatedRenderPasses() const { return false; }
+
+ void SetIsDrawable(bool is_drawable);
+
+ void SetReplicaLayer(Layer* layer);
+ Layer* replica_layer() { return replica_layer_.get(); }
+ const Layer* replica_layer() const { return replica_layer_.get(); }
+
+ bool has_mask() const { return !!mask_layer_; }
+ bool has_replica() const { return !!replica_layer_; }
+ bool replica_has_mask() const {
+ return replica_layer_ && (mask_layer_ || replica_layer_->mask_layer_);
+ }
+
+ // These methods typically need to be overwritten by derived classes.
+ virtual bool DrawsContent() const;
+ virtual void Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) {}
+ virtual bool NeedMoreUpdates();
+ virtual void SetIsMask(bool is_mask) {}
+
+ void SetDebugName(const std::string& debug_name);
+
+ virtual void PushPropertiesTo(LayerImpl* layer);
+
+ void ClearRenderSurface() { draw_properties_.render_surface.reset(); }
+ void CreateRenderSurface();
+
+ // The contentsScale converts from logical, non-page-scaled pixels to target
+ // pixels. The contentsScale is 1 for the root layer as it is already in
+ // physical pixels. By default contentsScale is forced to be 1 except for
+ // subclasses of ContentsScalingLayer.
+ float contents_scale_x() const { return draw_properties_.contents_scale_x; }
+ float contents_scale_y() const { return draw_properties_.contents_scale_y; }
+ gfx::Size content_bounds() const { return draw_properties_.content_bounds; }
+
+ virtual void CalculateContentsScale(float ideal_contents_scale,
+ bool animating_transform_to_screen,
+ float* contents_scale_x,
+ float* contents_scale_y,
+ gfx::Size* content_bounds);
+
+ // The scale at which contents should be rastered, to match the scale at
+ // which they will drawn to the screen. This scale is a component of the
+ // contentsScale() but does not include page/device scale factors.
+ void SetRasterScale(float scale);
+ float raster_scale() const { return raster_scale_; }
+
+ // When true, the RasterScale() will be set by the compositor. If false, it
+ // will use whatever value is given to it by the embedder.
+ bool automatically_compute_raster_scale() {
+ return automatically_compute_raster_scale_;
+ }
+ void SetAutomaticallyComputeRasterScale(bool automatic);
+
+ void ForceAutomaticRasterScaleToBeRecomputed();
+
+ // When true, the layer's contents are not scaled by the current page scale
+ // factor. SetBoundsContainPageScale() recursively sets the value on all
+ // child layers.
+ void SetBoundsContainPageScale(bool bounds_contain_page_scale);
+ bool bounds_contain_page_scale() const { return bounds_contain_page_scale_; }
+
+ LayerTreeHost* layer_tree_host() const { return layer_tree_host_; }
+
+ // Set the priority of all desired textures in this layer.
+ virtual void SetTexturePriorities(const PriorityCalculator& priority_calc) {}
+
+ bool AddAnimation(scoped_ptr<Animation> animation);
+ void PauseAnimation(int animation_id, double time_offset);
+ void RemoveAnimation(int animation_id);
+
+ void SuspendAnimations(double monotonic_time);
+ void ResumeAnimations(double monotonic_time);
+
+ LayerAnimationController* layer_animation_controller() {
+ return layer_animation_controller_.get();
+ }
+ void SetLayerAnimationController(
+ scoped_refptr<LayerAnimationController> controller);
+ scoped_refptr<LayerAnimationController> ReleaseLayerAnimationController();
+
+ void set_layer_animation_delegate(WebKit::WebAnimationDelegate* delegate) {
+ layer_animation_delegate_ = delegate;
+ }
+
+ bool HasActiveAnimation() const;
+
+ virtual void NotifyAnimationStarted(const AnimationEvent& event,
+ double wall_clock_time);
+ virtual void NotifyAnimationFinished(double wall_clock_time);
+ virtual void NotifyAnimationPropertyUpdate(const AnimationEvent& event);
+
+ void AddLayerAnimationEventObserver(
+ LayerAnimationEventObserver* animation_observer);
+ void RemoveLayerAnimationEventObserver(
+ LayerAnimationEventObserver* animation_observer);
+
+ virtual Region VisibleContentOpaqueRegion() const;
+
+ virtual ScrollbarLayer* ToScrollbarLayer();
+
+ gfx::Rect LayerRectToContentRect(const gfx::RectF& layer_rect) const;
+
+ // In impl-side painting, this returns true if this layer type is not
+ // compatible with the main thread running freely, such as a double-buffered
+ // canvas that doesn't want to be triple-buffered across all three trees.
+ virtual bool BlocksPendingCommit() const;
+ // Returns true if anything in this tree blocksPendingCommit.
+ bool BlocksPendingCommitRecursive() const;
+
+ virtual bool CanClipSelf() const;
+
+ // Constructs a LayerImpl of the correct runtime type for this Layer type.
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl);
+
+ bool NeedsDisplayForTesting() const { return needs_display_; }
+ void ResetNeedsDisplayForTesting() { needs_display_ = false; }
+
+ protected:
+ friend class LayerImpl;
+ friend class TreeSynchronizer;
+ virtual ~Layer();
+
+ Layer();
+
+ void SetNeedsCommit();
+ void SetNeedsFullTreeSync();
+
+ // This flag is set when layer need repainting/updating.
+ bool needs_display_;
+
+ // Tracks whether this layer may have changed stacking order with its
+ // siblings.
+ bool stacking_order_changed_;
+
+ // The update rect is the region of the compositor resource that was
+ // actually updated by the compositor. For layers that may do updating
+ // outside the compositor's control (i.e. plugin layers), this information
+ // is not available and the update rect will remain empty.
+ // Note this rect is in layer space (not content space).
+ gfx::RectF update_rect_;
+
+ scoped_refptr<Layer> mask_layer_;
+
+ int layer_id_;
+
+ // When true, the layer is about to perform an update. Any commit requests
+ // will be handled implcitly after the update completes.
+ bool ignore_set_needs_commit_;
+
+ private:
+ friend class base::RefCounted<Layer>;
+
+ void SetParent(Layer* layer);
+ bool HasAncestor(Layer* ancestor) const;
+ bool DescendantIsFixedToContainerLayer() const;
+
+ // Returns the index of the child or -1 if not found.
+ int IndexOfChild(const Layer* reference);
+
+ // This should only be called from RemoveFromParent().
+ void RemoveChildOrDependent(Layer* child);
+
+ // LayerAnimationValueObserver implementation.
+ virtual void OnOpacityAnimated(float opacity) OVERRIDE;
+ virtual void OnTransformAnimated(const gfx::Transform& transform) OVERRIDE;
+ virtual bool IsActive() const OVERRIDE;
+
+ LayerList children_;
+ Layer* parent_;
+
+ // Layer instances have a weak pointer to their LayerTreeHost.
+ // This pointer value is nil when a Layer is not in a tree and is
+ // updated via SetLayerTreeHost() if a layer moves between trees.
+ LayerTreeHost* layer_tree_host_;
+
+ ObserverList<LayerAnimationEventObserver> layer_animation_observers_;
+
+ scoped_refptr<LayerAnimationController> layer_animation_controller_;
+
+ // Layer properties.
+ gfx::Size bounds_;
+
+ gfx::Vector2d scroll_offset_;
+ gfx::Vector2d max_scroll_offset_;
+ bool scrollable_;
+ bool should_scroll_on_main_thread_;
+ bool have_wheel_event_handlers_;
+ Region non_fast_scrollable_region_;
+ Region touch_event_handler_region_;
+ gfx::PointF position_;
+ gfx::PointF anchor_point_;
+ SkColor background_color_;
+ std::string debug_name_;
+ float opacity_;
+ skia::RefPtr<SkImageFilter> filter_;
+ WebKit::WebFilterOperations filters_;
+ WebKit::WebFilterOperations background_filters_;
+ float anchor_point_z_;
+ bool is_container_for_fixed_position_layers_;
+ bool fixed_to_container_layer_;
+ bool is_drawable_;
+ bool masks_to_bounds_;
+ bool contents_opaque_;
+ bool double_sided_;
+ bool preserves_3d_;
+ bool use_parent_backface_visibility_;
+ bool draw_checkerboard_for_missing_tiles_;
+ bool force_render_surface_;
+
+ gfx::Transform transform_;
+ gfx::Transform sublayer_transform_;
+
+ // Replica layer used for reflections.
+ scoped_refptr<Layer> replica_layer_;
+
+ // Transient properties.
+ float raster_scale_;
+ bool automatically_compute_raster_scale_;
+ bool bounds_contain_page_scale_;
+
+ gfx::Transform impl_transform_;
+
+ WebKit::WebAnimationDelegate* layer_animation_delegate_;
+ WebKit::WebLayerScrollClient* layer_scroll_client_;
+
+ DrawProperties<Layer, RenderSurface> draw_properties_;
+
+ DISALLOW_COPY_AND_ASSIGN(Layer);
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_LAYER_H_
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
new file mode 100644
index 0000000..60fbbf35
--- /dev/null
+++ b/cc/layers/layer_impl.cc
@@ -0,0 +1,966 @@
+// Copyright 2011 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 "cc/layers/layer_impl.h"
+
+#include "base/debug/trace_event.h"
+#include "base/stringprintf.h"
+#include "base/values.h"
+#include "cc/animation/animation_registrar.h"
+#include "cc/animation/scrollbar_animation_controller.h"
+#include "cc/animation/scrollbar_animation_controller_linear_fade.h"
+#include "cc/base/math_util.h"
+#include "cc/debug/debug_colors.h"
+#include "cc/debug/layer_tree_debug_state.h"
+#include "cc/layers/quad_sink.h"
+#include "cc/layers/scrollbar_layer_impl.h"
+#include "cc/quads/debug_border_draw_quad.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "cc/trees/layer_tree_settings.h"
+#include "cc/trees/proxy.h"
+#include "ui/gfx/point_conversions.h"
+#include "ui/gfx/quad_f.h"
+#include "ui/gfx/rect_conversions.h"
+
+namespace cc {
+
+LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id)
+ : parent_(NULL),
+ mask_layer_id_(-1),
+ replica_layer_id_(-1),
+ layer_id_(id),
+ layer_tree_impl_(tree_impl),
+ anchor_point_(0.5f, 0.5f),
+ anchor_point_z_(0.f),
+ scrollable_(false),
+ should_scroll_on_main_thread_(false),
+ have_wheel_event_handlers_(false),
+ background_color_(0),
+ stacking_order_changed_(false),
+ double_sided_(true),
+ layer_property_changed_(false),
+ layer_surface_property_changed_(false),
+ masks_to_bounds_(false),
+ contents_opaque_(false),
+ opacity_(1.0),
+ preserves_3d_(false),
+ use_parent_backface_visibility_(false),
+ draw_checkerboard_for_missing_tiles_(false),
+ draws_content_(false),
+ force_render_surface_(false),
+ is_container_for_fixed_position_layers_(false),
+ fixed_to_container_layer_(false),
+ draw_depth_(0.f),
+#ifndef NDEBUG
+ between_will_draw_and_did_draw_(false),
+#endif
+ horizontal_scrollbar_layer_(NULL),
+ vertical_scrollbar_layer_(NULL) {
+ DCHECK(layer_id_ > 0);
+ DCHECK(layer_tree_impl_);
+ layer_tree_impl_->RegisterLayer(this);
+ AnimationRegistrar* registrar = layer_tree_impl_->animationRegistrar();
+ layer_animation_controller_ =
+ registrar->GetAnimationControllerForId(layer_id_);
+ layer_animation_controller_->AddObserver(this);
+}
+
+LayerImpl::~LayerImpl() {
+#ifndef NDEBUG
+ DCHECK(!between_will_draw_and_did_draw_);
+#endif
+ layer_tree_impl_->UnregisterLayer(this);
+ layer_animation_controller_->RemoveObserver(this);
+}
+
+void LayerImpl::AddChild(scoped_ptr<LayerImpl> child) {
+ child->set_parent(this);
+ DCHECK_EQ(layer_tree_impl(), child->layer_tree_impl());
+ children_.push_back(child.Pass());
+ layer_tree_impl()->set_needs_update_draw_properties();
+}
+
+scoped_ptr<LayerImpl> LayerImpl::RemoveChild(LayerImpl* child) {
+ for (ScopedPtrVector<LayerImpl>::iterator it = children_.begin();
+ it != children_.end();
+ ++it) {
+ if (*it == child) {
+ scoped_ptr<LayerImpl> ret = children_.take(it);
+ children_.erase(it);
+ layer_tree_impl()->set_needs_update_draw_properties();
+ return ret.Pass();
+ }
+ }
+ return scoped_ptr<LayerImpl>();
+}
+
+void LayerImpl::ClearChildList() {
+ if (children_.empty())
+ return;
+
+ children_.clear();
+ layer_tree_impl()->set_needs_update_draw_properties();
+}
+
+void LayerImpl::CreateRenderSurface() {
+ DCHECK(!draw_properties_.render_surface);
+ draw_properties_.render_surface =
+ make_scoped_ptr(new RenderSurfaceImpl(this));
+ draw_properties_.render_target = this;
+}
+
+scoped_ptr<SharedQuadState> LayerImpl::CreateSharedQuadState() const {
+ scoped_ptr<SharedQuadState> state = SharedQuadState::Create();
+ state->SetAll(draw_properties_.target_space_transform,
+ draw_properties_.content_bounds,
+ draw_properties_.visible_content_rect,
+ draw_properties_.clip_rect,
+ draw_properties_.is_clipped,
+ draw_properties_.opacity);
+ return state.Pass();
+}
+
+void LayerImpl::WillDraw(ResourceProvider* resource_provider) {
+#ifndef NDEBUG
+ // willDraw/didDraw must be matched.
+ DCHECK(!between_will_draw_and_did_draw_);
+ between_will_draw_and_did_draw_ = true;
+#endif
+}
+
+void LayerImpl::DidDraw(ResourceProvider* resource_provider) {
+#ifndef NDEBUG
+ DCHECK(between_will_draw_and_did_draw_);
+ between_will_draw_and_did_draw_ = false;
+#endif
+}
+
+bool LayerImpl::ShowDebugBorders() const {
+ return layer_tree_impl()->debug_state().showDebugBorders;
+}
+
+void LayerImpl::GetDebugBorderProperties(SkColor* color, float* width) const {
+ if (draws_content_) {
+ *color = DebugColors::ContentLayerBorderColor();
+ *width = DebugColors::ContentLayerBorderWidth(layer_tree_impl());
+ return;
+ }
+
+ if (masks_to_bounds_) {
+ *color = DebugColors::MaskingLayerBorderColor();
+ *width = DebugColors::MaskingLayerBorderWidth(layer_tree_impl());
+ return;
+ }
+
+ *color = DebugColors::ContainerLayerBorderColor();
+ *width = DebugColors::ContainerLayerBorderWidth(layer_tree_impl());
+}
+
+void LayerImpl::AppendDebugBorderQuad(
+ QuadSink* quad_sink,
+ const SharedQuadState* shared_quad_state,
+ AppendQuadsData* append_quads_data) const {
+ if (!ShowDebugBorders())
+ return;
+
+ SkColor color;
+ float width;
+ GetDebugBorderProperties(&color, &width);
+
+ gfx::Rect content_rect(content_bounds());
+ scoped_ptr<DebugBorderDrawQuad> debugBorderQuad =
+ DebugBorderDrawQuad::Create();
+ debugBorderQuad->SetNew(shared_quad_state, content_rect, color, width);
+ quad_sink->Append(debugBorderQuad.PassAs<DrawQuad>(), append_quads_data);
+}
+
+bool LayerImpl::HasDelegatedContent() const {
+ return false;
+}
+
+bool LayerImpl::HasContributingDelegatedRenderPasses() const {
+ return false;
+}
+
+RenderPass::Id LayerImpl::FirstContributingRenderPassId() const {
+ return RenderPass::Id(0, 0);
+}
+
+RenderPass::Id LayerImpl::NextContributingRenderPassId(RenderPass::Id id)
+ const {
+ return RenderPass::Id(0, 0);
+}
+
+ResourceProvider::ResourceId LayerImpl::ContentsResourceId() const {
+ NOTREACHED();
+ return 0;
+}
+
+void LayerImpl::SetSentScrollDelta(gfx::Vector2d sent_scroll_delta) {
+ // Pending tree never has sent scroll deltas
+ DCHECK(layer_tree_impl()->IsActiveTree());
+
+ if (sent_scroll_delta_ == sent_scroll_delta)
+ return;
+
+ sent_scroll_delta_ = sent_scroll_delta;
+}
+
+gfx::Vector2dF LayerImpl::ScrollBy(gfx::Vector2dF scroll) {
+ gfx::Vector2dF min_delta = -scroll_offset_;
+ gfx::Vector2dF max_delta = max_scroll_offset_ - scroll_offset_;
+ // Clamp new_delta so that position + delta stays within scroll bounds.
+ gfx::Vector2dF new_delta = (scroll_delta_ + scroll);
+ new_delta.ClampToMin(min_delta);
+ new_delta.ClampToMax(max_delta);
+ gfx::Vector2dF unscrolled = scroll_delta_ + scroll - new_delta;
+
+ SetScrollDelta(new_delta);
+ return unscrolled;
+}
+
+InputHandlerClient::ScrollStatus LayerImpl::TryScroll(
+ gfx::PointF screen_space_point,
+ InputHandlerClient::ScrollInputType type) const {
+ if (should_scroll_on_main_thread()) {
+ TRACE_EVENT0("cc", "LayerImpl::tryScroll: Failed shouldScrollOnMainThread");
+ return InputHandlerClient::ScrollOnMainThread;
+ }
+
+ if (!screen_space_transform().IsInvertible()) {
+ TRACE_EVENT0("cc", "LayerImpl::tryScroll: Ignored nonInvertibleTransform");
+ return InputHandlerClient::ScrollIgnored;
+ }
+
+ if (!non_fast_scrollable_region().IsEmpty()) {
+ bool clipped = false;
+ gfx::Transform inverse_screen_space_transform(
+ gfx::Transform::kSkipInitialization);
+ if (!screen_space_transform().GetInverse(&inverse_screen_space_transform)) {
+ // TODO(shawnsingh): We shouldn't be applying a projection if screen space
+ // transform is uninvertible here. Perhaps we should be returning
+ // ScrollOnMainThread in this case?
+ }
+
+ gfx::PointF hit_test_point_in_content_space =
+ MathUtil::ProjectPoint(inverse_screen_space_transform,
+ screen_space_point,
+ &clipped);
+ gfx::PointF hit_test_point_in_layer_space =
+ gfx::ScalePoint(hit_test_point_in_content_space,
+ 1.f / contents_scale_x(),
+ 1.f / contents_scale_y());
+ if (!clipped &&
+ non_fast_scrollable_region().Contains(
+ gfx::ToRoundedPoint(hit_test_point_in_layer_space))) {
+ TRACE_EVENT0("cc",
+ "LayerImpl::tryScroll: Failed nonFastScrollableRegion");
+ return InputHandlerClient::ScrollOnMainThread;
+ }
+ }
+
+ if (type == InputHandlerClient::Wheel && have_wheel_event_handlers()) {
+ TRACE_EVENT0("cc", "LayerImpl::tryScroll: Failed wheelEventHandlers");
+ return InputHandlerClient::ScrollOnMainThread;
+ }
+
+ if (!scrollable()) {
+ TRACE_EVENT0("cc", "LayerImpl::tryScroll: Ignored not scrollable");
+ return InputHandlerClient::ScrollIgnored;
+ }
+
+ if (max_scroll_offset_.x() <= 0 && max_scroll_offset_.y() <= 0) {
+ TRACE_EVENT0("cc",
+ "LayerImpl::tryScroll: Ignored. Technically scrollable,"
+ " but has no affordance in either direction.");
+ return InputHandlerClient::ScrollIgnored;
+ }
+
+ return InputHandlerClient::ScrollStarted;
+}
+
+bool LayerImpl::DrawCheckerboardForMissingTiles() const {
+ return draw_checkerboard_for_missing_tiles_ &&
+ !layer_tree_impl()->settings().backgroundColorInsteadOfCheckerboard;
+}
+
+gfx::Rect LayerImpl::LayerRectToContentRect(
+ const gfx::RectF& layer_rect) const {
+ gfx::RectF content_rect =
+ gfx::ScaleRect(layer_rect, contents_scale_x(), contents_scale_y());
+ // Intersect with content rect to avoid the extra pixel because for some
+ // values x and y, ceil((x / y) * y) may be x + 1.
+ content_rect.Intersect(gfx::Rect(content_bounds()));
+ return gfx::ToEnclosingRect(content_rect);
+}
+
+skia::RefPtr<SkPicture> LayerImpl::GetPicture() {
+ return skia::RefPtr<SkPicture>();
+}
+
+bool LayerImpl::CanClipSelf() const {
+ return false;
+}
+
+bool LayerImpl::AreVisibleResourcesReady() const {
+ return true;
+}
+
+scoped_ptr<LayerImpl> LayerImpl::CreateLayerImpl(LayerTreeImpl* tree_impl) {
+ return LayerImpl::Create(tree_impl, layer_id_);
+}
+
+void LayerImpl::PushPropertiesTo(LayerImpl* layer) {
+ layer->SetAnchorPoint(anchor_point_);
+ layer->SetAnchorPointZ(anchor_point_z_);
+ layer->SetBackgroundColor(background_color_);
+ layer->SetBounds(bounds_);
+ layer->SetContentBounds(content_bounds());
+ layer->SetContentsScale(contents_scale_x(), contents_scale_y());
+ layer->SetDebugName(debug_name_);
+ layer->SetDoubleSided(double_sided_);
+ layer->SetDrawCheckerboardForMissingTiles(
+ draw_checkerboard_for_missing_tiles_);
+ layer->SetForceRenderSurface(force_render_surface_);
+ layer->SetDrawsContent(DrawsContent());
+ layer->SetFilters(filters());
+ layer->SetFilter(filter());
+ layer->SetBackgroundFilters(background_filters());
+ layer->SetMasksToBounds(masks_to_bounds_);
+ layer->SetShouldScrollOnMainThread(should_scroll_on_main_thread_);
+ layer->SetHaveWheelEventHandlers(have_wheel_event_handlers_);
+ layer->SetNonFastScrollableRegion(non_fast_scrollable_region_);
+ layer->SetTouchEventHandlerRegion(touch_event_handler_region_);
+ layer->SetContentsOpaque(contents_opaque_);
+ layer->SetOpacity(opacity_);
+ layer->SetPosition(position_);
+ layer->SetIsContainerForFixedPositionLayers(
+ is_container_for_fixed_position_layers_);
+ layer->SetFixedToContainerLayer(fixed_to_container_layer_);
+ layer->SetPreserves3d(preserves_3d());
+ layer->SetUseParentBackfaceVisibility(use_parent_backface_visibility_);
+ layer->SetSublayerTransform(sublayer_transform_);
+ layer->SetTransform(transform_);
+
+ layer->SetScrollable(scrollable_);
+ layer->SetScrollOffset(scroll_offset_);
+ layer->SetMaxScrollOffset(max_scroll_offset_);
+
+ // If the main thread commits multiple times before the impl thread actually
+ // draws, then damage tracking will become incorrect if we simply clobber the
+ // updateRect here. The LayerImpl's updateRect needs to accumulate (i.e.
+ // union) any update changes that have occurred on the main thread.
+ update_rect_.Union(layer->update_rect());
+ layer->set_update_rect(update_rect_);
+
+ layer->SetScrollDelta(layer->scroll_delta() - layer->sent_scroll_delta());
+ layer->SetSentScrollDelta(gfx::Vector2d());
+
+ layer->SetStackingOrderChanged(stacking_order_changed_);
+
+ layer_animation_controller_->PushAnimationUpdatesTo(
+ layer->layer_animation_controller());
+
+ // Reset any state that should be cleared for the next update.
+ stacking_order_changed_ = false;
+ update_rect_ = gfx::RectF();
+}
+
+std::string LayerImpl::IndentString(int indent) {
+ std::string str;
+ for (int i = 0; i != indent; ++i)
+ str.append(" ");
+ return str;
+}
+
+void LayerImpl::DumpLayerProperties(std::string* str, int indent) const {
+ std::string indent_str = IndentString(indent);
+ str->append(indent_str);
+ base::StringAppendF(str, "layer ID: %d\n", layer_id_);
+
+ str->append(indent_str);
+ base::StringAppendF(
+ str, "bounds: %d, %d\n", bounds().width(), bounds().height());
+
+ if (draw_properties_.render_target) {
+ str->append(indent_str);
+ base::StringAppendF(
+ str, "renderTarget: %d\n", draw_properties_.render_target->layer_id_);
+ }
+
+ str->append(indent_str);
+ base::StringAppendF(str, "position: %f, %f\n", position_.x(), position_.y());
+
+ str->append(indent_str);
+ base::StringAppendF(str, "contentsOpaque: %d\n", contents_opaque_);
+
+ str->append(indent_str);
+ const gfx::Transform& transform = draw_properties_.target_space_transform;
+ base::StringAppendF(str,
+ "drawTransform: %f, %f, %f, %f // %f, %f, %f, %f"
+ " // %f, %f, %f, %f // %f, %f, %f, %f\n",
+ transform.matrix().getDouble(0, 0),
+ transform.matrix().getDouble(0, 1),
+ transform.matrix().getDouble(0, 2),
+ transform.matrix().getDouble(0, 3),
+ transform.matrix().getDouble(1, 0),
+ transform.matrix().getDouble(1, 1),
+ transform.matrix().getDouble(1, 2),
+ transform.matrix().getDouble(1, 3),
+ transform.matrix().getDouble(2, 0),
+ transform.matrix().getDouble(2, 1),
+ transform.matrix().getDouble(2, 2),
+ transform.matrix().getDouble(2, 3),
+ transform.matrix().getDouble(3, 0),
+ transform.matrix().getDouble(3, 1),
+ transform.matrix().getDouble(3, 2),
+ transform.matrix().getDouble(3, 3));
+
+ str->append(indent_str);
+ base::StringAppendF(
+ str, "draws_content: %s\n", draws_content_ ? "yes" : "no");
+}
+
+std::string LayerImpl::LayerTreeAsText() const {
+ std::string str;
+ DumpLayer(&str, 0);
+ return str;
+}
+
+void LayerImpl::DumpLayer(std::string* str, int indent) const {
+ str->append(IndentString(indent));
+ base::StringAppendF(str, "%s(%s)\n", LayerTypeAsString(), debug_name_.data());
+ DumpLayerProperties(str, indent+2);
+ if (replica_layer_) {
+ str->append(IndentString(indent+2));
+ str->append("Replica:\n");
+ replica_layer_->DumpLayer(str, indent+3);
+ }
+ if (mask_layer_) {
+ str->append(IndentString(indent+2));
+ str->append("Mask:\n");
+ mask_layer_->DumpLayer(str, indent+3);
+ }
+ for (size_t i = 0; i < children_.size(); ++i)
+ children_[i]->DumpLayer(str, indent+1);
+}
+
+base::DictionaryValue* LayerImpl::LayerTreeAsJson() const {
+ base::ListValue* list;
+ base::DictionaryValue* result = new base::DictionaryValue;
+ result->SetString("LayerType", LayerTypeAsString());
+
+ list = new base::ListValue;
+ list->AppendInteger(bounds().width());
+ list->AppendInteger(bounds().height());
+ result->Set("Bounds", list);
+
+ list = new base::ListValue;
+ list->AppendDouble(position_.x());
+ list->AppendDouble(position_.y());
+ result->Set("Position", list);
+
+ const gfx::Transform& gfx_transform = draw_properties_.target_space_transform;
+ double transform[16];
+ gfx_transform.matrix().asColMajord(transform);
+ list = new base::ListValue;
+ for (int i = 0; i < 16; ++i)
+ list->AppendDouble(transform[i]);
+ result->Set("DrawTransform", list);
+
+ result->SetBoolean("DrawsContent", draws_content_);
+ result->SetDouble("Opacity", opacity());
+
+ list = new base::ListValue;
+ for (size_t i = 0; i < children_.size(); ++i)
+ list->Append(children_[i]->LayerTreeAsJson());
+ result->Set("Children", list);
+
+ return result;
+}
+
+void LayerImpl::SetStackingOrderChanged(bool stacking_order_changed) {
+ if (stacking_order_changed) {
+ stacking_order_changed_ = true;
+ NoteLayerPropertyChangedForSubtree();
+ }
+}
+
+bool LayerImpl::LayerSurfacePropertyChanged() const {
+ if (layer_surface_property_changed_)
+ return true;
+
+ // If this layer's surface property hasn't changed, we want to see if
+ // some layer above us has changed this property. This is done for the
+ // case when such parent layer does not draw content, and therefore will
+ // not be traversed by the damage tracker. We need to make sure that
+ // property change on such layer will be caught by its descendants.
+ LayerImpl* current = this->parent_;
+ while (current && !current->draw_properties_.render_surface) {
+ if (current->layer_surface_property_changed_)
+ return true;
+ current = current->parent_;
+ }
+
+ return false;
+}
+
+void LayerImpl::NoteLayerSurfacePropertyChanged() {
+ layer_surface_property_changed_ = true;
+ layer_tree_impl()->set_needs_update_draw_properties();
+}
+
+void LayerImpl::NoteLayerPropertyChanged() {
+ layer_property_changed_ = true;
+ layer_tree_impl()->set_needs_update_draw_properties();
+}
+
+void LayerImpl::NoteLayerPropertyChangedForSubtree() {
+ NoteLayerPropertyChanged();
+ NoteLayerPropertyChangedForDescendants();
+}
+
+void LayerImpl::NoteLayerPropertyChangedForDescendants() {
+ layer_tree_impl()->set_needs_update_draw_properties();
+ for (size_t i = 0; i < children_.size(); ++i)
+ children_[i]->NoteLayerPropertyChangedForSubtree();
+}
+
+const char* LayerImpl::LayerTypeAsString() const {
+ return "Layer";
+}
+
+void LayerImpl::ResetAllChangeTrackingForSubtree() {
+ layer_property_changed_ = false;
+ layer_surface_property_changed_ = false;
+
+ update_rect_ = gfx::RectF();
+
+ if (draw_properties_.render_surface)
+ draw_properties_.render_surface->ResetPropertyChangedFlag();
+
+ if (mask_layer_)
+ mask_layer_->ResetAllChangeTrackingForSubtree();
+
+ if (replica_layer_) {
+ // This also resets the replica mask, if it exists.
+ replica_layer_->ResetAllChangeTrackingForSubtree();
+ }
+
+ for (size_t i = 0; i < children_.size(); ++i)
+ children_[i]->ResetAllChangeTrackingForSubtree();
+}
+
+bool LayerImpl::LayerIsAlwaysDamaged() const {
+ return false;
+}
+
+void LayerImpl::OnOpacityAnimated(float opacity) {
+ SetOpacity(opacity);
+}
+
+void LayerImpl::OnTransformAnimated(const gfx::Transform& transform) {
+ SetTransform(transform);
+}
+
+bool LayerImpl::IsActive() const {
+ return layer_tree_impl_->IsActiveTree();
+}
+
+void LayerImpl::SetBounds(gfx::Size bounds) {
+ if (bounds_ == bounds)
+ return;
+
+ bounds_ = bounds;
+
+ if (masks_to_bounds())
+ NoteLayerPropertyChangedForSubtree();
+ else
+ NoteLayerPropertyChanged();
+}
+
+void LayerImpl::SetMaskLayer(scoped_ptr<LayerImpl> mask_layer) {
+ int new_layer_id = mask_layer ? mask_layer->id() : -1;
+
+ if (mask_layer) {
+ DCHECK_EQ(layer_tree_impl(), mask_layer->layer_tree_impl());
+ DCHECK_NE(new_layer_id, mask_layer_id_);
+ } else if (new_layer_id == mask_layer_id_) {
+ return;
+ }
+
+ mask_layer_ = mask_layer.Pass();
+ mask_layer_id_ = new_layer_id;
+ if (mask_layer_)
+ mask_layer_->set_parent(this);
+ NoteLayerPropertyChangedForSubtree();
+}
+
+scoped_ptr<LayerImpl> LayerImpl::TakeMaskLayer() {
+ mask_layer_id_ = -1;
+ return mask_layer_.Pass();
+}
+
+void LayerImpl::SetReplicaLayer(scoped_ptr<LayerImpl> replica_layer) {
+ int new_layer_id = replica_layer ? replica_layer->id() : -1;
+
+ if (replica_layer) {
+ DCHECK_EQ(layer_tree_impl(), replica_layer->layer_tree_impl());
+ DCHECK_NE(new_layer_id, replica_layer_id_);
+ } else if (new_layer_id == replica_layer_id_) {
+ return;
+ }
+
+ replica_layer_ = replica_layer.Pass();
+ replica_layer_id_ = new_layer_id;
+ if (replica_layer_)
+ replica_layer_->set_parent(this);
+ NoteLayerPropertyChangedForSubtree();
+}
+
+scoped_ptr<LayerImpl> LayerImpl::TakeReplicaLayer() {
+ replica_layer_id_ = -1;
+ return replica_layer_.Pass();
+}
+
+ScrollbarLayerImpl* LayerImpl::ToScrollbarLayer() {
+ return NULL;
+}
+
+void LayerImpl::SetDrawsContent(bool draws_content) {
+ if (draws_content_ == draws_content)
+ return;
+
+ draws_content_ = draws_content;
+ NoteLayerPropertyChanged();
+}
+
+void LayerImpl::SetAnchorPoint(gfx::PointF anchor_point) {
+ if (anchor_point_ == anchor_point)
+ return;
+
+ anchor_point_ = anchor_point;
+ NoteLayerPropertyChangedForSubtree();
+}
+
+void LayerImpl::SetAnchorPointZ(float anchor_point_z) {
+ if (anchor_point_z_ == anchor_point_z)
+ return;
+
+ anchor_point_z_ = anchor_point_z;
+ NoteLayerPropertyChangedForSubtree();
+}
+
+void LayerImpl::SetBackgroundColor(SkColor background_color) {
+ if (background_color_ == background_color)
+ return;
+
+ background_color_ = background_color;
+ NoteLayerPropertyChanged();
+}
+
+void LayerImpl::SetFilters(const WebKit::WebFilterOperations& filters) {
+ if (filters_ == filters)
+ return;
+
+ DCHECK(!filter_);
+ filters_ = filters;
+ NoteLayerPropertyChangedForSubtree();
+}
+
+void LayerImpl::SetBackgroundFilters(
+ const WebKit::WebFilterOperations& filters) {
+ if (background_filters_ == filters)
+ return;
+
+ background_filters_ = filters;
+ NoteLayerPropertyChanged();
+}
+
+void LayerImpl::SetFilter(const skia::RefPtr<SkImageFilter>& filter) {
+ if (filter_.get() == filter.get())
+ return;
+
+ DCHECK(filters_.isEmpty());
+ filter_ = filter;
+ NoteLayerPropertyChangedForSubtree();
+}
+
+void LayerImpl::SetMasksToBounds(bool masks_to_bounds) {
+ if (masks_to_bounds_ == masks_to_bounds)
+ return;
+
+ masks_to_bounds_ = masks_to_bounds;
+ NoteLayerPropertyChangedForSubtree();
+}
+
+void LayerImpl::SetContentsOpaque(bool opaque) {
+ if (contents_opaque_ == opaque)
+ return;
+
+ contents_opaque_ = opaque;
+ NoteLayerPropertyChangedForSubtree();
+}
+
+void LayerImpl::SetOpacity(float opacity) {
+ if (opacity_ == opacity)
+ return;
+
+ opacity_ = opacity;
+ NoteLayerSurfacePropertyChanged();
+}
+
+bool LayerImpl::OpacityIsAnimating() const {
+ return layer_animation_controller_->IsAnimatingProperty(Animation::Opacity);
+}
+
+bool LayerImpl::OpacityIsAnimatingOnImplOnly() const {
+ Animation* opacity_animation =
+ layer_animation_controller_->GetAnimation(Animation::Opacity);
+ return opacity_animation && opacity_animation->is_impl_only();
+}
+
+void LayerImpl::SetPosition(gfx::PointF position) {
+ if (position_ == position)
+ return;
+
+ position_ = position;
+ NoteLayerPropertyChangedForSubtree();
+}
+
+void LayerImpl::SetPreserves3d(bool preserves3_d) {
+ if (preserves_3d_ == preserves3_d)
+ return;
+
+ preserves_3d_ = preserves3_d;
+ NoteLayerPropertyChangedForSubtree();
+}
+
+void LayerImpl::SetSublayerTransform(const gfx::Transform& sublayer_transform) {
+ if (sublayer_transform_ == sublayer_transform)
+ return;
+
+ sublayer_transform_ = sublayer_transform;
+ // Sublayer transform does not affect the current layer; it affects only its
+ // children.
+ NoteLayerPropertyChangedForDescendants();
+}
+
+void LayerImpl::SetTransform(const gfx::Transform& transform) {
+ if (transform_ == transform)
+ return;
+
+ transform_ = transform;
+ NoteLayerSurfacePropertyChanged();
+}
+
+bool LayerImpl::TransformIsAnimating() const {
+ return layer_animation_controller_->IsAnimatingProperty(Animation::Transform);
+}
+
+bool LayerImpl::TransformIsAnimatingOnImplOnly() const {
+ Animation* transform_animation =
+ layer_animation_controller_->GetAnimation(Animation::Transform);
+ return transform_animation && transform_animation->is_impl_only();
+}
+
+void LayerImpl::SetContentBounds(gfx::Size content_bounds) {
+ if (this->content_bounds() == content_bounds)
+ return;
+
+ draw_properties_.content_bounds = content_bounds;
+ NoteLayerPropertyChanged();
+}
+
+void LayerImpl::SetContentsScale(float contents_scale_x,
+ float contents_scale_y) {
+ if (this->contents_scale_x() == contents_scale_x &&
+ this->contents_scale_y() == contents_scale_y)
+ return;
+
+ draw_properties_.contents_scale_x = contents_scale_x;
+ draw_properties_.contents_scale_y = contents_scale_y;
+ NoteLayerPropertyChanged();
+}
+
+void LayerImpl::CalculateContentsScale(
+ float ideal_contents_scale,
+ bool animating_transform_to_screen,
+ float* contents_scale_x,
+ float* contents_scale_y,
+ gfx::Size* content_bounds) {
+ // Base LayerImpl has all of its content scales and content bounds pushed
+ // from its Layer during commit and just reuses those values as-is.
+ *contents_scale_x = this->contents_scale_x();
+ *contents_scale_y = this->contents_scale_y();
+ *content_bounds = this->content_bounds();
+}
+
+void LayerImpl::UpdateScrollbarPositions() {
+ gfx::Vector2dF current_offset = scroll_offset_ + scroll_delta_;
+
+ if (horizontal_scrollbar_layer_) {
+ horizontal_scrollbar_layer_->SetCurrentPos(current_offset.x());
+ horizontal_scrollbar_layer_->SetTotalSize(bounds_.width());
+ horizontal_scrollbar_layer_->SetMaximum(max_scroll_offset_.x());
+ }
+ if (vertical_scrollbar_layer_) {
+ vertical_scrollbar_layer_->SetCurrentPos(current_offset.y());
+ vertical_scrollbar_layer_->SetTotalSize(bounds_.height());
+ vertical_scrollbar_layer_->SetMaximum(max_scroll_offset_.y());
+ }
+
+ if (current_offset == last_scroll_offset_)
+ return;
+ last_scroll_offset_ = current_offset;
+
+ if (scrollbar_animation_controller_ &&
+ !scrollbar_animation_controller_->isScrollGestureInProgress()) {
+ scrollbar_animation_controller_->didProgrammaticallyUpdateScroll(
+ base::TimeTicks::Now());
+ }
+
+ // Get the current_offset_.y() value for a sanity-check on scrolling
+ // benchmark metrics. Specifically, we want to make sure
+ // BasicMouseWheelSmoothScrollGesture has proper scroll curves.
+ if (layer_tree_impl()->IsActiveTree()) {
+ TRACE_COUNTER_ID1("gpu", "scroll_offset_y", this->id(), current_offset.y());
+ }
+}
+
+void LayerImpl::SetScrollOffset(gfx::Vector2d scroll_offset) {
+ if (scroll_offset_ == scroll_offset)
+ return;
+
+ scroll_offset_ = scroll_offset;
+ NoteLayerPropertyChangedForSubtree();
+ UpdateScrollbarPositions();
+}
+
+void LayerImpl::SetScrollDelta(gfx::Vector2dF scroll_delta) {
+ if (scroll_delta_ == scroll_delta)
+ return;
+
+ if (layer_tree_impl()->IsActiveTree()) {
+ LayerImpl* pending_twin = layer_tree_impl()->FindPendingTreeLayerById(id());
+ if (pending_twin) {
+ // The pending twin can't mirror the scroll delta of the active
+ // layer. Although the delta - sent scroll delta difference is
+ // identical for both twins, the sent scroll delta for the pending
+ // layer is zero, as anything that has been sent has been baked
+ // into the layer's position/scroll offset as a part of commit.
+ DCHECK(pending_twin->sent_scroll_delta().IsZero());
+ pending_twin->SetScrollDelta(scroll_delta - sent_scroll_delta());
+ }
+ }
+
+ scroll_delta_ = scroll_delta;
+ NoteLayerPropertyChangedForSubtree();
+
+ UpdateScrollbarPositions();
+}
+
+gfx::Vector2dF LayerImpl::TotalScrollOffset() const {
+ return scroll_offset_ + scroll_delta_;
+}
+
+void LayerImpl::SetImplTransform(const gfx::Transform& transform) {
+ if (impl_transform_ == transform)
+ return;
+
+ impl_transform_ = transform;
+ NoteLayerPropertyChangedForSubtree();
+}
+
+void LayerImpl::SetDoubleSided(bool double_sided) {
+ if (double_sided_ == double_sided)
+ return;
+
+ double_sided_ = double_sided;
+ NoteLayerPropertyChangedForSubtree();
+}
+
+Region LayerImpl::VisibleContentOpaqueRegion() const {
+ if (contents_opaque())
+ return visible_content_rect();
+ return Region();
+}
+
+void LayerImpl::DidLoseOutputSurface() {}
+
+void LayerImpl::SetMaxScrollOffset(gfx::Vector2d max_scroll_offset) {
+ if (max_scroll_offset_ == max_scroll_offset)
+ return;
+ max_scroll_offset_ = max_scroll_offset;
+
+ layer_tree_impl()->set_needs_update_draw_properties();
+ UpdateScrollbarPositions();
+}
+
+void LayerImpl::SetScrollbarOpacity(float opacity) {
+ if (horizontal_scrollbar_layer_)
+ horizontal_scrollbar_layer_->SetOpacity(opacity);
+ if (vertical_scrollbar_layer_)
+ vertical_scrollbar_layer_->SetOpacity(opacity);
+}
+
+inline scoped_ptr<ScrollbarAnimationController>
+CreateScrollbarAnimationControllerWithFade(LayerImpl* layer) {
+ base::TimeDelta fadeout_delay = base::TimeDelta::FromMilliseconds(300);
+ base::TimeDelta fadeout_length = base::TimeDelta::FromMilliseconds(300);
+ return ScrollbarAnimationControllerLinearFade::create(
+ layer, fadeout_delay, fadeout_length)
+ .PassAs<ScrollbarAnimationController>();
+}
+
+void LayerImpl::DidBecomeActive() {
+ if (!layer_tree_impl_->settings().useLinearFadeScrollbarAnimator)
+ return;
+
+ bool need_scrollbar_animation_controller = horizontal_scrollbar_layer_ ||
+ vertical_scrollbar_layer_;
+ if (need_scrollbar_animation_controller) {
+ if (!scrollbar_animation_controller_) {
+ scrollbar_animation_controller_ =
+ CreateScrollbarAnimationControllerWithFade(this);
+ }
+ } else {
+ scrollbar_animation_controller_.reset();
+ }
+
+}
+void LayerImpl::SetHorizontalScrollbarLayer(
+ ScrollbarLayerImpl* scrollbar_layer) {
+ horizontal_scrollbar_layer_ = scrollbar_layer;
+ if (horizontal_scrollbar_layer_)
+ horizontal_scrollbar_layer_->set_scroll_layer_id(id());
+}
+
+void LayerImpl::SetVerticalScrollbarLayer(ScrollbarLayerImpl* scrollbar_layer) {
+ vertical_scrollbar_layer_ = scrollbar_layer;
+ if (vertical_scrollbar_layer_)
+ vertical_scrollbar_layer_->set_scroll_layer_id(id());
+}
+
+void LayerImpl::AsValueInto(base::DictionaryValue* dict) const {
+ dict->SetInteger("id", id());
+ dict->Set("bounds", MathUtil::AsValue(bounds()).release());
+ dict->SetInteger("draws_content", DrawsContent());
+
+ bool clipped;
+ gfx::QuadF layer_quad = MathUtil::MapQuad(
+ screen_space_transform(),
+ gfx::QuadF(gfx::Rect(content_bounds())),
+ &clipped);
+ dict->Set("layer_quad", MathUtil::AsValue(layer_quad).release());
+
+}
+
+scoped_ptr<base::Value> LayerImpl::AsValue() const {
+ scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue());
+ AsValueInto(state.get());
+ return state.PassAs<base::Value>();
+}
+
+} // namespace cc
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
new file mode 100644
index 0000000..8b09e2e
--- /dev/null
+++ b/cc/layers/layer_impl.h
@@ -0,0 +1,528 @@
+// Copyright 2011 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.
+
+#ifndef CC_LAYERS_LAYER_IMPL_H_
+#define CC_LAYERS_LAYER_IMPL_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "cc/animation/layer_animation_controller.h"
+#include "cc/animation/layer_animation_value_observer.h"
+#include "cc/base/cc_export.h"
+#include "cc/base/region.h"
+#include "cc/base/scoped_ptr_vector.h"
+#include "cc/input/input_handler.h"
+#include "cc/layers/draw_properties.h"
+#include "cc/layers/render_surface_impl.h"
+#include "cc/quads/render_pass.h"
+#include "cc/quads/shared_quad_state.h"
+#include "cc/resources/resource_provider.h"
+#include "skia/ext/refptr.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebFilterOperations.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkImageFilter.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/rect_f.h"
+#include "ui/gfx/transform.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace cc {
+
+class LayerTreeHostImpl;
+class LayerTreeImpl;
+class QuadSink;
+class Renderer;
+class ScrollbarAnimationController;
+class ScrollbarLayerImpl;
+class Layer;
+
+struct AppendQuadsData;
+
+class CC_EXPORT LayerImpl : LayerAnimationValueObserver {
+ public:
+ typedef ScopedPtrVector<LayerImpl> LayerList;
+
+ static scoped_ptr<LayerImpl> Create(LayerTreeImpl* tree_impl, int id) {
+ return make_scoped_ptr(new LayerImpl(tree_impl, id));
+ }
+
+ virtual ~LayerImpl();
+
+ int id() const { return layer_id_; }
+
+ // LayerAnimationValueObserver implementation.
+ virtual void OnOpacityAnimated(float opacity) OVERRIDE;
+ virtual void OnTransformAnimated(const gfx::Transform& transform) OVERRIDE;
+ virtual bool IsActive() const OVERRIDE;
+
+ // Tree structure.
+ LayerImpl* parent() { return parent_; }
+ const LayerImpl* parent() const { return parent_; }
+ const LayerList& children() const { return children_; }
+ LayerList& children() { return children_; }
+ LayerImpl* child_at(size_t index) const { return children_[index]; }
+ void AddChild(scoped_ptr<LayerImpl> child);
+ scoped_ptr<LayerImpl> RemoveChild(LayerImpl* child);
+ void set_parent(LayerImpl* parent) { parent_ = parent; }
+ // Warning: This does not preserve tree structure invariants.
+ void ClearChildList();
+
+ void SetMaskLayer(scoped_ptr<LayerImpl> mask_layer);
+ LayerImpl* mask_layer() { return mask_layer_.get(); }
+ const LayerImpl* mask_layer() const { return mask_layer_.get(); }
+ scoped_ptr<LayerImpl> TakeMaskLayer();
+
+ void SetReplicaLayer(scoped_ptr<LayerImpl> replica_layer);
+ LayerImpl* replica_layer() { return replica_layer_.get(); }
+ const LayerImpl* replica_layer() const { return replica_layer_.get(); }
+ scoped_ptr<LayerImpl> TakeReplicaLayer();
+
+ bool has_mask() const { return mask_layer_; }
+ bool has_replica() const { return replica_layer_; }
+ bool replica_has_mask() const {
+ return replica_layer_ && (mask_layer_ || replica_layer_->mask_layer_);
+ }
+
+ LayerTreeImpl* layer_tree_impl() const { return layer_tree_impl_; }
+
+ scoped_ptr<SharedQuadState> CreateSharedQuadState() const;
+ // willDraw must be called before appendQuads. If willDraw is called,
+ // didDraw is guaranteed to be called before another willDraw or before
+ // the layer is destroyed. To enforce this, any class that overrides
+ // willDraw/didDraw must call the base class version.
+ virtual void WillDraw(ResourceProvider* resource_provider);
+ virtual void AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) {}
+ virtual void DidDraw(ResourceProvider* resource_provider);
+
+ virtual ResourceProvider::ResourceId ContentsResourceId() const;
+
+ virtual bool HasDelegatedContent() const;
+ virtual bool HasContributingDelegatedRenderPasses() const;
+ virtual RenderPass::Id FirstContributingRenderPassId() const;
+ virtual RenderPass::Id NextContributingRenderPassId(RenderPass::Id id) const;
+
+ virtual void UpdateTilePriorities() {}
+
+ virtual ScrollbarLayerImpl* ToScrollbarLayer();
+
+ // Returns true if this layer has content to draw.
+ void SetDrawsContent(bool draws_content);
+ bool DrawsContent() const { return draws_content_; }
+
+ bool force_render_surface() const { return force_render_surface_; }
+ void SetForceRenderSurface(bool force) { force_render_surface_ = force; }
+
+ void SetAnchorPoint(gfx::PointF anchor_point);
+ gfx::PointF anchor_point() const { return anchor_point_; }
+
+ void SetAnchorPointZ(float anchor_point_z);
+ float anchor_point_z() const { return anchor_point_z_; }
+
+ void SetBackgroundColor(SkColor background_color);
+ SkColor background_color() const { return background_color_; }
+
+ void SetFilters(const WebKit::WebFilterOperations& filters);
+ const WebKit::WebFilterOperations& filters() const { return filters_; }
+
+ void SetBackgroundFilters(const WebKit::WebFilterOperations& filters);
+ const WebKit::WebFilterOperations& background_filters() const {
+ return background_filters_;
+ }
+
+ void SetFilter(const skia::RefPtr<SkImageFilter>& filter);
+ skia::RefPtr<SkImageFilter> filter() const { return filter_; }
+
+ void SetMasksToBounds(bool masks_to_bounds);
+ bool masks_to_bounds() const { return masks_to_bounds_; }
+
+ void SetContentsOpaque(bool opaque);
+ bool contents_opaque() const { return contents_opaque_; }
+
+ void SetOpacity(float opacity);
+ float opacity() const { return opacity_; }
+ bool OpacityIsAnimating() const;
+ bool OpacityIsAnimatingOnImplOnly() const;
+
+ void SetPosition(gfx::PointF position);
+ gfx::PointF position() const { return position_; }
+
+ void SetIsContainerForFixedPositionLayers(bool container) {
+ is_container_for_fixed_position_layers_ = container;
+ }
+ bool is_container_for_fixed_position_layers() const {
+ return is_container_for_fixed_position_layers_;
+ }
+
+ void SetFixedToContainerLayer(bool fixed) {
+ fixed_to_container_layer_ = fixed;
+ }
+ bool fixed_to_container_layer() const { return fixed_to_container_layer_; }
+
+ void SetPreserves3d(bool preserves_3d);
+ bool preserves_3d() const { return preserves_3d_; }
+
+ void SetUseParentBackfaceVisibility(bool use) {
+ use_parent_backface_visibility_ = use;
+ }
+ bool use_parent_backface_visibility() const {
+ return use_parent_backface_visibility_;
+ }
+
+ void SetSublayerTransform(const gfx::Transform& sublayer_transform);
+ const gfx::Transform& sublayer_transform() const {
+ return sublayer_transform_;
+ }
+
+ // Debug layer name.
+ void SetDebugName(const std::string& debug_name) { debug_name_ = debug_name; }
+ std::string debug_name() const { return debug_name_; }
+
+ bool ShowDebugBorders() const;
+
+ // These invalidate the host's render surface layer list. The caller
+ // is responsible for calling setNeedsUpdateDrawProperties on the host
+ // so that its list can be recreated.
+ void CreateRenderSurface();
+ void ClearRenderSurface() { draw_properties_.render_surface.reset(); }
+
+ DrawProperties<LayerImpl, RenderSurfaceImpl>& draw_properties() {
+ return draw_properties_;
+ }
+ const DrawProperties<LayerImpl, RenderSurfaceImpl>& draw_properties() const {
+ return draw_properties_;
+ }
+
+ // The following are shortcut accessors to get various information from
+ // draw_properties_
+ const gfx::Transform& draw_transform() const {
+ return draw_properties_.target_space_transform;
+ }
+ const gfx::Transform& screen_space_transform() const {
+ return draw_properties_.screen_space_transform;
+ }
+ float draw_opacity() const { return draw_properties_.opacity; }
+ bool draw_opacity_is_animating() const {
+ return draw_properties_.opacity_is_animating;
+ }
+ bool draw_transform_is_animating() const {
+ return draw_properties_.target_space_transform_is_animating;
+ }
+ bool screen_space_transform_is_animating() const {
+ return draw_properties_.screen_space_transform_is_animating;
+ }
+ bool screen_space_opacity_is_animating() const {
+ return draw_properties_.screen_space_opacity_is_animating;
+ }
+ bool can_use_lcd_text() const { return draw_properties_.can_use_lcd_text; }
+ bool is_clipped() const { return draw_properties_.is_clipped; }
+ gfx::Rect clip_rect() const { return draw_properties_.clip_rect; }
+ gfx::Rect drawable_content_rect() const {
+ return draw_properties_.drawable_content_rect;
+ }
+ gfx::Rect visible_content_rect() const {
+ return draw_properties_.visible_content_rect;
+ }
+ LayerImpl* render_target() {
+ DCHECK(!draw_properties_.render_target ||
+ draw_properties_.render_target->render_surface());
+ return draw_properties_.render_target;
+ }
+ const LayerImpl* render_target() const {
+ DCHECK(!draw_properties_.render_target ||
+ draw_properties_.render_target->render_surface());
+ return draw_properties_.render_target;
+ }
+ RenderSurfaceImpl* render_surface() const {
+ return draw_properties_.render_surface.get();
+ }
+
+ // The client should be responsible for setting bounds, contentBounds and
+ // contentsScale to appropriate values. LayerImpl doesn't calculate any of
+ // them from the other values.
+
+ void SetBounds(gfx::Size bounds);
+ gfx::Size bounds() const { return bounds_; }
+
+ void SetContentBounds(gfx::Size content_bounds);
+ gfx::Size content_bounds() const { return draw_properties_.content_bounds; }
+
+ float contents_scale_x() const { return draw_properties_.contents_scale_x; }
+ float contents_scale_y() const { return draw_properties_.contents_scale_y; }
+ void SetContentsScale(float contents_scale_x, float contents_scale_y);
+
+ virtual void CalculateContentsScale(float ideal_contents_scale,
+ bool animating_transform_to_screen,
+ float* contents_scale_x,
+ float* contents_scale_y,
+ gfx::Size* contentBounds);
+
+ void SetScrollOffset(gfx::Vector2d scroll_offset);
+ gfx::Vector2d scroll_offset() const { return scroll_offset_; }
+
+ void SetMaxScrollOffset(gfx::Vector2d max_scroll_offset);
+ gfx::Vector2d max_scroll_offset() const { return max_scroll_offset_; }
+
+ void SetScrollDelta(gfx::Vector2dF scroll_delta);
+ gfx::Vector2dF scroll_delta() const { return scroll_delta_; }
+
+ gfx::Vector2dF TotalScrollOffset() const;
+
+ void SetImplTransform(const gfx::Transform& transform);
+ const gfx::Transform& impl_transform() const { return impl_transform_; }
+
+ void SetSentScrollDelta(gfx::Vector2d sent_scroll_delta);
+ gfx::Vector2d sent_scroll_delta() const { return sent_scroll_delta_; }
+
+ // Returns the delta of the scroll that was outside of the bounds of the
+ // initial scroll
+ gfx::Vector2dF ScrollBy(gfx::Vector2dF scroll);
+
+ void SetScrollable(bool scrollable) { scrollable_ = scrollable; }
+ bool scrollable() const { return scrollable_; }
+
+ void SetShouldScrollOnMainThread(bool should_scroll_on_main_thread) {
+ should_scroll_on_main_thread_ = should_scroll_on_main_thread;
+ }
+ bool should_scroll_on_main_thread() const {
+ return should_scroll_on_main_thread_;
+ }
+
+ void SetHaveWheelEventHandlers(bool have_wheel_event_handlers) {
+ have_wheel_event_handlers_ = have_wheel_event_handlers;
+ }
+ bool have_wheel_event_handlers() const { return have_wheel_event_handlers_; }
+
+ void SetNonFastScrollableRegion(const Region& region) {
+ non_fast_scrollable_region_ = region;
+ }
+ const Region& non_fast_scrollable_region() const {
+ return non_fast_scrollable_region_;
+ }
+
+ void SetTouchEventHandlerRegion(const Region& region) {
+ touch_event_handler_region_ = region;
+ }
+ const Region& touch_event_handler_region() const {
+ return touch_event_handler_region_;
+ }
+
+ void SetDrawCheckerboardForMissingTiles(bool checkerboard) {
+ draw_checkerboard_for_missing_tiles_ = checkerboard;
+ }
+ bool DrawCheckerboardForMissingTiles() const;
+
+ InputHandlerClient::ScrollStatus TryScroll(
+ gfx::PointF screen_space_point,
+ InputHandlerClient::ScrollInputType type) const;
+
+ void SetDoubleSided(bool double_sided);
+ bool double_sided() const { return double_sided_; }
+
+ void SetTransform(const gfx::Transform& transform);
+ const gfx::Transform& transform() const { return transform_; }
+ bool TransformIsAnimating() const;
+ bool TransformIsAnimatingOnImplOnly() const;
+
+ void set_update_rect(const gfx::RectF& update_rect) {
+ update_rect_ = update_rect;
+ }
+ const gfx::RectF& update_rect() const { return update_rect_; }
+
+ std::string LayerTreeAsText() const;
+ virtual base::DictionaryValue* LayerTreeAsJson() const;
+
+ void SetStackingOrderChanged(bool stacking_order_changed);
+
+ bool LayerPropertyChanged() const {
+ return layer_property_changed_ || LayerIsAlwaysDamaged();
+ }
+ bool LayerSurfacePropertyChanged() const;
+
+ void ResetAllChangeTrackingForSubtree();
+
+ virtual bool LayerIsAlwaysDamaged() const;
+
+ LayerAnimationController* layer_animation_controller() {
+ return layer_animation_controller_.get();
+ }
+
+ virtual Region VisibleContentOpaqueRegion() const;
+
+ virtual void DidBecomeActive();
+
+ // Indicates that the surface previously used to render this layer
+ // was lost and that a new one has been created. Won't be called
+ // until the new surface has been created successfully.
+ virtual void DidLoseOutputSurface();
+
+ ScrollbarAnimationController* scrollbar_animation_controller() const {
+ return scrollbar_animation_controller_.get();
+ }
+
+ void SetScrollbarOpacity(float opacity);
+
+ void SetHorizontalScrollbarLayer(ScrollbarLayerImpl* scrollbar_layer);
+ ScrollbarLayerImpl* horizontal_scrollbar_layer() {
+ return horizontal_scrollbar_layer_;
+ }
+
+ void SetVerticalScrollbarLayer(ScrollbarLayerImpl* scrollbar_layer);
+ ScrollbarLayerImpl* vertical_scrollbar_layer() {
+ return vertical_scrollbar_layer_;
+ }
+
+ gfx::Rect LayerRectToContentRect(const gfx::RectF& layer_rect) const;
+
+ virtual skia::RefPtr<SkPicture> GetPicture();
+
+ virtual bool CanClipSelf() const;
+
+ virtual bool AreVisibleResourcesReady() const;
+
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl);
+ virtual void PushPropertiesTo(LayerImpl* layer);
+
+ virtual scoped_ptr<base::Value> AsValue() const;
+
+ protected:
+ LayerImpl(LayerTreeImpl* layer_impl, int id);
+
+ // Get the color and size of the layer's debug border.
+ virtual void GetDebugBorderProperties(SkColor* color, float* width) const;
+
+ void AppendDebugBorderQuad(QuadSink* quad_sink,
+ const SharedQuadState* shared_quad_state,
+ AppendQuadsData* append_quads_data) const;
+
+ virtual void DumpLayerProperties(std::string* str, int indent) const;
+ static std::string IndentString(int indent);
+
+ void AsValueInto(base::DictionaryValue* dict) const;
+
+ void NoteLayerSurfacePropertyChanged();
+ void NoteLayerPropertyChanged();
+ void NoteLayerPropertyChangedForSubtree();
+
+ // Note carefully this does not affect the current layer.
+ void NoteLayerPropertyChangedForDescendants();
+
+ private:
+ void UpdateScrollbarPositions();
+
+ virtual const char* LayerTypeAsString() const;
+
+ void DumpLayer(std::string* str, int indent) const;
+
+ // Properties internal to LayerImpl
+ LayerImpl* parent_;
+ LayerList children_;
+ // mask_layer_ can be temporarily stolen during tree sync, we need this ID to
+ // confirm newly assigned layer is still the previous one
+ int mask_layer_id_;
+ scoped_ptr<LayerImpl> mask_layer_;
+ int replica_layer_id_; // ditto
+ scoped_ptr<LayerImpl> replica_layer_;
+ int layer_id_;
+ LayerTreeImpl* layer_tree_impl_;
+
+ // Properties synchronized from the associated Layer.
+ gfx::PointF anchor_point_;
+ float anchor_point_z_;
+ gfx::Size bounds_;
+ gfx::Vector2d scroll_offset_;
+ bool scrollable_;
+ bool should_scroll_on_main_thread_;
+ bool have_wheel_event_handlers_;
+ Region non_fast_scrollable_region_;
+ Region touch_event_handler_region_;
+ SkColor background_color_;
+ bool stacking_order_changed_;
+
+ // Whether the "back" of this layer should draw.
+ bool double_sided_;
+
+ // Tracks if drawing-related properties have changed since last redraw.
+ bool layer_property_changed_;
+
+ // Indicates that a property has changed on this layer that would not
+ // affect the pixels on its target surface, but would require redrawing
+ // the targetSurface onto its ancestor targetSurface.
+ // For layers that do not own a surface this flag acts as
+ // layer_property_changed_.
+ bool layer_surface_property_changed_;
+
+ bool masks_to_bounds_;
+ bool contents_opaque_;
+ float opacity_;
+ gfx::PointF position_;
+ bool preserves_3d_;
+ bool use_parent_backface_visibility_;
+ bool draw_checkerboard_for_missing_tiles_;
+ gfx::Transform sublayer_transform_;
+ gfx::Transform transform_;
+
+ bool draws_content_;
+ bool force_render_surface_;
+
+ // Set for the layer that other layers are fixed to.
+ bool is_container_for_fixed_position_layers_;
+ // This is true if the layer should be fixed to the closest ancestor
+ // container.
+ bool fixed_to_container_layer_;
+
+ gfx::Vector2dF scroll_delta_;
+ gfx::Vector2d sent_scroll_delta_;
+ gfx::Vector2d max_scroll_offset_;
+ gfx::Transform impl_transform_;
+ gfx::Vector2dF last_scroll_offset_;
+
+ // The global depth value of the center of the layer. This value is used
+ // to sort layers from back to front.
+ float draw_depth_;
+
+ // Debug layer name.
+ std::string debug_name_;
+
+ WebKit::WebFilterOperations filters_;
+ WebKit::WebFilterOperations background_filters_;
+ skia::RefPtr<SkImageFilter> filter_;
+
+#ifndef NDEBUG
+ bool between_will_draw_and_did_draw_;
+#endif
+
+ // Rect indicating what was repainted/updated during update.
+ // Note that plugin layers bypass this and leave it empty.
+ // Uses layer's content space.
+ gfx::RectF update_rect_;
+
+ // Manages animations for this layer.
+ scoped_refptr<LayerAnimationController> layer_animation_controller_;
+
+ // Manages scrollbars for this layer
+ scoped_ptr<ScrollbarAnimationController> scrollbar_animation_controller_;
+
+ // Weak pointers to this layer's scrollbars, if it has them. Updated during
+ // tree synchronization.
+ ScrollbarLayerImpl* horizontal_scrollbar_layer_;
+ ScrollbarLayerImpl* vertical_scrollbar_layer_;
+
+ // Group of properties that need to be computed based on the layer tree
+ // hierarchy before layers can be drawn.
+ DrawProperties<LayerImpl, RenderSurfaceImpl> draw_properties_;
+
+ DISALLOW_COPY_AND_ASSIGN(LayerImpl);
+};
+
+}
+
+#endif // CC_LAYERS_LAYER_IMPL_H_
diff --git a/cc/layers/layer_impl_unittest.cc b/cc/layers/layer_impl_unittest.cc
new file mode 100644
index 0000000..6ff279a
--- /dev/null
+++ b/cc/layers/layer_impl_unittest.cc
@@ -0,0 +1,255 @@
+// Copyright 2011 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 "cc/layers/layer_impl.h"
+
+#include "cc/test/fake_impl_proxy.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/fake_output_surface.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "cc/trees/single_thread_proxy.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebFilterOperation.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebFilterOperations.h"
+#include "third_party/skia/include/effects/SkBlurImageFilter.h"
+
+using namespace WebKit;
+
+namespace cc {
+namespace {
+
+#define EXECUTE_AND_VERIFY_SUBTREE_CHANGED(codeToTest) \
+ root->ResetAllChangeTrackingForSubtree(); \
+ codeToTest; \
+ EXPECT_TRUE(root->LayerPropertyChanged()); \
+ EXPECT_TRUE(child->LayerPropertyChanged()); \
+ EXPECT_TRUE(grandChild->LayerPropertyChanged()); \
+ EXPECT_FALSE(root->LayerSurfacePropertyChanged())
+
+
+#define EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(codeToTest) \
+ root->ResetAllChangeTrackingForSubtree(); \
+ codeToTest; \
+ EXPECT_FALSE(root->LayerPropertyChanged()); \
+ EXPECT_FALSE(child->LayerPropertyChanged()); \
+ EXPECT_FALSE(grandChild->LayerPropertyChanged()); \
+ EXPECT_FALSE(root->LayerSurfacePropertyChanged())
+
+#define EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(codeToTest) \
+ root->ResetAllChangeTrackingForSubtree(); \
+ codeToTest; \
+ EXPECT_TRUE(root->LayerPropertyChanged()); \
+ EXPECT_FALSE(child->LayerPropertyChanged()); \
+ EXPECT_FALSE(grandChild->LayerPropertyChanged()); \
+ EXPECT_FALSE(root->LayerSurfacePropertyChanged())
+
+#define EXECUTE_AND_VERIFY_ONLY_SURFACE_CHANGED(codeToTest) \
+ root->ResetAllChangeTrackingForSubtree(); \
+ codeToTest; \
+ EXPECT_FALSE(root->LayerPropertyChanged()); \
+ EXPECT_FALSE(child->LayerPropertyChanged()); \
+ EXPECT_FALSE(grandChild->LayerPropertyChanged()); \
+ EXPECT_TRUE(root->LayerSurfacePropertyChanged())
+
+#define VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(codeToTest) \
+ root->ResetAllChangeTrackingForSubtree(); \
+ hostImpl.ForcePrepareToDraw(); \
+ EXPECT_FALSE(hostImpl.active_tree()->needs_update_draw_properties()); \
+ codeToTest; \
+ EXPECT_TRUE(hostImpl.active_tree()->needs_update_draw_properties());
+
+#define VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(codeToTest) \
+ root->ResetAllChangeTrackingForSubtree(); \
+ hostImpl.ForcePrepareToDraw(); \
+ EXPECT_FALSE(hostImpl.active_tree()->needs_update_draw_properties()); \
+ codeToTest; \
+ EXPECT_FALSE(hostImpl.active_tree()->needs_update_draw_properties());
+
+TEST(LayerImplTest, verifyLayerChangesAreTrackedProperly)
+{
+ //
+ // This test checks that layerPropertyChanged() has the correct behavior.
+ //
+
+ // The constructor on this will fake that we are on the correct thread.
+ // Create a simple LayerImpl tree:
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+ EXPECT_TRUE(hostImpl.InitializeRenderer(createFakeOutputSurface()));
+ scoped_ptr<LayerImpl> root = LayerImpl::Create(hostImpl.active_tree(), 1);
+ root->AddChild(LayerImpl::Create(hostImpl.active_tree(), 2));
+ LayerImpl* child = root->children()[0];
+ child->AddChild(LayerImpl::Create(hostImpl.active_tree(), 3));
+ LayerImpl* grandChild = child->children()[0];
+
+ // Adding children is an internal operation and should not mark layers as changed.
+ EXPECT_FALSE(root->LayerPropertyChanged());
+ EXPECT_FALSE(child->LayerPropertyChanged());
+ EXPECT_FALSE(grandChild->LayerPropertyChanged());
+
+ gfx::PointF arbitraryPointF = gfx::PointF(0.125f, 0.25f);
+ float arbitraryNumber = 0.352f;
+ gfx::Size arbitrarySize = gfx::Size(111, 222);
+ gfx::Point arbitraryPoint = gfx::Point(333, 444);
+ gfx::Vector2d arbitraryVector2d = gfx::Vector2d(111, 222);
+ gfx::Rect arbitraryRect = gfx::Rect(arbitraryPoint, arbitrarySize);
+ gfx::RectF arbitraryRectF = gfx::RectF(arbitraryPointF, gfx::SizeF(1.234f, 5.678f));
+ SkColor arbitraryColor = SkColorSetRGB(10, 20, 30);
+ gfx::Transform arbitraryTransform;
+ arbitraryTransform.Scale3d(0.1, 0.2, 0.3);
+ WebFilterOperations arbitraryFilters;
+ arbitraryFilters.append(WebFilterOperation::createOpacityFilter(0.5));
+ skia::RefPtr<SkImageFilter> arbitraryFilter = skia::AdoptRef(new SkBlurImageFilter(SK_Scalar1, SK_Scalar1));
+
+ // These properties are internal, and should not be considered "change" when they are used.
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->set_update_rect(arbitraryRectF));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetMaxScrollOffset(arbitraryVector2d));
+
+ // Changing these properties affects the entire subtree of layers.
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetAnchorPoint(arbitraryPointF));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetAnchorPointZ(arbitraryNumber));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetFilters(arbitraryFilters));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetFilters(WebFilterOperations()));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetFilter(arbitraryFilter));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetMaskLayer(LayerImpl::Create(hostImpl.active_tree(), 4)));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetMasksToBounds(true));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetContentsOpaque(true));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetReplicaLayer(LayerImpl::Create(hostImpl.active_tree(), 5)));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetPosition(arbitraryPointF));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetPreserves3d(true));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetDoubleSided(false)); // constructor initializes it to "true".
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->ScrollBy(arbitraryVector2d));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetScrollDelta(gfx::Vector2d()));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetScrollOffset(arbitraryVector2d));
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetImplTransform(arbitraryTransform));
+
+ // Changing these properties only affects the layer itself.
+ EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->SetContentBounds(arbitrarySize));
+ EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->SetContentsScale(arbitraryNumber, arbitraryNumber));
+ EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->SetDrawsContent(true));
+ EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->SetBackgroundColor(SK_ColorGRAY));
+ EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->SetBackgroundFilters(arbitraryFilters));
+
+ // Changing these properties only affects how render surface is drawn
+ EXECUTE_AND_VERIFY_ONLY_SURFACE_CHANGED(root->SetOpacity(arbitraryNumber));
+ EXECUTE_AND_VERIFY_ONLY_SURFACE_CHANGED(root->SetTransform(arbitraryTransform));
+
+ // Special case: check that sublayer transform changes all layer's descendants, but not the layer itself.
+ root->ResetAllChangeTrackingForSubtree();
+ root->SetSublayerTransform(arbitraryTransform);
+ EXPECT_FALSE(root->LayerPropertyChanged());
+ EXPECT_TRUE(child->LayerPropertyChanged());
+ EXPECT_TRUE(grandChild->LayerPropertyChanged());
+
+ // Special case: check that setBounds changes behavior depending on masksToBounds.
+ root->SetMasksToBounds(false);
+ EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->SetBounds(gfx::Size(135, 246)));
+ root->SetMasksToBounds(true);
+ EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetBounds(arbitrarySize)); // should be a different size than previous call, to ensure it marks tree changed.
+
+ // After setting all these properties already, setting to the exact same values again should
+ // not cause any change.
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetAnchorPoint(arbitraryPointF));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetAnchorPointZ(arbitraryNumber));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetMasksToBounds(true));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetPosition(arbitraryPointF));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetPreserves3d(true));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetTransform(arbitraryTransform));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetDoubleSided(false)); // constructor initializes it to "true".
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetScrollDelta(gfx::Vector2d()));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetScrollOffset(arbitraryVector2d));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetImplTransform(arbitraryTransform));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetContentBounds(arbitrarySize));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetContentsScale(arbitraryNumber, arbitraryNumber));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetContentsOpaque(true));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetOpacity(arbitraryNumber));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetDrawsContent(true));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetSublayerTransform(arbitraryTransform));
+ EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetBounds(arbitrarySize));
+}
+
+TEST(LayerImplTest, VerifyNeedsUpdateDrawProperties)
+{
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+ EXPECT_TRUE(hostImpl.InitializeRenderer(createFakeOutputSurface()));
+ scoped_ptr<LayerImpl> root = LayerImpl::Create(hostImpl.active_tree(), 1);
+
+ gfx::PointF arbitraryPointF = gfx::PointF(0.125f, 0.25f);
+ float arbitraryNumber = 0.352f;
+ gfx::Size arbitrarySize = gfx::Size(111, 222);
+ gfx::Point arbitraryPoint = gfx::Point(333, 444);
+ gfx::Vector2d arbitraryVector2d = gfx::Vector2d(111, 222);
+ gfx::Vector2d largeVector2d = gfx::Vector2d(1000, 1000);
+ gfx::Rect arbitraryRect = gfx::Rect(arbitraryPoint, arbitrarySize);
+ gfx::RectF arbitraryRectF = gfx::RectF(arbitraryPointF, gfx::SizeF(1.234f, 5.678f));
+ SkColor arbitraryColor = SkColorSetRGB(10, 20, 30);
+ gfx::Transform arbitraryTransform;
+ arbitraryTransform.Scale3d(0.1, 0.2, 0.3);
+ WebFilterOperations arbitraryFilters;
+ arbitraryFilters.append(WebFilterOperation::createOpacityFilter(0.5));
+ skia::RefPtr<SkImageFilter> arbitraryFilter = skia::AdoptRef(new SkBlurImageFilter(SK_Scalar1, SK_Scalar1));
+
+ // Related filter functions.
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetFilters(arbitraryFilters));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetFilters(arbitraryFilters));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetFilters(WebFilterOperations()));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetFilter(arbitraryFilter));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetFilter(arbitraryFilter));
+
+ // Related scrolling functions.
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetMaxScrollOffset(largeVector2d));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetMaxScrollOffset(largeVector2d));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->ScrollBy(arbitraryVector2d));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->ScrollBy(gfx::Vector2d()));
+ root->SetScrollDelta(gfx::Vector2d(0, 0));
+ hostImpl.ForcePrepareToDraw();
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetScrollDelta(arbitraryVector2d));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetScrollDelta(arbitraryVector2d));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetScrollOffset(arbitraryVector2d));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetScrollOffset(arbitraryVector2d));
+
+ // Unrelated functions, always set to new values, always set needs update.
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetAnchorPointZ(arbitraryNumber));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetMaskLayer(LayerImpl::Create(hostImpl.active_tree(), 4)));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetMasksToBounds(true));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetContentsOpaque(true));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetReplicaLayer(LayerImpl::Create(hostImpl.active_tree(), 5)));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetPosition(arbitraryPointF));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetPreserves3d(true));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetDoubleSided(false)); // constructor initializes it to "true".
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetImplTransform(arbitraryTransform));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetContentBounds(arbitrarySize));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetContentsScale(arbitraryNumber, arbitraryNumber));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetDrawsContent(true));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetBackgroundColor(SK_ColorGRAY));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetBackgroundFilters(arbitraryFilters));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetOpacity(arbitraryNumber));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetTransform(arbitraryTransform));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetSublayerTransform(arbitraryTransform));
+ VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetBounds(arbitrarySize));
+
+ // Unrelated functions, set to the same values, no needs update.
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetAnchorPointZ(arbitraryNumber));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetFilter(arbitraryFilter));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetMasksToBounds(true));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetContentsOpaque(true));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetPosition(arbitraryPointF));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetPreserves3d(true));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetDoubleSided(false)); // constructor initializes it to "true".
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetImplTransform(arbitraryTransform));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetContentBounds(arbitrarySize));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetContentsScale(arbitraryNumber, arbitraryNumber));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetDrawsContent(true));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetBackgroundColor(SK_ColorGRAY));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetBackgroundFilters(arbitraryFilters));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetOpacity(arbitraryNumber));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetTransform(arbitraryTransform));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetSublayerTransform(arbitraryTransform));
+ VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetBounds(arbitrarySize));
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/layer_iterator.cc b/cc/layers/layer_iterator.cc
new file mode 100644
index 0000000..58a9175
--- /dev/null
+++ b/cc/layers/layer_iterator.cc
@@ -0,0 +1,147 @@
+// Copyright 2012 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 "cc/layers/layer_iterator.h"
+
+#include "cc/layers/layer.h"
+#include "cc/layers/layer_impl.h"
+#include "cc/layers/render_surface.h"
+#include "cc/layers/render_surface_impl.h"
+
+namespace cc {
+
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+void LayerIteratorActions::BackToFront::begin(LayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>& it)
+{
+ it.m_targetRenderSurfaceLayerIndex = 0;
+ it.m_currentLayerIndex = LayerIteratorValue::LayerIndexRepresentingTargetRenderSurface;
+
+ m_highestTargetRenderSurfaceLayer = 0;
+}
+
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+void LayerIteratorActions::BackToFront::end(LayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>& it)
+{
+ it.m_targetRenderSurfaceLayerIndex = LayerIteratorValue::InvalidTargetRenderSurfaceLayerIndex;
+ it.m_currentLayerIndex = 0;
+}
+
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+void LayerIteratorActions::BackToFront::next(LayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>& it)
+{
+ // If the current layer has a RS, move to its layer list. Otherwise, visit the next layer in the current RS layer list.
+ if (it.currentLayerRepresentsContributingRenderSurface()) {
+ // Save our position in the childLayer list for the RenderSurfaceImpl, then jump to the next RenderSurfaceImpl. Save where we
+ // came from in the next RenderSurfaceImpl so we can get back to it.
+ it.targetRenderSurface()->current_layer_index_history_ = it.m_currentLayerIndex;
+ int previousTargetRenderSurfaceLayer = it.m_targetRenderSurfaceLayerIndex;
+
+ it.m_targetRenderSurfaceLayerIndex = ++m_highestTargetRenderSurfaceLayer;
+ it.m_currentLayerIndex = LayerIteratorValue::LayerIndexRepresentingTargetRenderSurface;
+
+ it.targetRenderSurface()->target_render_surface_layer_index_history_ = previousTargetRenderSurfaceLayer;
+ } else {
+ ++it.m_currentLayerIndex;
+
+ int targetRenderSurfaceNumChildren = it.targetRenderSurfaceChildren().size();
+ while (it.m_currentLayerIndex == targetRenderSurfaceNumChildren) {
+ // Jump back to the previous RenderSurfaceImpl, and get back the position where we were in that list, and move to the next position there.
+ if (!it.m_targetRenderSurfaceLayerIndex) {
+ // End of the list
+ it.m_targetRenderSurfaceLayerIndex = LayerIteratorValue::InvalidTargetRenderSurfaceLayerIndex;
+ it.m_currentLayerIndex = 0;
+ return;
+ }
+ it.m_targetRenderSurfaceLayerIndex = it.targetRenderSurface()->target_render_surface_layer_index_history_;
+ it.m_currentLayerIndex = it.targetRenderSurface()->current_layer_index_history_ + 1;
+
+ targetRenderSurfaceNumChildren = it.targetRenderSurfaceChildren().size();
+ }
+ }
+}
+
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+void LayerIteratorActions::FrontToBack::begin(LayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>& it)
+{
+ it.m_targetRenderSurfaceLayerIndex = 0;
+ it.m_currentLayerIndex = it.targetRenderSurfaceChildren().size() - 1;
+ goToHighestInSubtree(it);
+}
+
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+void LayerIteratorActions::FrontToBack::end(LayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>& it)
+{
+ it.m_targetRenderSurfaceLayerIndex = LayerIteratorValue::InvalidTargetRenderSurfaceLayerIndex;
+ it.m_currentLayerIndex = 0;
+}
+
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+void LayerIteratorActions::FrontToBack::next(LayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>& it)
+{
+ // Moves to the previous layer in the current RS layer list. Then we check if the
+ // new current layer has its own RS, in which case there are things in that RS layer list that are higher, so
+ // we find the highest layer in that subtree.
+ // If we move back past the front of the list, we jump up to the previous RS layer list, picking up again where we
+ // had previously recursed into the current RS layer list.
+
+ if (!it.currentLayerRepresentsTargetRenderSurface()) {
+ // Subtracting one here will eventually cause the current layer to become that layer
+ // representing the target render surface.
+ --it.m_currentLayerIndex;
+ goToHighestInSubtree(it);
+ } else {
+ while (it.currentLayerRepresentsTargetRenderSurface()) {
+ if (!it.m_targetRenderSurfaceLayerIndex) {
+ // End of the list
+ it.m_targetRenderSurfaceLayerIndex = LayerIteratorValue::InvalidTargetRenderSurfaceLayerIndex;
+ it.m_currentLayerIndex = 0;
+ return;
+ }
+ it.m_targetRenderSurfaceLayerIndex = it.targetRenderSurface()->target_render_surface_layer_index_history_;
+ it.m_currentLayerIndex = it.targetRenderSurface()->current_layer_index_history_;
+ }
+ }
+}
+
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+void LayerIteratorActions::FrontToBack::goToHighestInSubtree(LayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>& it)
+{
+ if (it.currentLayerRepresentsTargetRenderSurface())
+ return;
+ while (it.currentLayerRepresentsContributingRenderSurface()) {
+ // Save where we were in the current target surface, move to the next one, and save the target surface that we
+ // came from there so we can go back to it.
+ it.targetRenderSurface()->current_layer_index_history_ = it.m_currentLayerIndex;
+ int previousTargetRenderSurfaceLayer = it.m_targetRenderSurfaceLayerIndex;
+
+ for (LayerType* layer = it.currentLayer(); it.targetRenderSurfaceLayer() != layer; ++it.m_targetRenderSurfaceLayerIndex) { }
+ it.m_currentLayerIndex = it.targetRenderSurfaceChildren().size() - 1;
+
+ it.targetRenderSurface()->target_render_surface_layer_index_history_ = previousTargetRenderSurfaceLayer;
+ }
+}
+
+typedef std::vector<scoped_refptr<Layer> > LayerList;
+typedef std::vector<LayerImpl*> LayerImplList;
+
+// Declare each of the above functions for Layer and LayerImpl classes so that they are linked.
+template CC_EXPORT void LayerIteratorActions::BackToFront::begin(LayerIterator<Layer, LayerList, RenderSurface, BackToFront> &);
+template CC_EXPORT void LayerIteratorActions::BackToFront::end(LayerIterator<Layer, LayerList, RenderSurface, BackToFront>&);
+template CC_EXPORT void LayerIteratorActions::BackToFront::next(LayerIterator<Layer, LayerList, RenderSurface, BackToFront>&);
+
+template CC_EXPORT void LayerIteratorActions::BackToFront::begin(LayerIterator<LayerImpl, LayerImplList, RenderSurfaceImpl, BackToFront>&);
+template CC_EXPORT void LayerIteratorActions::BackToFront::end(LayerIterator<LayerImpl, LayerImplList, RenderSurfaceImpl, BackToFront>&);
+template CC_EXPORT void LayerIteratorActions::BackToFront::next(LayerIterator<LayerImpl, LayerImplList, RenderSurfaceImpl, BackToFront>&);
+
+template CC_EXPORT void LayerIteratorActions::FrontToBack::next(LayerIterator<Layer, LayerList, RenderSurface, FrontToBack>&);
+template CC_EXPORT void LayerIteratorActions::FrontToBack::end(LayerIterator<Layer, LayerList, RenderSurface, FrontToBack>&);
+template CC_EXPORT void LayerIteratorActions::FrontToBack::begin(LayerIterator<Layer, LayerList, RenderSurface, FrontToBack>&);
+template CC_EXPORT void LayerIteratorActions::FrontToBack::goToHighestInSubtree(LayerIterator<Layer, LayerList, RenderSurface, FrontToBack>&);
+
+template CC_EXPORT void LayerIteratorActions::FrontToBack::next(LayerIterator<LayerImpl, LayerImplList, RenderSurfaceImpl, FrontToBack>&);
+template CC_EXPORT void LayerIteratorActions::FrontToBack::end(LayerIterator<LayerImpl, LayerImplList, RenderSurfaceImpl, FrontToBack>&);
+template CC_EXPORT void LayerIteratorActions::FrontToBack::begin(LayerIterator<LayerImpl, LayerImplList, RenderSurfaceImpl, FrontToBack>&);
+template CC_EXPORT void LayerIteratorActions::FrontToBack::goToHighestInSubtree(LayerIterator<LayerImpl, LayerImplList, RenderSurfaceImpl, FrontToBack>&);
+
+} // namespace cc
diff --git a/cc/layers/layer_iterator.h b/cc/layers/layer_iterator.h
new file mode 100644
index 0000000..55b1d79
--- /dev/null
+++ b/cc/layers/layer_iterator.h
@@ -0,0 +1,208 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_LAYER_ITERATOR_H_
+#define CC_LAYERS_LAYER_ITERATOR_H_
+
+#include "base/memory/ref_counted.h"
+#include "cc/base/cc_export.h"
+#include "cc/trees/layer_tree_host_common.h"
+
+namespace cc {
+
+// These classes provide means to iterate over the RenderSurfaceImpl-Layer tree.
+
+// Example code follows, for a tree of Layer/RenderSurface objects. See below for details.
+//
+// void doStuffOnLayers(const std::vector<scoped_refptr<Layer> >& renderSurfaceLayerList)
+// {
+// typedef LayerIterator<Layer, RenderSurface, LayerIteratorActions::FrontToBack> LayerIteratorType;
+//
+// LayerIteratorType end = LayerIteratorType::end(&renderSurfaceLayerList);
+// for (LayerIteratorType it = LayerIteratorType::begin(&renderSurfaceLayerList); it != end; ++it) {
+// // Only one of these will be true
+// if (it.representsTargetRenderSurface())
+// foo(*it); // *it is a layer representing a target RenderSurfaceImpl
+// if (it.representsContributingRenderSurface())
+// bar(*it); // *it is a layer representing a RenderSurfaceImpl that contributes to the layer's target RenderSurfaceImpl
+// if (it.representsItself())
+// baz(*it); // *it is a layer representing itself, as it contributes to its own target RenderSurfaceImpl
+// }
+// }
+
+// A RenderSurfaceImpl R may be referred to in one of two different contexts. One RenderSurfaceImpl is "current" at any time, for
+// whatever operation is being performed. This current surface is referred to as a target surface. For example, when R is
+// being painted it would be the target surface. Once R has been painted, its contents may be included into another
+// surface S. While S is considered the target surface when it is being painted, R is called a contributing surface
+// in this context as it contributes to the content of the target surface S.
+//
+// The iterator's current position in the tree always points to some layer. The state of the iterator indicates the role of the
+// layer, and will be one of the following three states. A single layer L will appear in the iteration process in at least one,
+// and possibly all, of these states.
+// 1. Representing the target surface: The iterator in this state, pointing at layer L, indicates that the target RenderSurfaceImpl
+// is now the surface owned by L. This will occur exactly once for each RenderSurfaceImpl in the tree.
+// 2. Representing a contributing surface: The iterator in this state, pointing at layer L, refers to the RenderSurfaceImpl owned
+// by L as a contributing surface, without changing the current target RenderSurfaceImpl.
+// 3. Representing itself: The iterator in this state, pointing at layer L, refers to the layer itself, as a child of the
+// current target RenderSurfaceImpl.
+//
+// The BackToFront iterator will return a layer representing the target surface before returning layers representing themselves
+// as children of the current target surface. Whereas the FrontToBack ordering will iterate over children layers of a surface
+// before the layer representing the surface as a target surface.
+//
+// To use the iterators:
+//
+// Create a stepping iterator and end iterator by calling LayerIterator::begin() and LayerIterator::end() and passing in the
+// list of layers owning target RenderSurfaces. Step through the tree by incrementing the stepping iterator while it is != to
+// the end iterator. At each step the iterator knows what the layer is representing, and you can query the iterator to decide
+// what actions to perform with the layer given what it represents.
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Non-templated constants
+struct LayerIteratorValue {
+ static const int InvalidTargetRenderSurfaceLayerIndex = -1;
+ // This must be -1 since the iterator action code assumes that this value can be
+ // reached by subtracting one from the position of the first layer in the current
+ // target surface's child layer list, which is 0.
+ static const int LayerIndexRepresentingTargetRenderSurface = -1;
+};
+
+// The position of a layer iterator that is independent of its many template types.
+template <typename LayerType>
+struct LayerIteratorPosition {
+ bool representsTargetRenderSurface;
+ bool representsContributingRenderSurface;
+ bool representsItself;
+ LayerType* targetRenderSurfaceLayer;
+ LayerType* currentLayer;
+};
+
+// An iterator class for walking over layers in the RenderSurfaceImpl-Layer tree.
+template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename IteratorActionType>
+class LayerIterator {
+ typedef LayerIterator<LayerType, LayerList, RenderSurfaceType, IteratorActionType> LayerIteratorType;
+
+public:
+ LayerIterator() : m_renderSurfaceLayerList(0) { }
+
+ static LayerIteratorType begin(const LayerList* renderSurfaceLayerList) { return LayerIteratorType(renderSurfaceLayerList, true); }
+ static LayerIteratorType end(const LayerList* renderSurfaceLayerList) { return LayerIteratorType(renderSurfaceLayerList, false); }
+
+ LayerIteratorType& operator++() { m_actions.next(*this); return *this; }
+ bool operator==(const LayerIterator& other) const
+ {
+ return m_targetRenderSurfaceLayerIndex == other.m_targetRenderSurfaceLayerIndex
+ && m_currentLayerIndex == other.m_currentLayerIndex;
+ }
+ bool operator!=(const LayerIteratorType& other) const { return !(*this == other); }
+
+ LayerType* operator->() const { return currentLayer(); }
+ LayerType* operator*() const { return currentLayer(); }
+
+ bool representsTargetRenderSurface() const { return currentLayerRepresentsTargetRenderSurface(); }
+ bool representsContributingRenderSurface() const { return !representsTargetRenderSurface() && currentLayerRepresentsContributingRenderSurface(); }
+ bool representsItself() const { return !representsTargetRenderSurface() && !representsContributingRenderSurface(); }
+
+ LayerType* targetRenderSurfaceLayer() const { return getRawPtr((*m_renderSurfaceLayerList)[m_targetRenderSurfaceLayerIndex]); }
+
+ operator const LayerIteratorPosition<LayerType>() const
+ {
+ LayerIteratorPosition<LayerType> position;
+ position.representsTargetRenderSurface = representsTargetRenderSurface();
+ position.representsContributingRenderSurface = representsContributingRenderSurface();
+ position.representsItself = representsItself();
+ position.targetRenderSurfaceLayer = targetRenderSurfaceLayer();
+ position.currentLayer = currentLayer();
+ return position;
+ }
+
+private:
+ LayerIterator(const LayerList* renderSurfaceLayerList, bool start)
+ : m_renderSurfaceLayerList(renderSurfaceLayerList)
+ , m_targetRenderSurfaceLayerIndex(0)
+ {
+ for (size_t i = 0; i < renderSurfaceLayerList->size(); ++i) {
+ if (!(*renderSurfaceLayerList)[i]->render_surface()) {
+ NOTREACHED();
+ m_actions.end(*this);
+ return;
+ }
+ }
+
+ if (start && !renderSurfaceLayerList->empty())
+ m_actions.begin(*this);
+ else
+ m_actions.end(*this);
+ }
+
+ inline static Layer* getRawPtr(const scoped_refptr<Layer>& ptr) { return ptr.get(); }
+ inline static LayerImpl* getRawPtr(LayerImpl* ptr) { return ptr; }
+
+ inline LayerType* currentLayer() const { return currentLayerRepresentsTargetRenderSurface() ? targetRenderSurfaceLayer() : getRawPtr(targetRenderSurfaceChildren()[m_currentLayerIndex]); }
+
+ inline bool currentLayerRepresentsContributingRenderSurface() const { return LayerTreeHostCommon::renderSurfaceContributesToTarget<LayerType>(currentLayer(), targetRenderSurfaceLayer()->id()); }
+ inline bool currentLayerRepresentsTargetRenderSurface() const { return m_currentLayerIndex == LayerIteratorValue::LayerIndexRepresentingTargetRenderSurface; }
+
+ inline RenderSurfaceType* targetRenderSurface() const { return targetRenderSurfaceLayer()->render_surface(); }
+ inline const LayerList& targetRenderSurfaceChildren() const { return targetRenderSurface()->layer_list(); }
+
+ IteratorActionType m_actions;
+ const LayerList* m_renderSurfaceLayerList;
+
+ // The iterator's current position.
+
+ // A position in the renderSurfaceLayerList. This points to a layer which owns the current target surface.
+ // This is a value from 0 to n-1 (n = size of renderSurfaceLayerList = number of surfaces). A value outside of
+ // this range (for example, LayerIteratorValue::InvalidTargetRenderSurfaceLayerIndex) is used to
+ // indicate a position outside the bounds of the tree.
+ int m_targetRenderSurfaceLayerIndex;
+ // A position in the list of layers that are children of the current target surface. When pointing to one of
+ // these layers, this is a value from 0 to n-1 (n = number of children). Since the iterator must also stop at
+ // the layers representing the target surface, this is done by setting the currentLayerIndex to a value of
+ // LayerIteratorValue::LayerRepresentingTargetRenderSurface.
+ int m_currentLayerIndex;
+
+ friend struct LayerIteratorActions;
+};
+
+// Orderings for iterating over the RenderSurfaceImpl-Layer tree.
+struct CC_EXPORT LayerIteratorActions {
+ // Walks layers sorted by z-order from back to front.
+ class CC_EXPORT BackToFront {
+ public:
+ template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+ void begin(LayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>&);
+
+ template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+ void end(LayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>&);
+
+ template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+ void next(LayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>&);
+
+ private:
+ int m_highestTargetRenderSurfaceLayer;
+ };
+
+ // Walks layers sorted by z-order from front to back
+ class CC_EXPORT FrontToBack {
+ public:
+ template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+ void begin(LayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>&);
+
+ template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+ void end(LayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>&);
+
+ template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+ void next(LayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>&);
+
+ private:
+ template <typename LayerType, typename LayerList, typename RenderSurfaceType, typename ActionType>
+ void goToHighestInSubtree(LayerIterator<LayerType, LayerList, RenderSurfaceType, ActionType>&);
+ };
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_LAYER_ITERATOR_H_
diff --git a/cc/layers/layer_iterator_unittest.cc b/cc/layers/layer_iterator_unittest.cc
new file mode 100644
index 0000000..9d4dc52
--- /dev/null
+++ b/cc/layers/layer_iterator_unittest.cc
@@ -0,0 +1,254 @@
+// Copyright 2012 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 "cc/layers/layer_iterator.h"
+
+#include "cc/layers/layer.h"
+#include "cc/trees/layer_tree_host_common.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/transform.h"
+
+using ::testing::Mock;
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::AnyNumber;
+
+namespace cc {
+namespace {
+
+class TestLayer : public Layer {
+public:
+ static scoped_refptr<TestLayer> Create() { return make_scoped_refptr(new TestLayer()); }
+
+ int m_countRepresentingTargetSurface;
+ int m_countRepresentingContributingSurface;
+ int m_countRepresentingItself;
+
+ virtual bool DrawsContent() const OVERRIDE { return m_drawsContent; }
+ void setDrawsContent(bool drawsContent) { m_drawsContent = drawsContent; }
+
+private:
+ TestLayer()
+ : Layer()
+ , m_drawsContent(true)
+ {
+ SetBounds(gfx::Size(100, 100));
+ SetPosition(gfx::Point());
+ SetAnchorPoint(gfx::Point());
+ }
+ virtual ~TestLayer()
+ {
+ }
+
+ bool m_drawsContent;
+};
+
+#define EXPECT_COUNT(layer, target, contrib, itself) \
+ EXPECT_EQ(target, layer->m_countRepresentingTargetSurface); \
+ EXPECT_EQ(contrib, layer->m_countRepresentingContributingSurface); \
+ EXPECT_EQ(itself, layer->m_countRepresentingItself);
+
+typedef LayerIterator<Layer, std::vector<scoped_refptr<Layer> >, RenderSurface, LayerIteratorActions::FrontToBack> FrontToBack;
+typedef LayerIterator<Layer, std::vector<scoped_refptr<Layer> >, RenderSurface, LayerIteratorActions::BackToFront> BackToFront;
+
+void resetCounts(std::vector<scoped_refptr<Layer> >& renderSurfaceLayerList)
+{
+ for (unsigned surfaceIndex = 0; surfaceIndex < renderSurfaceLayerList.size(); ++surfaceIndex) {
+ TestLayer* renderSurfaceLayer = static_cast<TestLayer*>(renderSurfaceLayerList[surfaceIndex].get());
+ RenderSurface* renderSurface = renderSurfaceLayer->render_surface();
+
+ renderSurfaceLayer->m_countRepresentingTargetSurface = -1;
+ renderSurfaceLayer->m_countRepresentingContributingSurface = -1;
+ renderSurfaceLayer->m_countRepresentingItself = -1;
+
+ for (unsigned layerIndex = 0; layerIndex < renderSurface->layer_list().size(); ++layerIndex) {
+ TestLayer* layer = static_cast<TestLayer*>(renderSurface->layer_list()[layerIndex].get());
+
+ layer->m_countRepresentingTargetSurface = -1;
+ layer->m_countRepresentingContributingSurface = -1;
+ layer->m_countRepresentingItself = -1;
+ }
+ }
+}
+
+void iterateFrontToBack(std::vector<scoped_refptr<Layer> >* renderSurfaceLayerList)
+{
+ resetCounts(*renderSurfaceLayerList);
+ int count = 0;
+ for (FrontToBack it = FrontToBack::begin(renderSurfaceLayerList); it != FrontToBack::end(renderSurfaceLayerList); ++it, ++count) {
+ TestLayer* layer = static_cast<TestLayer*>(*it);
+ if (it.representsTargetRenderSurface())
+ layer->m_countRepresentingTargetSurface = count;
+ if (it.representsContributingRenderSurface())
+ layer->m_countRepresentingContributingSurface = count;
+ if (it.representsItself())
+ layer->m_countRepresentingItself = count;
+ }
+}
+
+void iterateBackToFront(std::vector<scoped_refptr<Layer> >* renderSurfaceLayerList)
+{
+ resetCounts(*renderSurfaceLayerList);
+ int count = 0;
+ for (BackToFront it = BackToFront::begin(renderSurfaceLayerList); it != BackToFront::end(renderSurfaceLayerList); ++it, ++count) {
+ TestLayer* layer = static_cast<TestLayer*>(*it);
+ if (it.representsTargetRenderSurface())
+ layer->m_countRepresentingTargetSurface = count;
+ if (it.representsContributingRenderSurface())
+ layer->m_countRepresentingContributingSurface = count;
+ if (it.representsItself())
+ layer->m_countRepresentingItself = count;
+ }
+}
+
+TEST(LayerIteratorTest, emptyTree)
+{
+ std::vector<scoped_refptr<Layer> > renderSurfaceLayerList;
+
+ iterateBackToFront(&renderSurfaceLayerList);
+ iterateFrontToBack(&renderSurfaceLayerList);
+}
+
+TEST(LayerIteratorTest, simpleTree)
+{
+ scoped_refptr<TestLayer> rootLayer = TestLayer::Create();
+ scoped_refptr<TestLayer> first = TestLayer::Create();
+ scoped_refptr<TestLayer> second = TestLayer::Create();
+ scoped_refptr<TestLayer> third = TestLayer::Create();
+ scoped_refptr<TestLayer> fourth = TestLayer::Create();
+
+ rootLayer->CreateRenderSurface();
+
+ rootLayer->AddChild(first);
+ rootLayer->AddChild(second);
+ rootLayer->AddChild(third);
+ rootLayer->AddChild(fourth);
+
+ std::vector<scoped_refptr<Layer> > renderSurfaceLayerList;
+ LayerTreeHostCommon::calculateDrawProperties(rootLayer.get(), rootLayer->bounds(), 1, 1, 256, false, renderSurfaceLayerList);
+
+ iterateBackToFront(&renderSurfaceLayerList);
+ EXPECT_COUNT(rootLayer, 0, -1, 1);
+ EXPECT_COUNT(first, -1, -1, 2);
+ EXPECT_COUNT(second, -1, -1, 3);
+ EXPECT_COUNT(third, -1, -1, 4);
+ EXPECT_COUNT(fourth, -1, -1, 5);
+
+ iterateFrontToBack(&renderSurfaceLayerList);
+ EXPECT_COUNT(rootLayer, 5, -1, 4);
+ EXPECT_COUNT(first, -1, -1, 3);
+ EXPECT_COUNT(second, -1, -1, 2);
+ EXPECT_COUNT(third, -1, -1, 1);
+ EXPECT_COUNT(fourth, -1, -1, 0);
+
+}
+
+TEST(LayerIteratorTest, complexTree)
+{
+ scoped_refptr<TestLayer> rootLayer = TestLayer::Create();
+ scoped_refptr<TestLayer> root1 = TestLayer::Create();
+ scoped_refptr<TestLayer> root2 = TestLayer::Create();
+ scoped_refptr<TestLayer> root3 = TestLayer::Create();
+ scoped_refptr<TestLayer> root21 = TestLayer::Create();
+ scoped_refptr<TestLayer> root22 = TestLayer::Create();
+ scoped_refptr<TestLayer> root23 = TestLayer::Create();
+ scoped_refptr<TestLayer> root221 = TestLayer::Create();
+ scoped_refptr<TestLayer> root231 = TestLayer::Create();
+
+ rootLayer->CreateRenderSurface();
+
+ rootLayer->AddChild(root1);
+ rootLayer->AddChild(root2);
+ rootLayer->AddChild(root3);
+ root2->AddChild(root21);
+ root2->AddChild(root22);
+ root2->AddChild(root23);
+ root22->AddChild(root221);
+ root23->AddChild(root231);
+
+ std::vector<scoped_refptr<Layer> > renderSurfaceLayerList;
+ LayerTreeHostCommon::calculateDrawProperties(rootLayer.get(), rootLayer->bounds(), 1, 1, 256, false, renderSurfaceLayerList);
+
+ iterateBackToFront(&renderSurfaceLayerList);
+ EXPECT_COUNT(rootLayer, 0, -1, 1);
+ EXPECT_COUNT(root1, -1, -1, 2);
+ EXPECT_COUNT(root2, -1, -1, 3);
+ EXPECT_COUNT(root21, -1, -1, 4);
+ EXPECT_COUNT(root22, -1, -1, 5);
+ EXPECT_COUNT(root221, -1, -1, 6);
+ EXPECT_COUNT(root23, -1, -1, 7);
+ EXPECT_COUNT(root231, -1, -1, 8);
+ EXPECT_COUNT(root3, -1, -1, 9);
+
+ iterateFrontToBack(&renderSurfaceLayerList);
+ EXPECT_COUNT(rootLayer, 9, -1, 8);
+ EXPECT_COUNT(root1, -1, -1, 7);
+ EXPECT_COUNT(root2, -1, -1, 6);
+ EXPECT_COUNT(root21, -1, -1, 5);
+ EXPECT_COUNT(root22, -1, -1, 4);
+ EXPECT_COUNT(root221, -1, -1, 3);
+ EXPECT_COUNT(root23, -1, -1, 2);
+ EXPECT_COUNT(root231, -1, -1, 1);
+ EXPECT_COUNT(root3, -1, -1, 0);
+
+}
+
+TEST(LayerIteratorTest, complexTreeMultiSurface)
+{
+ scoped_refptr<TestLayer> rootLayer = TestLayer::Create();
+ scoped_refptr<TestLayer> root1 = TestLayer::Create();
+ scoped_refptr<TestLayer> root2 = TestLayer::Create();
+ scoped_refptr<TestLayer> root3 = TestLayer::Create();
+ scoped_refptr<TestLayer> root21 = TestLayer::Create();
+ scoped_refptr<TestLayer> root22 = TestLayer::Create();
+ scoped_refptr<TestLayer> root23 = TestLayer::Create();
+ scoped_refptr<TestLayer> root221 = TestLayer::Create();
+ scoped_refptr<TestLayer> root231 = TestLayer::Create();
+
+ rootLayer->CreateRenderSurface();
+ rootLayer->render_surface()->SetContentRect(gfx::Rect(gfx::Point(), rootLayer->bounds()));
+
+ rootLayer->AddChild(root1);
+ rootLayer->AddChild(root2);
+ rootLayer->AddChild(root3);
+ root2->setDrawsContent(false);
+ root2->SetOpacity(0.5);
+ root2->SetForceRenderSurface(true); // Force the layer to own a new surface.
+ root2->AddChild(root21);
+ root2->AddChild(root22);
+ root2->AddChild(root23);
+ root22->SetOpacity(0.5);
+ root22->AddChild(root221);
+ root23->SetOpacity(0.5);
+ root23->AddChild(root231);
+
+ std::vector<scoped_refptr<Layer> > renderSurfaceLayerList;
+ LayerTreeHostCommon::calculateDrawProperties(rootLayer.get(), rootLayer->bounds(), 1, 1, 256, false, renderSurfaceLayerList);
+
+ iterateBackToFront(&renderSurfaceLayerList);
+ EXPECT_COUNT(rootLayer, 0, -1, 1);
+ EXPECT_COUNT(root1, -1, -1, 2);
+ EXPECT_COUNT(root2, 4, 3, -1);
+ EXPECT_COUNT(root21, -1, -1, 5);
+ EXPECT_COUNT(root22, 7, 6, 8);
+ EXPECT_COUNT(root221, -1, -1, 9);
+ EXPECT_COUNT(root23, 11, 10, 12);
+ EXPECT_COUNT(root231, -1, -1, 13);
+ EXPECT_COUNT(root3, -1, -1, 14);
+
+ iterateFrontToBack(&renderSurfaceLayerList);
+ EXPECT_COUNT(rootLayer, 14, -1, 13);
+ EXPECT_COUNT(root1, -1, -1, 12);
+ EXPECT_COUNT(root2, 10, 11, -1);
+ EXPECT_COUNT(root21, -1, -1, 9);
+ EXPECT_COUNT(root22, 7, 8, 6);
+ EXPECT_COUNT(root221, -1, -1, 5);
+ EXPECT_COUNT(root23, 3, 4, 2);
+ EXPECT_COUNT(root231, -1, -1, 1);
+ EXPECT_COUNT(root3, -1, -1, 0);
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
new file mode 100644
index 0000000..7b0dbcc
--- /dev/null
+++ b/cc/layers/layer_unittest.cc
@@ -0,0 +1,977 @@
+// Copyright 2011 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 "cc/layers/layer.h"
+
+#include "cc/animation/keyframed_animation_curve.h"
+#include "cc/base/math_util.h"
+#include "cc/base/thread.h"
+#include "cc/layers/layer_impl.h"
+#include "cc/resources/layer_painter.h"
+#include "cc/test/animation_test_common.h"
+#include "cc/test/fake_impl_proxy.h"
+#include "cc/test/fake_layer_tree_host_client.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/geometry_test_utils.h"
+#include "cc/trees/layer_tree_host.h"
+#include "cc/trees/single_thread_proxy.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/transform.h"
+
+using ::testing::AnyNumber;
+using ::testing::AtLeast;
+using ::testing::Mock;
+using ::testing::StrictMock;
+using ::testing::_;
+
+#define EXPECT_SET_NEEDS_COMMIT(expect, codeToTest) do { \
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times((expect)); \
+ codeToTest; \
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get()); \
+ } while (0)
+
+#define EXPECT_SET_NEEDS_FULL_TREE_SYNC(expect, codeToTest) do { \
+ EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times((expect)); \
+ codeToTest; \
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get()); \
+ } while (0)
+
+
+namespace cc {
+namespace {
+
+class MockLayerImplTreeHost : public LayerTreeHost {
+public:
+ MockLayerImplTreeHost()
+ : LayerTreeHost(&m_fakeClient, LayerTreeSettings())
+ {
+ Initialize(scoped_ptr<Thread>(NULL));
+ }
+
+ MOCK_METHOD0(SetNeedsCommit, void());
+ MOCK_METHOD0(SetNeedsFullTreeSync, void());
+
+private:
+ FakeLayerImplTreeHostClient m_fakeClient;
+};
+
+class MockLayerPainter : public LayerPainter {
+public:
+ virtual void Paint(SkCanvas* canvas, gfx::Rect content_rect, gfx::RectF* opaque) OVERRIDE { }
+};
+
+
+class LayerTest : public testing::Test {
+public:
+ LayerTest()
+ : m_hostImpl(&m_proxy)
+ {
+ }
+
+protected:
+ virtual void SetUp()
+ {
+ layer_tree_host_.reset(new StrictMock<MockLayerImplTreeHost>);
+ }
+
+ virtual void TearDown()
+ {
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+ EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(AnyNumber());
+ parent_ = NULL;
+ m_child1 = NULL;
+ m_child2 = NULL;
+ m_child3 = NULL;
+ m_grandChild1 = NULL;
+ m_grandChild2 = NULL;
+ m_grandChild3 = NULL;
+
+ layer_tree_host_->SetRootLayer(NULL);
+ layer_tree_host_.reset();
+ }
+
+ void verifyTestTreeInitialState() const
+ {
+ ASSERT_EQ(3U, parent_->children().size());
+ EXPECT_EQ(m_child1, parent_->children()[0]);
+ EXPECT_EQ(m_child2, parent_->children()[1]);
+ EXPECT_EQ(m_child3, parent_->children()[2]);
+ EXPECT_EQ(parent_.get(), m_child1->parent());
+ EXPECT_EQ(parent_.get(), m_child2->parent());
+ EXPECT_EQ(parent_.get(), m_child3->parent());
+
+ ASSERT_EQ(2U, m_child1->children().size());
+ EXPECT_EQ(m_grandChild1, m_child1->children()[0]);
+ EXPECT_EQ(m_grandChild2, m_child1->children()[1]);
+ EXPECT_EQ(m_child1.get(), m_grandChild1->parent());
+ EXPECT_EQ(m_child1.get(), m_grandChild2->parent());
+
+ ASSERT_EQ(1U, m_child2->children().size());
+ EXPECT_EQ(m_grandChild3, m_child2->children()[0]);
+ EXPECT_EQ(m_child2.get(), m_grandChild3->parent());
+
+ ASSERT_EQ(0U, m_child3->children().size());
+ }
+
+ void createSimpleTestTree()
+ {
+ parent_ = Layer::Create();
+ m_child1 = Layer::Create();
+ m_child2 = Layer::Create();
+ m_child3 = Layer::Create();
+ m_grandChild1 = Layer::Create();
+ m_grandChild2 = Layer::Create();
+ m_grandChild3 = Layer::Create();
+
+ EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(AnyNumber());
+ layer_tree_host_->SetRootLayer(parent_);
+
+ parent_->AddChild(m_child1);
+ parent_->AddChild(m_child2);
+ parent_->AddChild(m_child3);
+ m_child1->AddChild(m_grandChild1);
+ m_child1->AddChild(m_grandChild2);
+ m_child2->AddChild(m_grandChild3);
+
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+
+ verifyTestTreeInitialState();
+ }
+
+ FakeImplProxy m_proxy;
+ FakeLayerTreeHostImpl m_hostImpl;
+
+ scoped_ptr<StrictMock<MockLayerImplTreeHost> > layer_tree_host_;
+ scoped_refptr<Layer> parent_;
+ scoped_refptr<Layer> m_child1;
+ scoped_refptr<Layer> m_child2;
+ scoped_refptr<Layer> m_child3;
+ scoped_refptr<Layer> m_grandChild1;
+ scoped_refptr<Layer> m_grandChild2;
+ scoped_refptr<Layer> m_grandChild3;
+};
+
+TEST_F(LayerTest, basicCreateAndDestroy)
+{
+ scoped_refptr<Layer> testLayer = Layer::Create();
+ ASSERT_TRUE(testLayer);
+
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(0);
+ testLayer->SetLayerTreeHost(layer_tree_host_.get());
+}
+
+TEST_F(LayerTest, addAndRemoveChild)
+{
+ scoped_refptr<Layer> parent = Layer::Create();
+ scoped_refptr<Layer> child = Layer::Create();
+
+ // Upon creation, layers should not have children or parent.
+ ASSERT_EQ(0U, parent->children().size());
+ EXPECT_FALSE(child->parent());
+
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, layer_tree_host_->SetRootLayer(parent));
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, parent->AddChild(child));
+
+ ASSERT_EQ(1U, parent->children().size());
+ EXPECT_EQ(child.get(), parent->children()[0]);
+ EXPECT_EQ(parent.get(), child->parent());
+ EXPECT_EQ(parent.get(), child->RootLayer());
+
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(AtLeast(1), child->RemoveFromParent());
+}
+
+TEST_F(LayerTest, addSameChildTwice)
+{
+ EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(AtLeast(1));
+
+ scoped_refptr<Layer> parent = Layer::Create();
+ scoped_refptr<Layer> child = Layer::Create();
+
+ layer_tree_host_->SetRootLayer(parent);
+
+ ASSERT_EQ(0u, parent->children().size());
+
+ parent->AddChild(child);
+ ASSERT_EQ(1u, parent->children().size());
+ EXPECT_EQ(parent.get(), child->parent());
+
+ parent->AddChild(child);
+ ASSERT_EQ(1u, parent->children().size());
+ EXPECT_EQ(parent.get(), child->parent());
+}
+
+TEST_F(LayerTest, insertChild)
+{
+ scoped_refptr<Layer> parent = Layer::Create();
+ scoped_refptr<Layer> child1 = Layer::Create();
+ scoped_refptr<Layer> child2 = Layer::Create();
+ scoped_refptr<Layer> child3 = Layer::Create();
+ scoped_refptr<Layer> child4 = Layer::Create();
+
+ parent->SetLayerTreeHost(layer_tree_host_.get());
+
+ ASSERT_EQ(0U, parent->children().size());
+
+ // Case 1: inserting to empty list.
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, parent->InsertChild(child3, 0));
+ ASSERT_EQ(1U, parent->children().size());
+ EXPECT_EQ(child3, parent->children()[0]);
+ EXPECT_EQ(parent.get(), child3->parent());
+
+ // Case 2: inserting to beginning of list
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, parent->InsertChild(child1, 0));
+ ASSERT_EQ(2U, parent->children().size());
+ EXPECT_EQ(child1, parent->children()[0]);
+ EXPECT_EQ(child3, parent->children()[1]);
+ EXPECT_EQ(parent.get(), child1->parent());
+
+ // Case 3: inserting to middle of list
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, parent->InsertChild(child2, 1));
+ ASSERT_EQ(3U, parent->children().size());
+ EXPECT_EQ(child1, parent->children()[0]);
+ EXPECT_EQ(child2, parent->children()[1]);
+ EXPECT_EQ(child3, parent->children()[2]);
+ EXPECT_EQ(parent.get(), child2->parent());
+
+ // Case 4: inserting to end of list
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, parent->InsertChild(child4, 3));
+
+ ASSERT_EQ(4U, parent->children().size());
+ EXPECT_EQ(child1, parent->children()[0]);
+ EXPECT_EQ(child2, parent->children()[1]);
+ EXPECT_EQ(child3, parent->children()[2]);
+ EXPECT_EQ(child4, parent->children()[3]);
+ EXPECT_EQ(parent.get(), child4->parent());
+
+ EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(AtLeast(1));
+}
+
+TEST_F(LayerTest, insertChildPastEndOfList)
+{
+ scoped_refptr<Layer> parent = Layer::Create();
+ scoped_refptr<Layer> child1 = Layer::Create();
+ scoped_refptr<Layer> child2 = Layer::Create();
+
+ ASSERT_EQ(0U, parent->children().size());
+
+ // insert to an out-of-bounds index
+ parent->InsertChild(child1, 53);
+
+ ASSERT_EQ(1U, parent->children().size());
+ EXPECT_EQ(child1, parent->children()[0]);
+
+ // insert another child to out-of-bounds, when list is not already empty.
+ parent->InsertChild(child2, 2459);
+
+ ASSERT_EQ(2U, parent->children().size());
+ EXPECT_EQ(child1, parent->children()[0]);
+ EXPECT_EQ(child2, parent->children()[1]);
+}
+
+TEST_F(LayerTest, insertSameChildTwice)
+{
+ scoped_refptr<Layer> parent = Layer::Create();
+ scoped_refptr<Layer> child1 = Layer::Create();
+ scoped_refptr<Layer> child2 = Layer::Create();
+
+ parent->SetLayerTreeHost(layer_tree_host_.get());
+
+ ASSERT_EQ(0U, parent->children().size());
+
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, parent->InsertChild(child1, 0));
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, parent->InsertChild(child2, 1));
+
+ ASSERT_EQ(2U, parent->children().size());
+ EXPECT_EQ(child1, parent->children()[0]);
+ EXPECT_EQ(child2, parent->children()[1]);
+
+ // Inserting the same child again should cause the child to be removed and re-inserted at the new location.
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(AtLeast(1), parent->InsertChild(child1, 1));
+
+ // child1 should now be at the end of the list.
+ ASSERT_EQ(2U, parent->children().size());
+ EXPECT_EQ(child2, parent->children()[0]);
+ EXPECT_EQ(child1, parent->children()[1]);
+
+ EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(AtLeast(1));
+}
+
+TEST_F(LayerTest, replaceChildWithNewChild)
+{
+ createSimpleTestTree();
+ scoped_refptr<Layer> child4 = Layer::Create();
+
+ EXPECT_FALSE(child4->parent());
+
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(AtLeast(1), parent_->ReplaceChild(m_child2.get(), child4));
+ EXPECT_FALSE(parent_->NeedsDisplayForTesting());
+ EXPECT_FALSE(m_child1->NeedsDisplayForTesting());
+ EXPECT_FALSE(m_child2->NeedsDisplayForTesting());
+ EXPECT_FALSE(m_child3->NeedsDisplayForTesting());
+ EXPECT_FALSE(child4->NeedsDisplayForTesting());
+
+ ASSERT_EQ(static_cast<size_t>(3), parent_->children().size());
+ EXPECT_EQ(m_child1, parent_->children()[0]);
+ EXPECT_EQ(child4, parent_->children()[1]);
+ EXPECT_EQ(m_child3, parent_->children()[2]);
+ EXPECT_EQ(parent_.get(), child4->parent());
+
+ EXPECT_FALSE(m_child2->parent());
+}
+
+TEST_F(LayerTest, replaceChildWithNewChildAutomaticRasterScale)
+{
+ createSimpleTestTree();
+ scoped_refptr<Layer> child4 = Layer::Create();
+ EXPECT_SET_NEEDS_COMMIT(1, m_child1->SetAutomaticallyComputeRasterScale(true));
+ EXPECT_SET_NEEDS_COMMIT(1, m_child2->SetAutomaticallyComputeRasterScale(true));
+ EXPECT_SET_NEEDS_COMMIT(1, m_child3->SetAutomaticallyComputeRasterScale(true));
+
+ EXPECT_FALSE(child4->parent());
+
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(AtLeast(1), parent_->ReplaceChild(m_child2.get(), child4));
+ EXPECT_FALSE(parent_->NeedsDisplayForTesting());
+ EXPECT_FALSE(m_child1->NeedsDisplayForTesting());
+ EXPECT_FALSE(m_child2->NeedsDisplayForTesting());
+ EXPECT_FALSE(m_child3->NeedsDisplayForTesting());
+ EXPECT_FALSE(child4->NeedsDisplayForTesting());
+
+ ASSERT_EQ(3U, parent_->children().size());
+ EXPECT_EQ(m_child1, parent_->children()[0]);
+ EXPECT_EQ(child4, parent_->children()[1]);
+ EXPECT_EQ(m_child3, parent_->children()[2]);
+ EXPECT_EQ(parent_.get(), child4->parent());
+
+ EXPECT_FALSE(m_child2->parent());
+}
+
+TEST_F(LayerTest, replaceChildWithNewChildThatHasOtherParent)
+{
+ createSimpleTestTree();
+
+ // create another simple tree with testLayer and child4.
+ scoped_refptr<Layer> testLayer = Layer::Create();
+ scoped_refptr<Layer> child4 = Layer::Create();
+ testLayer->AddChild(child4);
+ ASSERT_EQ(1U, testLayer->children().size());
+ EXPECT_EQ(child4, testLayer->children()[0]);
+ EXPECT_EQ(testLayer.get(), child4->parent());
+
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(AtLeast(1), parent_->ReplaceChild(m_child2.get(), child4));
+
+ ASSERT_EQ(3U, parent_->children().size());
+ EXPECT_EQ(m_child1, parent_->children()[0]);
+ EXPECT_EQ(child4, parent_->children()[1]);
+ EXPECT_EQ(m_child3, parent_->children()[2]);
+ EXPECT_EQ(parent_.get(), child4->parent());
+
+ // testLayer should no longer have child4,
+ // and child2 should no longer have a parent.
+ ASSERT_EQ(0U, testLayer->children().size());
+ EXPECT_FALSE(m_child2->parent());
+}
+
+TEST_F(LayerTest, replaceChildWithSameChild)
+{
+ createSimpleTestTree();
+
+ // SetNeedsFullTreeSync / SetNeedsCommit should not be called because its the same child
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(0);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(0);
+ parent_->ReplaceChild(m_child2.get(), m_child2);
+
+ verifyTestTreeInitialState();
+}
+
+TEST_F(LayerTest, removeAllChildren)
+{
+ createSimpleTestTree();
+
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(AtLeast(3), parent_->RemoveAllChildren());
+
+ ASSERT_EQ(0U, parent_->children().size());
+ EXPECT_FALSE(m_child1->parent());
+ EXPECT_FALSE(m_child2->parent());
+ EXPECT_FALSE(m_child3->parent());
+}
+
+TEST_F(LayerTest, setChildren)
+{
+ scoped_refptr<Layer> oldParent = Layer::Create();
+ scoped_refptr<Layer> newParent = Layer::Create();
+
+ scoped_refptr<Layer> child1 = Layer::Create();
+ scoped_refptr<Layer> child2 = Layer::Create();
+
+ std::vector<scoped_refptr<Layer> > newChildren;
+ newChildren.push_back(child1);
+ newChildren.push_back(child2);
+
+ // Set up and verify initial test conditions: child1 has a parent, child2 has no parent.
+ oldParent->AddChild(child1);
+ ASSERT_EQ(0U, newParent->children().size());
+ EXPECT_EQ(oldParent.get(), child1->parent());
+ EXPECT_FALSE(child2->parent());
+
+ newParent->SetLayerTreeHost(layer_tree_host_.get());
+
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(AtLeast(1), newParent->SetChildren(newChildren));
+
+ ASSERT_EQ(2U, newParent->children().size());
+ EXPECT_EQ(newParent.get(), child1->parent());
+ EXPECT_EQ(newParent.get(), child2->parent());
+
+ EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(AtLeast(1));
+}
+
+TEST_F(LayerTest, getRootLayerAfterTreeManipulations)
+{
+ createSimpleTestTree();
+
+ // For this test we don't care about SetNeedsFullTreeSync calls.
+ EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times(AnyNumber());
+
+ scoped_refptr<Layer> child4 = Layer::Create();
+
+ EXPECT_EQ(parent_.get(), parent_->RootLayer());
+ EXPECT_EQ(parent_.get(), m_child1->RootLayer());
+ EXPECT_EQ(parent_.get(), m_child2->RootLayer());
+ EXPECT_EQ(parent_.get(), m_child3->RootLayer());
+ EXPECT_EQ(child4.get(), child4->RootLayer());
+ EXPECT_EQ(parent_.get(), m_grandChild1->RootLayer());
+ EXPECT_EQ(parent_.get(), m_grandChild2->RootLayer());
+ EXPECT_EQ(parent_.get(), m_grandChild3->RootLayer());
+
+ m_child1->RemoveFromParent();
+
+ // child1 and its children, grandChild1 and grandChild2 are now on a separate subtree.
+ EXPECT_EQ(parent_.get(), parent_->RootLayer());
+ EXPECT_EQ(m_child1.get(), m_child1->RootLayer());
+ EXPECT_EQ(parent_.get(), m_child2->RootLayer());
+ EXPECT_EQ(parent_.get(), m_child3->RootLayer());
+ EXPECT_EQ(child4.get(), child4->RootLayer());
+ EXPECT_EQ(m_child1.get(), m_grandChild1->RootLayer());
+ EXPECT_EQ(m_child1.get(), m_grandChild2->RootLayer());
+ EXPECT_EQ(parent_.get(), m_grandChild3->RootLayer());
+
+ m_grandChild3->AddChild(child4);
+
+ EXPECT_EQ(parent_.get(), parent_->RootLayer());
+ EXPECT_EQ(m_child1.get(), m_child1->RootLayer());
+ EXPECT_EQ(parent_.get(), m_child2->RootLayer());
+ EXPECT_EQ(parent_.get(), m_child3->RootLayer());
+ EXPECT_EQ(parent_.get(), child4->RootLayer());
+ EXPECT_EQ(m_child1.get(), m_grandChild1->RootLayer());
+ EXPECT_EQ(m_child1.get(), m_grandChild2->RootLayer());
+ EXPECT_EQ(parent_.get(), m_grandChild3->RootLayer());
+
+ m_child2->ReplaceChild(m_grandChild3.get(), m_child1);
+
+ // grandChild3 gets orphaned and the child1 subtree gets planted back into the tree under child2.
+ EXPECT_EQ(parent_.get(), parent_->RootLayer());
+ EXPECT_EQ(parent_.get(), m_child1->RootLayer());
+ EXPECT_EQ(parent_.get(), m_child2->RootLayer());
+ EXPECT_EQ(parent_.get(), m_child3->RootLayer());
+ EXPECT_EQ(m_grandChild3.get(), child4->RootLayer());
+ EXPECT_EQ(parent_.get(), m_grandChild1->RootLayer());
+ EXPECT_EQ(parent_.get(), m_grandChild2->RootLayer());
+ EXPECT_EQ(m_grandChild3.get(), m_grandChild3->RootLayer());
+}
+
+TEST_F(LayerTest, checkSetNeedsDisplayCausesCorrectBehavior)
+{
+ // The semantics for setNeedsDisplay which are tested here:
+ // 1. sets needsDisplay flag appropriately.
+ // 2. indirectly calls SetNeedsCommit, exactly once for each call to setNeedsDisplay.
+
+ scoped_refptr<Layer> testLayer = Layer::Create();
+ testLayer->SetLayerTreeHost(layer_tree_host_.get());
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetIsDrawable(true));
+
+ gfx::Size testBounds = gfx::Size(501, 508);
+
+ gfx::RectF dirty1 = gfx::RectF(10, 15, 1, 2);
+ gfx::RectF dirty2 = gfx::RectF(20, 25, 3, 4);
+ gfx::RectF emptyDirtyRect = gfx::RectF(40, 45, 0, 0);
+ gfx::RectF outOfBoundsDirtyRect = gfx::RectF(400, 405, 500, 502);
+
+ // Before anything, testLayer should not be dirty.
+ EXPECT_FALSE(testLayer->NeedsDisplayForTesting());
+
+ // This is just initialization, but SetNeedsCommit behavior is verified anyway to avoid warnings.
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetBounds(testBounds));
+ EXPECT_TRUE(testLayer->NeedsDisplayForTesting());
+
+ // The real test begins here.
+ testLayer->ResetNeedsDisplayForTesting();
+ EXPECT_FALSE(testLayer->NeedsDisplayForTesting());
+
+ // Case 1: Layer should accept dirty rects that go beyond its bounds.
+ testLayer->ResetNeedsDisplayForTesting();
+ EXPECT_FALSE(testLayer->NeedsDisplayForTesting());
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetNeedsDisplayRect(outOfBoundsDirtyRect));
+ EXPECT_TRUE(testLayer->NeedsDisplayForTesting());
+ testLayer->ResetNeedsDisplayForTesting();
+
+ // Case 2: SetNeedsDisplay() without the dirty rect arg.
+ testLayer->ResetNeedsDisplayForTesting();
+ EXPECT_FALSE(testLayer->NeedsDisplayForTesting());
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetNeedsDisplay());
+ EXPECT_TRUE(testLayer->NeedsDisplayForTesting());
+ testLayer->ResetNeedsDisplayForTesting();
+
+ // Case 3: SetNeedsDisplay() with a non-drawable layer
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetIsDrawable(false));
+ testLayer->ResetNeedsDisplayForTesting();
+ EXPECT_FALSE(testLayer->NeedsDisplayForTesting());
+ EXPECT_SET_NEEDS_COMMIT(0, testLayer->SetNeedsDisplayRect(dirty1));
+ EXPECT_TRUE(testLayer->NeedsDisplayForTesting());
+}
+
+TEST_F(LayerTest, checkPropertyChangeCausesCorrectBehavior)
+{
+ scoped_refptr<Layer> testLayer = Layer::Create();
+ testLayer->SetLayerTreeHost(layer_tree_host_.get());
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetIsDrawable(true));
+
+ scoped_refptr<Layer> dummyLayer1 = Layer::Create(); // just a dummy layer for this test case.
+ scoped_refptr<Layer> dummyLayer2 = Layer::Create(); // just a dummy layer for this test case.
+
+ // sanity check of initial test condition
+ EXPECT_FALSE(testLayer->NeedsDisplayForTesting());
+
+ // Next, test properties that should call SetNeedsCommit (but not setNeedsDisplay)
+ // All properties need to be set to new values in order for SetNeedsCommit to be called.
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetAnchorPoint(gfx::PointF(1.23f, 4.56f)));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetAnchorPointZ(0.7f));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetBackgroundColor(SK_ColorLTGRAY));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetMasksToBounds(true));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetOpacity(0.5));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetContentsOpaque(true));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetPosition(gfx::PointF(4, 9)));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetSublayerTransform(gfx::Transform(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetScrollable(true));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetScrollOffset(gfx::Vector2d(10, 10)));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetShouldScrollOnMainThread(true));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetNonFastScrollableRegion(gfx::Rect(1, 1, 2, 2)));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetHaveWheelEventHandlers(true));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetTransform(gfx::Transform(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetDoubleSided(false));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetDebugName("Test Layer"));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetDrawCheckerboardForMissingTiles(!testLayer->DrawCheckerboardForMissingTiles()));
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetForceRenderSurface(true));
+
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, testLayer->SetMaskLayer(dummyLayer1.get()));
+ EXPECT_SET_NEEDS_FULL_TREE_SYNC(1, testLayer->SetReplicaLayer(dummyLayer2.get()));
+
+ // The above tests should not have caused a change to the needsDisplay flag.
+ EXPECT_FALSE(testLayer->NeedsDisplayForTesting());
+
+ // As layers are removed from the tree, they will cause a tree sync.
+ EXPECT_CALL(*layer_tree_host_, SetNeedsFullTreeSync()).Times((AnyNumber()));
+}
+
+TEST_F(LayerTest, setBoundsTriggersSetNeedsRedrawAfterGettingNonEmptyBounds)
+{
+ scoped_refptr<Layer> testLayer = Layer::Create();
+ testLayer->SetLayerTreeHost(layer_tree_host_.get());
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetIsDrawable(true));
+
+ EXPECT_FALSE(testLayer->NeedsDisplayForTesting());
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetBounds(gfx::Size(0, 10)));
+ EXPECT_FALSE(testLayer->NeedsDisplayForTesting());
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetBounds(gfx::Size(10, 10)));
+ EXPECT_TRUE(testLayer->NeedsDisplayForTesting());
+
+ testLayer->ResetNeedsDisplayForTesting();
+ EXPECT_FALSE(testLayer->NeedsDisplayForTesting());
+
+ // Calling setBounds only invalidates on the first time.
+ EXPECT_SET_NEEDS_COMMIT(1, testLayer->SetBounds(gfx::Size(7, 10)));
+ EXPECT_FALSE(testLayer->NeedsDisplayForTesting());
+}
+
+TEST_F(LayerTest, verifyPushPropertiesAccumulatesUpdateRect)
+{
+ scoped_refptr<Layer> testLayer = Layer::Create();
+ scoped_ptr<LayerImpl> implLayer = LayerImpl::Create(m_hostImpl.active_tree(), 1);
+
+ testLayer->SetNeedsDisplayRect(gfx::RectF(gfx::PointF(), gfx::SizeF(5, 5)));
+ testLayer->PushPropertiesTo(implLayer.get());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(gfx::PointF(), gfx::SizeF(5, 5)), implLayer->update_rect());
+
+ // The LayerImpl's updateRect should be accumulated here, since we did not do anything to clear it.
+ testLayer->SetNeedsDisplayRect(gfx::RectF(gfx::PointF(10, 10), gfx::SizeF(5, 5)));
+ testLayer->PushPropertiesTo(implLayer.get());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(gfx::PointF(), gfx::SizeF(15, 15)), implLayer->update_rect());
+
+ // If we do clear the LayerImpl side, then the next updateRect should be fresh without accumulation.
+ implLayer->ResetAllChangeTrackingForSubtree();
+ testLayer->SetNeedsDisplayRect(gfx::RectF(gfx::PointF(10, 10), gfx::SizeF(5, 5)));
+ testLayer->PushPropertiesTo(implLayer.get());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(gfx::PointF(10, 10), gfx::SizeF(5, 5)), implLayer->update_rect());
+}
+
+TEST_F(LayerTest, verifyPushPropertiesCausesSurfacePropertyChangedForTransform)
+{
+ scoped_refptr<Layer> testLayer = Layer::Create();
+ scoped_ptr<LayerImpl> implLayer = LayerImpl::Create(m_hostImpl.active_tree(), 1);
+
+ gfx::Transform transform;
+ transform.Rotate(45.0);
+ testLayer->SetTransform(transform);
+
+ EXPECT_FALSE(implLayer->LayerSurfacePropertyChanged());
+
+ testLayer->PushPropertiesTo(implLayer.get());
+
+ EXPECT_TRUE(implLayer->LayerSurfacePropertyChanged());
+}
+
+TEST_F(LayerTest, verifyPushPropertiesCausesSurfacePropertyChangedForOpacity)
+{
+ scoped_refptr<Layer> testLayer = Layer::Create();
+ scoped_ptr<LayerImpl> implLayer = LayerImpl::Create(m_hostImpl.active_tree(), 1);
+
+ testLayer->SetOpacity(0.5);
+
+ EXPECT_FALSE(implLayer->LayerSurfacePropertyChanged());
+
+ testLayer->PushPropertiesTo(implLayer.get());
+
+ EXPECT_TRUE(implLayer->LayerSurfacePropertyChanged());
+}
+
+TEST_F(LayerTest, verifyPushPropertiesDoesNotCauseSurfacePropertyChangedDuringImplOnlyTransformAnimation)
+{
+ scoped_refptr<Layer> testLayer = Layer::Create();
+ scoped_ptr<LayerImpl> implLayer = LayerImpl::Create(m_hostImpl.active_tree(), 1);
+
+ scoped_ptr<AnimationRegistrar> registrar = AnimationRegistrar::create();
+ implLayer->layer_animation_controller()->SetAnimationRegistrar(registrar.get());
+
+ addAnimatedTransformToController(*implLayer->layer_animation_controller(), 1.0, 0, 100);
+
+ gfx::Transform transform;
+ transform.Rotate(45.0);
+ testLayer->SetTransform(transform);
+
+ EXPECT_FALSE(implLayer->LayerSurfacePropertyChanged());
+ testLayer->PushPropertiesTo(implLayer.get());
+ EXPECT_TRUE(implLayer->LayerSurfacePropertyChanged());
+
+ implLayer->ResetAllChangeTrackingForSubtree();
+ addAnimatedTransformToController(*implLayer->layer_animation_controller(), 1.0, 0, 100);
+ implLayer->layer_animation_controller()->GetAnimation(Animation::Transform)->set_is_impl_only(true);
+ transform.Rotate(45.0);
+ testLayer->SetTransform(transform);
+
+ EXPECT_FALSE(implLayer->LayerSurfacePropertyChanged());
+ testLayer->PushPropertiesTo(implLayer.get());
+ EXPECT_FALSE(implLayer->LayerSurfacePropertyChanged());
+}
+
+TEST_F(LayerTest, verifyPushPropertiesDoesNotCauseSurfacePropertyChangedDuringImplOnlyOpacityAnimation)
+{
+ scoped_refptr<Layer> testLayer = Layer::Create();
+ scoped_ptr<LayerImpl> implLayer = LayerImpl::Create(m_hostImpl.active_tree(), 1);
+
+ scoped_ptr<AnimationRegistrar> registrar = AnimationRegistrar::create();
+ implLayer->layer_animation_controller()->SetAnimationRegistrar(registrar.get());
+
+ addOpacityTransitionToController(*implLayer->layer_animation_controller(), 1.0, 0.3f, 0.7f, false);
+
+ testLayer->SetOpacity(0.5f);
+
+ EXPECT_FALSE(implLayer->LayerSurfacePropertyChanged());
+ testLayer->PushPropertiesTo(implLayer.get());
+ EXPECT_TRUE(implLayer->LayerSurfacePropertyChanged());
+
+ implLayer->ResetAllChangeTrackingForSubtree();
+ addOpacityTransitionToController(*implLayer->layer_animation_controller(), 1.0, 0.3f, 0.7f, false);
+ implLayer->layer_animation_controller()->GetAnimation(Animation::Opacity)->set_is_impl_only(true);
+ testLayer->SetOpacity(0.75f);
+
+ EXPECT_FALSE(implLayer->LayerSurfacePropertyChanged());
+ testLayer->PushPropertiesTo(implLayer.get());
+ EXPECT_FALSE(implLayer->LayerSurfacePropertyChanged());
+}
+
+
+TEST_F(LayerTest, maskAndReplicaHasParent)
+{
+ scoped_refptr<Layer> parent = Layer::Create();
+ scoped_refptr<Layer> child = Layer::Create();
+ scoped_refptr<Layer> mask = Layer::Create();
+ scoped_refptr<Layer> replica = Layer::Create();
+ scoped_refptr<Layer> replicaMask = Layer::Create();
+ scoped_refptr<Layer> maskReplacement = Layer::Create();
+ scoped_refptr<Layer> replicaReplacement = Layer::Create();
+ scoped_refptr<Layer> replicaMaskReplacement = Layer::Create();
+
+ parent->AddChild(child);
+ child->SetMaskLayer(mask.get());
+ child->SetReplicaLayer(replica.get());
+ replica->SetMaskLayer(replicaMask.get());
+
+ EXPECT_EQ(parent, child->parent());
+ EXPECT_EQ(child, mask->parent());
+ EXPECT_EQ(child, replica->parent());
+ EXPECT_EQ(replica, replicaMask->parent());
+
+ replica->SetMaskLayer(replicaMaskReplacement.get());
+ EXPECT_EQ(NULL, replicaMask->parent());
+ EXPECT_EQ(replica, replicaMaskReplacement->parent());
+
+ child->SetMaskLayer(maskReplacement.get());
+ EXPECT_EQ(NULL, mask->parent());
+ EXPECT_EQ(child, maskReplacement->parent());
+
+ child->SetReplicaLayer(replicaReplacement.get());
+ EXPECT_EQ(NULL, replica->parent());
+ EXPECT_EQ(child, replicaReplacement->parent());
+
+ EXPECT_EQ(replica, replica->mask_layer()->parent());
+}
+
+class FakeLayerImplTreeHost : public LayerTreeHost {
+public:
+ static scoped_ptr<FakeLayerImplTreeHost> Create()
+ {
+ scoped_ptr<FakeLayerImplTreeHost> host(new FakeLayerImplTreeHost(LayerTreeSettings()));
+ // The initialize call will fail, since our client doesn't provide a valid GraphicsContext3D, but it doesn't matter in the tests that use this fake so ignore the return value.
+ host->Initialize(scoped_ptr<Thread>(NULL));
+ return host.Pass();
+ }
+
+ static scoped_ptr<FakeLayerImplTreeHost> Create(LayerTreeSettings settings)
+ {
+ scoped_ptr<FakeLayerImplTreeHost> host(new FakeLayerImplTreeHost(settings));
+ host->Initialize(scoped_ptr<Thread>(NULL));
+ return host.Pass();
+ }
+
+private:
+ FakeLayerImplTreeHost(const LayerTreeSettings& settings)
+ : LayerTreeHost(&m_client, settings)
+ {
+ }
+
+ FakeLayerImplTreeHostClient m_client;
+};
+
+void assertLayerTreeHostMatchesForSubtree(Layer* layer, LayerTreeHost* host)
+{
+ EXPECT_EQ(host, layer->layer_tree_host());
+
+ for (size_t i = 0; i < layer->children().size(); ++i)
+ assertLayerTreeHostMatchesForSubtree(layer->children()[i].get(), host);
+
+ if (layer->mask_layer())
+ assertLayerTreeHostMatchesForSubtree(layer->mask_layer(), host);
+
+ if (layer->replica_layer())
+ assertLayerTreeHostMatchesForSubtree(layer->replica_layer(), host);
+}
+
+TEST(LayerLayerTreeHostTest, enteringTree)
+{
+ scoped_refptr<Layer> parent = Layer::Create();
+ scoped_refptr<Layer> child = Layer::Create();
+ scoped_refptr<Layer> mask = Layer::Create();
+ scoped_refptr<Layer> replica = Layer::Create();
+ scoped_refptr<Layer> replicaMask = Layer::Create();
+
+ // Set up a detached tree of layers. The host pointer should be nil for these layers.
+ parent->AddChild(child);
+ child->SetMaskLayer(mask.get());
+ child->SetReplicaLayer(replica.get());
+ replica->SetMaskLayer(replicaMask.get());
+
+ assertLayerTreeHostMatchesForSubtree(parent.get(), 0);
+
+ scoped_ptr<FakeLayerImplTreeHost> layerTreeHost(FakeLayerImplTreeHost::Create());
+ // Setting the root layer should set the host pointer for all layers in the tree.
+ layerTreeHost->SetRootLayer(parent.get());
+
+ assertLayerTreeHostMatchesForSubtree(parent.get(), layerTreeHost.get());
+
+ // Clearing the root layer should also clear out the host pointers for all layers in the tree.
+ layerTreeHost->SetRootLayer(NULL);
+
+ assertLayerTreeHostMatchesForSubtree(parent.get(), 0);
+}
+
+TEST(LayerLayerTreeHostTest, addingLayerSubtree)
+{
+ scoped_refptr<Layer> parent = Layer::Create();
+ scoped_ptr<FakeLayerImplTreeHost> layerTreeHost(FakeLayerImplTreeHost::Create());
+
+ layerTreeHost->SetRootLayer(parent.get());
+
+ EXPECT_EQ(parent->layer_tree_host(), layerTreeHost.get());
+
+ // Adding a subtree to a layer already associated with a host should set the host pointer on all layers in that subtree.
+ scoped_refptr<Layer> child = Layer::Create();
+ scoped_refptr<Layer> grandChild = Layer::Create();
+ child->AddChild(grandChild);
+
+ // Masks, replicas, and replica masks should pick up the new host too.
+ scoped_refptr<Layer> childMask = Layer::Create();
+ child->SetMaskLayer(childMask.get());
+ scoped_refptr<Layer> childReplica = Layer::Create();
+ child->SetReplicaLayer(childReplica.get());
+ scoped_refptr<Layer> childReplicaMask = Layer::Create();
+ childReplica->SetMaskLayer(childReplicaMask.get());
+
+ parent->AddChild(child);
+ assertLayerTreeHostMatchesForSubtree(parent.get(), layerTreeHost.get());
+
+ layerTreeHost->SetRootLayer(NULL);
+}
+
+TEST(LayerLayerTreeHostTest, changeHost)
+{
+ scoped_refptr<Layer> parent = Layer::Create();
+ scoped_refptr<Layer> child = Layer::Create();
+ scoped_refptr<Layer> mask = Layer::Create();
+ scoped_refptr<Layer> replica = Layer::Create();
+ scoped_refptr<Layer> replicaMask = Layer::Create();
+
+ // Same setup as the previous test.
+ parent->AddChild(child);
+ child->SetMaskLayer(mask.get());
+ child->SetReplicaLayer(replica.get());
+ replica->SetMaskLayer(replicaMask.get());
+
+ scoped_ptr<FakeLayerImplTreeHost> firstLayerTreeHost(FakeLayerImplTreeHost::Create());
+ firstLayerTreeHost->SetRootLayer(parent.get());
+
+ assertLayerTreeHostMatchesForSubtree(parent.get(), firstLayerTreeHost.get());
+
+ // Now re-root the tree to a new host (simulating what we do on a context lost event).
+ // This should update the host pointers for all layers in the tree.
+ scoped_ptr<FakeLayerImplTreeHost> secondLayerTreeHost(FakeLayerImplTreeHost::Create());
+ secondLayerTreeHost->SetRootLayer(parent.get());
+
+ assertLayerTreeHostMatchesForSubtree(parent.get(), secondLayerTreeHost.get());
+
+ secondLayerTreeHost->SetRootLayer(NULL);
+}
+
+TEST(LayerLayerTreeHostTest, changeHostInSubtree)
+{
+ scoped_refptr<Layer> firstParent = Layer::Create();
+ scoped_refptr<Layer> firstChild = Layer::Create();
+ scoped_refptr<Layer> secondParent = Layer::Create();
+ scoped_refptr<Layer> secondChild = Layer::Create();
+ scoped_refptr<Layer> secondGrandChild = Layer::Create();
+
+ // First put all children under the first parent and set the first host.
+ firstParent->AddChild(firstChild);
+ secondChild->AddChild(secondGrandChild);
+ firstParent->AddChild(secondChild);
+
+ scoped_ptr<FakeLayerImplTreeHost> firstLayerTreeHost(FakeLayerImplTreeHost::Create());
+ firstLayerTreeHost->SetRootLayer(firstParent.get());
+
+ assertLayerTreeHostMatchesForSubtree(firstParent.get(), firstLayerTreeHost.get());
+
+ // Now reparent the subtree starting at secondChild to a layer in a different tree.
+ scoped_ptr<FakeLayerImplTreeHost> secondLayerTreeHost(FakeLayerImplTreeHost::Create());
+ secondLayerTreeHost->SetRootLayer(secondParent.get());
+
+ secondParent->AddChild(secondChild);
+
+ // The moved layer and its children should point to the new host.
+ EXPECT_EQ(secondLayerTreeHost.get(), secondChild->layer_tree_host());
+ EXPECT_EQ(secondLayerTreeHost.get(), secondGrandChild->layer_tree_host());
+
+ // Test over, cleanup time.
+ firstLayerTreeHost->SetRootLayer(NULL);
+ secondLayerTreeHost->SetRootLayer(NULL);
+}
+
+TEST(LayerLayerTreeHostTest, replaceMaskAndReplicaLayer)
+{
+ scoped_refptr<Layer> parent = Layer::Create();
+ scoped_refptr<Layer> mask = Layer::Create();
+ scoped_refptr<Layer> replica = Layer::Create();
+ scoped_refptr<Layer> maskChild = Layer::Create();
+ scoped_refptr<Layer> replicaChild = Layer::Create();
+ scoped_refptr<Layer> maskReplacement = Layer::Create();
+ scoped_refptr<Layer> replicaReplacement = Layer::Create();
+
+ parent->SetMaskLayer(mask.get());
+ parent->SetReplicaLayer(replica.get());
+ mask->AddChild(maskChild);
+ replica->AddChild(replicaChild);
+
+ scoped_ptr<FakeLayerImplTreeHost> layerTreeHost(FakeLayerImplTreeHost::Create());
+ layerTreeHost->SetRootLayer(parent.get());
+
+ assertLayerTreeHostMatchesForSubtree(parent.get(), layerTreeHost.get());
+
+ // Replacing the mask should clear out the old mask's subtree's host pointers.
+ parent->SetMaskLayer(maskReplacement.get());
+ EXPECT_EQ(0, mask->layer_tree_host());
+ EXPECT_EQ(0, maskChild->layer_tree_host());
+
+ // Same for replacing a replica layer.
+ parent->SetReplicaLayer(replicaReplacement.get());
+ EXPECT_EQ(0, replica->layer_tree_host());
+ EXPECT_EQ(0, replicaChild->layer_tree_host());
+
+ // Test over, cleanup time.
+ layerTreeHost->SetRootLayer(NULL);
+}
+
+TEST(LayerLayerTreeHostTest, destroyHostWithNonNullRootLayer)
+{
+ scoped_refptr<Layer> root = Layer::Create();
+ scoped_refptr<Layer> child = Layer::Create();
+ root->AddChild(child);
+ scoped_ptr<FakeLayerImplTreeHost> layerTreeHost(FakeLayerImplTreeHost::Create());
+ layerTreeHost->SetRootLayer(root);
+}
+
+static bool addTestAnimation(Layer* layer)
+{
+ scoped_ptr<KeyframedFloatAnimationCurve> curve(KeyframedFloatAnimationCurve::Create());
+ curve->AddKeyframe(FloatKeyframe::Create(0.0, 0.3f, scoped_ptr<TimingFunction>()));
+ curve->AddKeyframe(FloatKeyframe::Create(1.0, 0.7f, scoped_ptr<TimingFunction>()));
+ scoped_ptr<Animation> animation(Animation::Create(curve.PassAs<AnimationCurve>(), 0, 0, Animation::Opacity));
+
+ return layer->AddAnimation(animation.Pass());
+}
+
+TEST(LayerLayerTreeHostTest, shouldNotAddAnimationWithoutAnimationRegistrar)
+{
+ scoped_refptr<Layer> layer = Layer::Create();
+
+ // Case 1: without a LayerTreeHost and without an AnimationRegistrar, the
+ // animation should not be accepted.
+ EXPECT_FALSE(addTestAnimation(layer.get()));
+
+ scoped_ptr<AnimationRegistrar> registrar = AnimationRegistrar::create();
+ layer->layer_animation_controller()->SetAnimationRegistrar(registrar.get());
+
+ // Case 2: with an AnimationRegistrar, the animation should be accepted.
+ EXPECT_TRUE(addTestAnimation(layer.get()));
+
+ LayerTreeSettings settings;
+ settings.acceleratedAnimationEnabled = false;
+ scoped_ptr<FakeLayerImplTreeHost> layerTreeHost(FakeLayerImplTreeHost::Create(settings));
+ layerTreeHost->SetRootLayer(layer);
+ layer->SetLayerTreeHost(layerTreeHost.get());
+ assertLayerTreeHostMatchesForSubtree(layer.get(), layerTreeHost.get());
+
+ // Case 3: with a LayerTreeHost where accelerated animation is disabled, the
+ // animation should be rejected.
+ EXPECT_FALSE(addTestAnimation(layer.get()));
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/nine_patch_layer.cc b/cc/layers/nine_patch_layer.cc
new file mode 100644
index 0000000..b239222
--- /dev/null
+++ b/cc/layers/nine_patch_layer.cc
@@ -0,0 +1,116 @@
+// Copyright 2012 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 "cc/layers/nine_patch_layer.h"
+
+#include "cc/layers/nine_patch_layer_impl.h"
+#include "cc/resources/prioritized_resource.h"
+#include "cc/resources/resource_update.h"
+#include "cc/resources/resource_update_queue.h"
+#include "cc/trees/layer_tree_host.h"
+
+namespace cc {
+
+scoped_refptr<NinePatchLayer> NinePatchLayer::Create() {
+ return make_scoped_refptr(new NinePatchLayer());
+}
+
+NinePatchLayer::NinePatchLayer()
+ : bitmap_dirty_(false) {}
+
+NinePatchLayer::~NinePatchLayer() {}
+
+scoped_ptr<LayerImpl> NinePatchLayer::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return NinePatchLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
+}
+
+void NinePatchLayer::SetTexturePriorities(
+ const PriorityCalculator& priority_calc) {
+ if (resource_ && !resource_->texture()->resourceManager()) {
+ // Release the resource here, as it is no longer tied to a resource manager.
+ resource_.reset();
+ if (!bitmap_.isNull())
+ CreateResource();
+ } else if (needs_display_ && bitmap_dirty_ && DrawsContent()) {
+ CreateResource();
+ }
+
+ if (resource_) {
+ resource_->texture()->setRequestPriority(
+ PriorityCalculator::UIPriority(true));
+ // FIXME: Need to support swizzle in the shader for
+ // !PlatformColor::sameComponentOrder(texture_format)
+ GLenum texture_format =
+ layer_tree_host()->GetRendererCapabilities().best_texture_format;
+ resource_->texture()->setDimensions(
+ gfx::Size(bitmap_.width(), bitmap_.height()), texture_format);
+ }
+}
+
+void NinePatchLayer::SetBitmap(const SkBitmap& bitmap, gfx::Rect aperture) {
+ bitmap_ = bitmap;
+ image_aperture_ = aperture;
+ bitmap_dirty_ = true;
+ SetNeedsDisplay();
+}
+
+void NinePatchLayer::Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) {
+ CreateUpdaterIfNeeded();
+
+ if (resource_ && (bitmap_dirty_ || resource_->texture()->resourceId() == 0)) {
+ gfx::Rect contentRect(gfx::Point(),
+ gfx::Size(bitmap_.width(), bitmap_.height()));
+ ResourceUpdate upload = ResourceUpdate::Create(resource_->texture(),
+ &bitmap_,
+ contentRect,
+ contentRect,
+ gfx::Vector2d());
+ queue->appendFullUpload(upload);
+ bitmap_dirty_ = false;
+ }
+}
+
+void NinePatchLayer::CreateUpdaterIfNeeded() {
+ if (updater_)
+ return;
+
+ updater_ = ImageLayerUpdater::Create();
+}
+
+void NinePatchLayer::CreateResource() {
+ DCHECK(!bitmap_.isNull());
+ CreateUpdaterIfNeeded();
+ updater_->set_bitmap(bitmap_);
+ needs_display_ = false;
+
+ if (!resource_) {
+ resource_ = updater_->CreateResource(
+ layer_tree_host()->contents_texture_manager());
+ }
+}
+
+bool NinePatchLayer::DrawsContent() const {
+ bool draws = !bitmap_.isNull() &&
+ Layer::DrawsContent() &&
+ bitmap_.width() &&
+ bitmap_.height();
+ return draws;
+}
+
+void NinePatchLayer::PushPropertiesTo(LayerImpl* layer) {
+ Layer::PushPropertiesTo(layer);
+ NinePatchLayerImpl* layer_impl = static_cast<NinePatchLayerImpl*>(layer);
+
+ if (resource_) {
+ DCHECK(!bitmap_.isNull());
+ layer_impl->SetResourceId(resource_->texture()->resourceId());
+ layer_impl->SetLayout(
+ gfx::Size(bitmap_.width(), bitmap_.height()), image_aperture_);
+ }
+}
+
+}
diff --git a/cc/layers/nine_patch_layer.h b/cc/layers/nine_patch_layer.h
new file mode 100644
index 0000000..21b8cb9
--- /dev/null
+++ b/cc/layers/nine_patch_layer.h
@@ -0,0 +1,61 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_NINE_PATCH_LAYER_H_
+#define CC_LAYERS_NINE_PATCH_LAYER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer.h"
+#include "cc/resources/image_layer_updater.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/rect.h"
+
+namespace cc {
+
+class ResourceUpdateQueue;
+
+class CC_EXPORT NinePatchLayer : public Layer {
+ public:
+ static scoped_refptr<NinePatchLayer> Create();
+
+ virtual bool DrawsContent() const OVERRIDE;
+ virtual void SetTexturePriorities(const PriorityCalculator& priority_calc)
+ OVERRIDE;
+ virtual void Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+
+ // aperture is in the pixel space of the bitmap resource and refers to
+ // the center patch of the ninepatch (which is unused in this
+ // implementation). We split off eight rects surrounding it and stick them
+ // on the edges of the layer. The corners are unscaled, the top and bottom
+ // rects are x-stretched to fit, and the left and right rects are
+ // y-stretched to fit.
+ void SetBitmap(const SkBitmap& bitmap, gfx::Rect aperture);
+
+ private:
+ NinePatchLayer();
+ virtual ~NinePatchLayer();
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+
+ void CreateUpdaterIfNeeded();
+ void CreateResource();
+
+ scoped_refptr<ImageLayerUpdater> updater_;
+ scoped_ptr<LayerUpdater::Resource> resource_;
+
+ SkBitmap bitmap_;
+ bool bitmap_dirty_;
+
+ // The transparent center region that shows the parent layer's contents in
+ // image space.
+ gfx::Rect image_aperture_;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_NINE_PATCH_LAYER_H_
diff --git a/cc/layers/nine_patch_layer_impl.cc b/cc/layers/nine_patch_layer_impl.cc
new file mode 100644
index 0000000..53daedc
--- /dev/null
+++ b/cc/layers/nine_patch_layer_impl.cc
@@ -0,0 +1,300 @@
+// Copyright 2012 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 "nine_patch_layer_impl.h"
+
+#include "base/stringprintf.h"
+#include "base/values.h"
+#include "cc/layers/quad_sink.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "ui/gfx/rect_f.h"
+
+namespace cc {
+
+NinePatchLayerImpl::NinePatchLayerImpl(LayerTreeImpl* tree_impl, int id)
+ : LayerImpl(tree_impl, id),
+ resource_id_(0) {}
+
+NinePatchLayerImpl::~NinePatchLayerImpl() {}
+
+ResourceProvider::ResourceId NinePatchLayerImpl::ContentsResourceId() const {
+ return 0;
+}
+
+scoped_ptr<LayerImpl> NinePatchLayerImpl::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return NinePatchLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
+}
+
+void NinePatchLayerImpl::PushPropertiesTo(LayerImpl* layer) {
+ LayerImpl::PushPropertiesTo(layer);
+ NinePatchLayerImpl* layer_impl = static_cast<NinePatchLayerImpl*>(layer);
+
+ if (!resource_id_)
+ return;
+
+ layer_impl->SetResourceId(resource_id_);
+ layer_impl->SetLayout(image_bounds_, image_aperture_);
+}
+
+void NinePatchLayerImpl::WillDraw(ResourceProvider* resource_provider) {}
+
+static gfx::RectF NormalizedRect(float x,
+ float y,
+ float width,
+ float height,
+ float total_width,
+ float total_height) {
+ return gfx::RectF(x / total_width,
+ y / total_height,
+ width / total_width,
+ height / total_height);
+}
+
+void NinePatchLayerImpl::SetLayout(gfx::Size image_bounds, gfx::Rect aperture) {
+ image_bounds_ = image_bounds;
+ image_aperture_ = aperture;
+}
+
+void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) {
+ if (!resource_id_)
+ return;
+
+ SharedQuadState* shared_quad_state =
+ quad_sink->UseSharedQuadState(CreateSharedQuadState());
+ AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
+
+ static const bool flipped = false;
+ static const bool premultiplied_alpha = true;
+
+ DCHECK(!bounds().IsEmpty());
+
+ // NinePatch border widths in bitmap pixel space
+ int left_width = image_aperture_.x();
+ int top_height = image_aperture_.y();
+ int right_width = image_bounds_.width() - image_aperture_.right();
+ int bottom_height = image_bounds_.height() - image_aperture_.bottom();
+
+ // If layer can't fit the corners, clip to show the outer edges of the
+ // image.
+ int corner_total_width = left_width + right_width;
+ int middle_width = bounds().width() - corner_total_width;
+ if (middle_width < 0) {
+ float left_width_proportion =
+ static_cast<float>(left_width) / corner_total_width;
+ int left_width_crop = middle_width * left_width_proportion;
+ left_width += left_width_crop;
+ right_width = bounds().width() - left_width;
+ middle_width = 0;
+ }
+ int corner_total_height = top_height + bottom_height;
+ int middle_height = bounds().height() - corner_total_height;
+ if (middle_height < 0) {
+ float top_height_proportion =
+ static_cast<float>(top_height) / corner_total_height;
+ int top_height_crop = middle_height * top_height_proportion;
+ top_height += top_height_crop;
+ bottom_height = bounds().height() - top_height;
+ middle_height = 0;
+ }
+
+ // Patch positions in layer space
+ gfx::Rect top_left(0, 0, left_width, top_height);
+ gfx::Rect topRight(
+ bounds().width() - right_width, 0, right_width, top_height);
+ gfx::Rect bottomLeft(
+ 0, bounds().height() - bottom_height, left_width, bottom_height);
+ gfx::Rect bottomRight(
+ topRight.x(), bottomLeft.y(), right_width, bottom_height);
+ gfx::Rect top(top_left.right(), 0, middle_width, top_height);
+ gfx::Rect left(0, top_left.bottom(), left_width, middle_height);
+ gfx::Rect right(topRight.x(), topRight.bottom(), right_width, left.height());
+ gfx::Rect bottom(top.x(), bottomLeft.y(), top.width(), bottom_height);
+
+ float img_width = image_bounds_.width();
+ float img_height = image_bounds_.height();
+
+ // Patch positions in bitmap UV space (from zero to one)
+ gfx::RectF uv_top_left = NormalizedRect(0,
+ 0,
+ left_width,
+ top_height,
+ img_width,
+ img_height);
+ gfx::RectF uv_top_right = NormalizedRect(img_width - right_width,
+ 0,
+ right_width,
+ top_height,
+ img_width,
+ img_height);
+ gfx::RectF uv_bottom_left = NormalizedRect(0,
+ img_height - bottom_height,
+ left_width,
+ bottom_height,
+ img_width,
+ img_height);
+ gfx::RectF uv_bottom_right = NormalizedRect(img_width - right_width,
+ img_height - bottom_height,
+ right_width,
+ bottom_height,
+ img_width,
+ img_height);
+ gfx::RectF uvTop(uv_top_left.right(),
+ 0,
+ (img_width - left_width - right_width) / img_width,
+ (top_height) / img_height);
+ gfx::RectF uvLeft(0,
+ uv_top_left.bottom(),
+ left_width / img_width,
+ (img_height - top_height - bottom_height) / img_height);
+ gfx::RectF uvRight(uv_top_right.x(),
+ uv_top_right.bottom(),
+ right_width / img_width,
+ uvLeft.height());
+ gfx::RectF uvBottom(uvTop.x(),
+ uv_bottom_left.y(),
+ uvTop.width(),
+ bottom_height / img_height);
+
+ // Nothing is opaque here.
+ // TODO(danakj): Should we look at the SkBitmaps to determine opaqueness?
+ gfx::Rect opaque_rect;
+ const float vertex_opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
+ scoped_ptr<TextureDrawQuad> quad;
+
+ quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ top_left,
+ opaque_rect,
+ resource_id_,
+ premultiplied_alpha,
+ uv_top_left.origin(),
+ uv_top_left.bottom_right(),
+ vertex_opacity, flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+
+ quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ topRight,
+ opaque_rect,
+ resource_id_,
+ premultiplied_alpha,
+ uv_top_right.origin(),
+ uv_top_right.bottom_right(),
+ vertex_opacity, flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+
+ quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ bottomLeft,
+ opaque_rect,
+ resource_id_,
+ premultiplied_alpha,
+ uv_bottom_left.origin(),
+ uv_bottom_left.bottom_right(),
+ vertex_opacity,
+ flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+
+ quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ bottomRight,
+ opaque_rect,
+ resource_id_,
+ premultiplied_alpha,
+ uv_bottom_right.origin(),
+ uv_bottom_right.bottom_right(),
+ vertex_opacity,
+ flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+
+ quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ top,
+ opaque_rect,
+ resource_id_,
+ premultiplied_alpha,
+ uvTop.origin(),
+ uvTop.bottom_right(),
+ vertex_opacity,
+ flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+
+ quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ left,
+ opaque_rect,
+ resource_id_,
+ premultiplied_alpha,
+ uvLeft.origin(),
+ uvLeft.bottom_right(),
+ vertex_opacity,
+ flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+
+ quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ right,
+ opaque_rect,
+ resource_id_,
+ premultiplied_alpha,
+ uvRight.origin(),
+ uvRight.bottom_right(),
+ vertex_opacity,
+ flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+
+ quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ bottom,
+ opaque_rect,
+ resource_id_,
+ premultiplied_alpha,
+ uvBottom.origin(),
+ uvBottom.bottom_right(),
+ vertex_opacity,
+ flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+}
+
+void NinePatchLayerImpl::DidDraw(ResourceProvider* resource_provider) {}
+
+void NinePatchLayerImpl::DidLoseOutputSurface() {
+ resource_id_ = 0;
+}
+
+const char* NinePatchLayerImpl::LayerTypeAsString() const {
+ return "NinePatchLayer";
+}
+
+void NinePatchLayerImpl::DumpLayerProperties(std::string* str, int indent)
+ const {
+ str->append(IndentString(indent));
+ base::StringAppendF(
+ str, "imageAperture: %s\n", image_aperture_.ToString().c_str());
+ base::StringAppendF(
+ str, "image_bounds: %s\n", image_bounds_.ToString().c_str());
+ LayerImpl::DumpLayerProperties(str, indent);
+}
+
+base::DictionaryValue* NinePatchLayerImpl::LayerTreeAsJson() const {
+ base::DictionaryValue* result = LayerImpl::LayerTreeAsJson();
+
+ base::ListValue* list = new base::ListValue;
+ list->AppendInteger(image_aperture_.origin().x());
+ list->AppendInteger(image_aperture_.origin().y());
+ list->AppendInteger(image_aperture_.size().width());
+ list->AppendInteger(image_aperture_.size().height());
+ result->Set("ImageAperture", list);
+
+ list = new base::ListValue;
+ list->AppendInteger(image_bounds_.width());
+ list->AppendInteger(image_bounds_.height());
+ result->Set("ImageBounds", list);
+
+ return result;
+}
+
+} // namespace cc
diff --git a/cc/layers/nine_patch_layer_impl.h b/cc/layers/nine_patch_layer_impl.h
new file mode 100644
index 0000000..7091f00
--- /dev/null
+++ b/cc/layers/nine_patch_layer_impl.h
@@ -0,0 +1,63 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_NINE_PATCH_LAYER_IMPL_H_
+#define CC_LAYERS_NINE_PATCH_LAYER_IMPL_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer_impl.h"
+#include "cc/resources/resource_provider.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace cc {
+
+class CC_EXPORT NinePatchLayerImpl : public LayerImpl {
+ public:
+ static scoped_ptr<NinePatchLayerImpl> Create(LayerTreeImpl* tree_impl,
+ int id) {
+ return make_scoped_ptr(new NinePatchLayerImpl(tree_impl, id));
+ }
+ virtual ~NinePatchLayerImpl();
+
+ void SetResourceId(unsigned id) { resource_id_ = id; }
+ void SetLayout(gfx::Size image_bounds, gfx::Rect aperture);
+
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+
+ virtual void WillDraw(ResourceProvider* resource_provider) OVERRIDE;
+ virtual void AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) OVERRIDE;
+ virtual void DidDraw(ResourceProvider* FIXMENAME) OVERRIDE;
+ virtual ResourceProvider::ResourceId ContentsResourceId() const OVERRIDE;
+ virtual void DumpLayerProperties(std::string* str, int indent) const OVERRIDE;
+ virtual void DidLoseOutputSurface() OVERRIDE;
+
+ virtual base::DictionaryValue* LayerTreeAsJson() const OVERRIDE;
+
+ protected:
+ NinePatchLayerImpl(LayerTreeImpl* tree_impl, int id);
+
+ private:
+ virtual const char* LayerTypeAsString() const OVERRIDE;
+
+ // The size of the NinePatch bitmap in pixels.
+ gfx::Size image_bounds_;
+
+ // The transparent center region that shows the parent layer's contents in
+ // image space.
+ gfx::Rect image_aperture_;
+
+ ResourceProvider::ResourceId resource_id_;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_NINE_PATCH_LAYER_IMPL_H_
diff --git a/cc/layers/nine_patch_layer_impl_unittest.cc b/cc/layers/nine_patch_layer_impl_unittest.cc
new file mode 100644
index 0000000..b4af387
--- /dev/null
+++ b/cc/layers/nine_patch_layer_impl_unittest.cc
@@ -0,0 +1,150 @@
+// Copyright 2012 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 <stdio.h>
+
+#include "cc/layers/append_quads_data.h"
+#include "cc/layers/nine_patch_layer_impl.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "cc/test/fake_impl_proxy.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/geometry_test_utils.h"
+#include "cc/test/layer_test_common.h"
+#include "cc/test/mock_quad_culler.h"
+#include "cc/trees/single_thread_proxy.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/safe_integer_conversions.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+namespace {
+
+gfx::Rect ToRoundedIntRect(gfx::RectF rect_f) {
+ return gfx::Rect(gfx::ToRoundedInt(rect_f.x()), gfx::ToRoundedInt(rect_f.y()), gfx::ToRoundedInt(rect_f.width()), gfx::ToRoundedInt(rect_f.height()));
+}
+
+TEST(NinePatchLayerImplTest, verifyDrawQuads)
+{
+ // Input is a 100x100 bitmap with a 40x50 aperture at x=20, y=30.
+ // The bounds of the layer are set to 400x400, so the draw quads
+ // generated should leave the border width (40) intact.
+ MockQuadCuller quadCuller;
+ gfx::Size bitmapSize(100, 100);
+ gfx::Size layerSize(400, 400);
+ gfx::Rect visibleContentRect(gfx::Point(), layerSize);
+ gfx::Rect apertureRect(20, 30, 40, 50);
+ gfx::Rect scaledApertureNonUniform(20, 30, 340, 350);
+
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+ scoped_ptr<NinePatchLayerImpl> layer = NinePatchLayerImpl::Create(hostImpl.active_tree(), 1);
+ layer->draw_properties().visible_content_rect = visibleContentRect;
+ layer->SetBounds(layerSize);
+ layer->SetContentBounds(layerSize);
+ layer->CreateRenderSurface();
+ layer->draw_properties().render_target = layer.get();
+ layer->SetLayout(bitmapSize, apertureRect);
+ layer->SetResourceId(1);
+
+ // This scale should not affect the generated quad geometry, but only
+ // the shared draw transform.
+ gfx::Transform transform;
+ transform.Scale(10, 10);
+ layer->draw_properties().target_space_transform = transform;
+
+ AppendQuadsData data;
+ layer->AppendQuads(&quadCuller, &data);
+
+ // Verify quad rects
+ const QuadList& quads = quadCuller.quadList();
+ EXPECT_EQ(8, quads.size());
+ Region remaining(visibleContentRect);
+ for (size_t i = 0; i < quads.size(); ++i) {
+ DrawQuad* quad = quads[i];
+ gfx::Rect quadRect = quad->rect;
+
+ EXPECT_TRUE(visibleContentRect.Contains(quadRect)) << i;
+ EXPECT_TRUE(remaining.Contains(quadRect)) << i;
+ EXPECT_EQ(transform, quad->quadTransform());
+ remaining.Subtract(Region(quadRect));
+ }
+ EXPECT_RECT_EQ(scaledApertureNonUniform, remaining.bounds());
+ Region scaledApertureRegion(scaledApertureNonUniform);
+ EXPECT_EQ(scaledApertureRegion, remaining);
+
+ // Verify UV rects
+ gfx::Rect bitmapRect(gfx::Point(), bitmapSize);
+ Region texRemaining(bitmapRect);
+ for (size_t i = 0; i < quads.size(); ++i) {
+ DrawQuad* quad = quads[i];
+ const TextureDrawQuad* texQuad = TextureDrawQuad::MaterialCast(quad);
+ gfx::RectF texRect = gfx::BoundingRect(texQuad->uv_top_left, texQuad->uv_bottom_right);
+ texRect.Scale(bitmapSize.width(), bitmapSize.height());
+ texRemaining.Subtract(Region(ToRoundedIntRect(texRect)));
+ }
+ EXPECT_RECT_EQ(apertureRect, texRemaining.bounds());
+ Region apertureRegion(apertureRect);
+ EXPECT_EQ(apertureRegion, texRemaining);
+}
+
+TEST(NinePatchLayerImplTest, verifyDrawQuadsForSqueezedLayer)
+{
+ // Test with a layer much smaller than the bitmap.
+ MockQuadCuller quadCuller;
+ gfx::Size bitmapSize(101, 101);
+ gfx::Size layerSize(51, 51);
+ gfx::Rect visibleContentRect(gfx::Point(), layerSize);
+ gfx::Rect apertureRect(20, 30, 40, 45); // rightWidth: 40, botHeight: 25
+
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+ scoped_ptr<NinePatchLayerImpl> layer = NinePatchLayerImpl::Create(hostImpl.active_tree(), 1);
+ layer->draw_properties().visible_content_rect = visibleContentRect;
+ layer->SetBounds(layerSize);
+ layer->SetContentBounds(layerSize);
+ layer->CreateRenderSurface();
+ layer->draw_properties().render_target = layer.get();
+ layer->SetLayout(bitmapSize, apertureRect);
+ layer->SetResourceId(1);
+
+ AppendQuadsData data;
+ layer->AppendQuads(&quadCuller, &data);
+
+ // Verify corner rects fill the layer and don't overlap
+ const QuadList& quads = quadCuller.quadList();
+ EXPECT_EQ(4, quads.size());
+ Region filled;
+ for (size_t i = 0; i < quads.size(); ++i) {
+ DrawQuad* quad = quads[i];
+ gfx::Rect quadRect = quad->rect;
+
+ EXPECT_FALSE(filled.Intersects(quadRect));
+ filled.Union(quadRect);
+ }
+ Region expectedFull(visibleContentRect);
+ EXPECT_EQ(expectedFull, filled);
+
+ // Verify UV rects cover the corners of the bitmap and the crop is weighted
+ // proportionately to the relative corner sizes (for uneven apertures).
+ gfx::Rect bitmapRect(gfx::Point(), bitmapSize);
+ Region texRemaining(bitmapRect);
+ for (size_t i = 0; i < quads.size(); ++i) {
+ DrawQuad* quad = quads[i];
+ const TextureDrawQuad* texQuad = TextureDrawQuad::MaterialCast(quad);
+ gfx::RectF texRect = gfx::BoundingRect(texQuad->uv_top_left, texQuad->uv_bottom_right);
+ texRect.Scale(bitmapSize.width(), bitmapSize.height());
+ texRemaining.Subtract(Region(ToRoundedIntRect(texRect)));
+ }
+ Region expectedRemainingRegion = Region(gfx::Rect(bitmapSize));
+ expectedRemainingRegion.Subtract(gfx::Rect(0, 0, 17, 28));
+ expectedRemainingRegion.Subtract(gfx::Rect(67, 0, 34, 28));
+ expectedRemainingRegion.Subtract(gfx::Rect(0, 78, 17, 23));
+ expectedRemainingRegion.Subtract(gfx::Rect(67, 78, 34, 23));
+ EXPECT_EQ(expectedRemainingRegion, texRemaining);
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/nine_patch_layer_unittest.cc b/cc/layers/nine_patch_layer_unittest.cc
new file mode 100644
index 0000000..f9c00aa
--- /dev/null
+++ b/cc/layers/nine_patch_layer_unittest.cc
@@ -0,0 +1,148 @@
+// Copyright 2012 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 "cc/layers/nine_patch_layer.h"
+
+#include "cc/debug/overdraw_metrics.h"
+#include "cc/resources/prioritized_resource_manager.h"
+#include "cc/resources/resource_provider.h"
+#include "cc/resources/resource_update_queue.h"
+#include "cc/scheduler/texture_uploader.h"
+#include "cc/test/fake_layer_tree_host_client.h"
+#include "cc/test/fake_output_surface.h"
+#include "cc/test/geometry_test_utils.h"
+#include "cc/test/layer_tree_test_common.h"
+#include "cc/trees/layer_tree_host.h"
+#include "cc/trees/occlusion_tracker.h"
+#include "cc/trees/single_thread_proxy.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+using ::testing::Mock;
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::AnyNumber;
+
+namespace cc {
+namespace {
+
+class MockLayerTreeHost : public LayerTreeHost {
+public:
+ MockLayerTreeHost()
+ : LayerTreeHost(&m_fakeClient, LayerTreeSettings())
+ {
+ Initialize(scoped_ptr<Thread>(NULL));
+ }
+
+private:
+ FakeLayerImplTreeHostClient m_fakeClient;
+};
+
+
+class NinePatchLayerTest : public testing::Test {
+public:
+ NinePatchLayerTest()
+ {
+ }
+
+ Proxy* proxy() const { return layer_tree_host_->proxy(); }
+
+protected:
+ virtual void SetUp()
+ {
+ layer_tree_host_.reset(new MockLayerTreeHost);
+ }
+
+ virtual void TearDown()
+ {
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+ }
+
+ scoped_ptr<MockLayerTreeHost> layer_tree_host_;
+};
+
+TEST_F(NinePatchLayerTest, triggerFullUploadOnceWhenChangingBitmap)
+{
+ scoped_refptr<NinePatchLayer> testLayer = NinePatchLayer::Create();
+ ASSERT_TRUE(testLayer);
+ testLayer->SetIsDrawable(true);
+ testLayer->SetBounds(gfx::Size(100, 100));
+
+ layer_tree_host_->SetRootLayer(testLayer);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+ EXPECT_EQ(testLayer->layer_tree_host(), layer_tree_host_.get());
+
+ layer_tree_host_->InitializeRendererIfNeeded();
+
+ PriorityCalculator calculator;
+ ResourceUpdateQueue queue;
+ OcclusionTracker occlusionTracker(gfx::Rect(), false);
+
+ // No bitmap set should not trigger any uploads.
+ testLayer->SetTexturePriorities(calculator);
+ testLayer->Update(&queue, &occlusionTracker, NULL);
+ EXPECT_EQ(queue.fullUploadSize(), 0);
+ EXPECT_EQ(queue.partialUploadSize(), 0);
+
+ // Setting a bitmap set should trigger a single full upload.
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, 10, 10);
+ bitmap.allocPixels();
+ testLayer->SetBitmap(bitmap, gfx::Rect(5, 5, 1, 1));
+ testLayer->SetTexturePriorities(calculator);
+ testLayer->Update(&queue, &occlusionTracker, NULL);
+ EXPECT_EQ(queue.fullUploadSize(), 1);
+ EXPECT_EQ(queue.partialUploadSize(), 0);
+ ResourceUpdate params = queue.takeFirstFullUpload();
+ EXPECT_TRUE(params.texture != NULL);
+
+ // Upload the texture.
+ layer_tree_host_->contents_texture_manager()->setMaxMemoryLimitBytes(1024 * 1024);
+ layer_tree_host_->contents_texture_manager()->prioritizeTextures();
+
+ scoped_ptr<OutputSurface> outputSurface;
+ scoped_ptr<ResourceProvider> resourceProvider;
+ {
+ DebugScopedSetImplThread implThread(proxy());
+ DebugScopedSetMainThreadBlocked mainThreadBlocked(proxy());
+ outputSurface = createFakeOutputSurface();
+ resourceProvider = ResourceProvider::Create(outputSurface.get());
+ params.texture->acquireBackingTexture(resourceProvider.get());
+ ASSERT_TRUE(params.texture->haveBackingTexture());
+ }
+
+ // Nothing changed, so no repeated upload.
+ testLayer->SetTexturePriorities(calculator);
+ testLayer->Update(&queue, &occlusionTracker, NULL);
+ EXPECT_EQ(queue.fullUploadSize(), 0);
+ EXPECT_EQ(queue.partialUploadSize(), 0);
+
+ {
+ DebugScopedSetImplThread implThread(proxy());
+ DebugScopedSetMainThreadBlocked mainThreadBlocked(proxy());
+ layer_tree_host_->contents_texture_manager()->clearAllMemory(resourceProvider.get());
+ }
+
+ // Reupload after eviction
+ testLayer->SetTexturePriorities(calculator);
+ testLayer->Update(&queue, &occlusionTracker, NULL);
+ EXPECT_EQ(queue.fullUploadSize(), 1);
+ EXPECT_EQ(queue.partialUploadSize(), 0);
+
+ // PrioritizedResourceManager clearing
+ layer_tree_host_->contents_texture_manager()->unregisterTexture(params.texture);
+ EXPECT_EQ(NULL, params.texture->resourceManager());
+ testLayer->SetTexturePriorities(calculator);
+ ResourceUpdateQueue queue2;
+ testLayer->Update(&queue2, &occlusionTracker, NULL);
+ EXPECT_EQ(queue2.fullUploadSize(), 1);
+ EXPECT_EQ(queue2.partialUploadSize(), 0);
+ params = queue2.takeFirstFullUpload();
+ EXPECT_TRUE(params.texture != NULL);
+ EXPECT_EQ(params.texture->resourceManager(), layer_tree_host_->contents_texture_manager());
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/picture_image_layer.cc b/cc/layers/picture_image_layer.cc
new file mode 100644
index 0000000..03e3a9e
--- /dev/null
+++ b/cc/layers/picture_image_layer.cc
@@ -0,0 +1,58 @@
+// Copyright 2010 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 "cc/layers/picture_image_layer.h"
+
+#include "cc/layers/picture_image_layer_impl.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+
+namespace cc {
+
+scoped_refptr<PictureImageLayer> PictureImageLayer::Create() {
+ return make_scoped_refptr(new PictureImageLayer());
+}
+
+PictureImageLayer::PictureImageLayer() : PictureLayer(this) {}
+
+PictureImageLayer::~PictureImageLayer() {
+ ClearClient();
+}
+
+scoped_ptr<LayerImpl> PictureImageLayer::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return PictureImageLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
+}
+
+bool PictureImageLayer::DrawsContent() const {
+ return !bitmap_.isNull() && PictureLayer::DrawsContent();
+}
+
+void PictureImageLayer::SetBitmap(const SkBitmap& bitmap) {
+ // SetBitmap() currently gets called whenever there is any
+ // style change that affects the layer even if that change doesn't
+ // affect the actual contents of the image (e.g. a CSS animation).
+ // With this check in place we avoid unecessary texture uploads.
+ if (bitmap.pixelRef() && bitmap.pixelRef() == bitmap_.pixelRef())
+ return;
+
+ bitmap_ = bitmap;
+ SetNeedsDisplay();
+}
+
+void PictureImageLayer::PaintContents(SkCanvas* canvas,
+ gfx::Rect clip,
+ gfx::RectF* opaque) {
+ if (!bitmap_.width() || !bitmap_.height())
+ return;
+
+ SkScalar content_to_layer_scale_x =
+ SkFloatToScalar(static_cast<float>(bounds().width()) / bitmap_.width());
+ SkScalar content_to_layer_scale_y =
+ SkFloatToScalar(static_cast<float>(bounds().height()) / bitmap_.height());
+ canvas->scale(content_to_layer_scale_x, content_to_layer_scale_y);
+
+ canvas->drawBitmap(bitmap_, 0, 0);
+}
+
+} // namespace cc
diff --git a/cc/layers/picture_image_layer.h b/cc/layers/picture_image_layer.h
new file mode 100644
index 0000000..fb4b5be
--- /dev/null
+++ b/cc/layers/picture_image_layer.h
@@ -0,0 +1,42 @@
+// Copyright 2010 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.
+
+#ifndef CC_LAYERS_PICTURE_IMAGE_LAYER_H_
+#define CC_LAYERS_PICTURE_IMAGE_LAYER_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/content_layer_client.h"
+#include "cc/layers/picture_layer.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/size.h"
+
+namespace cc {
+
+class CC_EXPORT PictureImageLayer : public PictureLayer, ContentLayerClient {
+ public:
+ static scoped_refptr<PictureImageLayer> Create();
+
+ void SetBitmap(const SkBitmap& image);
+
+ // Layer implementation.
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(
+ LayerTreeImpl* tree_impl) OVERRIDE;
+ virtual bool DrawsContent() const OVERRIDE;
+
+ // ContentLayerClient implementation.
+ virtual void PaintContents(
+ SkCanvas* canvas,
+ gfx::Rect clip,
+ gfx::RectF* opaque) OVERRIDE;
+
+ private:
+ PictureImageLayer();
+ virtual ~PictureImageLayer();
+
+ SkBitmap bitmap_;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_PICTURE_IMAGE_LAYER_H_
diff --git a/cc/layers/picture_image_layer_impl.cc b/cc/layers/picture_image_layer_impl.cc
new file mode 100644
index 0000000..9c478f0
--- /dev/null
+++ b/cc/layers/picture_image_layer_impl.cc
@@ -0,0 +1,45 @@
+// Copyright 2013 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 "cc/layers/picture_image_layer_impl.h"
+
+#include "cc/debug/debug_colors.h"
+#include "cc/trees/layer_tree_impl.h"
+
+namespace cc {
+
+PictureImageLayerImpl::PictureImageLayerImpl(LayerTreeImpl* treeImpl, int id)
+ : PictureLayerImpl(treeImpl, id) {
+}
+
+PictureImageLayerImpl::~PictureImageLayerImpl() {
+}
+
+const char* PictureImageLayerImpl::LayerTypeAsString() const {
+ return "PictureImageLayer";
+}
+
+scoped_ptr<LayerImpl> PictureImageLayerImpl::CreateLayerImpl(
+ LayerTreeImpl* treeImpl) {
+ return PictureImageLayerImpl::Create(treeImpl, id()).PassAs<LayerImpl>();
+}
+
+void PictureImageLayerImpl::GetDebugBorderProperties(
+ SkColor* color, float* width) const {
+ *color = DebugColors::ImageLayerBorderColor();
+ *width = DebugColors::ImageLayerBorderWidth(layer_tree_impl());
+}
+
+void PictureImageLayerImpl::CalculateRasterContentsScale(
+ bool animating_transform_to_screen,
+ float* raster_contents_scale,
+ float* low_res_raster_contents_scale) {
+ // Don't scale images during rastering to ensure image quality, save memory
+ // and avoid frequent re-rastering on change of scale.
+ *raster_contents_scale = std::max(1.f, MinimumContentsScale());
+ // We don't need low res tiles.
+ *low_res_raster_contents_scale = *raster_contents_scale;
+}
+
+} // namespace cc
diff --git a/cc/layers/picture_image_layer_impl.h b/cc/layers/picture_image_layer_impl.h
new file mode 100644
index 0000000..9c3f46d
--- /dev/null
+++ b/cc/layers/picture_image_layer_impl.h
@@ -0,0 +1,39 @@
+// Copyright 2013 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.
+
+#ifndef CC_LAYERS_PICTURE_IMAGE_LAYER_IMPL_H_
+#define CC_LAYERS_PICTURE_IMAGE_LAYER_IMPL_H_
+
+#include "cc/layers/picture_layer_impl.h"
+
+namespace cc {
+
+class CC_EXPORT PictureImageLayerImpl : public PictureLayerImpl {
+ public:
+ static scoped_ptr<PictureImageLayerImpl> Create(LayerTreeImpl* treeImpl,
+ int id) {
+ return make_scoped_ptr(new PictureImageLayerImpl(treeImpl, id));
+ }
+ virtual ~PictureImageLayerImpl();
+
+ virtual const char* LayerTypeAsString() const OVERRIDE;
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(
+ LayerTreeImpl* treeImpl) OVERRIDE;
+
+ protected:
+ PictureImageLayerImpl(LayerTreeImpl* treeImpl, int id);
+
+ virtual void CalculateRasterContentsScale(
+ bool animating_transform_to_screen,
+ float* raster_contents_scale,
+ float* low_res_raster_contents_scale) OVERRIDE;
+ virtual void GetDebugBorderProperties(
+ SkColor* color, float* width) const OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(PictureImageLayerImpl);
+};
+
+}
+
+#endif // CC_LAYERS_PICTURE_IMAGE_LAYER_IMPL_H_
diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc
new file mode 100644
index 0000000..e2d49a9
--- /dev/null
+++ b/cc/layers/picture_layer.cc
@@ -0,0 +1,97 @@
+// Copyright 2012 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 "cc/layers/picture_layer.h"
+
+#include "cc/debug/devtools_instrumentation.h"
+#include "cc/layers/picture_layer_impl.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "ui/gfx/rect_conversions.h"
+
+namespace cc {
+
+scoped_refptr<PictureLayer> PictureLayer::Create(ContentLayerClient* client) {
+ return make_scoped_refptr(new PictureLayer(client));
+}
+
+PictureLayer::PictureLayer(ContentLayerClient* client) :
+ client_(client),
+ pile_(make_scoped_refptr(new PicturePile())),
+ instrumentation_object_tracker_(id()),
+ is_mask_(false) {
+}
+
+PictureLayer::~PictureLayer() {
+}
+
+bool PictureLayer::DrawsContent() const {
+ return Layer::DrawsContent() && client_;
+}
+
+scoped_ptr<LayerImpl> PictureLayer::CreateLayerImpl(LayerTreeImpl* tree_impl) {
+ return PictureLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
+}
+
+void PictureLayer::PushPropertiesTo(LayerImpl* base_layer) {
+ Layer::PushPropertiesTo(base_layer);
+
+ PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer);
+ layer_impl->SetIsMask(is_mask_);
+ layer_impl->CreateTilingSet();
+ layer_impl->invalidation_.Clear();
+ layer_impl->invalidation_.Swap(pile_invalidation_);
+ layer_impl->pile_ = PicturePileImpl::CreateFromOther(pile_);
+
+ layer_impl->SyncFromActiveLayer();
+}
+
+void PictureLayer::SetLayerTreeHost(LayerTreeHost* host) {
+ Layer::SetLayerTreeHost(host);
+ if (host) {
+ pile_->SetMinContentsScale(host->settings().minimumContentsScale);
+ pile_->SetTileGridSize(host->settings().defaultTileSize);
+ pile_->set_num_raster_threads(host->settings().numRasterThreads);
+ pile_->set_slow_down_raster_scale_factor(
+ host->debug_state().slowDownRasterScaleFactor);
+ }
+}
+
+void PictureLayer::SetNeedsDisplayRect(const gfx::RectF& layer_rect) {
+ gfx::Rect rect = gfx::ToEnclosedRect(layer_rect);
+ if (!rect.IsEmpty()) {
+ // Clamp invalidation to the layer bounds.
+ rect.Intersect(gfx::Rect(bounds()));
+ pending_invalidation_.Union(rect);
+ }
+ Layer::SetNeedsDisplayRect(layer_rect);
+}
+
+void PictureLayer::Update(ResourceUpdateQueue*,
+ const OcclusionTracker*,
+ RenderingStats* stats) {
+ // Do not early-out of this function so that PicturePile::Update has a chance
+ // to record pictures due to changing visibility of this layer.
+
+ pile_->Resize(bounds());
+
+ // Calling paint in WebKit can sometimes cause invalidations, so save
+ // off the invalidation prior to calling update.
+ pile_invalidation_.Swap(pending_invalidation_);
+ pending_invalidation_.Clear();
+
+ gfx::Rect visible_layer_rect = gfx::ToEnclosingRect(
+ gfx::ScaleRect(visible_content_rect(), 1.f / contents_scale_x()));
+ devtools_instrumentation::ScopedPaintLayer paint_layer(id());
+ pile_->Update(client_,
+ background_color(),
+ pile_invalidation_,
+ visible_layer_rect,
+ stats);
+}
+
+void PictureLayer::SetIsMask(bool is_mask) {
+ is_mask_ = is_mask;
+}
+
+} // namespace cc
diff --git a/cc/layers/picture_layer.h b/cc/layers/picture_layer.h
new file mode 100644
index 0000000..0ba4031
--- /dev/null
+++ b/cc/layers/picture_layer.h
@@ -0,0 +1,57 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_PICTURE_LAYER_H_
+#define CC_LAYERS_PICTURE_LAYER_H_
+
+#include "cc/debug/devtools_instrumentation.h"
+#include "cc/layers/contents_scaling_layer.h"
+#include "cc/layers/layer.h"
+#include "cc/resources/picture_pile.h"
+#include "cc/trees/occlusion_tracker.h"
+
+namespace cc {
+
+class ContentLayerClient;
+class ResourceUpdateQueue;
+struct RenderingStats;
+
+class CC_EXPORT PictureLayer : public ContentsScalingLayer {
+ public:
+ static scoped_refptr<PictureLayer> Create(ContentLayerClient* client);
+
+ void ClearClient() { client_ = NULL; }
+
+ // Implement Layer interface
+ virtual bool DrawsContent() const OVERRIDE;
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(
+ LayerTreeImpl* tree_impl) OVERRIDE;
+ virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+ virtual void SetNeedsDisplayRect(const gfx::RectF& layer_rect) OVERRIDE;
+ virtual void Update(
+ ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) OVERRIDE;
+ virtual void SetIsMask(bool is_mask) OVERRIDE;
+
+ protected:
+ explicit PictureLayer(ContentLayerClient* client);
+ virtual ~PictureLayer();
+
+ private:
+ ContentLayerClient* client_;
+ scoped_refptr<PicturePile> pile_;
+ devtools_instrumentation::
+ ScopedLayerObjectTracker instrumentation_object_tracker_;
+ // Invalidation to use the next time update is called.
+ Region pending_invalidation_;
+ // Invalidation from the last time update was called.
+ Region pile_invalidation_;
+ bool is_mask_;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_PICTURE_LAYER_H_
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
new file mode 100644
index 0000000..0255621
--- /dev/null
+++ b/cc/layers/picture_layer_impl.cc
@@ -0,0 +1,828 @@
+// Copyright 2012 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 "cc/layers/picture_layer_impl.h"
+
+#include "base/time.h"
+#include "cc/base/math_util.h"
+#include "cc/base/util.h"
+#include "cc/debug/debug_colors.h"
+#include "cc/layers/append_quads_data.h"
+#include "cc/layers/quad_sink.h"
+#include "cc/quads/checkerboard_draw_quad.h"
+#include "cc/quads/debug_border_draw_quad.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/quads/tile_draw_quad.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "ui/gfx/quad_f.h"
+#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/size_conversions.h"
+
+namespace {
+const float kMaxScaleRatioDuringPinch = 2.0f;
+}
+
+namespace cc {
+
+PictureLayerImpl::PictureLayerImpl(LayerTreeImpl* tree_impl, int id)
+ : LayerImpl(tree_impl, id),
+ pile_(PicturePileImpl::Create()),
+ last_content_scale_(0),
+ ideal_contents_scale_(0),
+ is_mask_(false),
+ ideal_page_scale_(0.f),
+ ideal_device_scale_(0.f),
+ ideal_source_scale_(0.f),
+ raster_page_scale_(0.f),
+ raster_device_scale_(0.f),
+ raster_source_scale_(0.f),
+ raster_source_scale_was_animating_(false) {
+}
+
+PictureLayerImpl::~PictureLayerImpl() {
+}
+
+const char* PictureLayerImpl::LayerTypeAsString() const {
+ return "PictureLayer";
+}
+
+scoped_ptr<LayerImpl> PictureLayerImpl::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return PictureLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
+}
+
+void PictureLayerImpl::CreateTilingSet() {
+ DCHECK(layer_tree_impl()->IsPendingTree());
+ DCHECK(!tilings_);
+ tilings_.reset(new PictureLayerTilingSet(this));
+ tilings_->SetLayerBounds(bounds());
+}
+
+void PictureLayerImpl::TransferTilingSet(
+ scoped_ptr<PictureLayerTilingSet> tilings) {
+ DCHECK(layer_tree_impl()->IsActiveTree());
+ tilings->SetClient(this);
+ tilings_ = tilings.Pass();
+}
+
+void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) {
+ LayerImpl::PushPropertiesTo(base_layer);
+
+ PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer);
+
+ layer_impl->SetIsMask(is_mask_);
+ layer_impl->TransferTilingSet(tilings_.Pass());
+ layer_impl->pile_ = pile_;
+ pile_ = PicturePileImpl::Create();
+
+ layer_impl->raster_page_scale_ = raster_page_scale_;
+ layer_impl->raster_device_scale_ = raster_device_scale_;
+ layer_impl->raster_source_scale_ = raster_source_scale_;
+}
+
+
+void PictureLayerImpl::AppendQuads(QuadSink* quadSink,
+ AppendQuadsData* appendQuadsData) {
+ const gfx::Rect& rect = visible_content_rect();
+ gfx::Rect content_rect(content_bounds());
+
+ SharedQuadState* sharedQuadState =
+ quadSink->UseSharedQuadState(CreateSharedQuadState());
+ AppendDebugBorderQuad(quadSink, sharedQuadState, appendQuadsData);
+
+ bool clipped = false;
+ gfx::QuadF target_quad = MathUtil::MapQuad(
+ draw_transform(),
+ gfx::QuadF(rect),
+ &clipped);
+ bool is_axis_aligned_in_target = !clipped && target_quad.IsRectilinear();
+
+ bool is_pixel_aligned = is_axis_aligned_in_target &&
+ draw_transform().IsIdentityOrIntegerTranslation();
+ PictureLayerTiling::LayerDeviceAlignment layerDeviceAlignment =
+ is_pixel_aligned ? PictureLayerTiling::LayerAlignedToDevice
+ : PictureLayerTiling::LayerNotAlignedToDevice;
+
+ if (ShowDebugBorders()) {
+ for (PictureLayerTilingSet::Iterator iter(tilings_.get(),
+ contents_scale_x(),
+ rect,
+ ideal_contents_scale_,
+ layerDeviceAlignment);
+ iter;
+ ++iter) {
+ SkColor color;
+ float width;
+ if (*iter && iter->drawing_info().IsReadyToDraw()) {
+ ManagedTileState::DrawingInfo::Mode mode = iter->drawing_info().mode();
+ if (mode == ManagedTileState::DrawingInfo::SOLID_COLOR_MODE ||
+ mode == ManagedTileState::DrawingInfo::TRANSPARENT_MODE) {
+ color = DebugColors::SolidColorTileBorderColor();
+ width = DebugColors::SolidColorTileBorderWidth(layer_tree_impl());
+ } else if (iter->priority(ACTIVE_TREE).resolution == HIGH_RESOLUTION) {
+ color = DebugColors::HighResTileBorderColor();
+ width = DebugColors::HighResTileBorderWidth(layer_tree_impl());
+ } else if (iter->priority(ACTIVE_TREE).resolution == LOW_RESOLUTION) {
+ color = DebugColors::LowResTileBorderColor();
+ width = DebugColors::LowResTileBorderWidth(layer_tree_impl());
+ } else if (iter->contents_scale() > contents_scale_x()) {
+ color = DebugColors::ExtraHighResTileBorderColor();
+ width = DebugColors::ExtraHighResTileBorderWidth(layer_tree_impl());
+ } else {
+ color = DebugColors::ExtraLowResTileBorderColor();
+ width = DebugColors::ExtraLowResTileBorderWidth(layer_tree_impl());
+ }
+ } else {
+ color = DebugColors::MissingTileBorderColor();
+ width = DebugColors::MissingTileBorderWidth(layer_tree_impl());
+ }
+
+ scoped_ptr<DebugBorderDrawQuad> debugBorderQuad =
+ DebugBorderDrawQuad::Create();
+ gfx::Rect geometry_rect = iter.geometry_rect();
+ debugBorderQuad->SetNew(sharedQuadState, geometry_rect, color, width);
+ quadSink->Append(debugBorderQuad.PassAs<DrawQuad>(), appendQuadsData);
+ }
+ }
+
+ // Keep track of the tilings that were used so that tilings that are
+ // unused can be considered for removal.
+ std::vector<PictureLayerTiling*> seen_tilings;
+
+ for (PictureLayerTilingSet::Iterator iter(tilings_.get(),
+ contents_scale_x(),
+ rect,
+ ideal_contents_scale_,
+ layerDeviceAlignment);
+ iter;
+ ++iter) {
+
+ gfx::Rect geometry_rect = iter.geometry_rect();
+ if (!*iter || !iter->drawing_info().IsReadyToDraw()) {
+ if (DrawCheckerboardForMissingTiles()) {
+ // TODO(enne): Figure out how to show debug "invalidated checker" color
+ scoped_ptr<CheckerboardDrawQuad> quad = CheckerboardDrawQuad::Create();
+ SkColor color = DebugColors::DefaultCheckerboardColor();
+ quad->SetNew(sharedQuadState, geometry_rect, color);
+ if (quadSink->Append(quad.PassAs<DrawQuad>(), appendQuadsData))
+ appendQuadsData->numMissingTiles++;
+ } else {
+ scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create();
+ quad->SetNew(sharedQuadState, geometry_rect, background_color());
+ if (quadSink->Append(quad.PassAs<DrawQuad>(), appendQuadsData))
+ appendQuadsData->numMissingTiles++;
+ }
+
+ appendQuadsData->hadIncompleteTile = true;
+ continue;
+ }
+
+ const ManagedTileState::DrawingInfo& drawing_info = iter->drawing_info();
+ switch (drawing_info.mode()) {
+ case ManagedTileState::DrawingInfo::TEXTURE_MODE: {
+ if (iter->contents_scale() != ideal_contents_scale_)
+ appendQuadsData->hadIncompleteTile = true;
+
+ gfx::RectF texture_rect = iter.texture_rect();
+ gfx::Rect opaque_rect = iter->opaque_rect();
+ opaque_rect.Intersect(content_rect);
+
+ scoped_ptr<TileDrawQuad> quad = TileDrawQuad::Create();
+ quad->SetNew(sharedQuadState,
+ geometry_rect,
+ opaque_rect,
+ drawing_info.get_resource_id(),
+ texture_rect,
+ iter.texture_size(),
+ drawing_info.contents_swizzled());
+ quadSink->Append(quad.PassAs<DrawQuad>(), appendQuadsData);
+ break;
+ }
+ case ManagedTileState::DrawingInfo::SOLID_COLOR_MODE: {
+ scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create();
+ quad->SetNew(sharedQuadState,
+ geometry_rect,
+ drawing_info.get_solid_color());
+ quadSink->Append(quad.PassAs<DrawQuad>(), appendQuadsData);
+ break;
+ }
+ case ManagedTileState::DrawingInfo::TRANSPARENT_MODE:
+ break;
+ case ManagedTileState::DrawingInfo::PICTURE_PILE_MODE:
+ // TODO: crbug.com/173011 would fill this part in.
+ default:
+ NOTREACHED();
+ }
+
+ if (!seen_tilings.size() || seen_tilings.back() != iter.CurrentTiling())
+ seen_tilings.push_back(iter.CurrentTiling());
+ }
+
+ // Aggressively remove any tilings that are not seen to save memory. Note
+ // that this is at the expense of doing cause more frequent re-painting. A
+ // better scheme would be to maintain a tighter visibleContentRect for the
+ // finer tilings.
+ CleanUpTilingsOnActiveLayer(seen_tilings);
+}
+
+void PictureLayerImpl::DumpLayerProperties(std::string*, int indent) const {
+ // TODO(enne): implement me
+}
+
+void PictureLayerImpl::UpdateTilePriorities() {
+ int current_source_frame_number = layer_tree_impl()->source_frame_number();
+ double current_frame_time =
+ (layer_tree_impl()->CurrentFrameTime() - base::TimeTicks()).InSecondsF();
+
+ gfx::Transform current_screen_space_transform = screen_space_transform();
+
+ gfx::Rect viewport_in_content_space;
+ gfx::Transform screen_to_layer(gfx::Transform::kSkipInitialization);
+ if (screen_space_transform().GetInverse(&screen_to_layer)) {
+ gfx::Rect device_viewport(layer_tree_impl()->device_viewport_size());
+ viewport_in_content_space = gfx::ToEnclosingRect(
+ MathUtil::ProjectClippedRect(screen_to_layer, device_viewport));
+ }
+
+ WhichTree tree =
+ layer_tree_impl()->IsActiveTree() ? ACTIVE_TREE : PENDING_TREE;
+ bool store_screen_space_quads_on_tiles =
+ layer_tree_impl()->debug_state().traceAllRenderedFrames;
+ tilings_->UpdateTilePriorities(
+ tree,
+ layer_tree_impl()->device_viewport_size(),
+ viewport_in_content_space,
+ last_bounds_,
+ bounds(),
+ last_content_scale_,
+ contents_scale_x(),
+ last_screen_space_transform_,
+ current_screen_space_transform,
+ current_source_frame_number,
+ current_frame_time,
+ store_screen_space_quads_on_tiles);
+
+ last_screen_space_transform_ = current_screen_space_transform;
+ last_bounds_ = bounds();
+ last_content_scale_ = contents_scale_x();
+}
+
+void PictureLayerImpl::DidBecomeActive() {
+ LayerImpl::DidBecomeActive();
+ tilings_->DidBecomeActive();
+}
+
+void PictureLayerImpl::DidLoseOutputSurface() {
+ if (tilings_)
+ tilings_->RemoveAllTilings();
+}
+
+void PictureLayerImpl::CalculateContentsScale(
+ float ideal_contents_scale,
+ bool animating_transform_to_screen,
+ float* contents_scale_x,
+ float* contents_scale_y,
+ gfx::Size* content_bounds) {
+ if (!DrawsContent()) {
+ DCHECK(!tilings_->num_tilings());
+ return;
+ }
+
+ float min_contents_scale = MinimumContentsScale();
+ float min_page_scale = layer_tree_impl()->min_page_scale_factor();
+ float min_device_scale = 1.f;
+ float min_source_scale =
+ min_contents_scale / min_page_scale / min_device_scale;
+
+ float ideal_page_scale = layer_tree_impl()->total_page_scale_factor();
+ float ideal_device_scale = layer_tree_impl()->device_scale_factor();
+ float ideal_source_scale =
+ ideal_contents_scale / ideal_page_scale / ideal_device_scale;
+
+ ideal_contents_scale_ = std::max(ideal_contents_scale, min_contents_scale);
+ ideal_page_scale_ = ideal_page_scale;
+ ideal_device_scale_ = ideal_device_scale;
+ ideal_source_scale_ = std::max(ideal_source_scale, min_source_scale);
+
+ ManageTilings(animating_transform_to_screen);
+
+ // The content scale and bounds for a PictureLayerImpl is somewhat fictitious.
+ // There are (usually) several tilings at different scales. However, the
+ // content bounds is the (integer!) space in which quads are generated.
+ // In order to guarantee that we can fill this integer space with any set of
+ // tilings (and then map back to floating point texture coordinates), the
+ // contents scale must be at least as large as the largest of the tilings.
+ float max_contents_scale = min_contents_scale;
+ for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
+ const PictureLayerTiling* tiling = tilings_->tiling_at(i);
+ max_contents_scale = std::max(max_contents_scale, tiling->contents_scale());
+ }
+
+ *contents_scale_x = max_contents_scale;
+ *contents_scale_y = max_contents_scale;
+ *content_bounds = gfx::ToCeiledSize(
+ gfx::ScaleSize(bounds(), max_contents_scale, max_contents_scale));
+}
+
+skia::RefPtr<SkPicture> PictureLayerImpl::GetPicture() {
+ return pile_->GetFlattenedPicture();
+}
+
+scoped_refptr<Tile> PictureLayerImpl::CreateTile(PictureLayerTiling* tiling,
+ gfx::Rect content_rect) {
+ if (!pile_->CanRaster(tiling->contents_scale(), content_rect))
+ return scoped_refptr<Tile>();
+
+ return make_scoped_refptr(new Tile(
+ layer_tree_impl()->tile_manager(),
+ pile_.get(),
+ content_rect.size(),
+ GL_RGBA,
+ content_rect,
+ contents_opaque() ? content_rect : gfx::Rect(),
+ tiling->contents_scale(),
+ id()));
+}
+
+void PictureLayerImpl::UpdatePile(Tile* tile) {
+ tile->set_picture_pile(pile_);
+}
+
+gfx::Size PictureLayerImpl::CalculateTileSize(
+ gfx::Size current_tile_size,
+ gfx::Size content_bounds) {
+ if (is_mask_) {
+ int max_size = layer_tree_impl()->MaxTextureSize();
+ return gfx::Size(
+ std::min(max_size, content_bounds.width()),
+ std::min(max_size, content_bounds.height()));
+ }
+
+ gfx::Size default_tile_size = layer_tree_impl()->settings().defaultTileSize;
+ gfx::Size max_untiled_content_size =
+ layer_tree_impl()->settings().maxUntiledLayerSize;
+
+ bool any_dimension_too_large =
+ content_bounds.width() > max_untiled_content_size.width() ||
+ content_bounds.height() > max_untiled_content_size.height();
+
+ bool any_dimension_one_tile =
+ content_bounds.width() <= default_tile_size.width() ||
+ content_bounds.height() <= default_tile_size.height();
+
+ // If long and skinny, tile at the max untiled content size, and clamp
+ // the smaller dimension to the content size, e.g. 1000x12 layer with
+ // 500x500 max untiled size would get 500x12 tiles. Also do this
+ // if the layer is small.
+ if (any_dimension_one_tile || !any_dimension_too_large) {
+ int width =
+ std::min(max_untiled_content_size.width(), content_bounds.width());
+ int height =
+ std::min(max_untiled_content_size.height(), content_bounds.height());
+ // Round width and height up to the closest multiple of 64, or 56 if
+ // we should avoid power-of-two textures. This helps reduce the number
+ // of different textures sizes to help recycling, and also keeps all
+ // textures multiple-of-eight, which is preferred on some drivers (IMG).
+ bool avoid_pow2 =
+ layer_tree_impl()->rendererCapabilities().avoid_pow2_textures;
+ int round_up_to = avoid_pow2 ? 56 : 64;
+ width = RoundUp(width, round_up_to);
+ height = RoundUp(height, round_up_to);
+ return gfx::Size(width, height);
+ }
+
+ return default_tile_size;
+}
+
+void PictureLayerImpl::SyncFromActiveLayer() {
+ DCHECK(layer_tree_impl()->IsPendingTree());
+
+ if (!DrawsContent()) {
+ raster_page_scale_ = 0;
+ raster_device_scale_ = 0;
+ raster_source_scale_ = 0;
+ return;
+ }
+
+ // If there is an active tree version of this layer, get a copy of its
+ // tiles. This needs to be done last, after setting invalidation and the
+ // pile.
+ if (PictureLayerImpl* active_twin = ActiveTwin())
+ SyncFromActiveLayer(active_twin);
+}
+
+void PictureLayerImpl::SyncFromActiveLayer(const PictureLayerImpl* other) {
+ raster_page_scale_ = other->raster_page_scale_;
+ raster_device_scale_ = other->raster_device_scale_;
+ raster_source_scale_ = other->raster_source_scale_;
+
+ // Add synthetic invalidations for any recordings that were dropped. As
+ // tiles are updated to point to this new pile, this will force the dropping
+ // of tiles that can no longer be rastered. This is not ideal, but is a
+ // trade-off for memory (use the same pile as much as possible, by switching
+ // during DidBecomeActive) and for time (don't bother checking every tile
+ // during activation to see if the new pile can still raster it).
+ //
+ // TODO(enne): Clean up this double loop.
+ for (int x = 0; x < pile_->num_tiles_x(); ++x) {
+ for (int y = 0; y < pile_->num_tiles_y(); ++y) {
+ bool previously_had = other->pile_->HasRecordingAt(x, y);
+ bool now_has = pile_->HasRecordingAt(x, y);
+ if (now_has || !previously_had)
+ continue;
+ gfx::Rect layer_rect = pile_->tile_bounds(x, y);
+ invalidation_.Union(layer_rect);
+ }
+ }
+
+ tilings_->CloneAll(*other->tilings_, invalidation_, MinimumContentsScale());
+ DCHECK(bounds() == tilings_->LayerBounds());
+
+ // It's a sad but unfortunate fact that PicturePile tiling edges do not line
+ // up with PictureLayerTiling edges. Tiles can only be added if they are
+ // entirely covered by recordings (that may come from multiple PicturePile
+ // tiles). This check happens in this class's CreateTile() call.
+ for (int x = 0; x < pile_->num_tiles_x(); ++x) {
+ for (int y = 0; y < pile_->num_tiles_y(); ++y) {
+ bool previously_had = other->pile_->HasRecordingAt(x, y);
+ bool now_has = pile_->HasRecordingAt(x, y);
+ if (!now_has || previously_had)
+ continue;
+ gfx::Rect layer_rect = pile_->tile_bounds(x, y);
+ tilings_->CreateTilesFromLayerRect(layer_rect);
+ }
+ }
+}
+
+void PictureLayerImpl::SyncTiling(
+ const PictureLayerTiling* tiling,
+ const Region& pending_layer_invalidation) {
+ if (!DrawsContent() || tiling->contents_scale() < MinimumContentsScale())
+ return;
+ tilings_->Clone(tiling, pending_layer_invalidation);
+}
+
+void PictureLayerImpl::SetIsMask(bool is_mask) {
+ if (is_mask_ == is_mask)
+ return;
+ is_mask_ = is_mask;
+ if (tilings_)
+ tilings_->RemoveAllTiles();
+}
+
+ResourceProvider::ResourceId PictureLayerImpl::ContentsResourceId() const {
+ gfx::Rect content_rect(content_bounds());
+ float scale = contents_scale_x();
+ for (PictureLayerTilingSet::Iterator
+ iter(tilings_.get(),
+ scale,
+ content_rect,
+ ideal_contents_scale_,
+ PictureLayerTiling::LayerDeviceAlignmentUnknown);
+ iter;
+ ++iter) {
+ // Mask resource not ready yet.
+ if (!*iter ||
+ iter->drawing_info().mode() !=
+ ManagedTileState::DrawingInfo::TEXTURE_MODE ||
+ !iter->drawing_info().IsReadyToDraw())
+ return 0;
+ // Masks only supported if they fit on exactly one tile.
+ if (iter.geometry_rect() != content_rect)
+ return 0;
+ return iter->drawing_info().get_resource_id();
+ }
+ return 0;
+}
+
+bool PictureLayerImpl::AreVisibleResourcesReady() const {
+ DCHECK(layer_tree_impl()->IsPendingTree());
+ DCHECK(ideal_contents_scale_);
+
+ const gfx::Rect& rect = visible_content_rect();
+
+ float raster_contents_scale =
+ raster_page_scale_ *
+ raster_device_scale_ *
+ raster_source_scale_;
+
+ float min_acceptable_scale =
+ std::min(raster_contents_scale, ideal_contents_scale_);
+
+ TreePriority tree_priority =
+ layer_tree_impl()->tile_manager()->GlobalState().tree_priority;
+ bool should_force_uploads =
+ tree_priority != SMOOTHNESS_TAKES_PRIORITY &&
+ layer_tree_impl()->animationRegistrar()->
+ active_animation_controllers().empty();
+
+ if (PictureLayerImpl* twin = ActiveTwin()) {
+ float twin_raster_contents_scale =
+ twin->raster_page_scale_ *
+ twin->raster_device_scale_ *
+ twin->raster_source_scale_;
+
+ min_acceptable_scale = std::min(
+ min_acceptable_scale,
+ std::min(twin->ideal_contents_scale_, twin_raster_contents_scale));
+ }
+
+ Region missing_region = rect;
+ for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
+ PictureLayerTiling* tiling = tilings_->tiling_at(i);
+
+ if (tiling->contents_scale() < min_acceptable_scale)
+ continue;
+
+ for (PictureLayerTiling::Iterator
+ iter(tiling,
+ contents_scale_x(),
+ rect,
+ PictureLayerTiling::LayerDeviceAlignmentUnknown);
+ iter;
+ ++iter) {
+ if (should_force_uploads && iter)
+ layer_tree_impl()->tile_manager()->ForceTileUploadToComplete(*iter);
+ // A null tile (i.e. no recording) is considered "ready".
+ if (!*iter || iter->drawing_info().IsReadyToDraw())
+ missing_region.Subtract(iter.geometry_rect());
+ }
+ }
+
+ return missing_region.IsEmpty();
+}
+
+PictureLayerTiling* PictureLayerImpl::AddTiling(float contents_scale) {
+ DCHECK(contents_scale >= MinimumContentsScale());
+
+ PictureLayerTiling* tiling = tilings_->AddTiling(contents_scale);
+
+ const Region& recorded = pile_->recorded_region();
+ DCHECK(!recorded.IsEmpty());
+
+ for (Region::Iterator iter(recorded); iter.has_rect(); iter.next())
+ tiling->CreateTilesFromLayerRect(iter.rect());
+
+ PictureLayerImpl* twin =
+ layer_tree_impl()->IsPendingTree() ? ActiveTwin() : PendingTwin();
+ if (!twin)
+ return tiling;
+
+ if (layer_tree_impl()->IsPendingTree())
+ twin->SyncTiling(tiling, invalidation_);
+ else
+ twin->SyncTiling(tiling, twin->invalidation_);
+
+ return tiling;
+}
+
+void PictureLayerImpl::RemoveTiling(float contents_scale) {
+ for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
+ PictureLayerTiling* tiling = tilings_->tiling_at(i);
+ if (tiling->contents_scale() == contents_scale) {
+ tilings_->Remove(tiling);
+ break;
+ }
+ }
+}
+
+namespace {
+
+inline float PositiveRatio(float float1, float float2) {
+ DCHECK(float1 > 0);
+ DCHECK(float2 > 0);
+ return float1 > float2 ? float1 / float2 : float2 / float1;
+}
+
+inline bool IsCloserToThan(
+ PictureLayerTiling* layer1,
+ PictureLayerTiling* layer2,
+ float contents_scale) {
+ // Absolute value for ratios.
+ float ratio1 = PositiveRatio(layer1->contents_scale(), contents_scale);
+ float ratio2 = PositiveRatio(layer2->contents_scale(), contents_scale);
+ return ratio1 < ratio2;
+}
+
+} // namespace
+
+void PictureLayerImpl::ManageTilings(bool animating_transform_to_screen) {
+ DCHECK(ideal_contents_scale_);
+ DCHECK(ideal_page_scale_);
+ DCHECK(ideal_device_scale_);
+ DCHECK(ideal_source_scale_);
+
+ if (pile_->recorded_region().IsEmpty())
+ return;
+
+ bool is_active_layer = layer_tree_impl()->IsActiveTree();
+ bool is_pinching = layer_tree_impl()->PinchGestureActive();
+
+ bool change_target_tiling = false;
+
+ if (!raster_page_scale_ || !raster_device_scale_ || !raster_source_scale_)
+ change_target_tiling = true;
+
+ // TODO(danakj): Adjust raster_source_scale_ closer to ideal_source_scale_ at
+ // a throttled rate. Possibly make use of invalidation_.IsEmpty() on pending
+ // tree. This will allow CSS scale changes to get re-rastered at an
+ // appropriate rate.
+
+ if (is_active_layer) {
+ if (raster_source_scale_was_animating_ && !animating_transform_to_screen)
+ change_target_tiling = true;
+ raster_source_scale_was_animating_ = animating_transform_to_screen;
+ }
+
+ if (is_active_layer && is_pinching && raster_page_scale_) {
+ // If the page scale diverges too far during pinch, change raster target to
+ // the current page scale.
+ float ratio = PositiveRatio(ideal_page_scale_, raster_page_scale_);
+ if (ratio >= kMaxScaleRatioDuringPinch)
+ change_target_tiling = true;
+ }
+
+ if (!is_pinching) {
+ // When not pinching, match the ideal page scale factor.
+ if (raster_page_scale_ != ideal_page_scale_)
+ change_target_tiling = true;
+ }
+
+ // Always match the ideal device scale factor.
+ if (raster_device_scale_ != ideal_device_scale_)
+ change_target_tiling = true;
+
+ if (!change_target_tiling)
+ return;
+
+ raster_page_scale_ = ideal_page_scale_;
+ raster_device_scale_ = ideal_device_scale_;
+ raster_source_scale_ = ideal_source_scale_;
+
+ float raster_contents_scale;
+ float low_res_raster_contents_scale;
+ CalculateRasterContentsScale(animating_transform_to_screen,
+ &raster_contents_scale,
+ &low_res_raster_contents_scale);
+
+ PictureLayerTiling* high_res = NULL;
+ PictureLayerTiling* low_res = NULL;
+
+ for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
+ PictureLayerTiling* tiling = tilings_->tiling_at(i);
+ if (tiling->contents_scale() == raster_contents_scale)
+ high_res = tiling;
+ if (tiling->contents_scale() == low_res_raster_contents_scale)
+ low_res = tiling;
+
+ // Reset all tilings to non-ideal until the end of this function.
+ tiling->set_resolution(NON_IDEAL_RESOLUTION);
+ }
+
+ if (!high_res) {
+ high_res = AddTiling(raster_contents_scale);
+ if (raster_contents_scale == low_res_raster_contents_scale)
+ low_res = high_res;
+ }
+ if (!low_res && low_res != high_res)
+ low_res = AddTiling(low_res_raster_contents_scale);
+
+ if (high_res)
+ high_res->set_resolution(HIGH_RESOLUTION);
+ if (low_res && low_res != high_res)
+ low_res->set_resolution(LOW_RESOLUTION);
+}
+
+void PictureLayerImpl::CalculateRasterContentsScale(
+ bool animating_transform_to_screen,
+ float* raster_contents_scale,
+ float* low_res_raster_contents_scale) {
+ *raster_contents_scale = ideal_contents_scale_;
+
+ // Don't allow animating CSS scales to drop below 1.
+ if (animating_transform_to_screen) {
+ *raster_contents_scale = std::max(
+ *raster_contents_scale, 1.f * ideal_page_scale_ * ideal_device_scale_);
+ }
+
+ float low_res_factor =
+ layer_tree_impl()->settings().lowResContentsScaleFactor;
+ *low_res_raster_contents_scale = std::max(
+ *raster_contents_scale * low_res_factor,
+ MinimumContentsScale());
+}
+
+void PictureLayerImpl::CleanUpTilingsOnActiveLayer(
+ std::vector<PictureLayerTiling*> used_tilings) {
+ DCHECK(layer_tree_impl()->IsActiveTree());
+
+ float raster_contents_scale =
+ raster_page_scale_ * raster_device_scale_ * raster_source_scale_;
+
+ float min_acceptable_high_res_scale = std::min(
+ raster_contents_scale, ideal_contents_scale_);
+ float max_acceptable_high_res_scale = std::max(
+ raster_contents_scale, ideal_contents_scale_);
+
+ PictureLayerImpl* twin = PendingTwin();
+ if (twin) {
+ float twin_raster_contents_scale =
+ twin->raster_page_scale_ *
+ twin->raster_device_scale_ *
+ twin->raster_source_scale_;
+
+ min_acceptable_high_res_scale = std::min(
+ min_acceptable_high_res_scale,
+ std::min(twin_raster_contents_scale, twin->ideal_contents_scale_));
+ max_acceptable_high_res_scale = std::max(
+ max_acceptable_high_res_scale,
+ std::max(twin_raster_contents_scale, twin->ideal_contents_scale_));
+ }
+
+ float low_res_factor =
+ layer_tree_impl()->settings().lowResContentsScaleFactor;
+
+ float min_acceptable_low_res_scale =
+ low_res_factor * min_acceptable_high_res_scale;
+ float max_acceptable_low_res_scale =
+ low_res_factor * max_acceptable_high_res_scale;
+
+ std::vector<PictureLayerTiling*> to_remove;
+ for (size_t i = 0; i < tilings_->num_tilings(); ++i) {
+ PictureLayerTiling* tiling = tilings_->tiling_at(i);
+
+ if (tiling->contents_scale() >= min_acceptable_high_res_scale &&
+ tiling->contents_scale() <= max_acceptable_high_res_scale)
+ continue;
+
+ if (tiling->contents_scale() >= min_acceptable_low_res_scale &&
+ tiling->contents_scale() <= max_acceptable_low_res_scale)
+ continue;
+
+ // Don't remove tilings that are being used and expected to stay around.
+ if (std::find(used_tilings.begin(), used_tilings.end(), tiling) !=
+ used_tilings.end())
+ continue;
+
+ to_remove.push_back(tiling);
+ }
+
+ for (size_t i = 0; i < to_remove.size(); ++i) {
+ if (twin)
+ twin->RemoveTiling(to_remove[i]->contents_scale());
+ tilings_->Remove(to_remove[i]);
+ }
+}
+
+PictureLayerImpl* PictureLayerImpl::PendingTwin() const {
+ DCHECK(layer_tree_impl()->IsActiveTree());
+
+ PictureLayerImpl* twin = static_cast<PictureLayerImpl*>(
+ layer_tree_impl()->FindPendingTreeLayerById(id()));
+ if (twin)
+ DCHECK_EQ(id(), twin->id());
+ return twin;
+}
+
+PictureLayerImpl* PictureLayerImpl::ActiveTwin() const {
+ DCHECK(layer_tree_impl()->IsPendingTree());
+
+ PictureLayerImpl* twin = static_cast<PictureLayerImpl*>(
+ layer_tree_impl()->FindActiveTreeLayerById(id()));
+ if (twin)
+ DCHECK_EQ(id(), twin->id());
+ return twin;
+}
+
+float PictureLayerImpl::MinimumContentsScale() const {
+ float setting_min = layer_tree_impl()->settings().minimumContentsScale;
+
+ // If the contents scale is less than 1 / width (also for height),
+ // then it will end up having less than one pixel of content in that
+ // dimension. Bump the minimum contents scale up in this case to prevent
+ // this from happening.
+ int min_dimension = std::min(bounds().width(), bounds().height());
+ if (!min_dimension)
+ return setting_min;
+
+ return std::max(1.f / min_dimension, setting_min);
+}
+
+void PictureLayerImpl::GetDebugBorderProperties(
+ SkColor* color,
+ float* width) const {
+ *color = DebugColors::TiledContentLayerBorderColor();
+ *width = DebugColors::TiledContentLayerBorderWidth(layer_tree_impl());
+}
+
+scoped_ptr<base::Value> PictureLayerImpl::AsValue() const {
+ scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue());
+ LayerImpl::AsValueInto(state.get());
+
+ state->SetDouble("ideal_contents_scale", ideal_contents_scale_);
+ state->Set("tilings", tilings_->AsValue().release());
+ return state.PassAs<base::Value>();
+}
+
+} // namespace cc
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
new file mode 100644
index 0000000..d10b93f
--- /dev/null
+++ b/cc/layers/picture_layer_impl.h
@@ -0,0 +1,116 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_PICTURE_LAYER_IMPL_H_
+#define CC_LAYERS_PICTURE_LAYER_IMPL_H_
+
+#include "cc/base/scoped_ptr_vector.h"
+#include "cc/layers/layer_impl.h"
+#include "cc/resources/picture_layer_tiling.h"
+#include "cc/resources/picture_layer_tiling_set.h"
+#include "cc/resources/picture_pile_impl.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkPicture.h"
+
+namespace cc {
+
+struct AppendQuadsData;
+class QuadSink;
+
+class CC_EXPORT PictureLayerImpl : public LayerImpl,
+ public PictureLayerTilingClient {
+ public:
+ static scoped_ptr<PictureLayerImpl> Create(LayerTreeImpl* tree_impl, int id) {
+ return make_scoped_ptr(new PictureLayerImpl(tree_impl, id));
+ }
+ virtual ~PictureLayerImpl();
+
+ // LayerImpl overrides.
+ virtual const char* LayerTypeAsString() const OVERRIDE;
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+ virtual void AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) OVERRIDE;
+ virtual void DumpLayerProperties(std::string* str, int indent) const OVERRIDE;
+ virtual void UpdateTilePriorities() OVERRIDE;
+ virtual void DidBecomeActive() OVERRIDE;
+ virtual void DidLoseOutputSurface() OVERRIDE;
+ virtual void CalculateContentsScale(float ideal_contents_scale,
+ bool animating_transform_to_screen,
+ float* contents_scale_x,
+ float* contents_scale_y,
+ gfx::Size* content_bounds) OVERRIDE;
+ virtual skia::RefPtr<SkPicture> GetPicture() OVERRIDE;
+
+ // PictureLayerTilingClient overrides.
+ virtual scoped_refptr<Tile> CreateTile(PictureLayerTiling* tiling,
+ gfx::Rect content_rect) OVERRIDE;
+ virtual void UpdatePile(Tile* tile) OVERRIDE;
+ virtual gfx::Size CalculateTileSize(
+ gfx::Size current_tile_size,
+ gfx::Size content_bounds) OVERRIDE;
+
+ // PushPropertiesTo active tree => pending tree
+ void SyncFromActiveLayer();
+ void SyncTiling(
+ const PictureLayerTiling* tiling,
+ const Region& pending_layer_invalidation);
+
+ void CreateTilingSet();
+ void TransferTilingSet(scoped_ptr<PictureLayerTilingSet> tilings);
+
+ // Mask-related functions
+ void SetIsMask(bool is_mask);
+ virtual ResourceProvider::ResourceId ContentsResourceId() const OVERRIDE;
+
+ virtual bool AreVisibleResourcesReady() const OVERRIDE;
+
+ virtual scoped_ptr<base::Value> AsValue() const OVERRIDE;
+
+ protected:
+ PictureLayerImpl(LayerTreeImpl* tree_impl, int id);
+ PictureLayerTiling* AddTiling(float contents_scale);
+ void RemoveTiling(float contents_scale);
+ void SyncFromActiveLayer(const PictureLayerImpl* other);
+ void ManageTilings(bool animating_transform_to_screen);
+ virtual void CalculateRasterContentsScale(
+ bool animating_transform_to_screen,
+ float* raster_contents_scale,
+ float* low_res_raster_contents_scale);
+ void CleanUpTilingsOnActiveLayer(
+ std::vector<PictureLayerTiling*> used_tilings);
+ PictureLayerImpl* PendingTwin() const;
+ PictureLayerImpl* ActiveTwin() const;
+ float MinimumContentsScale() const;
+
+ virtual void GetDebugBorderProperties(
+ SkColor* color, float* width) const OVERRIDE;
+
+ scoped_ptr<PictureLayerTilingSet> tilings_;
+ scoped_refptr<PicturePileImpl> pile_;
+ Region invalidation_;
+
+ gfx::Transform last_screen_space_transform_;
+ gfx::Size last_bounds_;
+ float last_content_scale_;
+ float ideal_contents_scale_;
+ bool is_mask_;
+
+ float ideal_page_scale_;
+ float ideal_device_scale_;
+ float ideal_source_scale_;
+
+ float raster_page_scale_;
+ float raster_device_scale_;
+ float raster_source_scale_;
+ bool raster_source_scale_was_animating_;
+
+ friend class PictureLayer;
+ DISALLOW_COPY_AND_ASSIGN(PictureLayerImpl);
+};
+
+}
+
+#endif // CC_LAYERS_PICTURE_LAYER_IMPL_H_
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
new file mode 100644
index 0000000..48a5f51
--- /dev/null
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -0,0 +1,708 @@
+// Copyright 2013 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 "cc/layers/picture_layer_impl.h"
+
+#include "cc/layers/picture_layer.h"
+#include "cc/test/fake_content_layer_client.h"
+#include "cc/test/fake_impl_proxy.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/fake_output_surface.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "ui/gfx/rect_conversions.h"
+
+namespace cc {
+namespace {
+
+class TestablePictureLayerImpl : public PictureLayerImpl {
+ public:
+ static scoped_ptr<TestablePictureLayerImpl> Create(
+ LayerTreeImpl* tree_impl,
+ int id,
+ scoped_refptr<PicturePileImpl> pile)
+ {
+ return make_scoped_ptr(new TestablePictureLayerImpl(tree_impl, id, pile));
+ }
+
+ PictureLayerTilingSet& tilings() { return *tilings_; }
+ Region& invalidation() { return invalidation_; }
+
+ virtual gfx::Size CalculateTileSize(
+ gfx::Size current_tile_size,
+ gfx::Size content_bounds) OVERRIDE {
+ if (current_tile_size.IsEmpty())
+ return gfx::Size(100, 100);
+ return current_tile_size;
+ }
+
+ using PictureLayerImpl::AddTiling;
+ using PictureLayerImpl::CleanUpTilingsOnActiveLayer;
+
+ private:
+ TestablePictureLayerImpl(
+ LayerTreeImpl* tree_impl,
+ int id,
+ scoped_refptr<PicturePileImpl> pile)
+ : PictureLayerImpl(tree_impl, id) {
+ pile_ = pile;
+ SetBounds(pile_->size());
+ CreateTilingSet();
+ }
+};
+
+class ImplSidePaintingSettings : public LayerTreeSettings {
+ public:
+ ImplSidePaintingSettings() {
+ implSidePainting = true;
+ }
+};
+
+class TestablePicturePileImpl : public PicturePileImpl {
+ public:
+ static scoped_refptr<TestablePicturePileImpl> CreateFilledPile(
+ gfx::Size tile_size,
+ gfx::Size layer_bounds) {
+ scoped_refptr<TestablePicturePileImpl> pile(new TestablePicturePileImpl());
+ pile->tiling().SetTotalSize(layer_bounds);
+ pile->tiling().SetMaxTextureSize(tile_size);
+ pile->SetTileGridSize(ImplSidePaintingSettings().defaultTileSize);
+ for (int x = 0; x < pile->tiling().num_tiles_x(); ++x) {
+ for (int y = 0; y < pile->tiling().num_tiles_y(); ++y)
+ pile->AddRecordingAt(x, y);
+ }
+ pile->UpdateRecordedRegion();
+ return pile;
+ }
+
+ static scoped_refptr<TestablePicturePileImpl> CreateEmptyPile(
+ gfx::Size tile_size,
+ gfx::Size layer_bounds) {
+ scoped_refptr<TestablePicturePileImpl> pile(new TestablePicturePileImpl());
+ pile->tiling().SetTotalSize(layer_bounds);
+ pile->tiling().SetMaxTextureSize(tile_size);
+ pile->SetTileGridSize(ImplSidePaintingSettings().defaultTileSize);
+ pile->UpdateRecordedRegion();
+ return pile;
+ }
+
+ TilingData& tiling() { return tiling_; }
+
+ void AddRecordingAt(int x, int y) {
+ EXPECT_GE(x, 0);
+ EXPECT_GE(y, 0);
+ EXPECT_LT(x, tiling_.num_tiles_x());
+ EXPECT_LT(y, tiling_.num_tiles_y());
+
+ if (HasRecordingAt(x, y))
+ return;
+ gfx::Rect bounds(tiling().TileBounds(x, y));
+ scoped_refptr<Picture> picture(Picture::Create(bounds));
+ picture->Record(&client_, NULL, tile_grid_info_);
+ picture_list_map_[std::pair<int, int>(x, y)].push_back(picture);
+ EXPECT_TRUE(HasRecordingAt(x, y));
+
+ UpdateRecordedRegion();
+ }
+
+ void RemoveRecordingAt(int x, int y) {
+ EXPECT_GE(x, 0);
+ EXPECT_GE(y, 0);
+ EXPECT_LT(x, tiling_.num_tiles_x());
+ EXPECT_LT(y, tiling_.num_tiles_y());
+
+ if (!HasRecordingAt(x, y))
+ return;
+ picture_list_map_.erase(std::pair<int, int>(x, y));
+ EXPECT_FALSE(HasRecordingAt(x, y));
+
+ UpdateRecordedRegion();
+ }
+
+ void addDrawRect(const gfx::Rect& rect) {
+ client_.addDrawRect(rect);
+ }
+
+ protected:
+ virtual ~TestablePicturePileImpl() {
+ }
+
+ FakeContentLayerClient client_;
+};
+
+class MockCanvas : public SkCanvas {
+ public:
+ explicit MockCanvas(SkDevice* device) : SkCanvas(device) {}
+
+ virtual void drawRect(const SkRect& rect, const SkPaint& paint) OVERRIDE {
+ // Capture calls before SkCanvas quickReject kicks in
+ rects_.push_back(rect);
+ }
+
+ std::vector<SkRect> rects_;
+};
+
+class PictureLayerImplTest : public testing::Test {
+ public:
+ PictureLayerImplTest()
+ : host_impl_(ImplSidePaintingSettings(), &proxy_),
+ id_(7) {
+ host_impl_.InitializeRenderer(createFakeOutputSurface());
+ }
+
+ virtual ~PictureLayerImplTest() {
+ }
+
+ void SetupTrees(
+ scoped_refptr<PicturePileImpl> pending_pile,
+ scoped_refptr<PicturePileImpl> active_pile) {
+ SetupPendingTree(active_pile);
+ host_impl_.ActivatePendingTree();
+
+ active_layer_ = static_cast<TestablePictureLayerImpl*>(
+ host_impl_.active_tree()->LayerById(id_));
+
+ SetupPendingTree(pending_pile);
+ pending_layer_ = static_cast<TestablePictureLayerImpl*>(
+ host_impl_.pending_tree()->LayerById(id_));
+ }
+
+ void AddDefaultTilingsWithInvalidation(const Region& invalidation) {
+ active_layer_->AddTiling(2.3f);
+ active_layer_->AddTiling(1.0f);
+ active_layer_->AddTiling(0.5f);
+ pending_layer_->invalidation() = invalidation;
+ pending_layer_->SyncFromActiveLayer();
+ }
+
+ void SetupPendingTree(
+ scoped_refptr<PicturePileImpl> pile) {
+ host_impl_.CreatePendingTree();
+ LayerTreeImpl* pending_tree = host_impl_.pending_tree();
+ // Clear recycled tree.
+ pending_tree->DetachLayerTree();
+
+ scoped_ptr<TestablePictureLayerImpl> pending_layer =
+ TestablePictureLayerImpl::Create(pending_tree, id_, pile);
+ pending_layer->SetDrawsContent(true);
+ pending_tree->SetRootLayer(pending_layer.PassAs<LayerImpl>());
+ }
+
+ static void VerifyAllTilesExistAndHavePile(
+ const PictureLayerTiling* tiling,
+ PicturePileImpl* pile) {
+ for (PictureLayerTiling::Iterator
+ iter(tiling,
+ tiling->contents_scale(),
+ tiling->ContentRect(),
+ PictureLayerTiling::LayerDeviceAlignmentUnknown);
+ iter;
+ ++iter) {
+ EXPECT_TRUE(*iter);
+ EXPECT_EQ(pile, iter->picture_pile());
+ }
+ }
+
+ void SetContentsScaleOnBothLayers(float scale, bool animating_transform) {
+ float result_scale_x, result_scale_y;
+ gfx::Size result_bounds;
+ pending_layer_->CalculateContentsScale(
+ scale, animating_transform,
+ &result_scale_x, &result_scale_y, &result_bounds);
+ active_layer_->CalculateContentsScale(
+ scale, animating_transform,
+ &result_scale_x, &result_scale_y, &result_bounds);
+ }
+
+ protected:
+ void TestTileGridAlignmentCommon() {
+ // Layer to span 4 raster tiles in x and in y
+ ImplSidePaintingSettings settings;
+ gfx::Size layer_size(
+ settings.defaultTileSize.width() * 7 / 2,
+ settings.defaultTileSize.height() * 7 / 2);
+
+ scoped_refptr<TestablePicturePileImpl> pending_pile =
+ TestablePicturePileImpl::CreateFilledPile(layer_size, layer_size);
+ scoped_refptr<TestablePicturePileImpl> active_pile =
+ TestablePicturePileImpl::CreateFilledPile(layer_size, layer_size);
+
+ SetupTrees(pending_pile, active_pile);
+
+ host_impl_.active_tree()->SetPageScaleFactorAndLimits(1.f, 1.f, 1.f);
+ float result_scale_x, result_scale_y;
+ gfx::Size result_bounds;
+ active_layer_->CalculateContentsScale(
+ 1.f, false, &result_scale_x, &result_scale_y, &result_bounds);
+
+ // Add 1x1 rects at the centers of each tile, then re-record pile contents
+ std::vector<Tile*> tiles =
+ active_layer_->tilings().tiling_at(0)->AllTilesForTesting();
+ EXPECT_EQ(16, tiles.size());
+ std::vector<SkRect> rects;
+ std::vector<Tile*>::const_iterator tile_iter;
+ for (tile_iter = tiles.begin(); tile_iter < tiles.end(); tile_iter++) {
+ gfx::Point tile_center = (*tile_iter)->content_rect().CenterPoint();
+ gfx::Rect rect(tile_center.x(), tile_center.y(), 1, 1);
+ active_pile->addDrawRect(rect);
+ rects.push_back(SkRect::MakeXYWH(rect.x(), rect.y(), 1, 1));
+ }
+ // Force re-record with newly injected content
+ active_pile->RemoveRecordingAt(0, 0);
+ active_pile->AddRecordingAt(0, 0);
+
+ SkBitmap store;
+ store.setConfig(SkBitmap::kNo_Config, 1000, 1000);
+ SkDevice device(store);
+ int64 pixelsRasterized;
+
+ std::vector<SkRect>::const_iterator rect_iter = rects.begin();
+ for (tile_iter = tiles.begin(); tile_iter < tiles.end(); tile_iter++) {
+ MockCanvas mock_canvas(&device);
+ active_pile->Raster(&mock_canvas, (*tile_iter)->content_rect(),
+ 1.0f, &pixelsRasterized);
+
+ // This test verifies that when drawing the contents of a specific tile
+ // at content scale 1.0, the playback canvas never receives content from
+ // neighboring tiles which indicates that the tile grid embedded in
+ // SkPicture is perfectly aligned with the compositor's tiles.
+ // Note: There are two rects: the initial clear and the explicitly
+ // recorded rect. We only care about the second one.
+ EXPECT_EQ(2, mock_canvas.rects_.size());
+ EXPECT_EQ(*rect_iter, mock_canvas.rects_[1]);
+ rect_iter++;
+ }
+ }
+
+ FakeImplProxy proxy_;
+ FakeLayerTreeHostImpl host_impl_;
+ int id_;
+ TestablePictureLayerImpl* pending_layer_;
+ TestablePictureLayerImpl* active_layer_;
+
+ DISALLOW_COPY_AND_ASSIGN(PictureLayerImplTest);
+};
+
+TEST_F(PictureLayerImplTest, tileGridAlignment) {
+ host_impl_.SetDeviceScaleFactor(1.f);
+ TestTileGridAlignmentCommon();
+}
+
+TEST_F(PictureLayerImplTest, tileGridAlignmentHiDPI) {
+ host_impl_.SetDeviceScaleFactor(2.f);
+ TestTileGridAlignmentCommon();
+}
+
+TEST_F(PictureLayerImplTest, cloneNoInvalidation) {
+ gfx::Size tile_size(100, 100);
+ gfx::Size layer_bounds(400, 400);
+
+ scoped_refptr<TestablePicturePileImpl> pending_pile =
+ TestablePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+ scoped_refptr<TestablePicturePileImpl> active_pile =
+ TestablePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+
+ SetupTrees(pending_pile, active_pile);
+
+ Region invalidation;
+ AddDefaultTilingsWithInvalidation(invalidation);
+
+ EXPECT_EQ(pending_layer_->tilings().num_tilings(),
+ active_layer_->tilings().num_tilings());
+
+ const PictureLayerTilingSet& tilings = pending_layer_->tilings();
+ EXPECT_GT(tilings.num_tilings(), 0u);
+ for (size_t i = 0; i < tilings.num_tilings(); ++i)
+ VerifyAllTilesExistAndHavePile(tilings.tiling_at(i), active_pile.get());
+}
+
+TEST_F(PictureLayerImplTest, clonePartialInvalidation) {
+ gfx::Size tile_size(100, 100);
+ gfx::Size layer_bounds(400, 400);
+ gfx::Rect layer_invalidation(150, 200, 30, 180);
+
+ scoped_refptr<TestablePicturePileImpl> pending_pile =
+ TestablePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+ scoped_refptr<TestablePicturePileImpl> active_pile =
+ TestablePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+
+ SetupTrees(pending_pile, active_pile);
+
+ Region invalidation(layer_invalidation);
+ AddDefaultTilingsWithInvalidation(invalidation);
+
+ const PictureLayerTilingSet& tilings = pending_layer_->tilings();
+ EXPECT_GT(tilings.num_tilings(), 0u);
+ for (size_t i = 0; i < tilings.num_tilings(); ++i) {
+ const PictureLayerTiling* tiling = tilings.tiling_at(i);
+ gfx::Rect content_invalidation = gfx::ToEnclosingRect(gfx::ScaleRect(
+ layer_invalidation,
+ tiling->contents_scale()));
+ for (PictureLayerTiling::Iterator
+ iter(tiling,
+ tiling->contents_scale(),
+ tiling->ContentRect(),
+ PictureLayerTiling::LayerDeviceAlignmentUnknown);
+ iter;
+ ++iter) {
+ EXPECT_TRUE(*iter);
+ EXPECT_FALSE(iter.geometry_rect().IsEmpty());
+ if (iter.geometry_rect().Intersects(content_invalidation))
+ EXPECT_EQ(pending_pile, iter->picture_pile());
+ else
+ EXPECT_EQ(active_pile, iter->picture_pile());
+ }
+ }
+}
+
+TEST_F(PictureLayerImplTest, cloneFullInvalidation) {
+ gfx::Size tile_size(90, 80);
+ gfx::Size layer_bounds(300, 500);
+
+ scoped_refptr<TestablePicturePileImpl> pending_pile =
+ TestablePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+ scoped_refptr<TestablePicturePileImpl> active_pile =
+ TestablePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+
+ SetupTrees(pending_pile, active_pile);
+
+ Region invalidation((gfx::Rect(layer_bounds)));
+ AddDefaultTilingsWithInvalidation(invalidation);
+
+ EXPECT_EQ(pending_layer_->tilings().num_tilings(),
+ active_layer_->tilings().num_tilings());
+
+ const PictureLayerTilingSet& tilings = pending_layer_->tilings();
+ EXPECT_GT(tilings.num_tilings(), 0u);
+ for (size_t i = 0; i < tilings.num_tilings(); ++i)
+ VerifyAllTilesExistAndHavePile(tilings.tiling_at(i), pending_pile.get());
+}
+
+TEST_F(PictureLayerImplTest, noInvalidationBoundsChange) {
+ gfx::Size tile_size(90, 80);
+ gfx::Size active_layer_bounds(300, 500);
+ gfx::Size pending_layer_bounds(400, 800);
+
+ scoped_refptr<TestablePicturePileImpl> pending_pile =
+ TestablePicturePileImpl::CreateFilledPile(tile_size,
+ pending_layer_bounds);
+ scoped_refptr<TestablePicturePileImpl> active_pile =
+ TestablePicturePileImpl::CreateFilledPile(tile_size, active_layer_bounds);
+
+ SetupTrees(pending_pile, active_pile);
+
+ Region invalidation;
+ AddDefaultTilingsWithInvalidation(invalidation);
+
+ const PictureLayerTilingSet& tilings = pending_layer_->tilings();
+ EXPECT_GT(tilings.num_tilings(), 0u);
+ for (size_t i = 0; i < tilings.num_tilings(); ++i) {
+ const PictureLayerTiling* tiling = tilings.tiling_at(i);
+ gfx::Rect active_content_bounds = gfx::ToEnclosingRect(gfx::ScaleRect(
+ gfx::Rect(active_layer_bounds),
+ tiling->contents_scale()));
+ for (PictureLayerTiling::Iterator
+ iter(tiling,
+ tiling->contents_scale(),
+ tiling->ContentRect(),
+ PictureLayerTiling::LayerDeviceAlignmentUnknown);
+ iter;
+ ++iter) {
+ EXPECT_TRUE(*iter);
+ EXPECT_FALSE(iter.geometry_rect().IsEmpty());
+ if (iter.geometry_rect().right() >= active_content_bounds.width() ||
+ iter.geometry_rect().bottom() >= active_content_bounds.height()) {
+ EXPECT_EQ(pending_pile, iter->picture_pile());
+ } else {
+ EXPECT_EQ(active_pile, iter->picture_pile());
+ }
+ }
+ }
+}
+
+TEST_F(PictureLayerImplTest, addTilesFromNewRecording) {
+ gfx::Size tile_size(400, 400);
+ gfx::Size layer_bounds(1300, 1900);
+
+ scoped_refptr<TestablePicturePileImpl> pending_pile =
+ TestablePicturePileImpl::CreateEmptyPile(tile_size, layer_bounds);
+ scoped_refptr<TestablePicturePileImpl> active_pile =
+ TestablePicturePileImpl::CreateEmptyPile(tile_size, layer_bounds);
+
+ // Fill in some of active pile, but more of pending pile.
+ int hole_count = 0;
+ for (int x = 0; x < active_pile->tiling().num_tiles_x(); ++x) {
+ for (int y = 0; y < active_pile->tiling().num_tiles_y(); ++y) {
+ if ((x + y) % 2) {
+ pending_pile->AddRecordingAt(x, y);
+ active_pile->AddRecordingAt(x, y);
+ } else {
+ hole_count++;
+ if (hole_count % 2)
+ pending_pile->AddRecordingAt(x, y);
+ }
+ }
+ }
+
+ SetupTrees(pending_pile, active_pile);
+ Region invalidation;
+ AddDefaultTilingsWithInvalidation(invalidation);
+
+ const PictureLayerTilingSet& tilings = pending_layer_->tilings();
+ EXPECT_GT(tilings.num_tilings(), 0u);
+ for (size_t i = 0; i < tilings.num_tilings(); ++i) {
+ const PictureLayerTiling* tiling = tilings.tiling_at(i);
+
+ for (PictureLayerTiling::Iterator
+ iter(tiling,
+ tiling->contents_scale(),
+ tiling->ContentRect(),
+ PictureLayerTiling::LayerDeviceAlignmentUnknown);
+ iter;
+ ++iter) {
+ EXPECT_FALSE(iter.full_tile_geometry_rect().IsEmpty());
+ // Ensure there is a recording for this tile.
+ gfx::Rect layer_rect = gfx::ToEnclosingRect(gfx::ScaleRect(
+ iter.full_tile_geometry_rect(), 1.f / tiling->contents_scale()));
+ layer_rect.Intersect(gfx::Rect(layer_bounds));
+
+ bool in_pending = pending_pile->recorded_region().Contains(layer_rect);
+ bool in_active = active_pile->recorded_region().Contains(layer_rect);
+
+ if (in_pending && !in_active)
+ EXPECT_EQ(pending_pile, iter->picture_pile());
+ else if (in_active)
+ EXPECT_EQ(active_pile, iter->picture_pile());
+ else
+ EXPECT_FALSE(*iter);
+ }
+ }
+}
+
+TEST_F(PictureLayerImplTest, ManageTilingsWithNoRecording) {
+ gfx::Size tile_size(400, 400);
+ gfx::Size layer_bounds(1300, 1900);
+
+ scoped_refptr<TestablePicturePileImpl> pending_pile =
+ TestablePicturePileImpl::CreateEmptyPile(tile_size, layer_bounds);
+ scoped_refptr<TestablePicturePileImpl> active_pile =
+ TestablePicturePileImpl::CreateEmptyPile(tile_size, layer_bounds);
+
+ float result_scale_x, result_scale_y;
+ gfx::Size result_bounds;
+
+ SetupTrees(pending_pile, active_pile);
+
+ // These are included in the scale given to the layer.
+ host_impl_.SetDeviceScaleFactor(1.f);
+ host_impl_.pending_tree()->SetPageScaleFactorAndLimits(1.f, 1.f, 1.f);
+
+ pending_layer_->CalculateContentsScale(
+ 1.f, false, &result_scale_x, &result_scale_y, &result_bounds);
+
+ EXPECT_EQ(0u, pending_layer_->tilings().num_tilings());
+}
+
+TEST_F(PictureLayerImplTest, ManageTilingsCreatesTilings) {
+ gfx::Size tile_size(400, 400);
+ gfx::Size layer_bounds(1300, 1900);
+
+ scoped_refptr<TestablePicturePileImpl> pending_pile =
+ TestablePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+ scoped_refptr<TestablePicturePileImpl> active_pile =
+ TestablePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+
+ float result_scale_x, result_scale_y;
+ gfx::Size result_bounds;
+
+ SetupTrees(pending_pile, active_pile);
+ EXPECT_EQ(0u, pending_layer_->tilings().num_tilings());
+
+ float low_res_factor = host_impl_.settings().lowResContentsScaleFactor;
+ EXPECT_LT(low_res_factor, 1.f);
+
+ // These are included in the scale given to the layer.
+ host_impl_.SetDeviceScaleFactor(1.7f);
+ host_impl_.pending_tree()->SetPageScaleFactorAndLimits(3.2f, 3.2f, 3.2f);
+
+ pending_layer_->CalculateContentsScale(
+ 1.3f, false, &result_scale_x, &result_scale_y, &result_bounds);
+ ASSERT_EQ(2u, pending_layer_->tilings().num_tilings());
+ EXPECT_FLOAT_EQ(
+ 1.3f,
+ pending_layer_->tilings().tiling_at(0)->contents_scale());
+ EXPECT_FLOAT_EQ(
+ 1.3f * low_res_factor,
+ pending_layer_->tilings().tiling_at(1)->contents_scale());
+
+ // If we change the layer's CSS scale factor, then we should not get new
+ // tilings.
+ pending_layer_->CalculateContentsScale(
+ 1.8f, false, &result_scale_x, &result_scale_y, &result_bounds);
+ ASSERT_EQ(2u, pending_layer_->tilings().num_tilings());
+ EXPECT_FLOAT_EQ(
+ 1.3f,
+ pending_layer_->tilings().tiling_at(0)->contents_scale());
+ EXPECT_FLOAT_EQ(
+ 1.3f * low_res_factor,
+ pending_layer_->tilings().tiling_at(1)->contents_scale());
+
+ // If we change the page scale factor, then we should get new tilings.
+ host_impl_.pending_tree()->SetPageScaleFactorAndLimits(2.2f, 2.2f, 2.2f);
+
+ pending_layer_->CalculateContentsScale(
+ 1.8f, false, &result_scale_x, &result_scale_y, &result_bounds);
+ ASSERT_EQ(4u, pending_layer_->tilings().num_tilings());
+ EXPECT_FLOAT_EQ(
+ 1.8f,
+ pending_layer_->tilings().tiling_at(0)->contents_scale());
+ EXPECT_FLOAT_EQ(
+ 1.8f * low_res_factor,
+ pending_layer_->tilings().tiling_at(2)->contents_scale());
+
+ // If we change the device scale factor, then we should get new tilings.
+ host_impl_.SetDeviceScaleFactor(1.4f);
+
+ pending_layer_->CalculateContentsScale(
+ 1.9f, false, &result_scale_x, &result_scale_y, &result_bounds);
+ ASSERT_EQ(6u, pending_layer_->tilings().num_tilings());
+ EXPECT_FLOAT_EQ(
+ 1.9f,
+ pending_layer_->tilings().tiling_at(0)->contents_scale());
+ EXPECT_FLOAT_EQ(
+ 1.9f * low_res_factor,
+ pending_layer_->tilings().tiling_at(3)->contents_scale());
+
+ // If we change the device scale factor, but end up at the same total scale
+ // factor somehow, then we don't get new tilings.
+ host_impl_.SetDeviceScaleFactor(2.2f);
+ host_impl_.pending_tree()->SetPageScaleFactorAndLimits(1.4f, 1.4f, 1.4f);
+
+ pending_layer_->CalculateContentsScale(
+ 1.9f, false, &result_scale_x, &result_scale_y, &result_bounds);
+ ASSERT_EQ(6u, pending_layer_->tilings().num_tilings());
+ EXPECT_FLOAT_EQ(
+ 1.9f,
+ pending_layer_->tilings().tiling_at(0)->contents_scale());
+ EXPECT_FLOAT_EQ(
+ 1.9f * low_res_factor,
+ pending_layer_->tilings().tiling_at(3)->contents_scale());
+}
+
+TEST_F(PictureLayerImplTest, CleanUpTilings) {
+ gfx::Size tile_size(400, 400);
+ gfx::Size layer_bounds(1300, 1900);
+
+ scoped_refptr<TestablePicturePileImpl> pending_pile =
+ TestablePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+ scoped_refptr<TestablePicturePileImpl> active_pile =
+ TestablePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+
+ float result_scale_x, result_scale_y;
+ gfx::Size result_bounds;
+ std::vector<PictureLayerTiling*> used_tilings;
+
+ SetupTrees(pending_pile, active_pile);
+ EXPECT_EQ(0u, pending_layer_->tilings().num_tilings());
+
+ float low_res_factor = host_impl_.settings().lowResContentsScaleFactor;
+ EXPECT_LT(low_res_factor, 1.f);
+
+ // These are included in the scale given to the layer.
+ host_impl_.SetDeviceScaleFactor(1.7f);
+ host_impl_.pending_tree()->SetPageScaleFactorAndLimits(3.2f, 3.2f, 3.2f);
+ host_impl_.active_tree()->SetPageScaleFactorAndLimits(3.2f, 3.2f, 3.2f);
+
+ SetContentsScaleOnBothLayers(1.f, false);
+ ASSERT_EQ(2u, active_layer_->tilings().num_tilings());
+
+ // We only have ideal tilings, so they aren't removed.
+ used_tilings.clear();
+ active_layer_->CleanUpTilingsOnActiveLayer(used_tilings);
+ ASSERT_EQ(2u, active_layer_->tilings().num_tilings());
+
+ // Changing the ideal but not creating new tilings.
+ SetContentsScaleOnBothLayers(1.5f, false);
+ ASSERT_EQ(2u, active_layer_->tilings().num_tilings());
+
+ // The tilings are still our target scale, so they aren't removed.
+ used_tilings.clear();
+ active_layer_->CleanUpTilingsOnActiveLayer(used_tilings);
+ ASSERT_EQ(2u, active_layer_->tilings().num_tilings());
+
+ // Create a 1.2 scale tiling. Now we have 1.0 and 1.2 tilings. Ideal = 1.2.
+ host_impl_.pending_tree()->SetPageScaleFactorAndLimits(1.2f, 1.2f, 1.2f);
+ host_impl_.active_tree()->SetPageScaleFactorAndLimits(1.2f, 1.2f, 1.2f);
+ SetContentsScaleOnBothLayers(1.2f, false);
+ ASSERT_EQ(4u, active_layer_->tilings().num_tilings());
+ EXPECT_FLOAT_EQ(
+ 1.f,
+ active_layer_->tilings().tiling_at(1)->contents_scale());
+ EXPECT_FLOAT_EQ(
+ 1.f * low_res_factor,
+ active_layer_->tilings().tiling_at(3)->contents_scale());
+
+ // Mark the non-ideal tilings as used. They won't be removed.
+ used_tilings.clear();
+ used_tilings.push_back(active_layer_->tilings().tiling_at(1));
+ used_tilings.push_back(active_layer_->tilings().tiling_at(3));
+ active_layer_->CleanUpTilingsOnActiveLayer(used_tilings);
+ ASSERT_EQ(4u, active_layer_->tilings().num_tilings());
+
+ // Now move the ideal scale to 0.5. Our target stays 1.2.
+ SetContentsScaleOnBothLayers(0.5f, false);
+
+ // All the tilings are between are target and the ideal, so they are not
+ // removed.
+ used_tilings.clear();
+ active_layer_->CleanUpTilingsOnActiveLayer(used_tilings);
+ ASSERT_EQ(4u, active_layer_->tilings().num_tilings());
+
+ // Now move the ideal scale to 1.0. Our target stays 1.2.
+ SetContentsScaleOnBothLayers(1.f, false);
+
+ // All the tilings are between are target and the ideal, so they are not
+ // removed.
+ used_tilings.clear();
+ active_layer_->CleanUpTilingsOnActiveLayer(used_tilings);
+ ASSERT_EQ(4u, active_layer_->tilings().num_tilings());
+
+ // Now move the ideal scale to 1.1 on the active layer. Our target stays 1.2.
+ active_layer_->CalculateContentsScale(
+ 1.1f, false, &result_scale_x, &result_scale_y, &result_bounds);
+
+ // Because the pending layer's ideal scale is still 1.0, our tilings fall
+ // in the range [1.0,1.2] and are kept.
+ used_tilings.clear();
+ active_layer_->CleanUpTilingsOnActiveLayer(used_tilings);
+ ASSERT_EQ(4u, active_layer_->tilings().num_tilings());
+
+ // Move the ideal scale on the pending layer to 1.1 as well. Our target stays
+ // 1.2 still.
+ pending_layer_->CalculateContentsScale(
+ 1.1f, false, &result_scale_x, &result_scale_y, &result_bounds);
+
+ // Our 1.0 tiling now falls outside the range between our ideal scale and our
+ // target raster scale. But it is in our used tilings set, so nothing is
+ // deleted.
+ used_tilings.clear();
+ used_tilings.push_back(active_layer_->tilings().tiling_at(1));
+ used_tilings.push_back(active_layer_->tilings().tiling_at(3));
+ active_layer_->CleanUpTilingsOnActiveLayer(used_tilings);
+ ASSERT_EQ(4u, active_layer_->tilings().num_tilings());
+
+ // If we remove it from our used tilings set, it is outside the range to keep
+ // so it is deleted. Try one tiling at a time.
+ used_tilings.clear();
+ used_tilings.push_back(active_layer_->tilings().tiling_at(1));
+ active_layer_->CleanUpTilingsOnActiveLayer(used_tilings);
+ ASSERT_EQ(3u, active_layer_->tilings().num_tilings());
+ used_tilings.clear();
+ active_layer_->CleanUpTilingsOnActiveLayer(used_tilings);
+ ASSERT_EQ(2u, active_layer_->tilings().num_tilings());
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/quad_sink.h b/cc/layers/quad_sink.h
new file mode 100644
index 0000000..041b655
--- /dev/null
+++ b/cc/layers/quad_sink.h
@@ -0,0 +1,36 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_QUAD_SINK_H_
+#define CC_LAYERS_QUAD_SINK_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+class DrawQuad;
+class SharedQuadState;
+
+struct AppendQuadsData;
+
+class CC_EXPORT QuadSink {
+ public:
+ virtual ~QuadSink() {}
+
+ // Call this to add a SharedQuadState before appending quads that refer to it.
+ // Returns a pointer to the given SharedQuadState for convenience, that can be
+ // set on the quads to append.
+ virtual SharedQuadState* UseSharedQuadState(
+ scoped_ptr<SharedQuadState> shared_quad_state) = 0;
+
+ // Returns true if the quad is added to the list, and false if the quad is
+ // entirely culled.
+ virtual bool Append(scoped_ptr<DrawQuad> draw_quad,
+ AppendQuadsData* append_quads_data) = 0;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_QUAD_SINK_H_
diff --git a/cc/layers/render_pass_sink.h b/cc/layers/render_pass_sink.h
new file mode 100644
index 0000000..d307739e6
--- /dev/null
+++ b/cc/layers/render_pass_sink.h
@@ -0,0 +1,21 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_RENDER_PASS_SINK_H_
+#define CC_LAYERS_RENDER_PASS_SINK_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+
+namespace cc {
+class RenderPass;
+
+class CC_EXPORT RenderPassSink {
+ public:
+ virtual void AppendRenderPass(scoped_ptr<RenderPass> render_pass) = 0;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_RENDER_PASS_SINK_H_
diff --git a/cc/layers/render_surface.cc b/cc/layers/render_surface.cc
new file mode 100644
index 0000000..4eb5d34
--- /dev/null
+++ b/cc/layers/render_surface.cc
@@ -0,0 +1,33 @@
+// Copyright 2010 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 "cc/layers/render_surface.h"
+
+#include "cc/base/math_util.h"
+#include "cc/layers/layer.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+
+RenderSurface::RenderSurface(Layer* owning_layer)
+ : owning_layer_(owning_layer),
+ draw_opacity_(1),
+ draw_opacity_is_animating_(false),
+ target_surface_transforms_are_animating_(false),
+ screen_space_transforms_are_animating_(false),
+ is_clipped_(false),
+ nearest_ancestor_that_moves_pixels_(NULL) {}
+
+RenderSurface::~RenderSurface() {}
+
+gfx::RectF RenderSurface::DrawableContentRect() const {
+ gfx::RectF drawable_content_rect =
+ MathUtil::MapClippedRect(draw_transform_, content_rect_);
+ if (owning_layer_->has_replica())
+ drawable_content_rect.Union(
+ MathUtil::MapClippedRect(replica_draw_transform_, content_rect_));
+ return drawable_content_rect;
+}
+
+} // namespace cc
diff --git a/cc/layers/render_surface.h b/cc/layers/render_surface.h
new file mode 100644
index 0000000..4373e34
--- /dev/null
+++ b/cc/layers/render_surface.h
@@ -0,0 +1,139 @@
+// Copyright 2010 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.
+
+
+#ifndef CC_LAYERS_RENDER_SURFACE_H_
+#define CC_LAYERS_RENDER_SURFACE_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "cc/base/cc_export.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/rect_f.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+
+class Layer;
+
+class CC_EXPORT RenderSurface {
+ public:
+ explicit RenderSurface(Layer* owning_layer);
+ ~RenderSurface();
+
+ // Returns the rect that encloses the RenderSurfaceImpl including any
+ // reflection.
+ gfx::RectF DrawableContentRect() const;
+
+ void SetContentRect(gfx::Rect content_rect) { content_rect_ = content_rect; }
+ gfx::Rect content_rect() const { return content_rect_; }
+
+ void SetDrawOpacity(float opacity) { draw_opacity_ = opacity; }
+ float draw_opacity() const { return draw_opacity_; }
+
+ void SetDrawOpacityIsAnimating(bool draw_opacity_is_animating) {
+ draw_opacity_is_animating_ = draw_opacity_is_animating;
+ }
+ bool draw_opacity_is_animating() const { return draw_opacity_is_animating_; }
+
+ void SetDrawTransform(const gfx::Transform& draw_transform) {
+ draw_transform_ = draw_transform;
+ }
+ const gfx::Transform& draw_transform() const { return draw_transform_; }
+
+ void SetScreenSpaceTransform(const gfx::Transform& screen_space_transform) {
+ screen_space_transform_ = screen_space_transform;
+ }
+ const gfx::Transform& screen_space_transform() const {
+ return screen_space_transform_;
+ }
+
+ void SetReplicaDrawTransform(const gfx::Transform& replica_draw_transform) {
+ replica_draw_transform_ = replica_draw_transform;
+ }
+ const gfx::Transform& replica_draw_transform() const {
+ return replica_draw_transform_;
+ }
+
+ void SetReplicaScreenSpaceTransform(
+ const gfx::Transform& replica_screen_space_transform) {
+ replica_screen_space_transform_ = replica_screen_space_transform;
+ }
+ const gfx::Transform& replica_screen_space_transform() const {
+ return replica_screen_space_transform_;
+ }
+
+ void SetTargetSurfaceTransformsAreAnimating(bool animating) {
+ target_surface_transforms_are_animating_ = animating;
+ }
+ bool target_surface_transforms_are_animating() const {
+ return target_surface_transforms_are_animating_;
+ }
+ void SetScreenSpaceTransformsAreAnimating(bool animating) {
+ screen_space_transforms_are_animating_ = animating;
+ }
+ bool screen_space_transforms_are_animating() const {
+ return screen_space_transforms_are_animating_;
+ }
+
+ bool is_clipped() const { return is_clipped_; }
+ void SetIsClipped(bool is_clipped) { is_clipped_ = is_clipped; }
+
+ gfx::Rect clip_rect() const { return clip_rect_; }
+ void SetClipRect(gfx::Rect clip_rect) { clip_rect_ = clip_rect; }
+
+ typedef std::vector<scoped_refptr<Layer> > LayerList;
+ LayerList& layer_list() { return layer_list_; }
+ // A no-op since DelegatedRendererLayers on the main thread don't have any
+ // RenderPasses so they can't contribute to a surface.
+ void AddContributingDelegatedRenderPassLayer(Layer* layer) {}
+ void ClearLayerLists() { layer_list_.clear(); }
+
+ void SetNearestAncestorThatMovesPixels(RenderSurface* surface) {
+ nearest_ancestor_that_moves_pixels_ = surface;
+ }
+ const RenderSurface* nearest_ancestor_that_moves_pixels() const {
+ return nearest_ancestor_that_moves_pixels_;
+ }
+
+ private:
+ friend struct LayerIteratorActions;
+
+ Layer* owning_layer_;
+
+ // Uses this surface's space.
+ gfx::Rect content_rect_;
+
+ float draw_opacity_;
+ bool draw_opacity_is_animating_;
+ gfx::Transform draw_transform_;
+ gfx::Transform screen_space_transform_;
+ gfx::Transform replica_draw_transform_;
+ gfx::Transform replica_screen_space_transform_;
+ bool target_surface_transforms_are_animating_;
+ bool screen_space_transforms_are_animating_;
+
+ bool is_clipped_;
+
+ // Uses the space of the surface's target surface.
+ gfx::Rect clip_rect_;
+
+ LayerList layer_list_;
+
+ // The nearest ancestor target surface that will contain the contents of this
+ // surface, and that is going to move pixels within the surface (such as with
+ // a blur). This can point to itself.
+ RenderSurface* nearest_ancestor_that_moves_pixels_;
+
+ // For LayerIteratorActions
+ int target_render_surface_layer_index_history_;
+ int current_layer_index_history_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderSurface);
+};
+
+}
+#endif // CC_LAYERS_RENDER_SURFACE_H_
diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc
new file mode 100644
index 0000000..b437b2c
--- /dev/null
+++ b/cc/layers/render_surface_impl.cc
@@ -0,0 +1,290 @@
+// Copyright 2011 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 "cc/layers/render_surface_impl.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "cc/base/math_util.h"
+#include "cc/debug/debug_colors.h"
+#include "cc/layers/delegated_renderer_layer_impl.h"
+#include "cc/layers/layer_impl.h"
+#include "cc/layers/quad_sink.h"
+#include "cc/layers/render_pass_sink.h"
+#include "cc/quads/debug_border_draw_quad.h"
+#include "cc/quads/render_pass.h"
+#include "cc/quads/render_pass_draw_quad.h"
+#include "cc/quads/shared_quad_state.h"
+#include "cc/trees/damage_tracker.h"
+#include "third_party/skia/include/core/SkImageFilter.h"
+#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+
+RenderSurfaceImpl::RenderSurfaceImpl(LayerImpl* owning_layer)
+ : owning_layer_(owning_layer),
+ surface_property_changed_(false),
+ draw_opacity_(1),
+ draw_opacity_is_animating_(false),
+ target_surface_transforms_are_animating_(false),
+ screen_space_transforms_are_animating_(false),
+ is_clipped_(false),
+ nearest_ancestor_that_moves_pixels_(NULL),
+ target_render_surface_layer_index_history_(0),
+ current_layer_index_history_(0) {
+ damage_tracker_ = DamageTracker::Create();
+}
+
+RenderSurfaceImpl::~RenderSurfaceImpl() {}
+
+gfx::RectF RenderSurfaceImpl::DrawableContentRect() const {
+ gfx::RectF drawable_content_rect =
+ MathUtil::MapClippedRect(draw_transform_, content_rect_);
+ if (owning_layer_->has_replica()) {
+ drawable_content_rect.Union(
+ MathUtil::MapClippedRect(replica_draw_transform_, content_rect_));
+ }
+
+ return drawable_content_rect;
+}
+
+std::string RenderSurfaceImpl::Name() const {
+ return base::StringPrintf("RenderSurfaceImpl(id=%i,owner=%s)",
+ owning_layer_->id(),
+ owning_layer_->debug_name().data());
+}
+
+static std::string IndentString(int indent) {
+ std::string str;
+ for (int i = 0; i != indent; ++i)
+ str.append(" ");
+ return str;
+}
+
+void RenderSurfaceImpl::DumpSurface(std::string* str, int indent) const {
+ std::string indent_str = IndentString(indent);
+ str->append(indent_str);
+ base::StringAppendF(str, "%s\n", Name().data());
+
+ indent_str.append(" ");
+ str->append(indent_str);
+ base::StringAppendF(str,
+ "content_rect: (%d, %d, %d, %d)\n",
+ content_rect_.x(),
+ content_rect_.y(),
+ content_rect_.width(),
+ content_rect_.height());
+
+ str->append(indent_str);
+ base::StringAppendF(str,
+ "draw_transform: "
+ "%f, %f, %f, %f, "
+ "%f, %f, %f, %f, "
+ "%f, %f, %f, %f, "
+ "%f, %f, %f, %f\n",
+ draw_transform_.matrix().getDouble(0, 0),
+ draw_transform_.matrix().getDouble(0, 1),
+ draw_transform_.matrix().getDouble(0, 2),
+ draw_transform_.matrix().getDouble(0, 3),
+ draw_transform_.matrix().getDouble(1, 0),
+ draw_transform_.matrix().getDouble(1, 1),
+ draw_transform_.matrix().getDouble(1, 2),
+ draw_transform_.matrix().getDouble(1, 3),
+ draw_transform_.matrix().getDouble(2, 0),
+ draw_transform_.matrix().getDouble(2, 1),
+ draw_transform_.matrix().getDouble(2, 2),
+ draw_transform_.matrix().getDouble(2, 3),
+ draw_transform_.matrix().getDouble(3, 0),
+ draw_transform_.matrix().getDouble(3, 1),
+ draw_transform_.matrix().getDouble(3, 2),
+ draw_transform_.matrix().getDouble(3, 3));
+
+ str->append(indent_str);
+ base::StringAppendF(str,
+ "damageRect is pos(%f, %f), size(%f, %f)\n",
+ damage_tracker_->current_damage_rect().x(),
+ damage_tracker_->current_damage_rect().y(),
+ damage_tracker_->current_damage_rect().width(),
+ damage_tracker_->current_damage_rect().height());
+}
+
+int RenderSurfaceImpl::OwningLayerId() const {
+ return owning_layer_ ? owning_layer_->id() : 0;
+}
+
+
+void RenderSurfaceImpl::SetClipRect(gfx::Rect clip_rect) {
+ if (clip_rect_ == clip_rect)
+ return;
+
+ surface_property_changed_ = true;
+ clip_rect_ = clip_rect;
+}
+
+bool RenderSurfaceImpl::ContentsChanged() const {
+ return !damage_tracker_->current_damage_rect().IsEmpty();
+}
+
+void RenderSurfaceImpl::SetContentRect(gfx::Rect content_rect) {
+ if (content_rect_ == content_rect)
+ return;
+
+ surface_property_changed_ = true;
+ content_rect_ = content_rect;
+}
+
+bool RenderSurfaceImpl::SurfacePropertyChanged() const {
+ // Surface property changes are tracked as follows:
+ //
+ // - surface_property_changed_ is flagged when the clip_rect or content_rect
+ // change. As of now, these are the only two properties that can be affected
+ // by descendant layers.
+ //
+ // - all other property changes come from the owning layer (or some ancestor
+ // layer that propagates its change to the owning layer).
+ //
+ DCHECK(owning_layer_);
+ return surface_property_changed_ || owning_layer_->LayerPropertyChanged();
+}
+
+bool RenderSurfaceImpl::SurfacePropertyChangedOnlyFromDescendant() const {
+ return surface_property_changed_ && !owning_layer_->LayerPropertyChanged();
+}
+
+void RenderSurfaceImpl::AddContributingDelegatedRenderPassLayer(
+ LayerImpl* layer) {
+ DCHECK(std::find(layer_list_.begin(), layer_list_.end(), layer) !=
+ layer_list_.end());
+ DelegatedRendererLayerImpl* delegated_renderer_layer =
+ static_cast<DelegatedRendererLayerImpl*>(layer);
+ contributing_delegated_render_pass_layer_list_.push_back(
+ delegated_renderer_layer);
+}
+
+void RenderSurfaceImpl::ClearLayerLists() {
+ layer_list_.clear();
+ contributing_delegated_render_pass_layer_list_.clear();
+}
+
+RenderPass::Id RenderSurfaceImpl::RenderPassId() {
+ int layer_id = owning_layer_->id();
+ int sub_id = 0;
+ DCHECK_GT(layer_id, 0);
+ return RenderPass::Id(layer_id, sub_id);
+}
+
+void RenderSurfaceImpl::AppendRenderPasses(RenderPassSink* pass_sink) {
+ for (size_t i = 0;
+ i < contributing_delegated_render_pass_layer_list_.size();
+ ++i) {
+ DelegatedRendererLayerImpl* delegated_renderer_layer =
+ contributing_delegated_render_pass_layer_list_[i];
+ delegated_renderer_layer->AppendContributingRenderPasses(pass_sink);
+ }
+
+ scoped_ptr<RenderPass> pass = RenderPass::Create();
+ pass->SetNew(RenderPassId(),
+ content_rect_,
+ damage_tracker_->current_damage_rect(),
+ screen_space_transform_);
+ pass_sink->AppendRenderPass(pass.Pass());
+}
+
+void RenderSurfaceImpl::AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data,
+ bool for_replica,
+ RenderPass::Id render_pass_id) {
+ DCHECK(!for_replica || owning_layer_->has_replica());
+
+ const gfx::Transform& draw_transform =
+ for_replica ? replica_draw_transform_ : draw_transform_;
+ SharedQuadState* shared_quad_state =
+ quad_sink->UseSharedQuadState(SharedQuadState::Create());
+ shared_quad_state->SetAll(draw_transform,
+ content_rect_.size(),
+ content_rect_,
+ clip_rect_,
+ is_clipped_,
+ draw_opacity_);
+
+ if (owning_layer_->ShowDebugBorders()) {
+ SkColor color = for_replica ?
+ DebugColors::SurfaceReplicaBorderColor() :
+ DebugColors::SurfaceBorderColor();
+ float width = for_replica ?
+ DebugColors::SurfaceReplicaBorderWidth(
+ owning_layer_->layer_tree_impl()) :
+ DebugColors::SurfaceBorderWidth(
+ owning_layer_->layer_tree_impl());
+ scoped_ptr<DebugBorderDrawQuad> debug_border_quad =
+ DebugBorderDrawQuad::Create();
+ debug_border_quad->SetNew(shared_quad_state, content_rect_, color, width);
+ quad_sink->Append(debug_border_quad.PassAs<DrawQuad>(), append_quads_data);
+ }
+
+ // FIXME: By using the same RenderSurfaceImpl for both the content and its
+ // reflection, it's currently not possible to apply a separate mask to the
+ // reflection layer or correctly handle opacity in reflections (opacity must
+ // be applied after drawing both the layer and its reflection). The solution
+ // is to introduce yet another RenderSurfaceImpl to draw the layer and its
+ // reflection in. For now we only apply a separate reflection mask if the
+ // contents don't have a mask of their own.
+ LayerImpl* mask_layer = owning_layer_->mask_layer();
+ if (mask_layer &&
+ (!mask_layer->DrawsContent() || mask_layer->bounds().IsEmpty()))
+ mask_layer = NULL;
+
+ if (!mask_layer && for_replica) {
+ mask_layer = owning_layer_->replica_layer()->mask_layer();
+ if (mask_layer &&
+ (!mask_layer->DrawsContent() || mask_layer->bounds().IsEmpty()))
+ mask_layer = NULL;
+ }
+
+ gfx::RectF mask_uv_rect(0.f, 0.f, 1.f, 1.f);
+ if (mask_layer) {
+ gfx::Vector2dF owning_layer_draw_scale =
+ MathUtil::ComputeTransform2dScaleComponents(
+ owning_layer_->draw_transform(), 1.f);
+ gfx::SizeF unclipped_surface_size = gfx::ScaleSize(
+ owning_layer_->content_bounds(),
+ owning_layer_draw_scale.x(),
+ owning_layer_draw_scale.y());
+ // This assumes that the owning layer clips its subtree when a mask is
+ // present.
+ DCHECK(gfx::RectF(unclipped_surface_size).Contains(content_rect_));
+
+ float uv_scale_x = content_rect_.width() / unclipped_surface_size.width();
+ float uv_scale_y = content_rect_.height() / unclipped_surface_size.height();
+
+ mask_uv_rect = gfx::RectF(
+ uv_scale_x * content_rect_.x() / content_rect_.width(),
+ uv_scale_y * content_rect_.y() / content_rect_.height(),
+ uv_scale_x,
+ uv_scale_y);
+ }
+
+ ResourceProvider::ResourceId mask_resource_id =
+ mask_layer ? mask_layer->ContentsResourceId() : 0;
+ gfx::Rect contents_changed_since_last_frame =
+ ContentsChanged() ? content_rect_ : gfx::Rect();
+
+ scoped_ptr<RenderPassDrawQuad> quad = RenderPassDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ content_rect_,
+ render_pass_id,
+ for_replica,
+ mask_resource_id,
+ contents_changed_since_last_frame,
+ mask_uv_rect,
+ owning_layer_->filters(),
+ owning_layer_->filter(),
+ owning_layer_->background_filters());
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+}
+
+} // namespace cc
diff --git a/cc/layers/render_surface_impl.h b/cc/layers/render_surface_impl.h
new file mode 100644
index 0000000..1466bca
--- /dev/null
+++ b/cc/layers/render_surface_impl.h
@@ -0,0 +1,171 @@
+// Copyright 2011 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.
+
+#ifndef CC_LAYERS_RENDER_SURFACE_IMPL_H_
+#define CC_LAYERS_RENDER_SURFACE_IMPL_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/quads/render_pass.h"
+#include "cc/quads/shared_quad_state.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/rect_f.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+
+class DamageTracker;
+class DelegatedRendererLayerImpl;
+class QuadSink;
+class RenderPassSink;
+class LayerImpl;
+
+struct AppendQuadsData;
+
+class CC_EXPORT RenderSurfaceImpl {
+ public:
+ explicit RenderSurfaceImpl(LayerImpl* owning_layer);
+ virtual ~RenderSurfaceImpl();
+
+ std::string Name() const;
+ void DumpSurface(std::string* str, int indent) const;
+
+ gfx::PointF ContentRectCenter() const {
+ return gfx::RectF(content_rect_).CenterPoint();
+ }
+
+ // Returns the rect that encloses the RenderSurfaceImpl including any
+ // reflection.
+ gfx::RectF DrawableContentRect() const;
+
+ void SetDrawOpacity(float opacity) { draw_opacity_ = opacity; }
+ float draw_opacity() const { return draw_opacity_; }
+
+ void SetNearestAncestorThatMovesPixels(RenderSurfaceImpl* surface) {
+ nearest_ancestor_that_moves_pixels_ = surface;
+ }
+ const RenderSurfaceImpl* nearest_ancestor_that_moves_pixels() const {
+ return nearest_ancestor_that_moves_pixels_;
+ }
+
+ void SetDrawOpacityIsAnimating(bool draw_opacity_is_animating) {
+ draw_opacity_is_animating_ = draw_opacity_is_animating;
+ }
+ bool draw_opacity_is_animating() const { return draw_opacity_is_animating_; }
+
+ void SetDrawTransform(const gfx::Transform& draw_transform) {
+ draw_transform_ = draw_transform;
+ }
+ const gfx::Transform& draw_transform() const { return draw_transform_; }
+
+ void SetScreenSpaceTransform(const gfx::Transform& screen_space_transform) {
+ screen_space_transform_ = screen_space_transform;
+ }
+ const gfx::Transform& screen_space_transform() const {
+ return screen_space_transform_;
+ }
+
+ void SetReplicaDrawTransform(const gfx::Transform& replica_draw_transform) {
+ replica_draw_transform_ = replica_draw_transform;
+ }
+ const gfx::Transform& replica_draw_transform() const {
+ return replica_draw_transform_;
+ }
+
+ void SetReplicaScreenSpaceTransform(
+ const gfx::Transform& replica_screen_space_transform) {
+ replica_screen_space_transform_ = replica_screen_space_transform;
+ }
+ const gfx::Transform& replica_screen_space_transform() const {
+ return replica_screen_space_transform_;
+ }
+
+ void SetTargetSurfaceTransformsAreAnimating(bool animating) {
+ target_surface_transforms_are_animating_ = animating;
+ }
+ bool target_surface_transforms_are_animating() const {
+ return target_surface_transforms_are_animating_;
+ }
+ void SetScreenSpaceTransformsAreAnimating(bool animating) {
+ screen_space_transforms_are_animating_ = animating;
+ }
+ bool screen_space_transforms_are_animating() const {
+ return screen_space_transforms_are_animating_;
+ }
+
+ void SetIsClipped(bool is_clipped) { is_clipped_ = is_clipped; }
+ bool is_clipped() const { return is_clipped_; }
+
+ void SetClipRect(gfx::Rect clip_rect);
+ gfx::Rect clip_rect() const { return clip_rect_; }
+
+ bool ContentsChanged() const;
+
+ void SetContentRect(gfx::Rect content_rect);
+ gfx::Rect content_rect() const { return content_rect_; }
+
+ std::vector<LayerImpl*>& layer_list() { return layer_list_; }
+ void AddContributingDelegatedRenderPassLayer(LayerImpl* layer);
+ void ClearLayerLists();
+
+ int OwningLayerId() const;
+
+ void ResetPropertyChangedFlag() { surface_property_changed_ = false; }
+ bool SurfacePropertyChanged() const;
+ bool SurfacePropertyChangedOnlyFromDescendant() const;
+
+ DamageTracker* damage_tracker() const { return damage_tracker_.get(); }
+
+ RenderPass::Id RenderPassId();
+
+ void AppendRenderPasses(RenderPassSink* pass_sink);
+ void AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data,
+ bool for_replica,
+ RenderPass::Id render_pass_id);
+
+ private:
+ LayerImpl* owning_layer_;
+
+ // Uses this surface's space.
+ gfx::Rect content_rect_;
+ bool surface_property_changed_;
+
+ float draw_opacity_;
+ bool draw_opacity_is_animating_;
+ gfx::Transform draw_transform_;
+ gfx::Transform screen_space_transform_;
+ gfx::Transform replica_draw_transform_;
+ gfx::Transform replica_screen_space_transform_;
+ bool target_surface_transforms_are_animating_;
+ bool screen_space_transforms_are_animating_;
+
+ bool is_clipped_;
+
+ // Uses the space of the surface's target surface.
+ gfx::Rect clip_rect_;
+
+ std::vector<LayerImpl*> layer_list_;
+ std::vector<DelegatedRendererLayerImpl*>
+ contributing_delegated_render_pass_layer_list_;
+
+ // The nearest ancestor target surface that will contain the contents of this
+ // surface, and that is going to move pixels within the surface (such as with
+ // a blur). This can point to itself.
+ RenderSurfaceImpl* nearest_ancestor_that_moves_pixels_;
+
+ scoped_ptr<DamageTracker> damage_tracker_;
+
+ // For LayerIteratorActions
+ int target_render_surface_layer_index_history_;
+ int current_layer_index_history_;
+
+ friend struct LayerIteratorActions;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderSurfaceImpl);
+};
+
+}
+#endif // CC_LAYERS_RENDER_SURFACE_IMPL_H_
diff --git a/cc/layers/render_surface_unittest.cc b/cc/layers/render_surface_unittest.cc
new file mode 100644
index 0000000..30d9480
--- /dev/null
+++ b/cc/layers/render_surface_unittest.cc
@@ -0,0 +1,159 @@
+// Copyright 2011 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 "cc/base/scoped_ptr_vector.h"
+#include "cc/layers/append_quads_data.h"
+#include "cc/layers/layer_impl.h"
+#include "cc/layers/render_pass_sink.h"
+#include "cc/layers/render_surface_impl.h"
+#include "cc/quads/shared_quad_state.h"
+#include "cc/test/fake_impl_proxy.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/geometry_test_utils.h"
+#include "cc/test/mock_quad_culler.h"
+#include "cc/trees/single_thread_proxy.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+namespace {
+
+#define EXECUTE_AND_VERIFY_SURFACE_CHANGED(codeToTest) \
+ renderSurface->ResetPropertyChangedFlag(); \
+ codeToTest; \
+ EXPECT_TRUE(renderSurface->SurfacePropertyChanged())
+
+#define EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(codeToTest) \
+ renderSurface->ResetPropertyChangedFlag(); \
+ codeToTest; \
+ EXPECT_FALSE(renderSurface->SurfacePropertyChanged())
+
+TEST(RenderSurfaceTest, verifySurfaceChangesAreTrackedProperly)
+{
+ //
+ // This test checks that SurfacePropertyChanged() has the correct behavior.
+ //
+
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+ scoped_ptr<LayerImpl> owningLayer = LayerImpl::Create(hostImpl.active_tree(), 1);
+ owningLayer->CreateRenderSurface();
+ ASSERT_TRUE(owningLayer->render_surface());
+ RenderSurfaceImpl* renderSurface = owningLayer->render_surface();
+ gfx::Rect testRect = gfx::Rect(gfx::Point(3, 4), gfx::Size(5, 6));
+ owningLayer->ResetAllChangeTrackingForSubtree();
+
+ // Currently, the contentRect, clipRect, and owningLayer->layerPropertyChanged() are
+ // the only sources of change.
+ EXECUTE_AND_VERIFY_SURFACE_CHANGED(renderSurface->SetClipRect(testRect));
+ EXECUTE_AND_VERIFY_SURFACE_CHANGED(renderSurface->SetContentRect(testRect));
+
+ owningLayer->SetOpacity(0.5f);
+ EXPECT_TRUE(renderSurface->SurfacePropertyChanged());
+ owningLayer->ResetAllChangeTrackingForSubtree();
+
+ // Setting the surface properties to the same values again should not be considered "change".
+ EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(renderSurface->SetClipRect(testRect));
+ EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(renderSurface->SetContentRect(testRect));
+
+ scoped_ptr<LayerImpl> dummyMask = LayerImpl::Create(hostImpl.active_tree(), 2);
+ gfx::Transform dummyMatrix;
+ dummyMatrix.Translate(1.0, 2.0);
+
+ // The rest of the surface properties are either internal and should not cause change,
+ // or they are already accounted for by the owninglayer->layerPropertyChanged().
+ EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(renderSurface->SetDrawOpacity(0.5f));
+ EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(renderSurface->SetDrawTransform(dummyMatrix));
+ EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(renderSurface->SetReplicaDrawTransform(dummyMatrix));
+ EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(renderSurface->ClearLayerLists());
+}
+
+TEST(RenderSurfaceTest, sanityCheckSurfaceCreatesCorrectSharedQuadState)
+{
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+ scoped_ptr<LayerImpl> rootLayer = LayerImpl::Create(hostImpl.active_tree(), 1);
+
+ scoped_ptr<LayerImpl> owningLayer = LayerImpl::Create(hostImpl.active_tree(), 2);
+ owningLayer->CreateRenderSurface();
+ ASSERT_TRUE(owningLayer->render_surface());
+ owningLayer->draw_properties().render_target = owningLayer.get();
+ RenderSurfaceImpl* renderSurface = owningLayer->render_surface();
+
+ rootLayer->AddChild(owningLayer.Pass());
+
+ gfx::Rect contentRect = gfx::Rect(gfx::Point(), gfx::Size(50, 50));
+ gfx::Rect clipRect = gfx::Rect(gfx::Point(5, 5), gfx::Size(40, 40));
+ gfx::Transform origin;
+
+ origin.Translate(30, 40);
+
+ renderSurface->SetDrawTransform(origin);
+ renderSurface->SetContentRect(contentRect);
+ renderSurface->SetClipRect(clipRect);
+ renderSurface->SetDrawOpacity(1.f);
+
+ QuadList quadList;
+ SharedQuadStateList sharedStateList;
+ MockQuadCuller mockQuadCuller(quadList, sharedStateList);
+ AppendQuadsData appendQuadsData;
+
+ bool forReplica = false;
+ renderSurface->AppendQuads(&mockQuadCuller, &appendQuadsData, forReplica, RenderPass::Id(2, 0));
+
+ ASSERT_EQ(1u, sharedStateList.size());
+ SharedQuadState* sharedQuadState = sharedStateList[0];
+
+ EXPECT_EQ(30, sharedQuadState->content_to_target_transform.matrix().getDouble(0, 3));
+ EXPECT_EQ(40, sharedQuadState->content_to_target_transform.matrix().getDouble(1, 3));
+ EXPECT_RECT_EQ(contentRect, gfx::Rect(sharedQuadState->visible_content_rect));
+ EXPECT_EQ(1, sharedQuadState->opacity);
+}
+
+class TestRenderPassSink : public RenderPassSink {
+public:
+ virtual void AppendRenderPass(scoped_ptr<RenderPass> renderPass) OVERRIDE { m_renderPasses.push_back(renderPass.Pass()); }
+
+ const ScopedPtrVector<RenderPass>& renderPasses() const { return m_renderPasses; }
+
+private:
+ ScopedPtrVector<RenderPass> m_renderPasses;
+};
+
+TEST(RenderSurfaceTest, sanityCheckSurfaceCreatesCorrectRenderPass)
+{
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+ scoped_ptr<LayerImpl> rootLayer = LayerImpl::Create(hostImpl.active_tree(), 1);
+
+ scoped_ptr<LayerImpl> owningLayer = LayerImpl::Create(hostImpl.active_tree(), 2);
+ owningLayer->CreateRenderSurface();
+ ASSERT_TRUE(owningLayer->render_surface());
+ owningLayer->draw_properties().render_target = owningLayer.get();
+ RenderSurfaceImpl* renderSurface = owningLayer->render_surface();
+
+ rootLayer->AddChild(owningLayer.Pass());
+
+ gfx::Rect contentRect = gfx::Rect(gfx::Point(), gfx::Size(50, 50));
+ gfx::Transform origin;
+ origin.Translate(30, 40);
+
+ renderSurface->SetScreenSpaceTransform(origin);
+ renderSurface->SetContentRect(contentRect);
+
+ TestRenderPassSink passSink;
+
+ renderSurface->AppendRenderPasses(&passSink);
+
+ ASSERT_EQ(1u, passSink.renderPasses().size());
+ RenderPass* pass = passSink.renderPasses()[0];
+
+ EXPECT_EQ(RenderPass::Id(2, 0), pass->id);
+ EXPECT_RECT_EQ(contentRect, pass->output_rect);
+ EXPECT_EQ(origin, pass->transform_to_root_target);
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/scrollbar_geometry_fixed_thumb.cc b/cc/layers/scrollbar_geometry_fixed_thumb.cc
new file mode 100644
index 0000000..07c1cb5
--- /dev/null
+++ b/cc/layers/scrollbar_geometry_fixed_thumb.cc
@@ -0,0 +1,83 @@
+// Copyright 2012 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 "cc/layers/scrollbar_geometry_fixed_thumb.h"
+
+#include <cmath>
+
+#include "third_party/WebKit/Source/Platform/chromium/public/WebRect.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebScrollbar.h"
+
+using WebKit::WebRect;
+using WebKit::WebScrollbar;
+using WebKit::WebScrollbarThemeGeometry;
+
+namespace cc {
+
+scoped_ptr<ScrollbarGeometryFixedThumb> ScrollbarGeometryFixedThumb::create(scoped_ptr<WebScrollbarThemeGeometry> geometry)
+{
+ return make_scoped_ptr(new ScrollbarGeometryFixedThumb(geometry.Pass()));
+}
+
+ScrollbarGeometryFixedThumb::~ScrollbarGeometryFixedThumb()
+{
+}
+
+WebScrollbarThemeGeometry* ScrollbarGeometryFixedThumb::clone() const
+{
+ ScrollbarGeometryFixedThumb* geometry = new ScrollbarGeometryFixedThumb(make_scoped_ptr(ScrollbarGeometryStub::clone()));
+ geometry->m_thumbSize = m_thumbSize;
+ return geometry;
+}
+
+int ScrollbarGeometryFixedThumb::thumbLength(WebScrollbar* scrollbar)
+{
+ if (scrollbar->orientation() == WebScrollbar::Horizontal)
+ return m_thumbSize.width();
+ return m_thumbSize.height();
+}
+
+int ScrollbarGeometryFixedThumb::thumbPosition(WebScrollbar* scrollbar)
+{
+ if (scrollbar->enabled()) {
+ float size = scrollbar->maximum();
+ if (!size)
+ return 1;
+ int value = std::min(std::max(0, scrollbar->value()), scrollbar->maximum());
+ float pos = (trackLength(scrollbar) - thumbLength(scrollbar)) * value / size;
+ return static_cast<int>(floorf((pos < 1 && pos > 0) ? 1 : pos));
+ }
+ return 0;
+}
+void ScrollbarGeometryFixedThumb::splitTrack(WebScrollbar* scrollbar, const WebRect& unconstrainedTrackRect, WebRect& beforeThumbRect, WebRect& thumbRect, WebRect& afterThumbRect)
+{
+ // This is a reimplementation of ScrollbarThemeComposite::splitTrack.
+ // Because the WebScrollbarThemeGeometry functions call down to native
+ // ScrollbarThemeComposite code which uses ScrollbarThemeComposite virtual
+ // helpers, there's no way to override a helper like thumbLength from
+ // the WebScrollbarThemeGeometry level. So, these three functions
+ // (splitTrack, thumbPosition, thumbLength) are copied here so that the
+ // WebScrollbarThemeGeometry helper functions are used instead and
+ // a fixed size thumbLength can be used.
+
+ WebRect trackRect = constrainTrackRectToTrackPieces(scrollbar, unconstrainedTrackRect);
+ int thickness = scrollbar->orientation() == WebScrollbar::Horizontal ? scrollbar->size().height : scrollbar->size().width;
+ int thumbPos = thumbPosition(scrollbar);
+ if (scrollbar->orientation() == WebScrollbar::Horizontal) {
+ thumbRect = WebRect(trackRect.x + thumbPos, trackRect.y + (trackRect.height - thickness) / 2, thumbLength(scrollbar), thickness);
+ beforeThumbRect = WebRect(trackRect.x, trackRect.y, thumbPos + thumbRect.width / 2, trackRect.height);
+ afterThumbRect = WebRect(trackRect.x + beforeThumbRect.width, trackRect.y, trackRect.x + trackRect.width - beforeThumbRect.x - beforeThumbRect.width, trackRect.height);
+ } else {
+ thumbRect = WebRect(trackRect.x + (trackRect.width - thickness) / 2, trackRect.y + thumbPos, thickness, thumbLength(scrollbar));
+ beforeThumbRect = WebRect(trackRect.x, trackRect.y, trackRect.width, thumbPos + thumbRect.height / 2);
+ afterThumbRect = WebRect(trackRect.x, trackRect.y + beforeThumbRect.height, trackRect.width, trackRect.y + trackRect.height - beforeThumbRect.y - beforeThumbRect.height);
+ }
+}
+
+ScrollbarGeometryFixedThumb::ScrollbarGeometryFixedThumb(scoped_ptr<WebScrollbarThemeGeometry> geometry)
+ : ScrollbarGeometryStub(geometry.Pass())
+{
+}
+
+} // namespace cc
diff --git a/cc/layers/scrollbar_geometry_fixed_thumb.h b/cc/layers/scrollbar_geometry_fixed_thumb.h
new file mode 100644
index 0000000..f941104
--- /dev/null
+++ b/cc/layers/scrollbar_geometry_fixed_thumb.h
@@ -0,0 +1,39 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_SCROLLBAR_GEOMETRY_FIXED_THUMB_H_
+#define CC_LAYERS_SCROLLBAR_GEOMETRY_FIXED_THUMB_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/scrollbar_geometry_stub.h"
+#include "ui/gfx/size.h"
+
+namespace cc {
+
+// This scrollbar geometry class behaves exactly like a normal geometry except
+// it always returns a fixed thumb length. This allows a page to zoom (changing
+// the total size of the scrollable area, changing the thumb length) while not
+// requiring the thumb resource to be repainted.
+class CC_EXPORT ScrollbarGeometryFixedThumb : public ScrollbarGeometryStub {
+public:
+ static scoped_ptr<ScrollbarGeometryFixedThumb> create(scoped_ptr<WebKit::WebScrollbarThemeGeometry>);
+ virtual ~ScrollbarGeometryFixedThumb();
+
+ void setThumbSize(gfx::Size size) { m_thumbSize = size; }
+
+ // WebScrollbarThemeGeometry interface
+ virtual WebKit::WebScrollbarThemeGeometry* clone() const OVERRIDE;
+ virtual int thumbLength(WebKit::WebScrollbar*) OVERRIDE;
+ virtual int thumbPosition(WebKit::WebScrollbar*) OVERRIDE;
+ virtual void splitTrack(WebKit::WebScrollbar*, const WebKit::WebRect& track, WebKit::WebRect& startTrack, WebKit::WebRect& thumb, WebKit::WebRect& endTrack) OVERRIDE;
+
+private:
+ explicit ScrollbarGeometryFixedThumb(scoped_ptr<WebKit::WebScrollbarThemeGeometry>);
+
+ gfx::Size m_thumbSize;
+};
+
+}
+
+#endif // CC_LAYERS_SCROLLBAR_GEOMETRY_FIXED_THUMB_H_
diff --git a/cc/layers/scrollbar_geometry_stub.cc b/cc/layers/scrollbar_geometry_stub.cc
new file mode 100644
index 0000000..fa7b1da
--- /dev/null
+++ b/cc/layers/scrollbar_geometry_stub.cc
@@ -0,0 +1,109 @@
+// Copyright 2012 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 "cc/layers/scrollbar_geometry_stub.h"
+
+#include <cmath>
+
+using WebKit::WebRect;
+using WebKit::WebScrollbar;
+using WebKit::WebScrollbarThemeGeometry;
+
+namespace cc {
+
+ScrollbarGeometryStub::ScrollbarGeometryStub(scoped_ptr<WebScrollbarThemeGeometry> geometry)
+ : m_geometry(geometry.Pass())
+{
+}
+
+ScrollbarGeometryStub::~ScrollbarGeometryStub()
+{
+}
+
+WebScrollbarThemeGeometry* ScrollbarGeometryStub::clone() const
+{
+ return m_geometry->clone();
+}
+
+int ScrollbarGeometryStub::thumbPosition(WebScrollbar* scrollbar)
+{
+ return m_geometry->thumbPosition(scrollbar);
+}
+
+int ScrollbarGeometryStub::thumbLength(WebScrollbar* scrollbar)
+{
+ return std::max(0, m_geometry->thumbLength(scrollbar));
+}
+
+int ScrollbarGeometryStub::trackPosition(WebScrollbar* scrollbar)
+{
+ return m_geometry->trackPosition(scrollbar);
+}
+
+int ScrollbarGeometryStub::trackLength(WebScrollbar* scrollbar)
+{
+ return m_geometry->trackLength(scrollbar);
+}
+
+bool ScrollbarGeometryStub::hasButtons(WebScrollbar* scrollbar)
+{
+ return m_geometry->hasButtons(scrollbar);
+}
+
+bool ScrollbarGeometryStub::hasThumb(WebScrollbar* scrollbar)
+{
+ return m_geometry->hasThumb(scrollbar);
+}
+
+WebRect ScrollbarGeometryStub::trackRect(WebScrollbar* scrollbar)
+{
+ return m_geometry->trackRect(scrollbar);
+}
+
+WebRect ScrollbarGeometryStub::thumbRect(WebScrollbar* scrollbar)
+{
+ return m_geometry->thumbRect(scrollbar);
+}
+
+int ScrollbarGeometryStub::minimumThumbLength(WebScrollbar* scrollbar)
+{
+ return m_geometry->minimumThumbLength(scrollbar);
+}
+
+int ScrollbarGeometryStub::scrollbarThickness(WebScrollbar* scrollbar)
+{
+ return m_geometry->scrollbarThickness(scrollbar);
+}
+
+WebRect ScrollbarGeometryStub::backButtonStartRect(WebScrollbar* scrollbar)
+{
+ return m_geometry->backButtonStartRect(scrollbar);
+}
+
+WebRect ScrollbarGeometryStub::backButtonEndRect(WebScrollbar* scrollbar)
+{
+ return m_geometry->backButtonEndRect(scrollbar);
+}
+
+WebRect ScrollbarGeometryStub::forwardButtonStartRect(WebScrollbar* scrollbar)
+{
+ return m_geometry->forwardButtonStartRect(scrollbar);
+}
+
+WebRect ScrollbarGeometryStub::forwardButtonEndRect(WebScrollbar* scrollbar)
+{
+ return m_geometry->forwardButtonEndRect(scrollbar);
+}
+
+WebRect ScrollbarGeometryStub::constrainTrackRectToTrackPieces(WebScrollbar* scrollbar, const WebRect& rect)
+{
+ return m_geometry->constrainTrackRectToTrackPieces(scrollbar, rect);
+}
+
+void ScrollbarGeometryStub::splitTrack(WebScrollbar* scrollbar, const WebRect& unconstrainedTrackRect, WebRect& beforeThumbRect, WebRect& thumbRect, WebRect& afterThumbRect)
+{
+ m_geometry->splitTrack(scrollbar, unconstrainedTrackRect, beforeThumbRect, thumbRect, afterThumbRect);
+}
+
+} // namespace cc
diff --git a/cc/layers/scrollbar_geometry_stub.h b/cc/layers/scrollbar_geometry_stub.h
new file mode 100644
index 0000000..d671475
--- /dev/null
+++ b/cc/layers/scrollbar_geometry_stub.h
@@ -0,0 +1,54 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_SCROLLBAR_GEOMETRY_STUB_H_
+#define CC_LAYERS_SCROLLBAR_GEOMETRY_STUB_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebScrollbarThemeGeometry.h"
+
+namespace cc {
+
+// This subclass wraps an existing scrollbar geometry class so that
+// another class can derive from it and override specific functions, while
+// passing through the remaining ones.
+class CC_EXPORT ScrollbarGeometryStub : public NON_EXPORTED_BASE(WebKit::WebScrollbarThemeGeometry) {
+public:
+ virtual ~ScrollbarGeometryStub();
+
+ // Allow derived classes to update themselves from a scrollbar.
+ void update(WebKit::WebScrollbar*) { }
+
+ // WebScrollbarThemeGeometry interface
+ virtual WebKit::WebScrollbarThemeGeometry* clone() const OVERRIDE;
+ virtual int thumbPosition(WebKit::WebScrollbar*) OVERRIDE;
+ virtual int thumbLength(WebKit::WebScrollbar*) OVERRIDE;
+ virtual int trackPosition(WebKit::WebScrollbar*) OVERRIDE;
+ virtual int trackLength(WebKit::WebScrollbar*) OVERRIDE;
+ virtual bool hasButtons(WebKit::WebScrollbar*) OVERRIDE;
+ virtual bool hasThumb(WebKit::WebScrollbar*) OVERRIDE;
+ virtual WebKit::WebRect trackRect(WebKit::WebScrollbar*) OVERRIDE;
+ virtual WebKit::WebRect thumbRect(WebKit::WebScrollbar*) OVERRIDE;
+ virtual int minimumThumbLength(WebKit::WebScrollbar*) OVERRIDE;
+ virtual int scrollbarThickness(WebKit::WebScrollbar*) OVERRIDE;
+ virtual WebKit::WebRect backButtonStartRect(WebKit::WebScrollbar*) OVERRIDE;
+ virtual WebKit::WebRect backButtonEndRect(WebKit::WebScrollbar*) OVERRIDE;
+ virtual WebKit::WebRect forwardButtonStartRect(WebKit::WebScrollbar*) OVERRIDE;
+ virtual WebKit::WebRect forwardButtonEndRect(WebKit::WebScrollbar*) OVERRIDE;
+ virtual WebKit::WebRect constrainTrackRectToTrackPieces(WebKit::WebScrollbar*, const WebKit::WebRect&) OVERRIDE;
+ virtual void splitTrack(WebKit::WebScrollbar*, const WebKit::WebRect& track, WebKit::WebRect& startTrack, WebKit::WebRect& thumb, WebKit::WebRect& endTrack) OVERRIDE;
+
+protected:
+ explicit ScrollbarGeometryStub(scoped_ptr<WebKit::WebScrollbarThemeGeometry>);
+
+private:
+ scoped_ptr<WebKit::WebScrollbarThemeGeometry> m_geometry;
+
+ DISALLOW_COPY_AND_ASSIGN(ScrollbarGeometryStub);
+};
+
+}
+
+#endif // CC_LAYERS_SCROLLBAR_GEOMETRY_STUB_H_
diff --git a/cc/layers/scrollbar_layer.cc b/cc/layers/scrollbar_layer.cc
new file mode 100644
index 0000000..1550ad9
--- /dev/null
+++ b/cc/layers/scrollbar_layer.cc
@@ -0,0 +1,441 @@
+// Copyright 2012 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 "cc/layers/scrollbar_layer.h"
+
+#include "base/basictypes.h"
+#include "base/debug/trace_event.h"
+#include "cc/layers/scrollbar_layer_impl.h"
+#include "cc/resources/caching_bitmap_content_layer_updater.h"
+#include "cc/resources/layer_painter.h"
+#include "cc/resources/prioritized_resource.h"
+#include "cc/resources/resource_update_queue.h"
+#include "cc/trees/layer_tree_host.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebRect.h"
+#include "ui/gfx/rect_conversions.h"
+
+namespace cc {
+
+scoped_ptr<LayerImpl> ScrollbarLayer::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return ScrollbarLayerImpl::Create(
+ tree_impl,
+ id(),
+ ScrollbarGeometryFixedThumb::create(make_scoped_ptr(geometry_->clone())))
+ .PassAs<LayerImpl>();
+}
+
+scoped_refptr<ScrollbarLayer> ScrollbarLayer::Create(
+ scoped_ptr<WebKit::WebScrollbar> scrollbar,
+ scoped_ptr<ScrollbarThemePainter> painter,
+ scoped_ptr<WebKit::WebScrollbarThemeGeometry> geometry,
+ int scrollLayerId) {
+ return make_scoped_refptr(new ScrollbarLayer(scrollbar.Pass(),
+ painter.Pass(),
+ geometry.Pass(),
+ scrollLayerId));
+}
+
+ScrollbarLayer::ScrollbarLayer(
+ scoped_ptr<WebKit::WebScrollbar> scrollbar,
+ scoped_ptr<ScrollbarThemePainter> painter,
+ scoped_ptr<WebKit::WebScrollbarThemeGeometry> geometry,
+ int scrollLayerId)
+ : scrollbar_(scrollbar.Pass()),
+ painter_(painter.Pass()),
+ geometry_(geometry.Pass()),
+ scroll_layer_id_(scrollLayerId),
+ texture_format_(GL_INVALID_ENUM) {
+ if (!scrollbar_->isOverlay())
+ SetShouldScrollOnMainThread(true);
+}
+
+ScrollbarLayer::~ScrollbarLayer() {}
+
+void ScrollbarLayer::SetScrollLayerId(int id) {
+ if (id == scroll_layer_id_)
+ return;
+
+ scroll_layer_id_ = id;
+ SetNeedsFullTreeSync();
+}
+
+bool ScrollbarLayer::OpacityCanAnimateOnImplThread() const {
+ return scrollbar_->isOverlay();
+}
+
+WebKit::WebScrollbar::Orientation ScrollbarLayer::Orientation() const {
+ return scrollbar_->orientation();
+}
+
+int ScrollbarLayer::MaxTextureSize() {
+ DCHECK(layer_tree_host());
+ return layer_tree_host()->GetRendererCapabilities().max_texture_size;
+}
+
+float ScrollbarLayer::ClampScaleToMaxTextureSize(float scale) {
+ if (layer_tree_host()->settings().solidColorScrollbars)
+ return scale;
+
+ // If the scaled content_bounds() is bigger than the max texture size of the
+ // device, we need to clamp it by rescaling, since content_bounds() is used
+ // below to set the texture size.
+ gfx::Size scaled_bounds = ComputeContentBoundsForScale(scale, scale);
+ if (scaled_bounds.width() > MaxTextureSize() ||
+ scaled_bounds.height() > MaxTextureSize()) {
+ if (scaled_bounds.width() > scaled_bounds.height())
+ return (MaxTextureSize() - 1) / static_cast<float>(bounds().width());
+ else
+ return (MaxTextureSize() - 1) / static_cast<float>(bounds().height());
+ }
+ return scale;
+}
+
+void ScrollbarLayer::CalculateContentsScale(float ideal_contents_scale,
+ bool animating_transform_to_screen,
+ float* contents_scale_x,
+ float* contents_scale_y,
+ gfx::Size* contentBounds) {
+ ContentsScalingLayer::CalculateContentsScale(
+ ClampScaleToMaxTextureSize(ideal_contents_scale),
+ animating_transform_to_screen,
+ contents_scale_x,
+ contents_scale_y,
+ contentBounds);
+ DCHECK_LE(contentBounds->width(), MaxTextureSize());
+ DCHECK_LE(contentBounds->height(), MaxTextureSize());
+}
+
+void ScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
+ ContentsScalingLayer::PushPropertiesTo(layer);
+
+ ScrollbarLayerImpl* scrollbar_layer = static_cast<ScrollbarLayerImpl*>(layer);
+
+ scrollbar_layer->SetScrollbarData(scrollbar_.get());
+ scrollbar_layer->SetThumbSize(thumb_size_);
+
+ if (back_track_ && back_track_->texture()->haveBackingTexture()) {
+ scrollbar_layer->set_back_track_resource_id(
+ back_track_->texture()->resourceId());
+ } else {
+ scrollbar_layer->set_back_track_resource_id(0);
+ }
+
+ if (fore_track_ && fore_track_->texture()->haveBackingTexture()) {
+ scrollbar_layer->set_fore_track_resource_id(
+ fore_track_->texture()->resourceId());
+ } else {
+ scrollbar_layer->set_fore_track_resource_id(0);
+ }
+
+ if (thumb_ && thumb_->texture()->haveBackingTexture())
+ scrollbar_layer->set_thumb_resource_id(thumb_->texture()->resourceId());
+ else
+ scrollbar_layer->set_thumb_resource_id(0);
+
+ // Pinch zoom scrollbarLayerImpl does not get its scroll_layer_id_
+ // set in LayerImpl, so we need to push it here.
+ if (scroll_layer_id_ == Layer::PINCH_ZOOM_ROOT_SCROLL_LAYER_ID)
+ scrollbar_layer->set_scroll_layer_id(scroll_layer_id_);
+}
+
+ScrollbarLayer* ScrollbarLayer::ToScrollbarLayer() {
+ return this;
+}
+
+class ScrollbarBackgroundPainter : public LayerPainter {
+ public:
+ static scoped_ptr<ScrollbarBackgroundPainter> Create(
+ WebKit::WebScrollbar* scrollbar,
+ ScrollbarThemePainter *painter,
+ WebKit::WebScrollbarThemeGeometry* geometry,
+ WebKit::WebScrollbar::ScrollbarPart trackPart) {
+ return make_scoped_ptr(new ScrollbarBackgroundPainter(scrollbar,
+ painter,
+ geometry,
+ trackPart));
+ }
+
+ virtual void Paint(SkCanvas* canvas,
+ gfx::Rect content_rect,
+ gfx::RectF* opaque) OVERRIDE {
+ // The following is a simplification of ScrollbarThemeComposite::paint.
+ painter_->PaintScrollbarBackground(canvas, content_rect);
+
+ if (geometry_->hasButtons(scrollbar_)) {
+ gfx::Rect back_button_start_paint_rect =
+ geometry_->backButtonStartRect(scrollbar_);
+ painter_->PaintBackButtonStart(canvas, back_button_start_paint_rect);
+
+ gfx::Rect back_button_end_paint_rect =
+ geometry_->backButtonEndRect(scrollbar_);
+ painter_->PaintBackButtonEnd(canvas, back_button_end_paint_rect);
+
+ gfx::Rect forward_button_start_paint_rect =
+ geometry_->forwardButtonStartRect(scrollbar_);
+ painter_->PaintForwardButtonStart(canvas,
+ forward_button_start_paint_rect);
+
+ gfx::Rect forward_button_end_paint_rect =
+ geometry_->forwardButtonEndRect(scrollbar_);
+ painter_->PaintForwardButtonEnd(canvas, forward_button_end_paint_rect);
+ }
+
+ gfx::Rect track_paint_rect = geometry_->trackRect(scrollbar_);
+ painter_->PaintTrackBackground(canvas, track_paint_rect);
+
+ bool thumb_present = geometry_->hasThumb(scrollbar_);
+ if (thumb_present) {
+ if (track_part_ == WebKit::WebScrollbar::ForwardTrackPart)
+ painter_->PaintForwardTrackPart(canvas, track_paint_rect);
+ else
+ painter_->PaintBackTrackPart(canvas, track_paint_rect);
+ }
+
+ painter_->PaintTickmarks(canvas, track_paint_rect);
+ }
+ private:
+ ScrollbarBackgroundPainter(WebKit::WebScrollbar* scrollbar,
+ ScrollbarThemePainter *painter,
+ WebKit::WebScrollbarThemeGeometry* geometry,
+ WebKit::WebScrollbar::ScrollbarPart trackPart)
+ : scrollbar_(scrollbar),
+ painter_(painter),
+ geometry_(geometry),
+ track_part_(trackPart) {}
+
+ WebKit::WebScrollbar* scrollbar_;
+ ScrollbarThemePainter* painter_;
+ WebKit::WebScrollbarThemeGeometry* geometry_;
+ WebKit::WebScrollbar::ScrollbarPart track_part_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScrollbarBackgroundPainter);
+};
+
+class ScrollbarThumbPainter : public LayerPainter {
+ public:
+ static scoped_ptr<ScrollbarThumbPainter> Create(
+ WebKit::WebScrollbar* scrollbar,
+ ScrollbarThemePainter* painter,
+ WebKit::WebScrollbarThemeGeometry* geometry) {
+ return make_scoped_ptr(new ScrollbarThumbPainter(scrollbar,
+ painter,
+ geometry));
+ }
+
+ virtual void Paint(SkCanvas* canvas,
+ gfx::Rect content_rect,
+ gfx::RectF* opaque) OVERRIDE {
+ // Consider the thumb to be at the origin when painting.
+ gfx::Rect thumb_rect = geometry_->thumbRect(scrollbar_);
+ painter_->PaintThumb(canvas, gfx::Rect(thumb_rect.size()));
+ }
+
+ private:
+ ScrollbarThumbPainter(WebKit::WebScrollbar* scrollbar,
+ ScrollbarThemePainter* painter,
+ WebKit::WebScrollbarThemeGeometry* geometry)
+ : scrollbar_(scrollbar),
+ painter_(painter),
+ geometry_(geometry) {}
+
+ WebKit::WebScrollbar* scrollbar_;
+ ScrollbarThemePainter* painter_;
+ WebKit::WebScrollbarThemeGeometry* geometry_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScrollbarThumbPainter);
+};
+
+void ScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) {
+ if (!host || host != layer_tree_host()) {
+ back_track_updater_ = NULL;
+ back_track_.reset();
+ thumb_updater_ = NULL;
+ thumb_.reset();
+ }
+
+ ContentsScalingLayer::SetLayerTreeHost(host);
+}
+
+void ScrollbarLayer::CreateUpdaterIfNeeded() {
+ if (layer_tree_host()->settings().solidColorScrollbars)
+ return;
+
+ texture_format_ =
+ layer_tree_host()->GetRendererCapabilities().best_texture_format;
+
+ if (!back_track_updater_) {
+ back_track_updater_ = CachingBitmapContentLayerUpdater::Create(
+ ScrollbarBackgroundPainter::Create(
+ scrollbar_.get(),
+ painter_.get(),
+ geometry_.get(),
+ WebKit::WebScrollbar::BackTrackPart).PassAs<LayerPainter>());
+ }
+ if (!back_track_) {
+ back_track_ = back_track_updater_->CreateResource(
+ layer_tree_host()->contents_texture_manager());
+ }
+
+ // Only create two-part track if we think the two parts could be different in
+ // appearance.
+ if (scrollbar_->isCustomScrollbar()) {
+ if (!fore_track_updater_) {
+ fore_track_updater_ = CachingBitmapContentLayerUpdater::Create(
+ ScrollbarBackgroundPainter::Create(
+ scrollbar_.get(),
+ painter_.get(),
+ geometry_.get(),
+ WebKit::WebScrollbar::ForwardTrackPart).PassAs<LayerPainter>());
+ }
+ if (!fore_track_) {
+ fore_track_ = fore_track_updater_->CreateResource(
+ layer_tree_host()->contents_texture_manager());
+ }
+ }
+
+ if (!thumb_updater_) {
+ thumb_updater_ = CachingBitmapContentLayerUpdater::Create(
+ ScrollbarThumbPainter::Create(scrollbar_.get(),
+ painter_.get(),
+ geometry_.get()).PassAs<LayerPainter>());
+ }
+ if (!thumb_) {
+ thumb_ = thumb_updater_->CreateResource(
+ layer_tree_host()->contents_texture_manager());
+ }
+}
+
+void ScrollbarLayer::UpdatePart(CachingBitmapContentLayerUpdater* painter,
+ LayerUpdater::Resource* resource,
+ gfx::Rect rect,
+ ResourceUpdateQueue* queue,
+ RenderingStats* stats) {
+ if (layer_tree_host()->settings().solidColorScrollbars)
+ return;
+
+ // Skip painting and uploading if there are no invalidations and
+ // we already have valid texture data.
+ if (resource->texture()->haveBackingTexture() &&
+ resource->texture()->size() == rect.size() &&
+ !is_dirty())
+ return;
+
+ // We should always have enough memory for UI.
+ DCHECK(resource->texture()->canAcquireBackingTexture());
+ if (!resource->texture()->canAcquireBackingTexture())
+ return;
+
+ // Paint and upload the entire part.
+ gfx::Rect painted_opaque_rect;
+ painter->PrepareToUpdate(rect,
+ rect.size(),
+ contents_scale_x(),
+ contents_scale_y(),
+ &painted_opaque_rect,
+ stats);
+ if (!painter->pixels_did_change() &&
+ resource->texture()->haveBackingTexture()) {
+ TRACE_EVENT_INSTANT0("cc",
+ "ScrollbarLayer::updatePart no texture upload needed");
+ return;
+ }
+
+ bool partial_updates_allowed =
+ layer_tree_host()->settings().maxPartialTextureUpdates > 0;
+ if (!partial_updates_allowed)
+ resource->texture()->returnBackingTexture();
+
+ gfx::Vector2d dest_offset(0, 0);
+ resource->Update(queue, rect, dest_offset, partial_updates_allowed, stats);
+}
+
+gfx::Rect ScrollbarLayer::ScrollbarLayerRectToContentRect(
+ gfx::Rect layer_rect) const {
+ // Don't intersect with the bounds as in LayerRectToContentRect() because
+ // layer_rect here might be in coordinates of the containing layer.
+ gfx::RectF content_rect = gfx::ScaleRect(layer_rect,
+ contents_scale_y(),
+ contents_scale_y());
+ return gfx::ToEnclosingRect(content_rect);
+}
+
+void ScrollbarLayer::SetTexturePriorities(
+ const PriorityCalculator& priority_calc) {
+ if (layer_tree_host()->settings().solidColorScrollbars)
+ return;
+
+ if (content_bounds().IsEmpty())
+ return;
+ DCHECK_LE(content_bounds().width(), MaxTextureSize());
+ DCHECK_LE(content_bounds().height(), MaxTextureSize());
+
+ CreateUpdaterIfNeeded();
+
+ bool draws_to_root = !render_target()->parent();
+ if (back_track_) {
+ back_track_->texture()->setDimensions(content_bounds(), texture_format_);
+ back_track_->texture()->setRequestPriority(
+ PriorityCalculator::UIPriority(draws_to_root));
+ }
+ if (fore_track_) {
+ fore_track_->texture()->setDimensions(content_bounds(), texture_format_);
+ fore_track_->texture()->setRequestPriority(
+ PriorityCalculator::UIPriority(draws_to_root));
+ }
+ if (thumb_) {
+ gfx::Rect thumb_layer_rect = geometry_->thumbRect(scrollbar_.get());
+ gfx::Size thumb_size =
+ ScrollbarLayerRectToContentRect(thumb_layer_rect).size();
+ thumb_->texture()->setDimensions(thumb_size, texture_format_);
+ thumb_->texture()->setRequestPriority(
+ PriorityCalculator::UIPriority(draws_to_root));
+ }
+}
+
+void ScrollbarLayer::Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) {
+ ContentsScalingLayer::Update(queue, occlusion, stats);
+
+ dirty_rect_.Union(update_rect_);
+ if (content_bounds().IsEmpty())
+ return;
+ if (visible_content_rect().IsEmpty())
+ return;
+
+ CreateUpdaterIfNeeded();
+
+ gfx::Rect content_rect = ScrollbarLayerRectToContentRect(
+ gfx::Rect(scrollbar_->location(), bounds()));
+ UpdatePart(back_track_updater_.get(),
+ back_track_.get(),
+ content_rect,
+ queue,
+ stats);
+ if (fore_track_ && fore_track_updater_) {
+ UpdatePart(fore_track_updater_.get(),
+ fore_track_.get(),
+ content_rect,
+ queue,
+ stats);
+ }
+
+ // Consider the thumb to be at the origin when painting.
+ gfx::Rect thumb_rect = geometry_->thumbRect(scrollbar_.get());
+ thumb_size_ = thumb_rect.size();
+ gfx::Rect origin_thumb_rect =
+ ScrollbarLayerRectToContentRect(gfx::Rect(thumb_rect.size()));
+ if (!origin_thumb_rect.IsEmpty()) {
+ UpdatePart(thumb_updater_.get(),
+ thumb_.get(),
+ origin_thumb_rect,
+ queue,
+ stats);
+ }
+
+ dirty_rect_ = gfx::RectF();
+}
+
+} // namespace cc
diff --git a/cc/layers/scrollbar_layer.h b/cc/layers/scrollbar_layer.h
new file mode 100644
index 0000000..eafdde8
--- /dev/null
+++ b/cc/layers/scrollbar_layer.h
@@ -0,0 +1,99 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_SCROLLBAR_LAYER_H_
+#define CC_LAYERS_SCROLLBAR_LAYER_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/contents_scaling_layer.h"
+#include "cc/layers/scrollbar_theme_painter.h"
+#include "cc/resources/layer_updater.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebScrollbar.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebScrollbarThemeGeometry.h"
+
+namespace cc {
+class CachingBitmapContentLayerUpdater;
+class ResourceUpdateQueue;
+class Scrollbar;
+class ScrollbarThemeComposite;
+
+class CC_EXPORT ScrollbarLayer : public ContentsScalingLayer {
+ public:
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+
+ static scoped_refptr<ScrollbarLayer> Create(
+ scoped_ptr<WebKit::WebScrollbar>,
+ scoped_ptr<ScrollbarThemePainter>,
+ scoped_ptr<WebKit::WebScrollbarThemeGeometry>,
+ int scrollLayerId);
+
+ int scroll_layer_id() const { return scroll_layer_id_; }
+ void SetScrollLayerId(int id);
+
+ virtual bool OpacityCanAnimateOnImplThread() const OVERRIDE;
+
+ WebKit::WebScrollbar::Orientation Orientation() const;
+
+ // Layer interface
+ virtual void SetTexturePriorities(const PriorityCalculator& priority_calc)
+ OVERRIDE;
+ virtual void Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) OVERRIDE;
+ virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+ virtual void CalculateContentsScale(float ideal_contents_scale,
+ bool animating_transform_to_screen,
+ float* contents_scale_x,
+ float* contents_scale_y,
+ gfx::Size* contentBounds) OVERRIDE;
+
+ virtual ScrollbarLayer* ToScrollbarLayer() OVERRIDE;
+
+ protected:
+ ScrollbarLayer(
+ scoped_ptr<WebKit::WebScrollbar>,
+ scoped_ptr<ScrollbarThemePainter>,
+ scoped_ptr<WebKit::WebScrollbarThemeGeometry>,
+ int scrollLayerId);
+ virtual ~ScrollbarLayer();
+
+ private:
+ void UpdatePart(CachingBitmapContentLayerUpdater* painter,
+ LayerUpdater::Resource* resource,
+ gfx::Rect rect,
+ ResourceUpdateQueue* queue,
+ RenderingStats* stats);
+ void CreateUpdaterIfNeeded();
+ gfx::Rect ScrollbarLayerRectToContentRect(gfx::Rect layer_rect) const;
+
+ bool is_dirty() const { return !dirty_rect_.IsEmpty(); }
+
+ int MaxTextureSize();
+ float ClampScaleToMaxTextureSize(float scale);
+
+ scoped_ptr<WebKit::WebScrollbar> scrollbar_;
+ scoped_ptr<ScrollbarThemePainter> painter_;
+ scoped_ptr<WebKit::WebScrollbarThemeGeometry> geometry_;
+ gfx::Size thumb_size_;
+ int scroll_layer_id_;
+
+ unsigned texture_format_;
+
+ gfx::RectF dirty_rect_;
+
+ scoped_refptr<CachingBitmapContentLayerUpdater> back_track_updater_;
+ scoped_refptr<CachingBitmapContentLayerUpdater> fore_track_updater_;
+ scoped_refptr<CachingBitmapContentLayerUpdater> thumb_updater_;
+
+ // All the parts of the scrollbar except the thumb
+ scoped_ptr<LayerUpdater::Resource> back_track_;
+ scoped_ptr<LayerUpdater::Resource> fore_track_;
+ scoped_ptr<LayerUpdater::Resource> thumb_;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_SCROLLBAR_LAYER_H_
diff --git a/cc/layers/scrollbar_layer_impl.cc b/cc/layers/scrollbar_layer_impl.cc
new file mode 100644
index 0000000..63a17cc
--- /dev/null
+++ b/cc/layers/scrollbar_layer_impl.cc
@@ -0,0 +1,342 @@
+// Copyright 2012 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 "cc/layers/scrollbar_layer_impl.h"
+
+#include "cc/animation/scrollbar_animation_controller.h"
+#include "cc/layers/layer.h"
+#include "cc/layers/quad_sink.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "cc/trees/layer_tree_settings.h"
+#include "ui/gfx/rect_conversions.h"
+
+using WebKit::WebRect;
+using WebKit::WebScrollbar;
+
+namespace cc {
+
+scoped_ptr<ScrollbarLayerImpl> ScrollbarLayerImpl::Create(
+ LayerTreeImpl* tree_impl,
+ int id,
+ scoped_ptr<ScrollbarGeometryFixedThumb> geometry) {
+ return make_scoped_ptr(new ScrollbarLayerImpl(tree_impl,
+ id,
+ geometry.Pass()));
+}
+
+ScrollbarLayerImpl::ScrollbarLayerImpl(
+ LayerTreeImpl* tree_impl,
+ int id,
+ scoped_ptr<ScrollbarGeometryFixedThumb> geometry)
+ : ScrollbarLayerImplBase(tree_impl, id),
+ scrollbar_(this),
+ back_track_resource_id_(0),
+ fore_track_resource_id_(0),
+ thumb_resource_id_(0),
+ geometry_(geometry.Pass()),
+ current_pos_(0),
+ total_size_(0),
+ maximum_(0),
+ vertical_adjust_(0.f),
+ scroll_layer_id_(Layer::INVALID_ID),
+ scrollbar_overlay_style_(WebScrollbar::ScrollbarOverlayStyleDefault),
+ orientation_(WebScrollbar::Horizontal),
+ control_size_(WebScrollbar::RegularScrollbar),
+ pressed_part_(WebScrollbar::NoPart),
+ hovered_part_(WebScrollbar::NoPart),
+ is_scrollable_area_active_(false),
+ is_scroll_view_scrollbar_(false),
+ enabled_(false),
+ is_custom_scrollbar_(false),
+ is_overlay_scrollbar_(false) {}
+
+ScrollbarLayerImpl::~ScrollbarLayerImpl() {}
+
+ScrollbarLayerImpl* ScrollbarLayerImpl::ToScrollbarLayer() {
+ return this;
+}
+
+void ScrollbarLayerImpl::SetScrollbarData(WebScrollbar* scrollbar) {
+ scrollbar_overlay_style_ = scrollbar->scrollbarOverlayStyle();
+ orientation_ = scrollbar->orientation();
+ control_size_ = scrollbar->controlSize();
+ pressed_part_ = scrollbar->pressedPart();
+ hovered_part_ = scrollbar->hoveredPart();
+ is_scrollable_area_active_ = scrollbar->isScrollableAreaActive();
+ is_scroll_view_scrollbar_ = scrollbar->isScrollViewScrollbar();
+ enabled_ = scrollbar->enabled();
+ is_custom_scrollbar_ = scrollbar->isCustomScrollbar();
+ is_overlay_scrollbar_ = scrollbar->isOverlay();
+
+ scrollbar->getTickmarks(tickmarks_);
+}
+
+void ScrollbarLayerImpl::SetThumbSize(gfx::Size size) {
+ thumb_size_ = size;
+ if (!geometry_) {
+ // In impl-side painting, the ScrollbarLayerImpl in the pending tree
+ // simply holds properties that are later pushed to the active tree's
+ // layer, but it doesn't hold geometry or append quads.
+ DCHECK(layer_tree_impl()->IsPendingTree());
+ return;
+ }
+ geometry_->setThumbSize(size);
+}
+
+void ScrollbarLayerImpl::SetViewportWithinScrollableArea(
+ gfx::RectF scrollable_viewport, gfx::SizeF scrollable_area) {
+ normalized_viewport_ = gfx::RectF(
+ scrollable_viewport.x() / scrollable_area.width(),
+ scrollable_viewport.y() / scrollable_area.height(),
+ scrollable_viewport.width() / scrollable_area.width(),
+ scrollable_viewport.height() / scrollable_area.height());
+}
+
+float ScrollbarLayerImpl::CurrentPos() const {
+ return current_pos_;
+}
+
+int ScrollbarLayerImpl::TotalSize() const {
+ return total_size_;
+}
+
+int ScrollbarLayerImpl::Maximum() const {
+ return maximum_;
+}
+
+WebKit::WebScrollbar::Orientation ScrollbarLayerImpl::Orientation() const {
+ return orientation_;
+}
+
+static gfx::RectF ToUVRect(gfx::Rect r, gfx::Rect bounds) {
+ return gfx::ScaleRect(r, 1.f / bounds.width(), 1.f / bounds.height());
+}
+
+gfx::Rect ScrollbarLayerImpl::ScrollbarLayerRectToContentRect(
+ gfx::RectF layer_rect) const {
+ // Don't intersect with the bounds as in layerRectToContentRect() because
+ // layer_rect here might be in coordinates of the containing layer.
+ gfx::RectF content_rect = gfx::ScaleRect(layer_rect,
+ contents_scale_x(),
+ contents_scale_y());
+ return gfx::ToEnclosingRect(content_rect);
+}
+
+scoped_ptr<LayerImpl> ScrollbarLayerImpl::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return ScrollbarLayerImpl::Create(tree_impl,
+ id(),
+ geometry_.Pass()).PassAs<LayerImpl>();
+}
+
+void ScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) {
+ LayerImpl::PushPropertiesTo(layer);
+
+ ScrollbarLayerImpl* scrollbar_layer = static_cast<ScrollbarLayerImpl*>(layer);
+
+ scrollbar_layer->SetScrollbarData(&scrollbar_);
+ scrollbar_layer->SetThumbSize(thumb_size_);
+
+ scrollbar_layer->set_back_track_resource_id(back_track_resource_id_);
+ scrollbar_layer->set_fore_track_resource_id(fore_track_resource_id_);
+ scrollbar_layer->set_thumb_resource_id(thumb_resource_id_);
+}
+
+void ScrollbarLayerImpl::AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) {
+ bool premultipled_alpha = true;
+ bool flipped = false;
+ gfx::PointF uv_top_left(0.f, 0.f);
+ gfx::PointF uv_bottom_right(1.f, 1.f);
+ gfx::Rect boundsRect(bounds());
+ gfx::Rect contentBoundsRect(content_bounds());
+
+ SharedQuadState* shared_quad_state =
+ quad_sink->UseSharedQuadState(CreateSharedQuadState());
+ AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
+
+ if (layer_tree_impl()->settings().solidColorScrollbars) {
+ gfx::Rect track_rect = geometry_->trackRect(&scrollbar_);
+ int thickness_override =
+ layer_tree_impl()->settings().solidColorScrollbarThicknessDIP;
+ gfx::RectF thumb_rect;
+ if (scrollbar_.orientation() == WebScrollbar::Horizontal) {
+ track_rect.set_y(track_rect.y() + vertical_adjust_);
+ thumb_rect = gfx::RectF(track_rect.x() +
+ (normalized_viewport_.x() * track_rect.width()),
+ track_rect.y(),
+ normalized_viewport_.width() * track_rect.width(),
+ track_rect.height());
+ if (thickness_override != -1)
+ thumb_rect.set_height(thickness_override);
+ } else {
+ track_rect.set_height(track_rect.height() + vertical_adjust_);
+ thumb_rect = gfx::RectF(
+ track_rect.x(),
+ track_rect.y() + (normalized_viewport_.y() * track_rect.height()),
+ track_rect.width(),
+ normalized_viewport_.height() * track_rect.height());
+ if (thickness_override != -1)
+ thumb_rect.set_width(thickness_override);
+ }
+ gfx::Rect quad_rect(ScrollbarLayerRectToContentRect(thumb_rect));
+ scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ quad_rect,
+ layer_tree_impl()->settings().solidColorScrollbarColor);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+ return;
+ }
+
+ WebRect thumb_rect, back_track_rect, foreTrackRect;
+ geometry_->splitTrack(&scrollbar_,
+ geometry_->trackRect(&scrollbar_),
+ back_track_rect,
+ thumb_rect,
+ foreTrackRect);
+ if (!geometry_->hasThumb(&scrollbar_))
+ thumb_rect = WebRect();
+
+ if (thumb_resource_id_ && !thumb_rect.isEmpty()) {
+ gfx::Rect quad_rect(ScrollbarLayerRectToContentRect(gfx::Rect(thumb_rect)));
+ gfx::Rect opaque_rect;
+ const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
+ scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ quad_rect,
+ opaque_rect,
+ thumb_resource_id_,
+ premultipled_alpha,
+ uv_top_left,
+ uv_bottom_right,
+ opacity,
+ flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+ }
+
+ if (!back_track_resource_id_)
+ return;
+
+ // We only paint the track in two parts if we were given a texture for the
+ // forward track part.
+ if (fore_track_resource_id_ && !foreTrackRect.isEmpty()) {
+ gfx::Rect quad_rect(ScrollbarLayerRectToContentRect(
+ gfx::Rect(foreTrackRect)));
+ gfx::Rect opaque_rect(contents_opaque() ? quad_rect : gfx::Rect());
+ gfx::RectF uv_rect(ToUVRect(foreTrackRect, boundsRect));
+ const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
+ scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ quad_rect,
+ opaque_rect,
+ fore_track_resource_id_,
+ premultipled_alpha,
+ uv_rect.origin(),
+ uv_rect.bottom_right(),
+ opacity,
+ flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+ }
+
+ // Order matters here: since the back track texture is being drawn to the
+ // entire contents rect, we must append it after the thumb and fore track
+ // quads. The back track texture contains (and displays) the buttons.
+ if (!contentBoundsRect.IsEmpty()) {
+ gfx::Rect quad_rect(contentBoundsRect);
+ gfx::Rect opaque_rect(contents_opaque() ? quad_rect : gfx::Rect());
+ const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
+ scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ quad_rect,
+ opaque_rect,
+ back_track_resource_id_,
+ premultipled_alpha,
+ uv_top_left,
+ uv_bottom_right,
+ opacity,
+ flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+ }
+}
+
+void ScrollbarLayerImpl::DidLoseOutputSurface() {
+ back_track_resource_id_ = 0;
+ fore_track_resource_id_ = 0;
+ thumb_resource_id_ = 0;
+}
+
+bool ScrollbarLayerImpl::Scrollbar::isOverlay() const {
+ return owner_->is_overlay_scrollbar_;
+}
+
+int ScrollbarLayerImpl::Scrollbar::value() const {
+ return owner_->CurrentPos();
+}
+
+WebKit::WebPoint ScrollbarLayerImpl::Scrollbar::location() const {
+ return WebKit::WebPoint();
+}
+
+WebKit::WebSize ScrollbarLayerImpl::Scrollbar::size() const {
+ return WebKit::WebSize(owner_->bounds().width(), owner_->bounds().height());
+}
+
+bool ScrollbarLayerImpl::Scrollbar::enabled() const {
+ return owner_->enabled_;
+}
+
+int ScrollbarLayerImpl::Scrollbar::maximum() const {
+ return owner_->Maximum();
+}
+
+int ScrollbarLayerImpl::Scrollbar::totalSize() const {
+ return owner_->TotalSize();
+}
+
+bool ScrollbarLayerImpl::Scrollbar::isScrollViewScrollbar() const {
+ return owner_->is_scroll_view_scrollbar_;
+}
+
+bool ScrollbarLayerImpl::Scrollbar::isScrollableAreaActive() const {
+ return owner_->is_scrollable_area_active_;
+}
+
+void ScrollbarLayerImpl::Scrollbar::getTickmarks(
+ WebKit::WebVector<WebRect>& tickmarks) const {
+ tickmarks = owner_->tickmarks_;
+}
+
+WebScrollbar::ScrollbarControlSize ScrollbarLayerImpl::Scrollbar::controlSize()
+ const {
+ return owner_->control_size_;
+}
+
+WebScrollbar::ScrollbarPart ScrollbarLayerImpl::Scrollbar::pressedPart() const {
+ return owner_->pressed_part_;
+}
+
+WebScrollbar::ScrollbarPart ScrollbarLayerImpl::Scrollbar::hoveredPart() const {
+ return owner_->hovered_part_;
+}
+
+WebScrollbar::ScrollbarOverlayStyle
+ScrollbarLayerImpl::Scrollbar::scrollbarOverlayStyle() const {
+ return owner_->scrollbar_overlay_style_;
+}
+
+WebScrollbar::Orientation ScrollbarLayerImpl::Scrollbar::orientation() const {
+ return owner_->orientation_;
+}
+
+bool ScrollbarLayerImpl::Scrollbar::isCustomScrollbar() const {
+ return owner_->is_custom_scrollbar_;
+}
+
+const char* ScrollbarLayerImpl::LayerTypeAsString() const {
+ return "ScrollbarLayer";
+}
+
+} // namespace cc
diff --git a/cc/layers/scrollbar_layer_impl.h b/cc/layers/scrollbar_layer_impl.h
new file mode 100644
index 0000000..23422b3
--- /dev/null
+++ b/cc/layers/scrollbar_layer_impl.h
@@ -0,0 +1,150 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_SCROLLBAR_LAYER_IMPL_H_
+#define CC_LAYERS_SCROLLBAR_LAYER_IMPL_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/scrollbar_geometry_fixed_thumb.h"
+#include "cc/layers/scrollbar_layer_impl_base.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebRect.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebVector.h"
+
+namespace cc {
+
+class ScrollView;
+
+class CC_EXPORT ScrollbarLayerImpl : public ScrollbarLayerImplBase {
+ public:
+ static scoped_ptr<ScrollbarLayerImpl> Create(
+ LayerTreeImpl* tree_impl,
+ int id,
+ scoped_ptr<ScrollbarGeometryFixedThumb> geometry);
+ virtual ~ScrollbarLayerImpl();
+
+ virtual ScrollbarLayerImpl* ToScrollbarLayer() OVERRIDE;
+ int scroll_layer_id() const { return scroll_layer_id_; }
+ void set_scroll_layer_id(int id) { scroll_layer_id_ = id; }
+
+ void SetScrollbarData(WebKit::WebScrollbar* scrollbar);
+ void SetThumbSize(gfx::Size size);
+
+ void set_vertical_adjust(float vertical_adjust) {
+ vertical_adjust_ = vertical_adjust;
+ }
+ void SetViewportWithinScrollableArea(gfx::RectF scrollable_viewport,
+ gfx::SizeF scrollable_area);
+
+ void set_back_track_resource_id(ResourceProvider::ResourceId id) {
+ back_track_resource_id_ = id;
+ }
+ void set_fore_track_resource_id(ResourceProvider::ResourceId id) {
+ fore_track_resource_id_ = id;
+ }
+ void set_thumb_resource_id(ResourceProvider::ResourceId id) {
+ thumb_resource_id_ = id;
+ }
+ bool HasThumbTexture() { return thumb_resource_id_; }
+
+
+ // ScrollbarLayerImplBase implementation.
+ virtual float CurrentPos() const OVERRIDE;
+ virtual int TotalSize() const OVERRIDE;
+ virtual int Maximum() const OVERRIDE;
+
+ void SetCurrentPos(float current_pos) { current_pos_ = current_pos; }
+ void SetTotalSize(int total_size) { total_size_ = total_size; }
+ void SetMaximum(int maximum) { maximum_ = maximum; }
+
+ virtual WebKit::WebScrollbar::Orientation Orientation() const OVERRIDE;
+
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+
+ virtual void AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) OVERRIDE;
+
+ virtual void DidLoseOutputSurface() OVERRIDE;
+
+ protected:
+ ScrollbarLayerImpl(LayerTreeImpl* tree_impl,
+ int id,
+ scoped_ptr<ScrollbarGeometryFixedThumb> geometry);
+
+ private:
+ // nested class only to avoid namespace problem
+ class Scrollbar : public WebKit::WebScrollbar {
+ public:
+ explicit Scrollbar(ScrollbarLayerImpl* owner) : owner_(owner) {}
+
+ // WebScrollbar implementation
+ virtual bool isOverlay() const;
+ virtual int value() const;
+ virtual WebKit::WebPoint location() const;
+ virtual WebKit::WebSize size() const;
+ virtual bool enabled() const;
+ virtual int maximum() const;
+ virtual int totalSize() const;
+ virtual bool isScrollViewScrollbar() const;
+ virtual bool isScrollableAreaActive() const;
+ virtual void getTickmarks(WebKit::WebVector<WebKit::WebRect>& tickmarks)
+ const;
+ virtual WebScrollbar::ScrollbarControlSize controlSize() const;
+ virtual WebScrollbar::ScrollbarPart pressedPart() const;
+ virtual WebScrollbar::ScrollbarPart hoveredPart() const;
+ virtual WebScrollbar::ScrollbarOverlayStyle scrollbarOverlayStyle() const;
+ virtual WebScrollbar::Orientation orientation() const;
+ virtual bool isCustomScrollbar() const;
+
+ private:
+ ScrollbarLayerImpl* owner_;
+
+ };
+
+ virtual const char* LayerTypeAsString() const OVERRIDE;
+
+ gfx::Rect ScrollbarLayerRectToContentRect(gfx::RectF layer_rect) const;
+
+ Scrollbar scrollbar_;
+
+ ResourceProvider::ResourceId back_track_resource_id_;
+ ResourceProvider::ResourceId fore_track_resource_id_;
+ ResourceProvider::ResourceId thumb_resource_id_;
+
+ scoped_ptr<ScrollbarGeometryFixedThumb> geometry_;
+
+ float current_pos_;
+ int total_size_;
+ int maximum_;
+ gfx::Size thumb_size_;
+
+ // Difference between the clip layer's height and the visible viewport
+ // height (which may differ in the presence of top-controls hiding).
+ float vertical_adjust_;
+
+ // Specifies the position and size of the viewport within the scrollable
+ // area (normalized as if the scrollable area is a unit-sized box
+ // [0, 0, 1, 1]).
+ gfx::RectF normalized_viewport_;
+
+ int scroll_layer_id_;
+
+ // Data to implement Scrollbar
+ WebKit::WebScrollbar::ScrollbarOverlayStyle scrollbar_overlay_style_;
+ WebKit::WebVector<WebKit::WebRect> tickmarks_;
+ WebKit::WebScrollbar::Orientation orientation_;
+ WebKit::WebScrollbar::ScrollbarControlSize control_size_;
+ WebKit::WebScrollbar::ScrollbarPart pressed_part_;
+ WebKit::WebScrollbar::ScrollbarPart hovered_part_;
+
+ bool is_scrollable_area_active_;
+ bool is_scroll_view_scrollbar_;
+ bool enabled_;
+ bool is_custom_scrollbar_;
+ bool is_overlay_scrollbar_;
+};
+
+}
+#endif // CC_LAYERS_SCROLLBAR_LAYER_IMPL_H_
diff --git a/cc/layers/scrollbar_layer_impl_base.h b/cc/layers/scrollbar_layer_impl_base.h
new file mode 100644
index 0000000..714c0d7
--- /dev/null
+++ b/cc/layers/scrollbar_layer_impl_base.h
@@ -0,0 +1,30 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_
+#define CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer_impl.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebScrollbar.h"
+
+namespace cc {
+
+class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl {
+ public:
+ virtual ~ScrollbarLayerImplBase() {}
+
+ virtual float CurrentPos() const = 0;
+ virtual int TotalSize() const = 0;
+ virtual int Maximum() const = 0;
+ virtual WebKit::WebScrollbar::Orientation Orientation() const = 0;
+
+ protected:
+ ScrollbarLayerImplBase(LayerTreeImpl* tree_impl, int id)
+ : LayerImpl(tree_impl, id) {}
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_
diff --git a/cc/layers/scrollbar_layer_unittest.cc b/cc/layers/scrollbar_layer_unittest.cc
new file mode 100644
index 0000000..8c424a9
--- /dev/null
+++ b/cc/layers/scrollbar_layer_unittest.cc
@@ -0,0 +1,411 @@
+// Copyright 2012 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 "cc/layers/scrollbar_layer.h"
+
+#include "cc/animation/scrollbar_animation_controller.h"
+#include "cc/layers/append_quads_data.h"
+#include "cc/layers/scrollbar_layer_impl.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/resources/prioritized_resource_manager.h"
+#include "cc/resources/priority_calculator.h"
+#include "cc/resources/resource_update_queue.h"
+#include "cc/test/fake_impl_proxy.h"
+#include "cc/test/fake_layer_tree_host_client.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/fake_scrollbar_theme_painter.h"
+#include "cc/test/fake_web_scrollbar.h"
+#include "cc/test/fake_web_scrollbar_theme_geometry.h"
+#include "cc/test/geometry_test_utils.h"
+#include "cc/test/layer_tree_test_common.h"
+#include "cc/test/mock_quad_culler.h"
+#include "cc/test/test_web_graphics_context_3d.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "cc/trees/single_thread_proxy.h"
+#include "cc/trees/tree_synchronizer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebScrollbar.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebScrollbarThemeGeometry.h"
+
+namespace cc {
+namespace {
+
+scoped_ptr<LayerImpl> layerImplForScrollAreaAndScrollbar(
+ FakeLayerTreeHostImpl* host_impl,
+ scoped_ptr<WebKit::WebScrollbar> scrollbar,
+ bool reverse_order)
+{
+ scoped_refptr<Layer> layerTreeRoot = Layer::Create();
+ scoped_refptr<Layer> child1 = Layer::Create();
+ scoped_refptr<Layer> child2 = ScrollbarLayer::Create(scrollbar.Pass(), FakeScrollbarThemePainter::Create(false).PassAs<ScrollbarThemePainter>(), FakeWebScrollbarThemeGeometry::create(true), child1->id());
+ layerTreeRoot->AddChild(child1);
+ layerTreeRoot->InsertChild(child2, reverse_order ? 0 : 1);
+ scoped_ptr<LayerImpl> layerImpl = TreeSynchronizer::SynchronizeTrees(layerTreeRoot.get(), scoped_ptr<LayerImpl>(), host_impl->active_tree());
+ TreeSynchronizer::PushProperties(layerTreeRoot.get(), layerImpl.get());
+ return layerImpl.Pass();
+}
+
+TEST(ScrollbarLayerTest, resolveScrollLayerPointer)
+{
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+
+ {
+ scoped_ptr<WebKit::WebScrollbar> scrollbar(FakeWebScrollbar::Create());
+ scoped_ptr<LayerImpl> layerImplTreeRoot = layerImplForScrollAreaAndScrollbar(&hostImpl, scrollbar.Pass(), false);
+
+ LayerImpl* ccChild1 = layerImplTreeRoot->children()[0];
+ ScrollbarLayerImpl* ccChild2 = static_cast<ScrollbarLayerImpl*>(layerImplTreeRoot->children()[1]);
+
+ EXPECT_EQ(ccChild1->horizontal_scrollbar_layer(), ccChild2);
+ }
+
+ { // another traverse order
+ scoped_ptr<WebKit::WebScrollbar> scrollbar(FakeWebScrollbar::Create());
+ scoped_ptr<LayerImpl> layerImplTreeRoot = layerImplForScrollAreaAndScrollbar(&hostImpl, scrollbar.Pass(), true);
+
+ ScrollbarLayerImpl* ccChild1 = static_cast<ScrollbarLayerImpl*>(layerImplTreeRoot->children()[0]);
+ LayerImpl* ccChild2 = layerImplTreeRoot->children()[1];
+
+ EXPECT_EQ(ccChild2->horizontal_scrollbar_layer(), ccChild1);
+ }
+}
+
+TEST(ScrollbarLayerTest, shouldScrollNonOverlayOnMainThread)
+{
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+
+ // Create and attach a non-overlay scrollbar.
+ scoped_ptr<WebKit::WebScrollbar> scrollbar(FakeWebScrollbar::Create());
+ static_cast<FakeWebScrollbar*>(scrollbar.get())->setOverlay(false);
+ scoped_ptr<LayerImpl> layerImplTreeRoot = layerImplForScrollAreaAndScrollbar(&hostImpl, scrollbar.Pass(), false);
+ ScrollbarLayerImpl* scrollbarLayerImpl = static_cast<ScrollbarLayerImpl*>(layerImplTreeRoot->children()[1]);
+
+ // When the scrollbar is not an overlay scrollbar, the scroll should be
+ // responded to on the main thread as the compositor does not yet implement
+ // scrollbar scrolling.
+ EXPECT_EQ(InputHandlerClient::ScrollOnMainThread, scrollbarLayerImpl->TryScroll(gfx::Point(0, 0), InputHandlerClient::Gesture));
+
+ // Create and attach an overlay scrollbar.
+ scrollbar = FakeWebScrollbar::Create();
+ static_cast<FakeWebScrollbar*>(scrollbar.get())->setOverlay(true);
+
+ layerImplTreeRoot = layerImplForScrollAreaAndScrollbar(&hostImpl, scrollbar.Pass(), false);
+ scrollbarLayerImpl = static_cast<ScrollbarLayerImpl*>(layerImplTreeRoot->children()[1]);
+
+ // The user shouldn't be able to drag an overlay scrollbar and the scroll
+ // may be handled in the compositor.
+ EXPECT_EQ(InputHandlerClient::ScrollIgnored, scrollbarLayerImpl->TryScroll(gfx::Point(0, 0), InputHandlerClient::Gesture));
+}
+
+TEST(ScrollbarLayerTest, scrollOffsetSynchronization)
+{
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+
+ scoped_ptr<WebKit::WebScrollbar> scrollbar(FakeWebScrollbar::Create());
+ scoped_refptr<Layer> layerTreeRoot = Layer::Create();
+ scoped_refptr<Layer> contentLayer = Layer::Create();
+ scoped_refptr<Layer> scrollbarLayer = ScrollbarLayer::Create(scrollbar.Pass(), FakeScrollbarThemePainter::Create(false).PassAs<ScrollbarThemePainter>(), FakeWebScrollbarThemeGeometry::create(true), layerTreeRoot->id());
+ layerTreeRoot->AddChild(contentLayer);
+ layerTreeRoot->AddChild(scrollbarLayer);
+
+ layerTreeRoot->SetScrollOffset(gfx::Vector2d(10, 20));
+ layerTreeRoot->SetMaxScrollOffset(gfx::Vector2d(30, 50));
+ layerTreeRoot->SetBounds(gfx::Size(100, 200));
+ contentLayer->SetBounds(gfx::Size(100, 200));
+
+ scoped_ptr<LayerImpl> layerImplTreeRoot = TreeSynchronizer::SynchronizeTrees(layerTreeRoot.get(), scoped_ptr<LayerImpl>(), hostImpl.active_tree());
+ TreeSynchronizer::PushProperties(layerTreeRoot.get(), layerImplTreeRoot.get());
+
+ ScrollbarLayerImpl* ccScrollbarLayer = static_cast<ScrollbarLayerImpl*>(layerImplTreeRoot->children()[1]);
+
+ EXPECT_EQ(10, ccScrollbarLayer->CurrentPos());
+ EXPECT_EQ(100, ccScrollbarLayer->TotalSize());
+ EXPECT_EQ(30, ccScrollbarLayer->Maximum());
+
+ layerTreeRoot->SetScrollOffset(gfx::Vector2d(100, 200));
+ layerTreeRoot->SetMaxScrollOffset(gfx::Vector2d(300, 500));
+ layerTreeRoot->SetBounds(gfx::Size(1000, 2000));
+ contentLayer->SetBounds(gfx::Size(1000, 2000));
+
+ ScrollbarAnimationController* scrollbarController = layerImplTreeRoot->scrollbar_animation_controller();
+ layerImplTreeRoot = TreeSynchronizer::SynchronizeTrees(layerTreeRoot.get(), layerImplTreeRoot.Pass(), hostImpl.active_tree());
+ TreeSynchronizer::PushProperties(layerTreeRoot.get(), layerImplTreeRoot.get());
+ EXPECT_EQ(scrollbarController, layerImplTreeRoot->scrollbar_animation_controller());
+
+ EXPECT_EQ(100, ccScrollbarLayer->CurrentPos());
+ EXPECT_EQ(1000, ccScrollbarLayer->TotalSize());
+ EXPECT_EQ(300, ccScrollbarLayer->Maximum());
+
+ layerImplTreeRoot->ScrollBy(gfx::Vector2d(12, 34));
+
+ EXPECT_EQ(112, ccScrollbarLayer->CurrentPos());
+ EXPECT_EQ(1000, ccScrollbarLayer->TotalSize());
+ EXPECT_EQ(300, ccScrollbarLayer->Maximum());
+}
+
+TEST(ScrollbarLayerTest, solidColorDrawQuads)
+{
+ LayerTreeSettings layerTreeSettings;
+ layerTreeSettings.solidColorScrollbars = true;
+ layerTreeSettings.solidColorScrollbarThicknessDIP = 3;
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(layerTreeSettings, &proxy);
+
+ scoped_ptr<WebKit::WebScrollbar> scrollbar(FakeWebScrollbar::Create());
+ static_cast<FakeWebScrollbar*>(scrollbar.get())->setOverlay(true);
+ scoped_ptr<LayerImpl> layerImplTreeRoot = layerImplForScrollAreaAndScrollbar(&hostImpl, scrollbar.Pass(), false);
+ ScrollbarLayerImpl* scrollbarLayerImpl = static_cast<ScrollbarLayerImpl*>(layerImplTreeRoot->children()[1]);
+ scrollbarLayerImpl->SetThumbSize(gfx::Size(4, 4));
+ scrollbarLayerImpl->SetViewportWithinScrollableArea(
+ gfx::RectF(10.f, 0.f, 40.f, 0.f), gfx::SizeF(100.f, 100.f));
+
+ // Thickness should be overridden to 3.
+ {
+ MockQuadCuller quadCuller;
+ AppendQuadsData data;
+ scrollbarLayerImpl->AppendQuads(&quadCuller, &data);
+
+ const QuadList& quads = quadCuller.quadList();
+ ASSERT_EQ(1, quads.size());
+ EXPECT_EQ(DrawQuad::SOLID_COLOR, quads[0]->material);
+ EXPECT_RECT_EQ(gfx::Rect(1, 0, 4, 3), quads[0]->rect);
+ }
+
+ // Contents scale should scale the draw quad.
+ scrollbarLayerImpl->draw_properties().contents_scale_x = 2;
+ scrollbarLayerImpl->draw_properties().contents_scale_y = 2;
+ {
+ MockQuadCuller quadCuller;
+ AppendQuadsData data;
+ scrollbarLayerImpl->AppendQuads(&quadCuller, &data);
+
+ const QuadList& quads = quadCuller.quadList();
+ ASSERT_EQ(1, quads.size());
+ EXPECT_EQ(DrawQuad::SOLID_COLOR, quads[0]->material);
+ EXPECT_RECT_EQ(gfx::Rect(2, 0, 8, 6), quads[0]->rect);
+ }
+ scrollbarLayerImpl->draw_properties().contents_scale_x = 1;
+ scrollbarLayerImpl->draw_properties().contents_scale_y = 1;
+
+ // For solid color scrollbars, position and size should reflect the
+ // viewport, not the geometry object.
+ scrollbarLayerImpl->SetViewportWithinScrollableArea(
+ gfx::RectF(40.f, 0.f, 20.f, 0.f), gfx::SizeF(100.f, 100.f));
+ {
+ MockQuadCuller quadCuller;
+ AppendQuadsData data;
+ scrollbarLayerImpl->AppendQuads(&quadCuller, &data);
+
+ const QuadList& quads = quadCuller.quadList();
+ ASSERT_EQ(1, quads.size());
+ EXPECT_EQ(DrawQuad::SOLID_COLOR, quads[0]->material);
+ EXPECT_RECT_EQ(gfx::Rect(4, 0, 2, 3), quads[0]->rect);
+ }
+
+}
+
+class ScrollbarLayerTestMaxTextureSize : public ThreadedTest {
+public:
+ ScrollbarLayerTestMaxTextureSize() {}
+
+ void setScrollbarBounds(gfx::Size bounds) {
+ bounds_ = bounds;
+ }
+
+ virtual void beginTest() OVERRIDE
+ {
+ m_layerTreeHost->InitializeRendererIfNeeded();
+
+ scoped_ptr<WebKit::WebScrollbar> scrollbar(FakeWebScrollbar::Create());
+ m_scrollbarLayer = ScrollbarLayer::Create(scrollbar.Pass(), FakeScrollbarThemePainter::Create(false).PassAs<ScrollbarThemePainter>(), FakeWebScrollbarThemeGeometry::create(true), 1);
+ m_scrollbarLayer->SetLayerTreeHost(m_layerTreeHost.get());
+ m_scrollbarLayer->SetBounds(bounds_);
+ m_layerTreeHost->root_layer()->AddChild(m_scrollbarLayer);
+
+ m_scrollLayer = Layer::Create();
+ m_scrollbarLayer->SetScrollLayerId(m_scrollLayer->id());
+ m_layerTreeHost->root_layer()->AddChild(m_scrollLayer);
+
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void commitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE
+ {
+ const int kMaxTextureSize = impl->GetRendererCapabilities().max_texture_size;
+
+ // Check first that we're actually testing something.
+ EXPECT_GT(m_scrollbarLayer->bounds().width(), kMaxTextureSize);
+
+ EXPECT_EQ(m_scrollbarLayer->content_bounds().width(), kMaxTextureSize - 1);
+ EXPECT_EQ(m_scrollbarLayer->content_bounds().height(), kMaxTextureSize - 1);
+
+ endTest();
+ }
+
+ virtual void afterTest() OVERRIDE
+ {
+ }
+
+private:
+ scoped_refptr<ScrollbarLayer> m_scrollbarLayer;
+ scoped_refptr<Layer> m_scrollLayer;
+ gfx::Size bounds_;
+};
+
+TEST_F(ScrollbarLayerTestMaxTextureSize, runTest) {
+ scoped_ptr<TestWebGraphicsContext3D> context = TestWebGraphicsContext3D::Create();
+ int max_size = 0;
+ context->getIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
+ setScrollbarBounds(gfx::Size(max_size + 100, max_size + 100));
+ runTest(true);
+}
+
+class MockLayerTreeHost : public LayerTreeHost {
+public:
+ MockLayerTreeHost(const LayerTreeSettings& settings)
+ : LayerTreeHost(&m_fakeClient, settings)
+ {
+ Initialize(scoped_ptr<Thread>(NULL));
+ }
+
+private:
+ FakeLayerImplTreeHostClient m_fakeClient;
+};
+
+
+class ScrollbarLayerTestResourceCreation : public testing::Test {
+public:
+ ScrollbarLayerTestResourceCreation()
+ {
+ }
+
+ void testResourceUpload(int expectedResources)
+ {
+ m_layerTreeHost.reset(new MockLayerTreeHost(m_layerTreeSettings));
+
+ scoped_ptr<WebKit::WebScrollbar> scrollbar(FakeWebScrollbar::Create());
+ scoped_refptr<Layer> layerTreeRoot = Layer::Create();
+ scoped_refptr<Layer> contentLayer = Layer::Create();
+ scoped_refptr<Layer> scrollbarLayer = ScrollbarLayer::Create(scrollbar.Pass(), FakeScrollbarThemePainter::Create(false).PassAs<ScrollbarThemePainter>(), FakeWebScrollbarThemeGeometry::create(true), layerTreeRoot->id());
+ layerTreeRoot->AddChild(contentLayer);
+ layerTreeRoot->AddChild(scrollbarLayer);
+
+ m_layerTreeHost->InitializeRendererIfNeeded();
+ m_layerTreeHost->contents_texture_manager()->setMaxMemoryLimitBytes(1024 * 1024);
+ m_layerTreeHost->SetRootLayer(layerTreeRoot);
+
+ scrollbarLayer->SetIsDrawable(true);
+ scrollbarLayer->SetBounds(gfx::Size(100, 100));
+ layerTreeRoot->SetScrollOffset(gfx::Vector2d(10, 20));
+ layerTreeRoot->SetMaxScrollOffset(gfx::Vector2d(30, 50));
+ layerTreeRoot->SetBounds(gfx::Size(100, 200));
+ contentLayer->SetBounds(gfx::Size(100, 200));
+ scrollbarLayer->draw_properties().content_bounds = gfx::Size(100, 200);
+ scrollbarLayer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 200);
+ scrollbarLayer->CreateRenderSurface();
+ scrollbarLayer->draw_properties().render_target = scrollbarLayer;
+
+ testing::Mock::VerifyAndClearExpectations(m_layerTreeHost.get());
+ EXPECT_EQ(scrollbarLayer->layer_tree_host(), m_layerTreeHost.get());
+
+ PriorityCalculator calculator;
+ ResourceUpdateQueue queue;
+ OcclusionTracker occlusionTracker(gfx::Rect(), false);
+
+ scrollbarLayer->SetTexturePriorities(calculator);
+ m_layerTreeHost->contents_texture_manager()->prioritizeTextures();
+ scrollbarLayer->Update(&queue, &occlusionTracker, NULL);
+ EXPECT_EQ(0, queue.fullUploadSize());
+ EXPECT_EQ(expectedResources, queue.partialUploadSize());
+
+ testing::Mock::VerifyAndClearExpectations(m_layerTreeHost.get());
+ }
+
+protected:
+ scoped_ptr<MockLayerTreeHost> m_layerTreeHost;
+ LayerTreeSettings m_layerTreeSettings;
+};
+
+TEST_F(ScrollbarLayerTestResourceCreation, resourceUpload)
+{
+ m_layerTreeSettings.solidColorScrollbars = false;
+ testResourceUpload(2);
+}
+
+TEST_F(ScrollbarLayerTestResourceCreation, solidColorNoResourceUpload)
+{
+ m_layerTreeSettings.solidColorScrollbars = true;
+ testResourceUpload(0);
+}
+
+TEST(ScrollbarLayerTest, pinchZoomScrollbarUpdates)
+{
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+
+ scoped_refptr<Layer> layerTreeRoot = Layer::Create();
+ layerTreeRoot->SetScrollable(true);
+
+ scoped_refptr<Layer> contentLayer = Layer::Create();
+ scoped_ptr<WebKit::WebScrollbar> scrollbar1(FakeWebScrollbar::Create());
+ scoped_refptr<Layer> scrollbarLayerHorizontal =
+ ScrollbarLayer::Create(scrollbar1.Pass(),
+ FakeScrollbarThemePainter::Create(false).PassAs<ScrollbarThemePainter>(),
+ FakeWebScrollbarThemeGeometry::create(true),
+ Layer::PINCH_ZOOM_ROOT_SCROLL_LAYER_ID);
+ scoped_ptr<WebKit::WebScrollbar> scrollbar2(FakeWebScrollbar::Create());
+ scoped_refptr<Layer> scrollbarLayerVertical =
+ ScrollbarLayer::Create(scrollbar2.Pass(),
+ FakeScrollbarThemePainter::Create(false).PassAs<ScrollbarThemePainter>(),
+ FakeWebScrollbarThemeGeometry::create(true),
+ Layer::PINCH_ZOOM_ROOT_SCROLL_LAYER_ID);
+
+ layerTreeRoot->AddChild(contentLayer);
+ layerTreeRoot->AddChild(scrollbarLayerHorizontal);
+ layerTreeRoot->AddChild(scrollbarLayerVertical);
+
+ layerTreeRoot->SetScrollOffset(gfx::Vector2d(10, 20));
+ layerTreeRoot->SetMaxScrollOffset(gfx::Vector2d(30, 50));
+ layerTreeRoot->SetBounds(gfx::Size(100, 200));
+ contentLayer->SetBounds(gfx::Size(100, 200));
+
+ scoped_ptr<LayerImpl> layerImplTreeRoot =
+ TreeSynchronizer::SynchronizeTrees(layerTreeRoot.get(),
+ scoped_ptr<LayerImpl>(), hostImpl.active_tree());
+ TreeSynchronizer::PushProperties(layerTreeRoot.get(),
+ layerImplTreeRoot.get());
+
+ ScrollbarLayerImpl* pinchZoomHorizontal = static_cast<ScrollbarLayerImpl*>(
+ layerImplTreeRoot->children()[1]);
+ ScrollbarLayerImpl* pinchZoomVertical = static_cast<ScrollbarLayerImpl*>(
+ layerImplTreeRoot->children()[2]);
+
+ // Need a root layer in the active tree in order for DidUpdateScroll()
+ // to work.
+ hostImpl.active_tree()->SetRootLayer(layerImplTreeRoot.Pass());
+ hostImpl.active_tree()->FindRootScrollLayer();
+
+ // Manually set the pinch-zoom layers: normally this is done by
+ // LayerTreeHost.
+ hostImpl.active_tree()->SetPinchZoomHorizontalLayerId(
+ pinchZoomHorizontal->id());
+ hostImpl.active_tree()->SetPinchZoomVerticalLayerId(
+ pinchZoomVertical->id());
+
+ hostImpl.active_tree()->DidUpdateScroll();
+
+ EXPECT_EQ(10, pinchZoomHorizontal->CurrentPos());
+ EXPECT_EQ(100, pinchZoomHorizontal->TotalSize());
+ EXPECT_EQ(30, pinchZoomHorizontal->Maximum());
+ EXPECT_EQ(20, pinchZoomVertical->CurrentPos());
+ EXPECT_EQ(200, pinchZoomVertical->TotalSize());
+ EXPECT_EQ(50, pinchZoomVertical->Maximum());
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/scrollbar_theme_painter.h b/cc/layers/scrollbar_theme_painter.h
new file mode 100644
index 0000000..50fec7e
--- /dev/null
+++ b/cc/layers/scrollbar_theme_painter.h
@@ -0,0 +1,36 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_SCROLLBAR_THEME_PAINTER_H_
+#define CC_LAYERS_SCROLLBAR_THEME_PAINTER_H_
+
+#include "cc/base/cc_export.h"
+
+class SkCanvas;
+
+namespace gfx {
+class Rect;
+}
+
+namespace cc {
+
+class CC_EXPORT ScrollbarThemePainter {
+ public:
+ virtual ~ScrollbarThemePainter() {}
+
+ virtual void PaintScrollbarBackground(SkCanvas*, const gfx::Rect&) = 0;
+ virtual void PaintTrackBackground(SkCanvas*, const gfx::Rect&) = 0;
+ virtual void PaintBackTrackPart(SkCanvas*, const gfx::Rect&) = 0;
+ virtual void PaintForwardTrackPart(SkCanvas*, const gfx::Rect&) = 0;
+ virtual void PaintBackButtonStart(SkCanvas*, const gfx::Rect&) = 0;
+ virtual void PaintBackButtonEnd(SkCanvas*, const gfx::Rect&) = 0;
+ virtual void PaintForwardButtonStart(SkCanvas*, const gfx::Rect&) = 0;
+ virtual void PaintForwardButtonEnd(SkCanvas*, const gfx::Rect&) = 0;
+ virtual void PaintTickmarks(SkCanvas*, const gfx::Rect&) = 0;
+ virtual void PaintThumb(SkCanvas*, const gfx::Rect&) = 0;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_SCROLLBAR_THEME_PAINTER_H_
diff --git a/cc/layers/solid_color_layer.cc b/cc/layers/solid_color_layer.cc
new file mode 100644
index 0000000..5a1e26f
--- /dev/null
+++ b/cc/layers/solid_color_layer.cc
@@ -0,0 +1,30 @@
+// Copyright 2012 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 "cc/layers/solid_color_layer.h"
+
+#include "cc/layers/solid_color_layer_impl.h"
+
+namespace cc {
+
+scoped_ptr<LayerImpl> SolidColorLayer::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return SolidColorLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
+}
+
+scoped_refptr<SolidColorLayer> SolidColorLayer::Create() {
+ return make_scoped_refptr(new SolidColorLayer());
+}
+
+SolidColorLayer::SolidColorLayer()
+ : Layer() {}
+
+SolidColorLayer::~SolidColorLayer() {}
+
+void SolidColorLayer::SetBackgroundColor(SkColor color) {
+ SetContentsOpaque(SkColorGetA(color) == 255);
+ Layer::SetBackgroundColor(color);
+}
+
+} // namespace cc
diff --git a/cc/layers/solid_color_layer.h b/cc/layers/solid_color_layer.h
new file mode 100644
index 0000000..475f3ed
--- /dev/null
+++ b/cc/layers/solid_color_layer.h
@@ -0,0 +1,33 @@
+// Copyright 2012 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.
+
+
+#ifndef CC_LAYERS_SOLID_COLOR_LAYER_H_
+#define CC_LAYERS_SOLID_COLOR_LAYER_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer.h"
+
+namespace cc {
+
+// A Layer that renders a solid color. The color is specified by using
+// SetBackgroundColor() on the base class.
+class CC_EXPORT SolidColorLayer : public Layer {
+ public:
+ static scoped_refptr<SolidColorLayer> Create();
+
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+
+ virtual void SetBackgroundColor(SkColor color) OVERRIDE;
+
+ protected:
+ SolidColorLayer();
+
+ private:
+ virtual ~SolidColorLayer();
+};
+
+}
+#endif // CC_LAYERS_SOLID_COLOR_LAYER_H_
diff --git a/cc/layers/solid_color_layer_impl.cc b/cc/layers/solid_color_layer_impl.cc
new file mode 100644
index 0000000..114386e
--- /dev/null
+++ b/cc/layers/solid_color_layer_impl.cc
@@ -0,0 +1,50 @@
+// Copyright 2012 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 "cc/layers/solid_color_layer_impl.h"
+
+#include "cc/layers/quad_sink.h"
+#include "cc/quads/solid_color_draw_quad.h"
+
+namespace cc {
+
+SolidColorLayerImpl::SolidColorLayerImpl(LayerTreeImpl* tree_impl, int id)
+ : LayerImpl(tree_impl, id),
+ tile_size_(256) {}
+
+SolidColorLayerImpl::~SolidColorLayerImpl() {}
+
+scoped_ptr<LayerImpl> SolidColorLayerImpl::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return SolidColorLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
+}
+
+void SolidColorLayerImpl::AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) {
+ SharedQuadState* shared_quad_state =
+ quad_sink->UseSharedQuadState(CreateSharedQuadState());
+ AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
+
+ // We create a series of smaller quads instead of just one large one so that
+ // the culler can reduce the total pixels drawn.
+ int width = content_bounds().width();
+ int height = content_bounds().height();
+ for (int x = 0; x < width; x += tile_size_) {
+ for (int y = 0; y < height; y += tile_size_) {
+ gfx::Rect solidTileRect(x,
+ y,
+ std::min(width - x, tile_size_),
+ std::min(height - y, tile_size_));
+ scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create();
+ quad->SetNew(shared_quad_state, solidTileRect, background_color());
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+ }
+ }
+}
+
+const char* SolidColorLayerImpl::LayerTypeAsString() const {
+ return "SolidColorLayer";
+}
+
+} // namespace cc
diff --git a/cc/layers/solid_color_layer_impl.h b/cc/layers/solid_color_layer_impl.h
new file mode 100644
index 0000000..db99945
--- /dev/null
+++ b/cc/layers/solid_color_layer_impl.h
@@ -0,0 +1,39 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_SOLID_COLOR_LAYER_IMPL_H_
+#define CC_LAYERS_SOLID_COLOR_LAYER_IMPL_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer_impl.h"
+
+namespace cc {
+
+class CC_EXPORT SolidColorLayerImpl : public LayerImpl {
+ public:
+ static scoped_ptr<SolidColorLayerImpl> Create(LayerTreeImpl* tree_impl,
+ int id) {
+ return make_scoped_ptr(new SolidColorLayerImpl(tree_impl, id));
+ }
+ virtual ~SolidColorLayerImpl();
+
+ // LayerImpl overrides.
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+ virtual void AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) OVERRIDE;
+
+ protected:
+ SolidColorLayerImpl(LayerTreeImpl* tree_impl, int id);
+
+ private:
+ virtual const char* LayerTypeAsString() const OVERRIDE;
+
+ const int tile_size_;
+};
+
+}
+
+#endif // CC_LAYERS_SOLID_COLOR_LAYER_IMPL_H_
diff --git a/cc/layers/solid_color_layer_impl_unittest.cc b/cc/layers/solid_color_layer_impl_unittest.cc
new file mode 100644
index 0000000..039732e
--- /dev/null
+++ b/cc/layers/solid_color_layer_impl_unittest.cc
@@ -0,0 +1,163 @@
+// Copyright 2012 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 "cc/layers/solid_color_layer_impl.h"
+
+#include "cc/layers/append_quads_data.h"
+#include "cc/layers/solid_color_layer.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/test/fake_impl_proxy.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/layer_test_common.h"
+#include "cc/test/mock_quad_culler.h"
+#include "cc/trees/single_thread_proxy.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+TEST(SolidColorLayerImplTest, verifyTilingCompleteAndNoOverlap)
+{
+ MockQuadCuller quadCuller;
+ gfx::Size layerSize = gfx::Size(800, 600);
+ gfx::Rect visibleContentRect = gfx::Rect(gfx::Point(), layerSize);
+
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+ scoped_ptr<SolidColorLayerImpl> layer = SolidColorLayerImpl::Create(hostImpl.active_tree(), 1);
+ layer->draw_properties().visible_content_rect = visibleContentRect;
+ layer->SetBounds(layerSize);
+ layer->SetContentBounds(layerSize);
+ layer->CreateRenderSurface();
+ layer->draw_properties().render_target = layer.get();
+
+ AppendQuadsData data;
+ layer->AppendQuads(&quadCuller, &data);
+
+ LayerTestCommon::verifyQuadsExactlyCoverRect(quadCuller.quadList(), visibleContentRect);
+}
+
+TEST(SolidColorLayerImplTest, verifyCorrectBackgroundColorInQuad)
+{
+ SkColor testColor = 0xFFA55AFF;
+
+ MockQuadCuller quadCuller;
+ gfx::Size layerSize = gfx::Size(100, 100);
+ gfx::Rect visibleContentRect = gfx::Rect(gfx::Point(), layerSize);
+
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+ scoped_ptr<SolidColorLayerImpl> layer = SolidColorLayerImpl::Create(hostImpl.active_tree(), 1);
+ layer->draw_properties().visible_content_rect = visibleContentRect;
+ layer->SetBounds(layerSize);
+ layer->SetContentBounds(layerSize);
+ layer->SetBackgroundColor(testColor);
+ layer->CreateRenderSurface();
+ layer->draw_properties().render_target = layer.get();
+
+ AppendQuadsData data;
+ layer->AppendQuads(&quadCuller, &data);
+
+ ASSERT_EQ(quadCuller.quadList().size(), 1U);
+ EXPECT_EQ(SolidColorDrawQuad::MaterialCast(quadCuller.quadList()[0])->color, testColor);
+}
+
+TEST(SolidColorLayerImplTest, verifyCorrectOpacityInQuad)
+{
+ const float opacity = 0.5f;
+
+ MockQuadCuller quadCuller;
+ gfx::Size layerSize = gfx::Size(100, 100);
+ gfx::Rect visibleContentRect = gfx::Rect(gfx::Point(), layerSize);
+
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+ scoped_ptr<SolidColorLayerImpl> layer = SolidColorLayerImpl::Create(hostImpl.active_tree(), 1);
+ layer->draw_properties().visible_content_rect = visibleContentRect;
+ layer->SetBounds(layerSize);
+ layer->SetContentBounds(layerSize);
+ layer->draw_properties().opacity = opacity;
+ layer->CreateRenderSurface();
+ layer->draw_properties().render_target = layer.get();
+
+ AppendQuadsData data;
+ layer->AppendQuads(&quadCuller, &data);
+
+ ASSERT_EQ(quadCuller.quadList().size(), 1U);
+ EXPECT_EQ(opacity, SolidColorDrawQuad::MaterialCast(quadCuller.quadList()[0])->opacity());
+}
+
+TEST(SolidColorLayerImplTest, verifyOpaqueRect)
+{
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl hostImpl(&proxy);
+
+ gfx::Size layerSize = gfx::Size(100, 100);
+ gfx::Rect visibleContentRect = gfx::Rect(gfx::Point(), layerSize);
+
+ scoped_refptr<SolidColorLayer> layer = SolidColorLayer::Create();
+ layer->SetBounds(layerSize);
+ layer->SetForceRenderSurface(true);
+
+ scoped_refptr<Layer> root = Layer::Create();
+ root->AddChild(layer);
+
+ std::vector<scoped_refptr<Layer> > renderSurfaceLayerList;
+ LayerTreeHostCommon::calculateDrawProperties(
+ root,
+ gfx::Size(500, 500),
+ 1,
+ 1,
+ 1024,
+ false,
+ renderSurfaceLayerList);
+
+ EXPECT_FALSE(layer->contents_opaque());
+ layer->SetBackgroundColor(SkColorSetARGBInline(255, 10, 20, 30));
+ EXPECT_TRUE(layer->contents_opaque());
+
+ {
+ scoped_ptr<SolidColorLayerImpl> layerImpl = SolidColorLayerImpl::Create(hostImpl.active_tree(), layer->id());
+ layer->PushPropertiesTo(layerImpl.get());
+
+ // The impl layer should call itself opaque as well.
+ EXPECT_TRUE(layerImpl->contents_opaque());
+
+ // Impl layer has 1 opacity, and the color is opaque, so the opaqueRect should be the full tile.
+ layerImpl->draw_properties().opacity = 1;
+
+ MockQuadCuller quadCuller;
+ AppendQuadsData data;
+ layerImpl->AppendQuads(&quadCuller, &data);
+
+ ASSERT_EQ(quadCuller.quadList().size(), 1U);
+ EXPECT_EQ(visibleContentRect.ToString(), quadCuller.quadList()[0]->opaque_rect.ToString());
+ }
+
+ EXPECT_TRUE(layer->contents_opaque());
+ layer->SetBackgroundColor(SkColorSetARGBInline(254, 10, 20, 30));
+ EXPECT_FALSE(layer->contents_opaque());
+
+ {
+ scoped_ptr<SolidColorLayerImpl> layerImpl = SolidColorLayerImpl::Create(hostImpl.active_tree(), layer->id());
+ layer->PushPropertiesTo(layerImpl.get());
+
+ // The impl layer should callnot itself opaque anymore.
+ EXPECT_FALSE(layerImpl->contents_opaque());
+
+ // Impl layer has 1 opacity, but the color is not opaque, so the opaque_rect should be empty.
+ layerImpl->draw_properties().opacity = 1;
+
+ MockQuadCuller quadCuller;
+ AppendQuadsData data;
+ layerImpl->AppendQuads(&quadCuller, &data);
+
+ ASSERT_EQ(quadCuller.quadList().size(), 1U);
+ EXPECT_EQ(gfx::Rect().ToString(), quadCuller.quadList()[0]->opaque_rect.ToString());
+ }
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/texture_layer.cc b/cc/layers/texture_layer.cc
new file mode 100644
index 0000000..2590470
--- /dev/null
+++ b/cc/layers/texture_layer.cc
@@ -0,0 +1,204 @@
+// Copyright 2010 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 "cc/layers/texture_layer.h"
+
+#include "cc/base/thread.h"
+#include "cc/layers/texture_layer_client.h"
+#include "cc/layers/texture_layer_impl.h"
+#include "cc/trees/layer_tree_host.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
+
+namespace cc {
+
+static void RunCallbackOnMainThread(
+ const TextureMailbox::ReleaseCallback& callback,
+ unsigned sync_point) {
+ callback.Run(sync_point);
+}
+
+static void PostCallbackToMainThread(
+ Thread* main_thread,
+ const TextureMailbox::ReleaseCallback& callback,
+ unsigned sync_point) {
+ main_thread->PostTask(
+ base::Bind(&RunCallbackOnMainThread, callback, sync_point));
+}
+
+scoped_refptr<TextureLayer> TextureLayer::Create(TextureLayerClient* client) {
+ return scoped_refptr<TextureLayer>(new TextureLayer(client, false));
+}
+
+scoped_refptr<TextureLayer> TextureLayer::CreateForMailbox() {
+ return scoped_refptr<TextureLayer>(new TextureLayer(NULL, true));
+}
+
+TextureLayer::TextureLayer(TextureLayerClient* client, bool uses_mailbox)
+ : Layer(),
+ client_(client),
+ uses_mailbox_(uses_mailbox),
+ flipped_(true),
+ uv_top_left_(0.f, 0.f),
+ uv_bottom_right_(1.f, 1.f),
+ premultiplied_alpha_(true),
+ rate_limit_context_(false),
+ context_lost_(false),
+ texture_id_(0),
+ content_committed_(false),
+ own_mailbox_(false) {
+ vertex_opacity_[0] = 1.0f;
+ vertex_opacity_[1] = 1.0f;
+ vertex_opacity_[2] = 1.0f;
+ vertex_opacity_[3] = 1.0f;
+}
+
+TextureLayer::~TextureLayer() {
+ if (layer_tree_host()) {
+ if (texture_id_)
+ layer_tree_host()->AcquireLayerTextures();
+ if (rate_limit_context_ && client_)
+ layer_tree_host()->StopRateLimiter(client_->context());
+ }
+ if (own_mailbox_)
+ texture_mailbox_.RunReleaseCallback(texture_mailbox_.sync_point());
+}
+
+scoped_ptr<LayerImpl> TextureLayer::CreateLayerImpl(LayerTreeImpl* tree_impl) {
+ return TextureLayerImpl::Create(tree_impl, id(), uses_mailbox_).
+ PassAs<LayerImpl>();
+}
+
+void TextureLayer::SetFlipped(bool flipped) {
+ flipped_ = flipped;
+ SetNeedsCommit();
+}
+
+void TextureLayer::SetUV(gfx::PointF top_left, gfx::PointF bottom_right) {
+ uv_top_left_ = top_left;
+ uv_bottom_right_ = bottom_right;
+ SetNeedsCommit();
+}
+
+void TextureLayer::SetVertexOpacity(float bottom_left,
+ float top_left,
+ float top_right,
+ float bottom_right) {
+ // Indexing according to the quad vertex generation:
+ // 1--2
+ // | |
+ // 0--3
+ vertex_opacity_[0] = bottom_left;
+ vertex_opacity_[1] = top_left;
+ vertex_opacity_[2] = top_right;
+ vertex_opacity_[3] = bottom_right;
+ SetNeedsCommit();
+}
+
+void TextureLayer::SetPremultipliedAlpha(bool premultiplied_alpha) {
+ premultiplied_alpha_ = premultiplied_alpha;
+ SetNeedsCommit();
+}
+
+void TextureLayer::SetRateLimitContext(bool rate_limit) {
+ if (!rate_limit && rate_limit_context_ && client_ && layer_tree_host())
+ layer_tree_host()->StopRateLimiter(client_->context());
+
+ rate_limit_context_ = rate_limit;
+}
+
+void TextureLayer::SetTextureId(unsigned id) {
+ DCHECK(!uses_mailbox_);
+ if (texture_id_ == id)
+ return;
+ if (texture_id_ && layer_tree_host())
+ layer_tree_host()->AcquireLayerTextures();
+ texture_id_ = id;
+ SetNeedsCommit();
+}
+
+void TextureLayer::SetTextureMailbox(const TextureMailbox& mailbox) {
+ DCHECK(uses_mailbox_);
+ DCHECK(mailbox.IsEmpty() || !mailbox.Equals(texture_mailbox_));
+ // If we never commited the mailbox, we need to release it here
+ if (own_mailbox_)
+ texture_mailbox_.RunReleaseCallback(texture_mailbox_.sync_point());
+ texture_mailbox_ = mailbox;
+ own_mailbox_ = true;
+
+ SetNeedsCommit();
+}
+
+void TextureLayer::WillModifyTexture() {
+ if (layer_tree_host() && (DrawsContent() || content_committed_)) {
+ layer_tree_host()->AcquireLayerTextures();
+ content_committed_ = false;
+ }
+}
+
+void TextureLayer::SetNeedsDisplayRect(const gfx::RectF& dirty_rect) {
+ Layer::SetNeedsDisplayRect(dirty_rect);
+
+ if (rate_limit_context_ && client_ && layer_tree_host() && DrawsContent())
+ layer_tree_host()->StartRateLimiter(client_->context());
+}
+
+void TextureLayer::SetLayerTreeHost(LayerTreeHost* host) {
+ if (texture_id_ && layer_tree_host() && host != layer_tree_host())
+ layer_tree_host()->AcquireLayerTextures();
+ Layer::SetLayerTreeHost(host);
+}
+
+bool TextureLayer::DrawsContent() const {
+ return (client_ || texture_id_ || !texture_mailbox_.IsEmpty()) &&
+ !context_lost_ && Layer::DrawsContent();
+}
+
+void TextureLayer::Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) {
+ if (client_) {
+ texture_id_ = client_->prepareTexture(*queue);
+ context_lost_ =
+ client_->context()->getGraphicsResetStatusARB() != GL_NO_ERROR;
+ }
+
+ needs_display_ = false;
+}
+
+void TextureLayer::PushPropertiesTo(LayerImpl* layer) {
+ Layer::PushPropertiesTo(layer);
+
+ TextureLayerImpl* texture_layer = static_cast<TextureLayerImpl*>(layer);
+ texture_layer->set_flipped(flipped_);
+ texture_layer->set_uv_top_left(uv_top_left_);
+ texture_layer->set_uv_bottom_right(uv_bottom_right_);
+ texture_layer->set_vertex_opacity(vertex_opacity_);
+ texture_layer->set_premultiplied_alpha(premultiplied_alpha_);
+ if (uses_mailbox_ && own_mailbox_) {
+ Thread* main_thread = layer_tree_host()->proxy()->MainThread();
+ TextureMailbox::ReleaseCallback callback;
+ if (!texture_mailbox_.IsEmpty())
+ callback = base::Bind(
+ &PostCallbackToMainThread, main_thread, texture_mailbox_.callback());
+ texture_layer->SetTextureMailbox(TextureMailbox(
+ texture_mailbox_.name(), callback, texture_mailbox_.sync_point()));
+ own_mailbox_ = false;
+ } else {
+ texture_layer->set_texture_id(texture_id_);
+ }
+ content_committed_ = DrawsContent();
+}
+
+bool TextureLayer::BlocksPendingCommit() const {
+ // Double-buffered texture layers need to be blocked until they can be made
+ // triple-buffered. Single-buffered layers already prevent draws, so
+ // can block too for simplicity.
+ return DrawsContent();
+}
+
+bool TextureLayer::CanClipSelf() const {
+ return true;
+}
+
+} // namespace cc
diff --git a/cc/layers/texture_layer.h b/cc/layers/texture_layer.h
new file mode 100644
index 0000000..a957b4c
--- /dev/null
+++ b/cc/layers/texture_layer.h
@@ -0,0 +1,106 @@
+// Copyright 2010 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.
+
+#ifndef CC_LAYERS_TEXTURE_LAYER_H_
+#define CC_LAYERS_TEXTURE_LAYER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer.h"
+#include "cc/resources/texture_mailbox.h"
+
+namespace WebKit { class WebGraphicsContext3D; }
+
+namespace cc {
+
+class TextureLayerClient;
+
+// A Layer containing a the rendered output of a plugin instance.
+class CC_EXPORT TextureLayer : public Layer {
+ public:
+ // If this texture layer requires special preparation logic for each frame
+ // driven by the compositor, pass in a non-nil client. Pass in a nil client
+ // pointer if texture updates are driven by an external process.
+ static scoped_refptr<TextureLayer> Create(TextureLayerClient* client);
+
+ // Used when mailbox names are specified instead of texture IDs.
+ static scoped_refptr<TextureLayer> CreateForMailbox();
+
+ void ClearClient() { client_ = NULL; }
+
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+
+ // Sets whether this texture should be Y-flipped at draw time. Defaults to
+ // true.
+ void SetFlipped(bool flipped);
+
+ // Sets a UV transform to be used at draw time. Defaults to (0, 0) and (1, 1).
+ void SetUV(gfx::PointF top_left, gfx::PointF bottom_right);
+
+ // Sets an opacity value per vertex. It will be multiplied by the layer
+ // opacity value.
+ void SetVertexOpacity(float bottom_left,
+ float top_left,
+ float top_right,
+ float bottom_right);
+
+ // Sets whether the alpha channel is premultiplied or unpremultiplied.
+ // Defaults to true.
+ void SetPremultipliedAlpha(bool premultiplied_alpha);
+
+ // Sets whether this context should rate limit on damage to prevent too many
+ // frames from being queued up before the compositor gets a chance to run.
+ // Requires a non-nil client. Defaults to false.
+ void SetRateLimitContext(bool rate_limit);
+
+ // Code path for plugins which supply their own texture ID.
+ void SetTextureId(unsigned texture_id);
+
+ // Code path for plugins which supply their own mailbox.
+ void SetTextureMailbox(const TextureMailbox& mailbox);
+
+ void WillModifyTexture();
+
+ virtual void SetNeedsDisplayRect(const gfx::RectF& dirty_rect) OVERRIDE;
+
+ virtual void SetLayerTreeHost(LayerTreeHost* layer_tree_host) OVERRIDE;
+ virtual bool DrawsContent() const OVERRIDE;
+ virtual void Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+ virtual bool BlocksPendingCommit() const OVERRIDE;
+
+ virtual bool CanClipSelf() const OVERRIDE;
+
+ protected:
+ TextureLayer(TextureLayerClient* client, bool uses_mailbox);
+ virtual ~TextureLayer();
+
+ private:
+ TextureLayerClient* client_;
+ bool uses_mailbox_;
+
+ bool flipped_;
+ gfx::PointF uv_top_left_;
+ gfx::PointF uv_bottom_right_;
+ // [bottom left, top left, top right, bottom right]
+ float vertex_opacity_[4];
+ bool premultiplied_alpha_;
+ bool rate_limit_context_;
+ bool context_lost_;
+ bool content_committed_;
+
+ unsigned texture_id_;
+ TextureMailbox texture_mailbox_;
+ bool own_mailbox_;
+
+ DISALLOW_COPY_AND_ASSIGN(TextureLayer);
+};
+
+}
+#endif // CC_LAYERS_TEXTURE_LAYER_H_
diff --git a/cc/layers/texture_layer_client.h b/cc/layers/texture_layer_client.h
new file mode 100644
index 0000000..f0a9ee0
--- /dev/null
+++ b/cc/layers/texture_layer_client.h
@@ -0,0 +1,31 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_TEXTURE_LAYER_CLIENT_H_
+#define CC_LAYERS_TEXTURE_LAYER_CLIENT_H_
+
+namespace WebKit {
+class WebGraphicsContext3D;
+}
+
+namespace cc {
+class ResourceUpdateQueue;
+
+class TextureLayerClient {
+public:
+ // Called to prepare this layer's texture for compositing. The client may queue a texture
+ // upload or copy on the ResourceUpdateQueue.
+ // Returns the texture ID to be used for compositing.
+ virtual unsigned prepareTexture(ResourceUpdateQueue&) = 0;
+
+ // Returns the context that is providing the texture. Used for rate limiting and detecting lost context.
+ virtual WebKit::WebGraphicsContext3D* context() = 0;
+
+protected:
+ virtual ~TextureLayerClient() { }
+};
+
+}
+
+#endif // CC_LAYERS_TEXTURE_LAYER_CLIENT_H_
diff --git a/cc/layers/texture_layer_impl.cc b/cc/layers/texture_layer_impl.cc
new file mode 100644
index 0000000..ac366ad
--- /dev/null
+++ b/cc/layers/texture_layer_impl.cc
@@ -0,0 +1,163 @@
+// Copyright 2011 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 "cc/layers/texture_layer_impl.h"
+
+#include "base/stringprintf.h"
+#include "cc/layers/quad_sink.h"
+#include "cc/output/renderer.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "cc/trees/layer_tree_impl.h"
+
+namespace cc {
+
+TextureLayerImpl::TextureLayerImpl(LayerTreeImpl* tree_impl,
+ int id,
+ bool uses_mailbox)
+ : LayerImpl(tree_impl, id),
+ texture_id_(0),
+ external_texture_resource_(0),
+ premultiplied_alpha_(true),
+ flipped_(true),
+ uv_top_left_(0.f, 0.f),
+ uv_bottom_right_(1.f, 1.f),
+ uses_mailbox_(uses_mailbox),
+ own_mailbox_(false) {
+ vertex_opacity_[0] = 1.0f;
+ vertex_opacity_[1] = 1.0f;
+ vertex_opacity_[2] = 1.0f;
+ vertex_opacity_[3] = 1.0f;
+}
+
+TextureLayerImpl::~TextureLayerImpl() { FreeTextureMailbox(); }
+
+void TextureLayerImpl::SetTextureMailbox(const TextureMailbox& mailbox) {
+ DCHECK(uses_mailbox_);
+ DCHECK(mailbox.IsEmpty() || !mailbox.Equals(texture_mailbox_));
+ FreeTextureMailbox();
+ texture_mailbox_ = mailbox;
+ own_mailbox_ = true;
+}
+
+scoped_ptr<LayerImpl> TextureLayerImpl::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return TextureLayerImpl::Create(tree_impl, id(), uses_mailbox_).
+ PassAs<LayerImpl>();
+}
+
+void TextureLayerImpl::PushPropertiesTo(LayerImpl* layer) {
+ LayerImpl::PushPropertiesTo(layer);
+
+ TextureLayerImpl* texture_layer = static_cast<TextureLayerImpl*>(layer);
+ texture_layer->set_flipped(flipped_);
+ texture_layer->set_uv_top_left(uv_top_left_);
+ texture_layer->set_uv_bottom_right(uv_bottom_right_);
+ texture_layer->set_vertex_opacity(vertex_opacity_);
+ texture_layer->set_premultiplied_alpha(premultiplied_alpha_);
+ if (uses_mailbox_ && own_mailbox_) {
+ texture_layer->SetTextureMailbox(texture_mailbox_);
+ own_mailbox_ = false;
+ } else {
+ texture_layer->set_texture_id(texture_id_);
+ }
+}
+
+void TextureLayerImpl::WillDraw(ResourceProvider* resource_provider) {
+ if (uses_mailbox_ || !texture_id_)
+ return;
+ DCHECK(!external_texture_resource_);
+ external_texture_resource_ =
+ resource_provider->CreateResourceFromExternalTexture(texture_id_);
+}
+
+void TextureLayerImpl::AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) {
+ if (!external_texture_resource_)
+ return;
+
+ SharedQuadState* shared_quad_state =
+ quad_sink->UseSharedQuadState(CreateSharedQuadState());
+ AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
+
+ gfx::Rect quad_rect(content_bounds());
+ gfx::Rect opaque_rect(contents_opaque() ? quad_rect : gfx::Rect());
+ scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ quad_rect,
+ opaque_rect,
+ external_texture_resource_,
+ premultiplied_alpha_,
+ uv_top_left_,
+ uv_bottom_right_,
+ vertex_opacity_,
+ flipped_);
+
+ // Perform explicit clipping on a quad to avoid setting a scissor later.
+ if (shared_quad_state->is_clipped && quad->PerformClipping())
+ shared_quad_state->is_clipped = false;
+ if (!quad->rect.IsEmpty())
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+}
+
+void TextureLayerImpl::DidDraw(ResourceProvider* resource_provider) {
+ if (uses_mailbox_ || !external_texture_resource_)
+ return;
+ // FIXME: the following assert will not be true when sending resources to a
+ // parent compositor. A synchronization scheme (double-buffering or
+ // pipelining of updates) for the client will need to exist to solve this.
+ DCHECK(!resource_provider->InUseByConsumer(external_texture_resource_));
+ resource_provider->DeleteResource(external_texture_resource_);
+ external_texture_resource_ = 0;
+}
+
+void TextureLayerImpl::DumpLayerProperties(std::string* str, int indent) const {
+ str->append(IndentString(indent));
+ base::StringAppendF(str,
+ "texture layer texture id: %u premultiplied: %d\n",
+ texture_id_,
+ premultiplied_alpha_);
+ LayerImpl::DumpLayerProperties(str, indent);
+}
+
+void TextureLayerImpl::DidLoseOutputSurface() {
+ texture_id_ = 0;
+ external_texture_resource_ = 0;
+}
+
+const char* TextureLayerImpl::LayerTypeAsString() const {
+ return "TextureLayer";
+}
+
+bool TextureLayerImpl::CanClipSelf() const {
+ return true;
+}
+
+void TextureLayerImpl::DidBecomeActive() {
+ if (!own_mailbox_)
+ return;
+ DCHECK(!external_texture_resource_);
+ ResourceProvider* resource_provider = layer_tree_impl()->resource_provider();
+ if (!texture_mailbox_.IsEmpty()) {
+ external_texture_resource_ =
+ resource_provider->CreateResourceFromTextureMailbox(texture_mailbox_);
+ }
+ own_mailbox_ = false;
+}
+
+void TextureLayerImpl::FreeTextureMailbox() {
+ if (!uses_mailbox_)
+ return;
+ if (own_mailbox_) {
+ DCHECK(!external_texture_resource_);
+ texture_mailbox_.RunReleaseCallback(texture_mailbox_.sync_point());
+ } else if (external_texture_resource_) {
+ DCHECK(!own_mailbox_);
+ ResourceProvider* resource_provider =
+ layer_tree_impl()->resource_provider();
+ resource_provider->DeleteResource(external_texture_resource_);
+ external_texture_resource_ = 0;
+ }
+}
+
+} // namespace cc
diff --git a/cc/layers/texture_layer_impl.h b/cc/layers/texture_layer_impl.h
new file mode 100644
index 0000000..bee739a
--- /dev/null
+++ b/cc/layers/texture_layer_impl.h
@@ -0,0 +1,87 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_TEXTURE_LAYER_IMPL_H_
+#define CC_LAYERS_TEXTURE_LAYER_IMPL_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer_impl.h"
+
+namespace cc {
+
+class CC_EXPORT TextureLayerImpl : public LayerImpl {
+ public:
+ static scoped_ptr<TextureLayerImpl> Create(LayerTreeImpl* tree_impl,
+ int id,
+ bool uses_mailbox) {
+ return make_scoped_ptr(new TextureLayerImpl(tree_impl, id, uses_mailbox));
+ }
+ virtual ~TextureLayerImpl();
+
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* layer_tree_impl)
+ OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+
+ virtual void WillDraw(ResourceProvider* resource_provider) OVERRIDE;
+ virtual void AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) OVERRIDE;
+ virtual void DidDraw(ResourceProvider* resource_provider) OVERRIDE;
+
+ virtual void DidLoseOutputSurface() OVERRIDE;
+
+ virtual void DumpLayerProperties(std::string* str, int indent) const OVERRIDE;
+ virtual void DidBecomeActive() OVERRIDE;
+
+ unsigned texture_id() const { return texture_id_; }
+ void set_texture_id(unsigned id) { texture_id_ = id; }
+ void set_premultiplied_alpha(bool premultiplied_alpha) {
+ premultiplied_alpha_ = premultiplied_alpha;
+ }
+ void set_flipped(bool flipped) { flipped_ = flipped; }
+ void set_uv_top_left(gfx::PointF top_left) { uv_top_left_ = top_left; }
+ void set_uv_bottom_right(gfx::PointF bottom_right) {
+ uv_bottom_right_ = bottom_right;
+ }
+
+ // 1--2
+ // | |
+ // 0--3
+ void set_vertex_opacity(const float vertex_opacity[4]) {
+ vertex_opacity_[0] = vertex_opacity[0];
+ vertex_opacity_[1] = vertex_opacity[1];
+ vertex_opacity_[2] = vertex_opacity[2];
+ vertex_opacity_[3] = vertex_opacity[3];
+ }
+
+ virtual bool CanClipSelf() const OVERRIDE;
+
+ void SetTextureMailbox(const TextureMailbox& mailbox);
+
+ private:
+ TextureLayerImpl(LayerTreeImpl* tree_impl, int id, bool uses_mailbox);
+
+ virtual const char* LayerTypeAsString() const OVERRIDE;
+ void FreeTextureMailbox();
+
+ unsigned texture_id_;
+ ResourceProvider::ResourceId external_texture_resource_;
+ bool premultiplied_alpha_;
+ bool flipped_;
+ gfx::PointF uv_top_left_;
+ gfx::PointF uv_bottom_right_;
+ float vertex_opacity_[4];
+
+ TextureMailbox texture_mailbox_;
+ bool uses_mailbox_;
+ bool own_mailbox_;
+
+ DISALLOW_COPY_AND_ASSIGN(TextureLayerImpl);
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_TEXTURE_LAYER_IMPL_H_
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc
new file mode 100644
index 0000000..0e48b1b
--- /dev/null
+++ b/cc/layers/texture_layer_unittest.cc
@@ -0,0 +1,471 @@
+// Copyright 2012 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 "cc/layers/texture_layer.h"
+
+#include <string>
+
+#include "base/callback.h"
+#include "cc/base/thread.h"
+#include "cc/layers/texture_layer_impl.h"
+#include "cc/test/fake_impl_proxy.h"
+#include "cc/test/fake_layer_tree_host_client.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/layer_tree_test_common.h"
+#include "cc/trees/layer_tree_host.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "cc/trees/single_thread_proxy.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::Mock;
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::AnyNumber;
+
+namespace cc {
+namespace {
+
+class MockLayerImplTreeHost : public LayerTreeHost {
+ public:
+ MockLayerImplTreeHost() : LayerTreeHost(&fake_client_, LayerTreeSettings()) {
+ Initialize(scoped_ptr<Thread>(NULL));
+ }
+
+ MOCK_METHOD0(AcquireLayerTextures, void());
+ MOCK_METHOD0(SetNeedsCommit, void());
+
+ private:
+ FakeLayerImplTreeHostClient fake_client_;
+};
+
+class TextureLayerTest : public testing::Test {
+ public:
+ TextureLayerTest() : host_impl_(&proxy_) {}
+
+ protected:
+ virtual void SetUp() {
+ layer_tree_host_.reset(new MockLayerImplTreeHost);
+ }
+
+ virtual void TearDown() {
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AnyNumber());
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AnyNumber());
+
+ layer_tree_host_->SetRootLayer(NULL);
+ layer_tree_host_.reset();
+ }
+
+ scoped_ptr<MockLayerImplTreeHost> layer_tree_host_;
+ FakeImplProxy proxy_;
+ FakeLayerTreeHostImpl host_impl_;
+};
+
+TEST_F(TextureLayerTest, SyncImplWhenChangingTextureId) {
+ scoped_refptr<TextureLayer> test_layer = TextureLayer::Create(NULL);
+ ASSERT_TRUE(test_layer);
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AnyNumber());
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AnyNumber());
+ layer_tree_host_->SetRootLayer(test_layer);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+ EXPECT_EQ(test_layer->layer_tree_host(), layer_tree_host_.get());
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
+ test_layer->SetTextureId(1);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AtLeast(1));
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
+ test_layer->SetTextureId(2);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AtLeast(1));
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
+ test_layer->SetTextureId(0);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+}
+
+TEST_F(TextureLayerTest, SyncImplWhenDrawing) {
+ gfx::RectF dirty_rect(0.f, 0.f, 1.f, 1.f);
+
+ scoped_refptr<TextureLayer> test_layer = TextureLayer::Create(NULL);
+ ASSERT_TRUE(test_layer);
+ scoped_ptr<TextureLayerImpl> impl_layer;
+ impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, false);
+ ASSERT_TRUE(impl_layer);
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AnyNumber());
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AnyNumber());
+ layer_tree_host_->SetRootLayer(test_layer);
+ test_layer->SetTextureId(1);
+ test_layer->SetIsDrawable(true);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+ EXPECT_EQ(test_layer->layer_tree_host(), layer_tree_host_.get());
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(1);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(0);
+ test_layer->WillModifyTexture();
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1);
+ test_layer->SetNeedsDisplayRect(dirty_rect);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1);
+ test_layer->PushPropertiesTo(impl_layer.get()); // fake commit
+ test_layer->SetIsDrawable(false);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+
+ // Verify that non-drawable layers don't signal the compositor,
+ // except for the first draw after last commit, which must acquire
+ // the texture.
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(1);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(0);
+ test_layer->WillModifyTexture();
+ test_layer->SetNeedsDisplayRect(dirty_rect);
+ test_layer->PushPropertiesTo(impl_layer.get()); // fake commit
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+
+ // Second draw with layer in non-drawable state: no texture
+ // acquisition.
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(0);
+ test_layer->WillModifyTexture();
+ test_layer->SetNeedsDisplayRect(dirty_rect);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+}
+
+TEST_F(TextureLayerTest, SyncImplWhenRemovingFromTree) {
+ scoped_refptr<Layer> root_layer = Layer::Create();
+ ASSERT_TRUE(root_layer);
+ scoped_refptr<Layer> child_layer = Layer::Create();
+ ASSERT_TRUE(child_layer);
+ root_layer->AddChild(child_layer);
+ scoped_refptr<TextureLayer> test_layer = TextureLayer::Create(NULL);
+ ASSERT_TRUE(test_layer);
+ test_layer->SetTextureId(0);
+ child_layer->AddChild(test_layer);
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AnyNumber());
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AnyNumber());
+ layer_tree_host_->SetRootLayer(root_layer);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
+ test_layer->RemoveFromParent();
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
+ child_layer->AddChild(test_layer);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
+ test_layer->SetTextureId(1);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AtLeast(1));
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
+ test_layer->RemoveFromParent();
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+}
+
+class MockMailboxCallback {
+ public:
+ MOCK_METHOD2(Release, void(const std::string& mailbox, unsigned sync_point));
+};
+
+struct CommonMailboxObjects {
+ CommonMailboxObjects()
+ : mailbox_name1_(64, '1'),
+ mailbox_name2_(64, '2'),
+ sync_point1_(1),
+ sync_point2_(2) {
+ release_mailbox1_ = base::Bind(&MockMailboxCallback::Release,
+ base::Unretained(&mock_callback_),
+ mailbox_name1_);
+ release_mailbox2_ = base::Bind(&MockMailboxCallback::Release,
+ base::Unretained(&mock_callback_),
+ mailbox_name2_);
+ gpu::Mailbox m1;
+ m1.SetName(reinterpret_cast<const int8*>(mailbox_name1_.data()));
+ mailbox1_ = TextureMailbox(m1, release_mailbox1_, sync_point1_);
+ gpu::Mailbox m2;
+ m2.SetName(reinterpret_cast<const int8*>(mailbox_name2_.data()));
+ mailbox2_ = TextureMailbox(m2, release_mailbox2_, sync_point2_);
+ }
+
+ std::string mailbox_name1_;
+ std::string mailbox_name2_;
+ MockMailboxCallback mock_callback_;
+ TextureMailbox::ReleaseCallback release_mailbox1_;
+ TextureMailbox::ReleaseCallback release_mailbox2_;
+ TextureMailbox mailbox1_;
+ TextureMailbox mailbox2_;
+ unsigned sync_point1_;
+ unsigned sync_point2_;
+};
+
+class TextureLayerWithMailboxTest : public TextureLayerTest {
+ protected:
+ virtual void TearDown() {
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+ EXPECT_CALL(test_data_.mock_callback_,
+ Release(test_data_.mailbox_name1_,
+ test_data_.sync_point1_)).Times(1);
+ TextureLayerTest::TearDown();
+ }
+
+ CommonMailboxObjects test_data_;
+};
+
+TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) {
+ scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox();
+ ASSERT_TRUE(test_layer);
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AnyNumber());
+ layer_tree_host_->SetRootLayer(test_layer);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
+ test_layer->SetTextureMailbox(test_data_.mailbox1_);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
+ EXPECT_CALL(test_data_.mock_callback_,
+ Release(test_data_.mailbox_name1_, test_data_.sync_point1_))
+ .Times(1);
+ test_layer->SetTextureMailbox(test_data_.mailbox2_);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
+ EXPECT_CALL(test_data_.mock_callback_,
+ Release(test_data_.mailbox_name2_, test_data_.sync_point2_))
+ .Times(1);
+ test_layer->SetTextureMailbox(TextureMailbox());
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+ // Test destructor.
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
+ test_layer->SetTextureMailbox(test_data_.mailbox1_);
+}
+
+class TextureLayerImplWithMailboxThreadedCallback : public ThreadedTest {
+ public:
+ TextureLayerImplWithMailboxThreadedCallback()
+ : callback_count_(0),
+ commit_count_(0) {}
+
+ // Make sure callback is received on main and doesn't block the impl thread.
+ void ReleaseCallback(unsigned sync_point) {
+ EXPECT_EQ(true, proxy()->IsMainThread());
+ ++callback_count_;
+ }
+
+ void SetMailbox(char mailbox_char) {
+ TextureMailbox mailbox(
+ std::string(64, mailbox_char),
+ base::Bind(
+ &TextureLayerImplWithMailboxThreadedCallback::ReleaseCallback,
+ base::Unretained(this)));
+ layer_->SetTextureMailbox(mailbox);
+ }
+
+ virtual void beginTest() OVERRIDE {
+ gfx::Size bounds(100, 100);
+ root_ = Layer::Create();
+ root_->SetAnchorPoint(gfx::PointF());
+ root_->SetBounds(bounds);
+
+ layer_ = TextureLayer::CreateForMailbox();
+ layer_->SetIsDrawable(true);
+ layer_->SetAnchorPoint(gfx::PointF());
+ layer_->SetBounds(bounds);
+
+ root_->AddChild(layer_);
+ m_layerTreeHost->SetRootLayer(root_);
+ m_layerTreeHost->SetViewportSize(bounds, bounds);
+ SetMailbox('1');
+ EXPECT_EQ(0, callback_count_);
+
+ // Case #1: change mailbox before the commit. The old mailbox should be
+ // released immediately.
+ SetMailbox('2');
+ EXPECT_EQ(1, callback_count_);
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void didCommit() OVERRIDE {
+ ++commit_count_;
+ switch (commit_count_) {
+ case 1:
+ // Case #2: change mailbox after the commit (and draw), where the
+ // layer draws. The old mailbox should be released during the next
+ // commit.
+ SetMailbox('3');
+ EXPECT_EQ(1, callback_count_);
+ break;
+ case 2:
+ // Old mailbox was released, task was posted, but won't execute
+ // until this didCommit returns.
+ // TODO(piman): fix this.
+ EXPECT_EQ(1, callback_count_);
+ m_layerTreeHost->SetNeedsCommit();
+ break;
+ case 3:
+ EXPECT_EQ(2, callback_count_);
+ // Case #3: change mailbox when the layer doesn't draw. The old
+ // mailbox should be released during the next commit.
+ layer_->SetBounds(gfx::Size());
+ SetMailbox('4');
+ break;
+ case 4:
+ // Old mailbox was released, task was posted, but won't execute
+ // until this didCommit returns.
+ // TODO(piman): fix this.
+ EXPECT_EQ(2, callback_count_);
+ m_layerTreeHost->SetNeedsCommit();
+ break;
+ case 5:
+ EXPECT_EQ(3, callback_count_);
+ // Case #4: release mailbox that was committed but never drawn. The
+ // old mailbox should be released during the next commit.
+ layer_->SetTextureMailbox(TextureMailbox());
+ break;
+ case 6:
+ // Old mailbox was released, task was posted, but won't execute
+ // until this didCommit returns.
+ // TODO(piman): fix this.
+ EXPECT_EQ(3, callback_count_);
+ m_layerTreeHost->SetNeedsCommit();
+ break;
+ case 7:
+ EXPECT_EQ(4, callback_count_);
+ endTest();
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ virtual void afterTest() OVERRIDE {}
+
+ private:
+ int callback_count_;
+ int commit_count_;
+ scoped_refptr<Layer> root_;
+ scoped_refptr<TextureLayer> layer_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(TextureLayerImplWithMailboxThreadedCallback);
+
+class TextureLayerImplWithMailboxTest : public TextureLayerTest {
+ protected:
+ virtual void SetUp() {
+ TextureLayerTest::SetUp();
+ layer_tree_host_.reset(new MockLayerImplTreeHost);
+ EXPECT_TRUE(host_impl_.InitializeRenderer(createFakeOutputSurface()));
+ }
+
+ CommonMailboxObjects test_data_;
+};
+
+TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) {
+ host_impl_.CreatePendingTree();
+ scoped_ptr<TextureLayerImpl> pending_layer;
+ pending_layer = TextureLayerImpl::Create(host_impl_.pending_tree(), 1, true);
+ ASSERT_TRUE(pending_layer);
+
+ scoped_ptr<LayerImpl> activeLayer(
+ pending_layer->CreateLayerImpl(host_impl_.active_tree()));
+ ASSERT_TRUE(activeLayer);
+
+ pending_layer->SetTextureMailbox(test_data_.mailbox1_);
+
+ // Test multiple commits without an activation.
+ EXPECT_CALL(test_data_.mock_callback_,
+ Release(test_data_.mailbox_name1_, test_data_.sync_point1_))
+ .Times(1);
+ pending_layer->SetTextureMailbox(test_data_.mailbox2_);
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+ // Test callback after activation.
+ pending_layer->PushPropertiesTo(activeLayer.get());
+ activeLayer->DidBecomeActive();
+
+ EXPECT_CALL(test_data_.mock_callback_, Release(_, _)).Times(0);
+ pending_layer->SetTextureMailbox(test_data_.mailbox1_);
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+ EXPECT_CALL(test_data_.mock_callback_, Release(test_data_.mailbox_name2_, _))
+ .Times(1);
+ pending_layer->PushPropertiesTo(activeLayer.get());
+ activeLayer->DidBecomeActive();
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+ // Test resetting the mailbox.
+ EXPECT_CALL(test_data_.mock_callback_, Release(test_data_.mailbox_name1_, _))
+ .Times(1);
+ pending_layer->SetTextureMailbox(TextureMailbox());
+ pending_layer->PushPropertiesTo(activeLayer.get());
+ activeLayer->DidBecomeActive();
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+ // Test destructor.
+ EXPECT_CALL(test_data_.mock_callback_,
+ Release(test_data_.mailbox_name1_, test_data_.sync_point1_))
+ .Times(1);
+ pending_layer->SetTextureMailbox(test_data_.mailbox1_);
+}
+
+TEST_F(TextureLayerImplWithMailboxTest,
+ TestDestructorCallbackOnCreatedResource) {
+ scoped_ptr<TextureLayerImpl> impl_layer;
+ impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true);
+ ASSERT_TRUE(impl_layer);
+
+ EXPECT_CALL(test_data_.mock_callback_, Release(test_data_.mailbox_name1_, _))
+ .Times(1);
+ impl_layer->SetTextureMailbox(test_data_.mailbox1_);
+ impl_layer->WillDraw(host_impl_.active_tree()->resource_provider());
+ impl_layer->DidDraw(host_impl_.active_tree()->resource_provider());
+ impl_layer->SetTextureMailbox(TextureMailbox());
+}
+
+TEST_F(TextureLayerImplWithMailboxTest, TestCallbackOnInUseResource) {
+ ResourceProvider* provider = host_impl_.active_tree()->resource_provider();
+ ResourceProvider::ResourceId id =
+ provider->CreateResourceFromTextureMailbox(test_data_.mailbox1_);
+ provider->AllocateForTesting(id);
+
+ // Transfer some resources to the parent.
+ ResourceProvider::ResourceIdArray resource_ids_to_transfer;
+ resource_ids_to_transfer.push_back(id);
+ TransferableResourceArray list;
+ provider->PrepareSendToParent(resource_ids_to_transfer, &list);
+ EXPECT_TRUE(provider->InUseByConsumer(id));
+ EXPECT_CALL(test_data_.mock_callback_, Release(_, _)).Times(0);
+ provider->DeleteResource(id);
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+ EXPECT_CALL(test_data_.mock_callback_, Release(test_data_.mailbox_name1_, _))
+ .Times(1);
+ provider->ReceiveFromParent(list);
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/tiled_layer.cc b/cc/layers/tiled_layer.cc
new file mode 100644
index 0000000..e64222b
--- /dev/null
+++ b/cc/layers/tiled_layer.cc
@@ -0,0 +1,888 @@
+// Copyright 2011 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 "cc/layers/tiled_layer.h"
+
+#include "base/auto_reset.h"
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "cc/debug/overdraw_metrics.h"
+#include "cc/layers/layer_impl.h"
+#include "cc/layers/tiled_layer_impl.h"
+#include "cc/resources/layer_updater.h"
+#include "cc/resources/prioritized_resource.h"
+#include "cc/resources/priority_calculator.h"
+#include "cc/trees/layer_tree_host.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "ui/gfx/rect_conversions.h"
+
+namespace cc {
+
+// Maximum predictive expansion of the visible area.
+static const int kMaxPredictiveTilesCount = 2;
+
+// Number of rows/columns of tiles to pre-paint.
+// We should increase these further as all textures are
+// prioritized and we insure performance doesn't suffer.
+static const int kPrepaintRows = 4;
+static const int kPrepaintColumns = 2;
+
+class UpdatableTile : public LayerTilingData::Tile {
+ public:
+ static scoped_ptr<UpdatableTile> Create(
+ scoped_ptr<LayerUpdater::Resource> updater_resource) {
+ return make_scoped_ptr(new UpdatableTile(updater_resource.Pass()));
+ }
+
+ LayerUpdater::Resource* updater_resource() { return updater_resource_.get(); }
+ PrioritizedResource* managed_resource() {
+ return updater_resource_->texture();
+ }
+
+ bool is_dirty() const { return !dirty_rect.IsEmpty(); }
+
+ // Reset update state for the current frame. This should occur before painting
+ // for all layers. Since painting one layer can invalidate another layer after
+ // it has already painted, mark all non-dirty tiles as valid before painting
+ // such that invalidations during painting won't prevent them from being
+ // pushed.
+ void ResetUpdateState() {
+ update_rect = gfx::Rect();
+ occluded = false;
+ partial_update = false;
+ valid_for_frame = !is_dirty();
+ }
+
+ // This promises to update the tile and therefore also guarantees the tile
+ // will be valid for this frame. dirty_rect is copied into update_rect so we
+ // can continue to track re-entrant invalidations that occur during painting.
+ void MarkForUpdate() {
+ valid_for_frame = true;
+ update_rect = dirty_rect;
+ dirty_rect = gfx::Rect();
+ }
+
+ gfx::Rect dirty_rect;
+ gfx::Rect update_rect;
+ bool partial_update;
+ bool valid_for_frame;
+ bool occluded;
+
+ private:
+ explicit UpdatableTile(scoped_ptr<LayerUpdater::Resource> updater_resource)
+ : partial_update(false),
+ valid_for_frame(false),
+ occluded(false),
+ updater_resource_(updater_resource.Pass()) {}
+
+ scoped_ptr<LayerUpdater::Resource> updater_resource_;
+
+ DISALLOW_COPY_AND_ASSIGN(UpdatableTile);
+};
+
+TiledLayer::TiledLayer()
+ : ContentsScalingLayer(),
+ texture_format_(GL_INVALID_ENUM),
+ skips_draw_(false),
+ failed_update_(false),
+ tiling_option_(AUTO_TILE) {
+ tiler_ =
+ LayerTilingData::Create(gfx::Size(), LayerTilingData::HAS_BORDER_TEXELS);
+}
+
+TiledLayer::~TiledLayer() {}
+
+scoped_ptr<LayerImpl> TiledLayer::CreateLayerImpl(LayerTreeImpl* tree_impl) {
+ return TiledLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
+}
+
+void TiledLayer::UpdateTileSizeAndTilingOption() {
+ DCHECK(layer_tree_host());
+
+ gfx::Size default_tile_size = layer_tree_host()->settings().defaultTileSize;
+ gfx::Size max_untiled_layer_size =
+ layer_tree_host()->settings().maxUntiledLayerSize;
+ int layer_width = content_bounds().width();
+ int layer_height = content_bounds().height();
+
+ gfx::Size tile_size(std::min(default_tile_size.width(), layer_width),
+ std::min(default_tile_size.height(), layer_height));
+
+ // Tile if both dimensions large, or any one dimension large and the other
+ // extends into a second tile but the total layer area isn't larger than that
+ // of the largest possible untiled layer. This heuristic allows for long
+ // skinny layers (e.g. scrollbars) that are Nx1 tiles to minimize wasted
+ // texture space but still avoids creating very large tiles.
+ bool any_dimension_large = layer_width > max_untiled_layer_size.width() ||
+ layer_height > max_untiled_layer_size.height();
+ bool any_dimension_one_tile =
+ (layer_width <= default_tile_size.width() ||
+ layer_height <= default_tile_size.height()) &&
+ (layer_width * layer_height) <= (max_untiled_layer_size.width() *
+ max_untiled_layer_size.height());
+ bool auto_tiled = any_dimension_large && !any_dimension_one_tile;
+
+ bool is_tiled;
+ if (tiling_option_ == ALWAYS_TILE)
+ is_tiled = true;
+ else if (tiling_option_ == NEVER_TILE)
+ is_tiled = false;
+ else
+ is_tiled = auto_tiled;
+
+ gfx::Size requested_size = is_tiled ? tile_size : content_bounds();
+ const int max_size =
+ layer_tree_host()->GetRendererCapabilities().max_texture_size;
+ requested_size.ClampToMax(gfx::Size(max_size, max_size));
+ SetTileSize(requested_size);
+}
+
+void TiledLayer::UpdateBounds() {
+ gfx::Size old_bounds = tiler_->bounds();
+ gfx::Size new_bounds = content_bounds();
+ if (old_bounds == new_bounds)
+ return;
+ tiler_->SetBounds(new_bounds);
+
+ // Invalidate any areas that the new bounds exposes.
+ Region old_region = gfx::Rect(gfx::Point(), old_bounds);
+ Region new_region = gfx::Rect(gfx::Point(), new_bounds);
+ new_region.Subtract(old_region);
+ for (Region::Iterator new_rects(new_region);
+ new_rects.has_rect();
+ new_rects.next())
+ InvalidateContentRect(new_rects.rect());
+}
+
+void TiledLayer::SetTileSize(gfx::Size size) { tiler_->SetTileSize(size); }
+
+void TiledLayer::SetBorderTexelOption(
+ LayerTilingData::BorderTexelOption border_texel_option) {
+ tiler_->SetBorderTexelOption(border_texel_option);
+}
+
+bool TiledLayer::DrawsContent() const {
+ if (!ContentsScalingLayer::DrawsContent())
+ return false;
+
+ bool has_more_than_one_tile =
+ tiler_->num_tiles_x() > 1 || tiler_->num_tiles_y() > 1;
+ if (tiling_option_ == NEVER_TILE && has_more_than_one_tile)
+ return false;
+
+ return true;
+}
+
+void TiledLayer::SetIsMask(bool is_mask) {
+ set_tiling_option(is_mask ? NEVER_TILE : AUTO_TILE);
+}
+
+void TiledLayer::PushPropertiesTo(LayerImpl* layer) {
+ ContentsScalingLayer::PushPropertiesTo(layer);
+
+ TiledLayerImpl* tiled_layer = static_cast<TiledLayerImpl*>(layer);
+
+ tiled_layer->set_skips_draw(skips_draw_);
+ tiled_layer->SetTilingData(*tiler_);
+ std::vector<UpdatableTile*> invalid_tiles;
+
+ for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
+ iter != tiler_->tiles().end();
+ ++iter) {
+ int i = iter->first.first;
+ int j = iter->first.second;
+ UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+
+ if (!tile->managed_resource()->haveBackingTexture()) {
+ // Evicted tiles get deleted from both layers
+ invalid_tiles.push_back(tile);
+ continue;
+ }
+
+ if (!tile->valid_for_frame) {
+ // Invalidated tiles are set so they can get different debug colors.
+ tiled_layer->PushInvalidTile(i, j);
+ continue;
+ }
+
+ tiled_layer->PushTileProperties(
+ i,
+ j,
+ tile->managed_resource()->resourceId(),
+ tile->opaque_rect(),
+ tile->managed_resource()->contentsSwizzled());
+ }
+ for (std::vector<UpdatableTile*>::const_iterator iter = invalid_tiles.begin();
+ iter != invalid_tiles.end();
+ ++iter)
+ tiler_->TakeTile((*iter)->i(), (*iter)->j());
+}
+
+bool TiledLayer::BlocksPendingCommit() const { return true; }
+
+PrioritizedResourceManager* TiledLayer::ResourceManager() const {
+ if (!layer_tree_host())
+ return NULL;
+ return layer_tree_host()->contents_texture_manager();
+}
+
+const PrioritizedResource* TiledLayer::ResourceAtForTesting(int i,
+ int j) const {
+ UpdatableTile* tile = TileAt(i, j);
+ if (!tile)
+ return NULL;
+ return tile->managed_resource();
+}
+
+void TiledLayer::SetLayerTreeHost(LayerTreeHost* host) {
+ if (host && host != layer_tree_host()) {
+ for (LayerTilingData::TileMap::const_iterator
+ iter = tiler_->tiles().begin();
+ iter != tiler_->tiles().end();
+ ++iter) {
+ UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+ tile->managed_resource()->setTextureManager(
+ host->contents_texture_manager());
+ }
+ }
+ ContentsScalingLayer::SetLayerTreeHost(host);
+}
+
+UpdatableTile* TiledLayer::TileAt(int i, int j) const {
+ return static_cast<UpdatableTile*>(tiler_->TileAt(i, j));
+}
+
+UpdatableTile* TiledLayer::CreateTile(int i, int j) {
+ CreateUpdaterIfNeeded();
+
+ scoped_ptr<UpdatableTile> tile(
+ UpdatableTile::Create(Updater()->CreateResource(ResourceManager())));
+ tile->managed_resource()->setDimensions(tiler_->tile_size(), texture_format_);
+
+ UpdatableTile* added_tile = tile.get();
+ tiler_->AddTile(tile.PassAs<LayerTilingData::Tile>(), i, j);
+
+ added_tile->dirty_rect = tiler_->TileRect(added_tile);
+
+ // Temporary diagnostic crash.
+ CHECK(added_tile);
+ CHECK(TileAt(i, j));
+
+ return added_tile;
+}
+
+void TiledLayer::SetNeedsDisplayRect(const gfx::RectF& dirty_rect) {
+ InvalidateContentRect(LayerRectToContentRect(dirty_rect));
+ ContentsScalingLayer::SetNeedsDisplayRect(dirty_rect);
+}
+
+void TiledLayer::InvalidateContentRect(gfx::Rect content_rect) {
+ UpdateBounds();
+ if (tiler_->is_empty() || content_rect.IsEmpty() || skips_draw_)
+ return;
+
+ for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
+ iter != tiler_->tiles().end();
+ ++iter) {
+ UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
+ DCHECK(tile);
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+ gfx::Rect bound = tiler_->TileRect(tile);
+ bound.Intersect(content_rect);
+ tile->dirty_rect.Union(bound);
+ }
+}
+
+// Returns true if tile is dirty and only part of it needs to be updated.
+bool TiledLayer::TileOnlyNeedsPartialUpdate(UpdatableTile* tile) {
+ return !tile->dirty_rect.Contains(tiler_->TileRect(tile)) &&
+ tile->managed_resource()->haveBackingTexture();
+}
+
+bool TiledLayer::UpdateTiles(int left,
+ int top,
+ int right,
+ int bottom,
+ ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats,
+ bool* did_paint) {
+ *did_paint = false;
+ CreateUpdaterIfNeeded();
+
+ bool ignore_occlusions = !occlusion;
+ if (!HaveTexturesForTiles(left, top, right, bottom, ignore_occlusions)) {
+ failed_update_ = true;
+ return false;
+ }
+
+ gfx::Rect paint_rect =
+ MarkTilesForUpdate(left, top, right, bottom, ignore_occlusions);
+
+ if (occlusion)
+ occlusion->overdraw_metrics()->DidPaint(paint_rect);
+
+ if (paint_rect.IsEmpty())
+ return true;
+
+ *did_paint = true;
+ UpdateTileTextures(
+ paint_rect, left, top, right, bottom, queue, occlusion, stats);
+ return true;
+}
+
+void TiledLayer::MarkOcclusionsAndRequestTextures(
+ int left,
+ int top,
+ int right,
+ int bottom,
+ const OcclusionTracker* occlusion) {
+ // There is some difficult dependancies between occlusions, recording
+ // occlusion metrics and requesting memory so those are encapsulated in this
+ // function: - We only want to call RequestLate on unoccluded textures (to
+ // preserve memory for other layers when near OOM). - We only want to record
+ // occlusion metrics if all memory requests succeed.
+
+ int occluded_tile_count = 0;
+ bool succeeded = true;
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ UpdatableTile* tile = TileAt(i, j);
+ DCHECK(tile); // Did SetTexturePriorities get skipped?
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+ // Did ResetUpdateState get skipped? Are we doing more than one occlusion
+ // pass?
+ DCHECK(!tile->occluded);
+ gfx::Rect visible_tile_rect = gfx::IntersectRects(
+ tiler_->tile_bounds(i, j), visible_content_rect());
+ if (occlusion && occlusion->Occluded(render_target(),
+ visible_tile_rect,
+ draw_transform(),
+ draw_transform_is_animating(),
+ is_clipped(),
+ clip_rect(),
+ NULL)) {
+ tile->occluded = true;
+ occluded_tile_count++;
+ } else {
+ succeeded &= tile->managed_resource()->requestLate();
+ }
+ }
+ }
+
+ if (!succeeded)
+ return;
+ if (occlusion)
+ occlusion->overdraw_metrics()->DidCullTilesForUpload(occluded_tile_count);
+}
+
+bool TiledLayer::HaveTexturesForTiles(int left,
+ int top,
+ int right,
+ int bottom,
+ bool ignore_occlusions) {
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ UpdatableTile* tile = TileAt(i, j);
+ DCHECK(tile); // Did SetTexturePriorites get skipped?
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+
+ // Ensure the entire tile is dirty if we don't have the texture.
+ if (!tile->managed_resource()->haveBackingTexture())
+ tile->dirty_rect = tiler_->TileRect(tile);
+
+ // If using occlusion and the visible region of the tile is occluded,
+ // don't reserve a texture or update the tile.
+ if (tile->occluded && !ignore_occlusions)
+ continue;
+
+ if (!tile->managed_resource()->canAcquireBackingTexture())
+ return false;
+ }
+ }
+ return true;
+}
+
+gfx::Rect TiledLayer::MarkTilesForUpdate(int left,
+ int top,
+ int right,
+ int bottom,
+ bool ignore_occlusions) {
+ gfx::Rect paint_rect;
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ UpdatableTile* tile = TileAt(i, j);
+ DCHECK(tile); // Did SetTexturePriorites get skipped?
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+ if (tile->occluded && !ignore_occlusions)
+ continue;
+ // FIXME: Decide if partial update should be allowed based on cost
+ // of update. https://bugs.webkit.org/show_bug.cgi?id=77376
+ if (tile->is_dirty() && layer_tree_host() &&
+ layer_tree_host()->buffered_updates()) {
+ // If we get a partial update, we use the same texture, otherwise return
+ // the current texture backing, so we don't update visible textures
+ // non-atomically. If the current backing is in-use, it won't be
+ // deleted until after the commit as the texture manager will not allow
+ // deletion or recycling of in-use textures.
+ if (TileOnlyNeedsPartialUpdate(tile) &&
+ layer_tree_host()->RequestPartialTextureUpdate()) {
+ tile->partial_update = true;
+ } else {
+ tile->dirty_rect = tiler_->TileRect(tile);
+ tile->managed_resource()->returnBackingTexture();
+ }
+ }
+
+ paint_rect.Union(tile->dirty_rect);
+ tile->MarkForUpdate();
+ }
+ }
+ return paint_rect;
+}
+
+void TiledLayer::UpdateTileTextures(gfx::Rect paint_rect,
+ int left,
+ int top,
+ int right,
+ int bottom,
+ ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) {
+ // The update_rect should be in layer space. So we have to convert the
+ // paint_rect from content space to layer space.
+ float width_scale =
+ bounds().width() / static_cast<float>(content_bounds().width());
+ float height_scale =
+ bounds().height() / static_cast<float>(content_bounds().height());
+ update_rect_ = gfx::ScaleRect(paint_rect, width_scale, height_scale);
+
+ // Calling PrepareToUpdate() calls into WebKit to paint, which may have the
+ // side effect of disabling compositing, which causes our reference to the
+ // texture updater to be deleted. However, we can't free the memory backing
+ // the SkCanvas until the paint finishes, so we grab a local reference here to
+ // hold the updater alive until the paint completes.
+ scoped_refptr<LayerUpdater> protector(Updater());
+ gfx::Rect painted_opaque_rect;
+ Updater()->PrepareToUpdate(paint_rect,
+ tiler_->tile_size(),
+ 1.f / width_scale,
+ 1.f / height_scale,
+ &painted_opaque_rect,
+ stats);
+
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ UpdatableTile* tile = TileAt(i, j);
+ DCHECK(tile); // Did SetTexturePriorites get skipped?
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+
+ gfx::Rect tile_rect = tiler_->tile_bounds(i, j);
+
+ // Use update_rect as the above loop copied the dirty rect for this frame
+ // to update_rect.
+ gfx::Rect dirty_rect = tile->update_rect;
+ if (dirty_rect.IsEmpty())
+ continue;
+
+ // Save what was painted opaque in the tile. Keep the old area if the
+ // paint didn't touch it, and didn't paint some other part of the tile
+ // opaque.
+ gfx::Rect tile_painted_rect = gfx::IntersectRects(tile_rect, paint_rect);
+ gfx::Rect tile_painted_opaque_rect =
+ gfx::IntersectRects(tile_rect, painted_opaque_rect);
+ if (!tile_painted_rect.IsEmpty()) {
+ gfx::Rect paint_inside_tile_opaque_rect =
+ gfx::IntersectRects(tile->opaque_rect(), tile_painted_rect);
+ bool paint_inside_tile_opaque_rect_is_non_opaque =
+ !tile_painted_opaque_rect.Contains(paint_inside_tile_opaque_rect);
+ bool opaque_paint_not_inside_tile_opaque_rect =
+ !tile_painted_opaque_rect.IsEmpty() &&
+ !tile->opaque_rect().Contains(tile_painted_opaque_rect);
+
+ if (paint_inside_tile_opaque_rect_is_non_opaque ||
+ opaque_paint_not_inside_tile_opaque_rect)
+ tile->set_opaque_rect(tile_painted_opaque_rect);
+ }
+
+ // source_rect starts as a full-sized tile with border texels included.
+ gfx::Rect source_rect = tiler_->TileRect(tile);
+ source_rect.Intersect(dirty_rect);
+ // Paint rect not guaranteed to line up on tile boundaries, so
+ // make sure that source_rect doesn't extend outside of it.
+ source_rect.Intersect(paint_rect);
+
+ tile->update_rect = source_rect;
+
+ if (source_rect.IsEmpty())
+ continue;
+
+ const gfx::Point anchor = tiler_->TileRect(tile).origin();
+
+ // Calculate tile-space rectangle to upload into.
+ gfx::Vector2d dest_offset = source_rect.origin() - anchor;
+ CHECK_GE(dest_offset.x(), 0);
+ CHECK_GE(dest_offset.y(), 0);
+
+ // Offset from paint rectangle to this tile's dirty rectangle.
+ gfx::Vector2d paint_offset = source_rect.origin() - paint_rect.origin();
+ CHECK_GE(paint_offset.x(), 0);
+ CHECK_GE(paint_offset.y(), 0);
+ CHECK_LE(paint_offset.x() + source_rect.width(), paint_rect.width());
+ CHECK_LE(paint_offset.y() + source_rect.height(), paint_rect.height());
+
+ tile->updater_resource()->Update(
+ queue, source_rect, dest_offset, tile->partial_update, stats);
+ if (occlusion) {
+ occlusion->overdraw_metrics()->
+ DidUpload(gfx::Transform(), source_rect, tile->opaque_rect());
+ }
+
+ }
+ }
+}
+
+// This picks a small animated layer to be anything less than one viewport. This
+// is specifically for page transitions which are viewport-sized layers. The
+// extra tile of padding is due to these layers being slightly larger than the
+// viewport in some cases.
+bool TiledLayer::IsSmallAnimatedLayer() const {
+ if (!draw_transform_is_animating() && !screen_space_transform_is_animating())
+ return false;
+ gfx::Size viewport_size =
+ layer_tree_host() ? layer_tree_host()->device_viewport_size()
+ : gfx::Size();
+ gfx::Rect content_rect(gfx::Point(), content_bounds());
+ return content_rect.width() <=
+ viewport_size.width() + tiler_->tile_size().width() &&
+ content_rect.height() <=
+ viewport_size.height() + tiler_->tile_size().height();
+}
+
+namespace {
+// FIXME: Remove this and make this based on distance once distance can be
+// calculated for offscreen layers. For now, prioritize all small animated
+// layers after 512 pixels of pre-painting.
+void SetPriorityForTexture(gfx::Rect visible_rect,
+ gfx::Rect tile_rect,
+ bool draws_to_root,
+ bool is_small_animated_layer,
+ PrioritizedResource* texture) {
+ int priority = PriorityCalculator::LowestPriority();
+ if (!visible_rect.IsEmpty()) {
+ priority = PriorityCalculator::PriorityFromDistance(
+ visible_rect, tile_rect, draws_to_root);
+ }
+
+ if (is_small_animated_layer) {
+ priority = PriorityCalculator::max_priority(
+ priority, PriorityCalculator::SmallAnimatedLayerMinPriority());
+ }
+
+ if (priority != PriorityCalculator::LowestPriority())
+ texture->setRequestPriority(priority);
+}
+} // namespace
+
+void TiledLayer::SetTexturePriorities(const PriorityCalculator& priority_calc) {
+ UpdateBounds();
+ ResetUpdateState();
+ UpdateScrollPrediction();
+
+ if (tiler_->has_empty_bounds())
+ return;
+
+ bool draws_to_root = !render_target()->parent();
+ bool small_animated_layer = IsSmallAnimatedLayer();
+
+ // Minimally create the tiles in the desired pre-paint rect.
+ gfx::Rect create_tiles_rect = IdlePaintRect();
+ if (small_animated_layer)
+ create_tiles_rect = gfx::Rect(gfx::Point(), content_bounds());
+ if (!create_tiles_rect.IsEmpty()) {
+ int left, top, right, bottom;
+ tiler_->ContentRectToTileIndices(
+ create_tiles_rect, &left, &top, &right, &bottom);
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ if (!TileAt(i, j))
+ CreateTile(i, j);
+ }
+ }
+ }
+
+ // Now update priorities on all tiles we have in the layer, no matter where
+ // they are.
+ for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
+ iter != tiler_->tiles().end();
+ ++iter) {
+ UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+ gfx::Rect tile_rect = tiler_->TileRect(tile);
+ SetPriorityForTexture(predicted_visible_rect_,
+ tile_rect,
+ draws_to_root,
+ small_animated_layer,
+ tile->managed_resource());
+ }
+}
+
+Region TiledLayer::VisibleContentOpaqueRegion() const {
+ if (skips_draw_)
+ return Region();
+ if (contents_opaque())
+ return visible_content_rect();
+ return tiler_->OpaqueRegionInContentRect(visible_content_rect());
+}
+
+void TiledLayer::ResetUpdateState() {
+ skips_draw_ = false;
+ failed_update_ = false;
+
+ LayerTilingData::TileMap::const_iterator end = tiler_->tiles().end();
+ for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
+ iter != end;
+ ++iter) {
+ UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
+ // FIXME: This should not ever be null.
+ if (!tile)
+ continue;
+ tile->ResetUpdateState();
+ }
+}
+
+namespace {
+gfx::Rect ExpandRectByDelta(gfx::Rect rect, gfx::Vector2d delta) {
+ int width = rect.width() + std::abs(delta.x());
+ int height = rect.height() + std::abs(delta.y());
+ int x = rect.x() + ((delta.x() < 0) ? delta.x() : 0);
+ int y = rect.y() + ((delta.y() < 0) ? delta.y() : 0);
+ return gfx::Rect(x, y, width, height);
+}
+}
+
+void TiledLayer::UpdateScrollPrediction() {
+ // This scroll prediction is very primitive and should be replaced by a
+ // a recursive calculation on all layers which uses actual scroll/animation
+ // velocities. To insure this doesn't miss-predict, we only use it to predict
+ // the visible_rect if:
+ // - content_bounds() hasn't changed.
+ // - visible_rect.size() hasn't changed.
+ // These two conditions prevent rotations, scales, pinch-zooms etc. where
+ // the prediction would be incorrect.
+ gfx::Vector2d delta = visible_content_rect().CenterPoint() -
+ previous_visible_rect_.CenterPoint();
+ predicted_scroll_ = -delta;
+ predicted_visible_rect_ = visible_content_rect();
+ if (previous_content_bounds_ == content_bounds() &&
+ previous_visible_rect_.size() == visible_content_rect().size()) {
+ // Only expand the visible rect in the major scroll direction, to prevent
+ // massive paints due to diagonal scrolls.
+ gfx::Vector2d major_scroll_delta =
+ (std::abs(delta.x()) > std::abs(delta.y())) ?
+ gfx::Vector2d(delta.x(), 0) :
+ gfx::Vector2d(0, delta.y());
+ predicted_visible_rect_ =
+ ExpandRectByDelta(visible_content_rect(), major_scroll_delta);
+
+ // Bound the prediction to prevent unbounded paints, and clamp to content
+ // bounds.
+ gfx::Rect bound = visible_content_rect();
+ bound.Inset(-tiler_->tile_size().width() * kMaxPredictiveTilesCount,
+ -tiler_->tile_size().height() * kMaxPredictiveTilesCount);
+ bound.Intersect(gfx::Rect(gfx::Point(), content_bounds()));
+ predicted_visible_rect_.Intersect(bound);
+ }
+ previous_content_bounds_ = content_bounds();
+ previous_visible_rect_ = visible_content_rect();
+}
+
+void TiledLayer::Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) {
+ DCHECK(!skips_draw_ && !failed_update_); // Did ResetUpdateState get skipped?
+ {
+ base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_,
+ true);
+
+ ContentsScalingLayer::Update(queue, occlusion, stats);
+ UpdateBounds();
+ }
+
+ if (tiler_->has_empty_bounds() || !DrawsContent())
+ return;
+
+ bool did_paint = false;
+
+ // Animation pre-paint. If the layer is small, try to paint it all
+ // immediately whether or not it is occluded, to avoid paint/upload
+ // hiccups while it is animating.
+ if (IsSmallAnimatedLayer()) {
+ int left, top, right, bottom;
+ tiler_->ContentRectToTileIndices(gfx::Rect(gfx::Point(), content_bounds()),
+ &left,
+ &top,
+ &right,
+ &bottom);
+ UpdateTiles(left, top, right, bottom, queue, NULL, stats, &did_paint);
+ if (did_paint)
+ return;
+ // This was an attempt to paint the entire layer so if we fail it's okay,
+ // just fallback on painting visible etc. below.
+ failed_update_ = false;
+ }
+
+ if (predicted_visible_rect_.IsEmpty())
+ return;
+
+ // Visible painting. First occlude visible tiles and paint the non-occluded
+ // tiles.
+ int left, top, right, bottom;
+ tiler_->ContentRectToTileIndices(
+ predicted_visible_rect_, &left, &top, &right, &bottom);
+ MarkOcclusionsAndRequestTextures(left, top, right, bottom, occlusion);
+ skips_draw_ = !UpdateTiles(
+ left, top, right, bottom, queue, occlusion, stats, &did_paint);
+ if (skips_draw_)
+ tiler_->reset();
+ if (skips_draw_ || did_paint)
+ return;
+
+ // If we have already painting everything visible. Do some pre-painting while
+ // idle.
+ gfx::Rect idle_paint_content_rect = IdlePaintRect();
+ if (idle_paint_content_rect.IsEmpty())
+ return;
+
+ // Prepaint anything that was occluded but inside the layer's visible region.
+ if (!UpdateTiles(left, top, right, bottom, queue, NULL, stats, &did_paint) ||
+ did_paint)
+ return;
+
+ int prepaint_left, prepaint_top, prepaint_right, prepaint_bottom;
+ tiler_->ContentRectToTileIndices(idle_paint_content_rect,
+ &prepaint_left,
+ &prepaint_top,
+ &prepaint_right,
+ &prepaint_bottom);
+
+ // Then expand outwards one row/column at a time until we find a dirty
+ // row/column to update. Increment along the major and minor scroll directions
+ // first.
+ gfx::Vector2d delta = -predicted_scroll_;
+ delta = gfx::Vector2d(delta.x() == 0 ? 1 : delta.x(),
+ delta.y() == 0 ? 1 : delta.y());
+ gfx::Vector2d major_delta =
+ (abs(delta.x()) > abs(delta.y())) ? gfx::Vector2d(delta.x(), 0)
+ : gfx::Vector2d(0, delta.y());
+ gfx::Vector2d minor_delta =
+ (abs(delta.x()) <= abs(delta.y())) ? gfx::Vector2d(delta.x(), 0)
+ : gfx::Vector2d(0, delta.y());
+ gfx::Vector2d deltas[4] = { major_delta, minor_delta, -major_delta,
+ -minor_delta };
+ for (int i = 0; i < 4; i++) {
+ if (deltas[i].y() > 0) {
+ while (bottom < prepaint_bottom) {
+ ++bottom;
+ if (!UpdateTiles(
+ left, bottom, right, bottom, queue, NULL, stats, &did_paint) ||
+ did_paint)
+ return;
+ }
+ }
+ if (deltas[i].y() < 0) {
+ while (top > prepaint_top) {
+ --top;
+ if (!UpdateTiles(
+ left, top, right, top, queue, NULL, stats, &did_paint) ||
+ did_paint)
+ return;
+ }
+ }
+ if (deltas[i].x() < 0) {
+ while (left > prepaint_left) {
+ --left;
+ if (!UpdateTiles(
+ left, top, left, bottom, queue, NULL, stats, &did_paint) ||
+ did_paint)
+ return;
+ }
+ }
+ if (deltas[i].x() > 0) {
+ while (right < prepaint_right) {
+ ++right;
+ if (!UpdateTiles(
+ right, top, right, bottom, queue, NULL, stats, &did_paint) ||
+ did_paint)
+ return;
+ }
+ }
+ }
+}
+
+bool TiledLayer::NeedsIdlePaint() {
+ // Don't trigger more paints if we failed (as we'll just fail again).
+ if (failed_update_ || visible_content_rect().IsEmpty() ||
+ tiler_->has_empty_bounds() || !DrawsContent())
+ return false;
+
+ gfx::Rect idle_paint_content_rect = IdlePaintRect();
+ if (idle_paint_content_rect.IsEmpty())
+ return false;
+
+ int left, top, right, bottom;
+ tiler_->ContentRectToTileIndices(
+ idle_paint_content_rect, &left, &top, &right, &bottom);
+
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ UpdatableTile* tile = TileAt(i, j);
+ DCHECK(tile); // Did SetTexturePriorities get skipped?
+ if (!tile)
+ continue;
+
+ bool updated = !tile->update_rect.IsEmpty();
+ bool can_acquire = tile->managed_resource()->canAcquireBackingTexture();
+ bool dirty =
+ tile->is_dirty() || !tile->managed_resource()->haveBackingTexture();
+ if (!updated && can_acquire && dirty)
+ return true;
+ }
+ }
+ return false;
+}
+
+gfx::Rect TiledLayer::IdlePaintRect() {
+ // Don't inflate an empty rect.
+ if (visible_content_rect().IsEmpty())
+ return gfx::Rect();
+
+ gfx::Rect prepaint_rect = visible_content_rect();
+ prepaint_rect.Inset(-tiler_->tile_size().width() * kPrepaintColumns,
+ -tiler_->tile_size().height() * kPrepaintRows);
+ gfx::Rect content_rect(content_bounds());
+ prepaint_rect.Intersect(content_rect);
+
+ return prepaint_rect;
+}
+
+} // namespace cc
diff --git a/cc/layers/tiled_layer.h b/cc/layers/tiled_layer.h
new file mode 100644
index 0000000..94ebada
--- /dev/null
+++ b/cc/layers/tiled_layer.h
@@ -0,0 +1,143 @@
+// Copyright 2011 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.
+
+#ifndef CC_LAYERS_TILED_LAYER_H_
+#define CC_LAYERS_TILED_LAYER_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/contents_scaling_layer.h"
+#include "cc/resources/layer_tiling_data.h"
+
+namespace cc {
+class LayerUpdater;
+class PrioritizedResourceManager;
+class PrioritizedResource;
+class UpdatableTile;
+
+class CC_EXPORT TiledLayer : public ContentsScalingLayer {
+ public:
+ enum TilingOption {
+ ALWAYS_TILE,
+ NEVER_TILE,
+ AUTO_TILE,
+ };
+
+ // Layer implementation.
+ virtual void SetIsMask(bool is_mask) OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+ virtual bool BlocksPendingCommit() const OVERRIDE;
+ virtual bool DrawsContent() const OVERRIDE;
+ virtual void SetNeedsDisplayRect(const gfx::RectF& dirty_rect) OVERRIDE;
+ virtual void SetLayerTreeHost(LayerTreeHost* layer_tree_host) OVERRIDE;
+ virtual void SetTexturePriorities(const PriorityCalculator& priority_calc)
+ OVERRIDE;
+ virtual Region VisibleContentOpaqueRegion() const OVERRIDE;
+ virtual void Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats) OVERRIDE;
+
+ protected:
+ TiledLayer();
+ virtual ~TiledLayer();
+
+ void UpdateTileSizeAndTilingOption();
+ void UpdateBounds();
+
+ // Exposed to subclasses for testing.
+ void SetTileSize(gfx::Size size);
+ void SetTextureFormat(unsigned texture_format) {
+ texture_format_ = texture_format;
+ }
+ void SetBorderTexelOption(LayerTilingData::BorderTexelOption option);
+ size_t NumPaintedTiles() { return tiler_->tiles().size(); }
+
+ virtual LayerUpdater* Updater() const = 0;
+ virtual void CreateUpdaterIfNeeded() = 0;
+
+ // Set invalidations to be potentially repainted during Update().
+ void InvalidateContentRect(gfx::Rect content_rect);
+
+ // Reset state on tiles that will be used for updating the layer.
+ void ResetUpdateState();
+
+ // After preparing an update, returns true if more painting is needed.
+ bool NeedsIdlePaint();
+ gfx::Rect IdlePaintRect();
+
+ bool SkipsDraw() const { return skips_draw_; }
+
+ // Virtual for testing
+ virtual PrioritizedResourceManager* ResourceManager() const;
+ const LayerTilingData* TilerForTesting() const { return tiler_.get(); }
+ const PrioritizedResource* ResourceAtForTesting(int i, int j) const;
+
+ private:
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+
+ void CreateTilerIfNeeded();
+ void set_tiling_option(TilingOption tiling_option) {
+ tiling_option_ = tiling_option;
+ }
+
+ bool TileOnlyNeedsPartialUpdate(UpdatableTile* tile);
+ bool TileNeedsBufferedUpdate(UpdatableTile* tile);
+
+ void MarkOcclusionsAndRequestTextures(int left,
+ int top,
+ int right,
+ int bottom,
+ const OcclusionTracker* occlusion);
+
+ bool UpdateTiles(int left,
+ int top,
+ int right,
+ int bottom,
+ ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats,
+ bool* did_paint);
+ bool HaveTexturesForTiles(int left,
+ int top,
+ int right,
+ int bottom,
+ bool ignore_occlusions);
+ gfx::Rect MarkTilesForUpdate(int left,
+ int top,
+ int right,
+ int bottom,
+ bool ignore_occlusions);
+ void UpdateTileTextures(gfx::Rect paint_rect,
+ int left,
+ int top,
+ int right,
+ int bottom,
+ ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion,
+ RenderingStats* stats);
+ void UpdateScrollPrediction();
+
+ UpdatableTile* TileAt(int i, int j) const;
+ UpdatableTile* CreateTile(int i, int j);
+
+ bool IsSmallAnimatedLayer() const;
+
+ unsigned texture_format_;
+ bool skips_draw_;
+ bool failed_update_;
+
+ // Used for predictive painting.
+ gfx::Vector2d predicted_scroll_;
+ gfx::Rect predicted_visible_rect_;
+ gfx::Rect previous_visible_rect_;
+ gfx::Size previous_content_bounds_;
+
+ TilingOption tiling_option_;
+ scoped_ptr<LayerTilingData> tiler_;
+
+ DISALLOW_COPY_AND_ASSIGN(TiledLayer);
+};
+
+}
+#endif // CC_LAYERS_TILED_LAYER_H_
diff --git a/cc/layers/tiled_layer_impl.cc b/cc/layers/tiled_layer_impl.cc
new file mode 100644
index 0000000..2f098ce
--- /dev/null
+++ b/cc/layers/tiled_layer_impl.cc
@@ -0,0 +1,307 @@
+// Copyright 2011 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 "cc/layers/tiled_layer_impl.h"
+
+#include "base/basictypes.h"
+#include "base/stringprintf.h"
+#include "cc/base/math_util.h"
+#include "cc/debug/debug_colors.h"
+#include "cc/layers/append_quads_data.h"
+#include "cc/layers/quad_sink.h"
+#include "cc/quads/checkerboard_draw_quad.h"
+#include "cc/quads/debug_border_draw_quad.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/quads/tile_draw_quad.h"
+#include "cc/resources/layer_tiling_data.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/quad_f.h"
+
+namespace cc {
+
+// Temporary diagnostic.
+static bool s_safe_to_delete_drawable_tile = false;
+
+class DrawableTile : public LayerTilingData::Tile {
+ public:
+ static scoped_ptr<DrawableTile> Create() {
+ return make_scoped_ptr(new DrawableTile());
+ }
+
+ virtual ~DrawableTile() { CHECK(s_safe_to_delete_drawable_tile); }
+
+ ResourceProvider::ResourceId resource_id() const { return resource_id_; }
+ void set_resource_id(ResourceProvider::ResourceId resource_id) {
+ resource_id_ = resource_id;
+ }
+ bool contents_swizzled() { return contents_swizzled_; }
+ void set_contents_swizzled(bool contents_swizzled) {
+ contents_swizzled_ = contents_swizzled;
+ }
+
+ private:
+ DrawableTile() : resource_id_(0), contents_swizzled_(false) {}
+
+ ResourceProvider::ResourceId resource_id_;
+ bool contents_swizzled_;
+
+ DISALLOW_COPY_AND_ASSIGN(DrawableTile);
+};
+
+TiledLayerImpl::TiledLayerImpl(LayerTreeImpl* tree_impl, int id)
+ : LayerImpl(tree_impl, id), skips_draw_(true) {}
+
+TiledLayerImpl::~TiledLayerImpl() {
+ s_safe_to_delete_drawable_tile = true;
+ if (tiler_)
+ tiler_->reset();
+ s_safe_to_delete_drawable_tile = false;
+}
+
+ResourceProvider::ResourceId TiledLayerImpl::ContentsResourceId() const {
+ // This function is only valid for single texture layers, e.g. masks.
+ DCHECK(tiler_);
+ DCHECK_EQ(tiler_->num_tiles_x(), 1);
+ DCHECK_EQ(tiler_->num_tiles_y(), 1);
+
+ DrawableTile* tile = TileAt(0, 0);
+ ResourceProvider::ResourceId resource_id = tile ? tile->resource_id() : 0;
+ return resource_id;
+}
+
+void TiledLayerImpl::DumpLayerProperties(std::string* str, int indent) const {
+ str->append(IndentString(indent));
+ base::StringAppendF(str, "skipsDraw: %d\n", (!tiler_ || skips_draw_));
+ LayerImpl::DumpLayerProperties(str, indent);
+}
+
+bool TiledLayerImpl::HasTileAt(int i, int j) const {
+ return tiler_->TileAt(i, j);
+}
+
+bool TiledLayerImpl::HasResourceIdForTileAt(int i, int j) const {
+ return HasTileAt(i, j) && TileAt(i, j)->resource_id();
+}
+
+DrawableTile* TiledLayerImpl::TileAt(int i, int j) const {
+ return static_cast<DrawableTile*>(tiler_->TileAt(i, j));
+}
+
+DrawableTile* TiledLayerImpl::CreateTile(int i, int j) {
+ scoped_ptr<DrawableTile> tile(DrawableTile::Create());
+ DrawableTile* added_tile = tile.get();
+ tiler_->AddTile(tile.PassAs<LayerTilingData::Tile>(), i, j);
+
+ // Temporary diagnostic checks.
+ CHECK(added_tile);
+ CHECK(TileAt(i, j));
+
+ return added_tile;
+}
+
+void TiledLayerImpl::GetDebugBorderProperties(SkColor* color,
+ float* width) const {
+ *color = DebugColors::TiledContentLayerBorderColor();
+ *width = DebugColors::TiledContentLayerBorderWidth(layer_tree_impl());
+}
+
+scoped_ptr<LayerImpl> TiledLayerImpl::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return TiledLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
+}
+
+void TiledLayerImpl::PushPropertiesTo(LayerImpl* layer) {
+ LayerImpl::PushPropertiesTo(layer);
+
+ TiledLayerImpl* tiled_layer = static_cast<TiledLayerImpl*>(layer);
+
+ tiled_layer->set_skips_draw(skips_draw_);
+ tiled_layer->SetTilingData(*tiler_);
+
+ for (LayerTilingData::TileMap::const_iterator iter = tiler_->tiles().begin();
+ iter != tiler_->tiles().end();
+ ++iter) {
+ int i = iter->first.first;
+ int j = iter->first.second;
+ DrawableTile* tile = static_cast<DrawableTile*>(iter->second);
+
+ tiled_layer->PushTileProperties(i,
+ j,
+ tile->resource_id(),
+ tile->opaque_rect(),
+ tile->contents_swizzled());
+ }
+}
+
+void TiledLayerImpl::AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) {
+ gfx::Rect content_rect = visible_content_rect();
+
+ if (!tiler_ || tiler_->has_empty_bounds() || content_rect.IsEmpty())
+ return;
+
+ SharedQuadState* shared_quad_state =
+ quad_sink->UseSharedQuadState(CreateSharedQuadState());
+ AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
+
+ int left, top, right, bottom;
+ tiler_->ContentRectToTileIndices(content_rect, &left, &top, &right, &bottom);
+
+ if (ShowDebugBorders()) {
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ DrawableTile* tile = TileAt(i, j);
+ gfx::Rect tile_rect = tiler_->tile_bounds(i, j);
+ SkColor border_color;
+ float border_width;
+
+ if (skips_draw_ || !tile || !tile->resource_id()) {
+ border_color = DebugColors::MissingTileBorderColor();
+ border_width = DebugColors::MissingTileBorderWidth(layer_tree_impl());
+ } else {
+ border_color = DebugColors::HighResTileBorderColor();
+ border_width = DebugColors::HighResTileBorderWidth(layer_tree_impl());
+ }
+ scoped_ptr<DebugBorderDrawQuad> debug_border_quad =
+ DebugBorderDrawQuad::Create();
+ debug_border_quad->SetNew(
+ shared_quad_state, tile_rect, border_color, border_width);
+ quad_sink->Append(debug_border_quad.PassAs<DrawQuad>(),
+ append_quads_data);
+ }
+ }
+ }
+
+ if (skips_draw_)
+ return;
+
+ for (int j = top; j <= bottom; ++j) {
+ for (int i = left; i <= right; ++i) {
+ DrawableTile* tile = TileAt(i, j);
+ gfx::Rect tile_rect = tiler_->tile_bounds(i, j);
+ gfx::Rect display_rect = tile_rect;
+ tile_rect.Intersect(content_rect);
+
+ // Skip empty tiles.
+ if (tile_rect.IsEmpty())
+ continue;
+
+ if (!tile || !tile->resource_id()) {
+ if (DrawCheckerboardForMissingTiles()) {
+ SkColor checker_color;
+ if (ShowDebugBorders()) {
+ checker_color =
+ tile ? DebugColors::InvalidatedTileCheckerboardColor()
+ : DebugColors::EvictedTileCheckerboardColor();
+ } else {
+ checker_color = DebugColors::DefaultCheckerboardColor();
+ }
+
+ scoped_ptr<CheckerboardDrawQuad> checkerboard_quad =
+ CheckerboardDrawQuad::Create();
+ checkerboard_quad->SetNew(
+ shared_quad_state, tile_rect, checker_color);
+ if (quad_sink->Append(checkerboard_quad.PassAs<DrawQuad>(),
+ append_quads_data))
+ append_quads_data->numMissingTiles++;
+ } else {
+ scoped_ptr<SolidColorDrawQuad> solid_color_quad =
+ SolidColorDrawQuad::Create();
+ solid_color_quad->SetNew(
+ shared_quad_state, tile_rect, background_color());
+ if (quad_sink->Append(solid_color_quad.PassAs<DrawQuad>(),
+ append_quads_data))
+ append_quads_data->numMissingTiles++;
+ }
+ continue;
+ }
+
+ gfx::Rect tile_opaque_rect = contents_opaque() ? tile_rect :
+ gfx::IntersectRects(tile->opaque_rect(), content_rect);
+
+ // Keep track of how the top left has moved, so the texture can be
+ // offset the same amount.
+ gfx::Vector2d display_offset = tile_rect.origin() - display_rect.origin();
+ gfx::Vector2d texture_offset =
+ tiler_->texture_offset(i, j) + display_offset;
+ gfx::RectF tex_coord_rect = gfx::RectF(tile_rect.size()) + texture_offset;
+
+ float tile_width = static_cast<float>(tiler_->tile_size().width());
+ float tile_height = static_cast<float>(tiler_->tile_size().height());
+ gfx::Size texture_size(tile_width, tile_height);
+
+ scoped_ptr<TileDrawQuad> quad = TileDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ tile_rect,
+ tile_opaque_rect,
+ tile->resource_id(),
+ tex_coord_rect,
+ texture_size,
+ tile->contents_swizzled());
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+ }
+ }
+}
+
+void TiledLayerImpl::SetTilingData(const LayerTilingData& tiler) {
+ s_safe_to_delete_drawable_tile = true;
+
+ if (tiler_) {
+ tiler_->reset();
+ } else {
+ tiler_ = LayerTilingData::Create(tiler.tile_size(),
+ tiler.has_border_texels()
+ ? LayerTilingData::HAS_BORDER_TEXELS
+ : LayerTilingData::NO_BORDER_TEXELS);
+ }
+ *tiler_ = tiler;
+
+ s_safe_to_delete_drawable_tile = false;
+}
+
+void TiledLayerImpl::PushTileProperties(
+ int i,
+ int j,
+ ResourceProvider::ResourceId resource_id,
+ gfx::Rect opaque_rect,
+ bool contents_swizzled) {
+ DrawableTile* tile = TileAt(i, j);
+ if (!tile)
+ tile = CreateTile(i, j);
+ tile->set_resource_id(resource_id);
+ tile->set_opaque_rect(opaque_rect);
+ tile->set_contents_swizzled(contents_swizzled);
+}
+
+void TiledLayerImpl::PushInvalidTile(int i, int j) {
+ DrawableTile* tile = TileAt(i, j);
+ if (!tile)
+ tile = CreateTile(i, j);
+ tile->set_resource_id(0);
+ tile->set_opaque_rect(gfx::Rect());
+ tile->set_contents_swizzled(false);
+}
+
+Region TiledLayerImpl::VisibleContentOpaqueRegion() const {
+ if (skips_draw_)
+ return Region();
+ if (contents_opaque())
+ return visible_content_rect();
+ return tiler_->OpaqueRegionInContentRect(visible_content_rect());
+}
+
+void TiledLayerImpl::DidLoseOutputSurface() {
+ s_safe_to_delete_drawable_tile = true;
+ // Temporary diagnostic check.
+ CHECK(tiler_);
+ tiler_->reset();
+ s_safe_to_delete_drawable_tile = false;
+}
+
+const char* TiledLayerImpl::LayerTypeAsString() const {
+ return "ContentLayer";
+}
+
+} // namespace cc
diff --git a/cc/layers/tiled_layer_impl.h b/cc/layers/tiled_layer_impl.h
new file mode 100644
index 0000000..ec0c484
--- /dev/null
+++ b/cc/layers/tiled_layer_impl.h
@@ -0,0 +1,70 @@
+// Copyright 2011 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.
+
+#ifndef CC_LAYERS_TILED_LAYER_IMPL_H_
+#define CC_LAYERS_TILED_LAYER_IMPL_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer_impl.h"
+
+namespace cc {
+
+class LayerTilingData;
+class DrawableTile;
+
+class CC_EXPORT TiledLayerImpl : public LayerImpl {
+ public:
+ static scoped_ptr<TiledLayerImpl> Create(LayerTreeImpl* tree_impl, int id) {
+ return make_scoped_ptr(new TiledLayerImpl(tree_impl, id));
+ }
+ virtual ~TiledLayerImpl();
+
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+
+ virtual void AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) OVERRIDE;
+
+ virtual ResourceProvider::ResourceId ContentsResourceId() const OVERRIDE;
+
+ virtual void DumpLayerProperties(std::string* str, int indent) const OVERRIDE;
+
+ void set_skips_draw(bool skips_draw) { skips_draw_ = skips_draw; }
+ void SetTilingData(const LayerTilingData& tiler);
+ void PushTileProperties(int i,
+ int j,
+ ResourceProvider::ResourceId resource,
+ gfx::Rect opaque_rect,
+ bool contents_swizzled);
+ void PushInvalidTile(int i, int j);
+
+ virtual Region VisibleContentOpaqueRegion() const OVERRIDE;
+ virtual void DidLoseOutputSurface() OVERRIDE;
+
+ protected:
+ TiledLayerImpl(LayerTreeImpl* tree_impl, int id);
+ // Exposed for testing.
+ bool HasTileAt(int i, int j) const;
+ bool HasResourceIdForTileAt(int i, int j) const;
+
+ virtual void GetDebugBorderProperties(SkColor* color, float* width) const
+ OVERRIDE;
+
+ private:
+ virtual const char* LayerTypeAsString() const OVERRIDE;
+
+ DrawableTile* TileAt(int i, int j) const;
+ DrawableTile* CreateTile(int i, int j);
+
+ bool skips_draw_;
+
+ scoped_ptr<LayerTilingData> tiler_;
+
+ DISALLOW_COPY_AND_ASSIGN(TiledLayerImpl);
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_TILED_LAYER_IMPL_H_
diff --git a/cc/layers/tiled_layer_impl_unittest.cc b/cc/layers/tiled_layer_impl_unittest.cc
new file mode 100644
index 0000000..994e8b7
--- /dev/null
+++ b/cc/layers/tiled_layer_impl_unittest.cc
@@ -0,0 +1,265 @@
+// Copyright 2012 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 "cc/layers/tiled_layer_impl.h"
+
+#include "cc/layers/append_quads_data.h"
+#include "cc/quads/tile_draw_quad.h"
+#include "cc/resources/layer_tiling_data.h"
+#include "cc/test/fake_impl_proxy.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/layer_test_common.h"
+#include "cc/test/mock_quad_culler.h"
+#include "cc/trees/single_thread_proxy.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class TiledLayerImplTest : public testing::Test {
+ public:
+ TiledLayerImplTest() : host_impl_(&proxy_) {}
+
+ // Create a default tiled layer with textures for all tiles and a default
+ // visibility of the entire layer size.
+ scoped_ptr<TiledLayerImpl> CreateLayer(
+ gfx::Size tile_size,
+ gfx::Size layer_size,
+ LayerTilingData::BorderTexelOption border_texels) {
+ scoped_ptr<TiledLayerImpl> layer =
+ TiledLayerImpl::Create(host_impl_.active_tree(), 1);
+ scoped_ptr<LayerTilingData> tiler =
+ LayerTilingData::Create(tile_size, border_texels);
+ tiler->SetBounds(layer_size);
+ layer->SetTilingData(*tiler);
+ layer->set_skips_draw(false);
+ layer->draw_properties().visible_content_rect =
+ gfx::Rect(gfx::Point(), layer_size);
+ layer->draw_properties().opacity = 1;
+ layer->SetBounds(layer_size);
+ layer->SetContentBounds(layer_size);
+ layer->CreateRenderSurface();
+ layer->draw_properties().render_target = layer.get();
+
+ ResourceProvider::ResourceId resource_id = 1;
+ for (int i = 0; i < tiler->num_tiles_x(); ++i) {
+ for (int j = 0; j < tiler->num_tiles_y(); ++j) {
+ layer->PushTileProperties(
+ i, j, resource_id++, gfx::Rect(0, 0, 1, 1), false);
+ }
+ }
+
+ return layer.Pass();
+ }
+
+ void GetQuads(QuadList* quads,
+ SharedQuadStateList* shared_states,
+ gfx::Size tile_size,
+ gfx::Size layer_size,
+ LayerTilingData::BorderTexelOption border_texel_option,
+ gfx::Rect visible_content_rect) {
+ scoped_ptr<TiledLayerImpl> layer =
+ CreateLayer(tile_size, layer_size, border_texel_option);
+ layer->draw_properties().visible_content_rect = visible_content_rect;
+ layer->SetBounds(layer_size);
+
+ MockQuadCuller quad_culler(*quads, *shared_states);
+ AppendQuadsData data;
+ layer->AppendQuads(&quad_culler, &data);
+ }
+
+ protected:
+ FakeImplProxy proxy_;
+ FakeLayerTreeHostImpl host_impl_;
+};
+
+TEST_F(TiledLayerImplTest, EmptyQuadList) {
+ gfx::Size tile_size(90, 90);
+ int num_tiles_x = 8;
+ int num_tiles_y = 4;
+ gfx::Size layer_size(tile_size.width() * num_tiles_x,
+ tile_size.height() * num_tiles_y);
+
+ // Verify default layer does creates quads
+ {
+ scoped_ptr<TiledLayerImpl> layer =
+ CreateLayer(tile_size, layer_size, LayerTilingData::NO_BORDER_TEXELS);
+ MockQuadCuller quad_culler;
+ AppendQuadsData data;
+ layer->AppendQuads(&quad_culler, &data);
+ unsigned num_tiles = num_tiles_x * num_tiles_y;
+ EXPECT_EQ(quad_culler.quadList().size(), num_tiles);
+ }
+
+ // Layer with empty visible layer rect produces no quads
+ {
+ scoped_ptr<TiledLayerImpl> layer =
+ CreateLayer(tile_size, layer_size, LayerTilingData::NO_BORDER_TEXELS);
+ layer->draw_properties().visible_content_rect = gfx::Rect();
+
+ MockQuadCuller quad_culler;
+ AppendQuadsData data;
+ layer->AppendQuads(&quad_culler, &data);
+ EXPECT_EQ(quad_culler.quadList().size(), 0u);
+ }
+
+ // Layer with non-intersecting visible layer rect produces no quads
+ {
+ scoped_ptr<TiledLayerImpl> layer =
+ CreateLayer(tile_size, layer_size, LayerTilingData::NO_BORDER_TEXELS);
+
+ gfx::Rect outsideBounds(gfx::Point(-100, -100), gfx::Size(50, 50));
+ layer->draw_properties().visible_content_rect = outsideBounds;
+
+ MockQuadCuller quad_culler;
+ AppendQuadsData data;
+ layer->AppendQuads(&quad_culler, &data);
+ EXPECT_EQ(quad_culler.quadList().size(), 0u);
+ }
+
+ // Layer with skips draw produces no quads
+ {
+ scoped_ptr<TiledLayerImpl> layer =
+ CreateLayer(tile_size, layer_size, LayerTilingData::NO_BORDER_TEXELS);
+ layer->set_skips_draw(true);
+
+ MockQuadCuller quad_culler;
+ AppendQuadsData data;
+ layer->AppendQuads(&quad_culler, &data);
+ EXPECT_EQ(quad_culler.quadList().size(), 0u);
+ }
+}
+
+TEST_F(TiledLayerImplTest, Checkerboarding) {
+ gfx::Size tile_size(10, 10);
+ int num_tiles_x = 2;
+ int num_tiles_y = 2;
+ gfx::Size layer_size(tile_size.width() * num_tiles_x,
+ tile_size.height() * num_tiles_y);
+
+ scoped_ptr<TiledLayerImpl> layer =
+ CreateLayer(tile_size, layer_size, LayerTilingData::NO_BORDER_TEXELS);
+
+ // No checkerboarding
+ {
+ MockQuadCuller quad_culler;
+ AppendQuadsData data;
+ layer->AppendQuads(&quad_culler, &data);
+ EXPECT_EQ(quad_culler.quadList().size(), 4u);
+ EXPECT_EQ(0u, data.numMissingTiles);
+
+ for (size_t i = 0; i < quad_culler.quadList().size(); ++i)
+ EXPECT_EQ(quad_culler.quadList()[i]->material, DrawQuad::TILED_CONTENT);
+ }
+
+ for (int i = 0; i < num_tiles_x; ++i)
+ for (int j = 0; j < num_tiles_y; ++j)
+ layer->PushTileProperties(i, j, 0, gfx::Rect(), false);
+
+ // All checkerboarding
+ {
+ MockQuadCuller quad_culler;
+ AppendQuadsData data;
+ layer->AppendQuads(&quad_culler, &data);
+ EXPECT_LT(0u, data.numMissingTiles);
+ EXPECT_EQ(quad_culler.quadList().size(), 4u);
+ for (size_t i = 0; i < quad_culler.quadList().size(); ++i)
+ EXPECT_NE(quad_culler.quadList()[i]->material, DrawQuad::TILED_CONTENT);
+ }
+}
+
+// Test with both border texels and without.
+#define WITH_AND_WITHOUT_BORDER_TEST(text_fixture_name) \
+ TEST_F(TiledLayerImplBorderTest, text_fixture_name##NoBorders) { \
+ text_fixture_name(LayerTilingData::NO_BORDER_TEXELS); \
+ } \
+ TEST_F(TiledLayerImplBorderTest, text_fixture_name##HasBorders) { \
+ text_fixture_name(LayerTilingData::HAS_BORDER_TEXELS); \
+ }
+
+class TiledLayerImplBorderTest : public TiledLayerImplTest {
+ public:
+ void CoverageVisibleRectOnTileBoundaries(
+ LayerTilingData::BorderTexelOption borders) {
+ gfx::Size layer_size(1000, 1000);
+ QuadList quads;
+ SharedQuadStateList shared_states;
+ GetQuads(&quads,
+ &shared_states,
+ gfx::Size(100, 100),
+ layer_size,
+ borders,
+ gfx::Rect(gfx::Point(), layer_size));
+ LayerTestCommon::verifyQuadsExactlyCoverRect(
+ quads, gfx::Rect(gfx::Point(), layer_size));
+ }
+
+ void CoverageVisibleRectIntersectsTiles(
+ LayerTilingData::BorderTexelOption borders) {
+ // This rect intersects the middle 3x3 of the 5x5 tiles.
+ gfx::Point top_left(65, 73);
+ gfx::Point bottom_right(182, 198);
+ gfx::Rect visible_content_rect = gfx::BoundingRect(top_left, bottom_right);
+
+ gfx::Size layer_size(250, 250);
+ QuadList quads;
+ SharedQuadStateList shared_states;
+ GetQuads(&quads,
+ &shared_states,
+ gfx::Size(50, 50),
+ gfx::Size(250, 250),
+ LayerTilingData::NO_BORDER_TEXELS,
+ visible_content_rect);
+ LayerTestCommon::verifyQuadsExactlyCoverRect(quads, visible_content_rect);
+ }
+
+ void CoverageVisibleRectIntersectsBounds(
+ LayerTilingData::BorderTexelOption borders) {
+ gfx::Size layer_size(220, 210);
+ gfx::Rect visible_content_rect(layer_size);
+ QuadList quads;
+ SharedQuadStateList shared_states;
+ GetQuads(&quads,
+ &shared_states,
+ gfx::Size(100, 100),
+ layer_size,
+ LayerTilingData::NO_BORDER_TEXELS,
+ visible_content_rect);
+ LayerTestCommon::verifyQuadsExactlyCoverRect(quads, visible_content_rect);
+ }
+};
+WITH_AND_WITHOUT_BORDER_TEST(CoverageVisibleRectOnTileBoundaries);
+
+WITH_AND_WITHOUT_BORDER_TEST(CoverageVisibleRectIntersectsTiles);
+
+WITH_AND_WITHOUT_BORDER_TEST(CoverageVisibleRectIntersectsBounds);
+
+TEST_F(TiledLayerImplTest, TextureInfoForLayerNoBorders) {
+ gfx::Size tile_size(50, 50);
+ gfx::Size layer_size(250, 250);
+ QuadList quads;
+ SharedQuadStateList shared_states;
+ GetQuads(&quads,
+ &shared_states,
+ tile_size,
+ layer_size,
+ LayerTilingData::NO_BORDER_TEXELS,
+ gfx::Rect(gfx::Point(), layer_size));
+
+ for (size_t i = 0; i < quads.size(); ++i) {
+ const TileDrawQuad* quad = TileDrawQuad::MaterialCast(quads[i]);
+
+ EXPECT_NE(0u, quad->resource_id) << LayerTestCommon::quadString << i;
+ EXPECT_EQ(gfx::RectF(gfx::PointF(), tile_size), quad->tex_coord_rect)
+ << LayerTestCommon::quadString << i;
+ EXPECT_EQ(tile_size, quad->texture_size) << LayerTestCommon::quadString
+ << i;
+ EXPECT_EQ(gfx::Rect(0, 0, 1, 1), quad->opaque_rect)
+ << LayerTestCommon::quadString << i;
+ }
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/tiled_layer_unittest.cc b/cc/layers/tiled_layer_unittest.cc
new file mode 100644
index 0000000..671fd0d
--- /dev/null
+++ b/cc/layers/tiled_layer_unittest.cc
@@ -0,0 +1,1672 @@
+// Copyright 2011 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 "cc/layers/tiled_layer.h"
+
+#include "cc/debug/overdraw_metrics.h"
+#include "cc/resources/bitmap_content_layer_updater.h"
+#include "cc/resources/layer_painter.h"
+#include "cc/resources/prioritized_resource_manager.h"
+#include "cc/resources/resource_update_controller.h"
+#include "cc/test/animation_test_common.h"
+#include "cc/test/fake_layer_tree_host_client.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_proxy.h"
+#include "cc/test/geometry_test_utils.h"
+#include "cc/test/tiled_layer_test_common.h"
+#include "cc/trees/single_thread_proxy.h" // For DebugScopedSetImplThread
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+namespace {
+
+class TestOcclusionTracker : public OcclusionTracker {
+public:
+ TestOcclusionTracker()
+ : OcclusionTracker(gfx::Rect(0, 0, 1000, 1000), true)
+ {
+ stack_.push_back(StackObject());
+ }
+
+ void setRenderTarget(Layer* renderTarget)
+ {
+ stack_.back().target = renderTarget;
+ }
+
+ void setOcclusion(const Region& occlusion)
+ {
+ stack_.back().occlusion_from_inside_target = occlusion;
+ }
+};
+
+class TiledLayerTest : public testing::Test {
+public:
+ TiledLayerTest()
+ : m_proxy(NULL)
+ , m_outputSurface(createFakeOutputSurface())
+ , m_queue(make_scoped_ptr(new ResourceUpdateQueue))
+ , m_occlusion(0)
+ {
+ m_settings.maxPartialTextureUpdates = std::numeric_limits<size_t>::max();
+ }
+
+ virtual void SetUp()
+ {
+ layer_tree_host_ = LayerTreeHost::Create(&m_fakeLayerImplTreeHostClient, m_settings, scoped_ptr<Thread>(NULL));
+ m_proxy = layer_tree_host_->proxy();
+ m_resourceManager = PrioritizedResourceManager::create(m_proxy);
+ layer_tree_host_->InitializeRendererIfNeeded();
+ layer_tree_host_->SetRootLayer(Layer::Create());
+
+ DebugScopedSetImplThreadAndMainThreadBlocked implThreadAndMainThreadBlocked(m_proxy);
+ m_resourceProvider = ResourceProvider::Create(m_outputSurface.get());
+ m_hostImpl = make_scoped_ptr(new FakeLayerTreeHostImpl(m_proxy));
+ }
+
+ virtual ~TiledLayerTest()
+ {
+ resourceManagerClearAllMemory(m_resourceManager.get(), m_resourceProvider.get());
+
+ DebugScopedSetImplThreadAndMainThreadBlocked implThreadAndMainThreadBlocked(m_proxy);
+ m_resourceProvider.reset();
+ m_hostImpl.reset();
+ }
+
+ void resourceManagerClearAllMemory(PrioritizedResourceManager* resourceManager, ResourceProvider* resourceProvider)
+ {
+ {
+ DebugScopedSetImplThreadAndMainThreadBlocked implThreadAndMainThreadBlocked(m_proxy);
+ resourceManager->clearAllMemory(resourceProvider);
+ resourceManager->reduceMemory(resourceProvider);
+ }
+ resourceManager->unlinkAndClearEvictedBackings();
+ }
+ void updateTextures()
+ {
+ DebugScopedSetImplThreadAndMainThreadBlocked implThreadAndMainThreadBlocked(m_proxy);
+ DCHECK(m_queue);
+ scoped_ptr<ResourceUpdateController> updateController =
+ ResourceUpdateController::Create(
+ NULL,
+ m_proxy->ImplThread(),
+ m_queue.Pass(),
+ m_resourceProvider.get());
+ updateController->Finalize();
+ m_queue = make_scoped_ptr(new ResourceUpdateQueue);
+ }
+ void layerPushPropertiesTo(FakeTiledLayer* layer, FakeTiledLayerImpl* layerImpl)
+ {
+ DebugScopedSetImplThreadAndMainThreadBlocked implThreadAndMainThreadBlocked(m_proxy);
+ layer->PushPropertiesTo(layerImpl);
+ }
+ void layerUpdate(FakeTiledLayer* layer, TestOcclusionTracker* occluded)
+ {
+ DebugScopedSetMainThread mainThread(m_proxy);
+ layer->Update(m_queue.get(), occluded, NULL);
+ }
+
+ void calcDrawProps(const scoped_refptr<FakeTiledLayer>& layer1)
+ {
+ scoped_refptr<FakeTiledLayer> layer2;
+ calcDrawProps(layer1, layer2);
+ }
+
+ void calcDrawProps(const scoped_refptr<FakeTiledLayer>& layer1,
+ const scoped_refptr<FakeTiledLayer>& layer2)
+ {
+ if (layer1 && !layer1->parent())
+ layer_tree_host_->root_layer()->AddChild(layer1);
+ if (layer2 && !layer2->parent())
+ layer_tree_host_->root_layer()->AddChild(layer2);
+ if (m_occlusion)
+ m_occlusion->setRenderTarget(layer_tree_host_->root_layer());
+
+ std::vector<scoped_refptr<Layer> > renderSurfaceLayerList;
+ LayerTreeHostCommon::calculateDrawProperties(
+ layer_tree_host_->root_layer(),
+ layer_tree_host_->device_viewport_size(),
+ layer_tree_host_->device_scale_factor(),
+ 1, // page_scale_factor
+ layer_tree_host_->GetRendererCapabilities().max_texture_size,
+ false, // can_use_lcd_text
+ renderSurfaceLayerList);
+ }
+
+ bool updateAndPush(const scoped_refptr<FakeTiledLayer>& layer1,
+ const scoped_ptr<FakeTiledLayerImpl>& layerImpl1)
+ {
+ scoped_refptr<FakeTiledLayer> layer2;
+ scoped_ptr<FakeTiledLayerImpl> layerImpl2;
+ return updateAndPush(layer1, layerImpl1, layer2, layerImpl2);
+ }
+
+ bool updateAndPush(const scoped_refptr<FakeTiledLayer>& layer1,
+ const scoped_ptr<FakeTiledLayerImpl>& layerImpl1,
+ const scoped_refptr<FakeTiledLayer>& layer2,
+ const scoped_ptr<FakeTiledLayerImpl>& layerImpl2)
+ {
+ // Get textures
+ m_resourceManager->clearPriorities();
+ if (layer1)
+ layer1->SetTexturePriorities(m_priorityCalculator);
+ if (layer2)
+ layer2->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+
+ // Update content
+ if (layer1)
+ layer1->Update(m_queue.get(), m_occlusion, NULL);
+ if (layer2)
+ layer2->Update(m_queue.get(), m_occlusion, NULL);
+
+ bool needsUpdate = false;
+ if (layer1)
+ needsUpdate |= layer1->NeedsIdlePaint();
+ if (layer2)
+ needsUpdate |= layer2->NeedsIdlePaint();
+
+ // Update textures and push.
+ updateTextures();
+ if (layer1)
+ layerPushPropertiesTo(layer1.get(), layerImpl1.get());
+ if (layer2)
+ layerPushPropertiesTo(layer2.get(), layerImpl2.get());
+
+ return needsUpdate;
+ }
+
+public:
+ Proxy* m_proxy;
+ LayerTreeSettings m_settings;
+ scoped_ptr<OutputSurface> m_outputSurface;
+ scoped_ptr<ResourceProvider> m_resourceProvider;
+ scoped_ptr<ResourceUpdateQueue> m_queue;
+ PriorityCalculator m_priorityCalculator;
+ FakeLayerImplTreeHostClient m_fakeLayerImplTreeHostClient;
+ scoped_ptr<LayerTreeHost> layer_tree_host_;
+ scoped_ptr<FakeLayerTreeHostImpl> m_hostImpl;
+ scoped_ptr<PrioritizedResourceManager> m_resourceManager;
+ TestOcclusionTracker* m_occlusion;
+};
+
+TEST_F(TiledLayerTest, pushDirtyTiles)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles.
+ layer->SetBounds(gfx::Size(100, 200));
+ calcDrawProps(layer);
+ updateAndPush(layer, layerImpl);
+
+ // We should have both tiles on the impl side.
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 1));
+
+ // Invalidates both tiles, but then only update one of them.
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 100, 200));
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 100);
+ updateAndPush(layer, layerImpl);
+
+ // We should only have the first tile since the other tile was invalidated but not painted.
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 0));
+ EXPECT_FALSE(layerImpl->HasResourceIdForTileAt(0, 1));
+}
+
+TEST_F(TiledLayerTest, pushOccludedDirtyTiles)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+ TestOcclusionTracker occluded;
+ m_occlusion = &occluded;
+ layer_tree_host_->SetViewportSize(gfx::Size(1000, 1000), gfx::Size(1000, 1000));
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles.
+ layer->SetBounds(gfx::Size(100, 200));
+ calcDrawProps(layer);
+ updateAndPush(layer, layerImpl);
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 20000, 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ // We should have both tiles on the impl side.
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 1));
+
+ // Invalidates part of the top tile...
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 50, 50));
+ // ....but the area is occluded.
+ occluded.setOcclusion(gfx::Rect(0, 0, 50, 50));
+ calcDrawProps(layer);
+ updateAndPush(layer, layerImpl);
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 20000 + 2500, 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ // We should still have both tiles, as part of the top tile is still unoccluded.
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 1));
+}
+
+TEST_F(TiledLayerTest, pushDeletedTiles)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles.
+ layer->SetBounds(gfx::Size(100, 200));
+ calcDrawProps(layer);
+ updateAndPush(layer, layerImpl);
+
+ // We should have both tiles on the impl side.
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 1));
+
+ m_resourceManager->clearPriorities();
+ resourceManagerClearAllMemory(m_resourceManager.get(), m_resourceProvider.get());
+ m_resourceManager->setMaxMemoryLimitBytes(4*1024*1024);
+
+ // This should drop the tiles on the impl thread.
+ layerPushPropertiesTo(layer.get(), layerImpl.get());
+
+ // We should now have no textures on the impl thread.
+ EXPECT_FALSE(layerImpl->HasResourceIdForTileAt(0, 0));
+ EXPECT_FALSE(layerImpl->HasResourceIdForTileAt(0, 1));
+
+ // This should recreate and update one of the deleted textures.
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 100);
+ updateAndPush(layer, layerImpl);
+
+ // We should have one tiles on the impl side.
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 0));
+ EXPECT_FALSE(layerImpl->HasResourceIdForTileAt(0, 1));
+}
+
+TEST_F(TiledLayerTest, pushIdlePaintTiles)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+
+ // The tile size is 100x100. Setup 5x5 tiles with one visible tile in the center.
+ // This paints 1 visible of the 25 invalid tiles.
+ layer->SetBounds(gfx::Size(500, 500));
+ calcDrawProps(layer);
+ layer->draw_properties().visible_content_rect = gfx::Rect(200, 200, 100, 100);
+ bool needsUpdate = updateAndPush(layer, layerImpl);
+ // We should need idle-painting for surrounding tiles.
+ EXPECT_TRUE(needsUpdate);
+
+ // We should have one tile on the impl side.
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(2, 2));
+
+ // For the next four updates, we should detect we still need idle painting.
+ for (int i = 0; i < 4; i++) {
+ needsUpdate = updateAndPush(layer, layerImpl);
+ EXPECT_TRUE(needsUpdate);
+ }
+
+ // We should always finish painting eventually.
+ for (int i = 0; i < 20; i++)
+ needsUpdate = updateAndPush(layer, layerImpl);
+
+ // We should have pre-painted all of the surrounding tiles.
+ for (int i = 0; i < 5; i++) {
+ for (int j = 0; j < 5; j++)
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(i, j));
+ }
+
+ EXPECT_FALSE(needsUpdate);
+}
+
+TEST_F(TiledLayerTest, predictivePainting)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+
+ // Prepainting should occur in the scroll direction first, and the
+ // visible rect should be extruded only along the dominant axis.
+ gfx::Vector2d directions[6] = { gfx::Vector2d(-10, 0),
+ gfx::Vector2d(10, 0),
+ gfx::Vector2d(0, -10),
+ gfx::Vector2d(0, 10),
+ gfx::Vector2d(10, 20),
+ gfx::Vector2d(-20, 10) };
+ // We should push all tiles that touch the extruded visible rect.
+ gfx::Rect pushedVisibleTiles[6] = { gfx::Rect(2, 2, 2, 1),
+ gfx::Rect(1, 2, 2, 1),
+ gfx::Rect(2, 2, 1, 2),
+ gfx::Rect(2, 1, 1, 2),
+ gfx::Rect(2, 1, 1, 2),
+ gfx::Rect(2, 2, 2, 1) };
+ // The first pre-paint should also paint first in the scroll
+ // direction so we should find one additional tile in the scroll direction.
+ gfx::Rect pushedPrepaintTiles[6] = { gfx::Rect(2, 2, 3, 1),
+ gfx::Rect(0, 2, 3, 1),
+ gfx::Rect(2, 2, 1, 3),
+ gfx::Rect(2, 0, 1, 3),
+ gfx::Rect(2, 0, 1, 3),
+ gfx::Rect(2, 2, 3, 1) };
+ for(int k = 0; k < 6; k++) {
+ // The tile size is 100x100. Setup 5x5 tiles with one visible tile
+ // in the center.
+ gfx::Size bounds = gfx::Size(500, 500);
+ gfx::Rect visibleRect = gfx::Rect(200, 200, 100, 100);
+ gfx::Rect previousVisibleRect = gfx::Rect(visibleRect.origin() + directions[k], visibleRect.size());
+ gfx::Rect nextVisibleRect = gfx::Rect(visibleRect.origin() - directions[k], visibleRect.size());
+
+ // Setup. Use the previousVisibleRect to setup the prediction for next frame.
+ layer->SetBounds(bounds);
+ calcDrawProps(layer);
+ layer->draw_properties().visible_content_rect = previousVisibleRect;
+ bool needsUpdate = updateAndPush(layer, layerImpl);
+
+ // Invalidate and move the visibleRect in the scroll direction.
+ // Check that the correct tiles have been painted in the visible pass.
+ layer->SetNeedsDisplay();
+ layer->draw_properties().visible_content_rect = visibleRect;
+ needsUpdate = updateAndPush(layer, layerImpl);
+ for (int i = 0; i < 5; i++) {
+ for (int j = 0; j < 5; j++)
+ EXPECT_EQ(layerImpl->HasResourceIdForTileAt(i, j), pushedVisibleTiles[k].Contains(i, j));
+ }
+
+ // Move the transform in the same direction without invalidating.
+ // Check that non-visible pre-painting occured in the correct direction.
+ // Ignore diagonal scrolls here (k > 3) as these have new visible content now.
+ if (k <= 3) {
+ layer->draw_properties().visible_content_rect = nextVisibleRect;
+ needsUpdate = updateAndPush(layer, layerImpl);
+ for (int i = 0; i < 5; i++) {
+ for (int j = 0; j < 5; j++)
+ EXPECT_EQ(layerImpl->HasResourceIdForTileAt(i, j), pushedPrepaintTiles[k].Contains(i, j));
+ }
+ }
+
+ // We should always finish painting eventually.
+ for (int i = 0; i < 20; i++)
+ needsUpdate = updateAndPush(layer, layerImpl);
+ EXPECT_FALSE(needsUpdate);
+ }
+}
+
+TEST_F(TiledLayerTest, pushTilesAfterIdlePaintFailed)
+{
+ // Start with 2mb of memory, but the test is going to try to use just more than 1mb, so we reduce to 1mb later.
+ m_resourceManager->setMaxMemoryLimitBytes(2 * 1024 * 1024);
+ scoped_refptr<FakeTiledLayer> layer1 = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl1 = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+ scoped_refptr<FakeTiledLayer> layer2 = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl2 = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 2));
+
+ // For this test we have two layers. layer1 exhausts most texture memory, leaving room for 2 more tiles from
+ // layer2, but not all three tiles. First we paint layer1, and one tile from layer2. Then when we idle paint
+ // layer2, we will fail on the third tile of layer2, and this should not leave the second tile in a bad state.
+
+ // This uses 960000 bytes, leaving 88576 bytes of memory left, which is enough for 2 tiles only in the other layer.
+ gfx::Rect layer1Rect(0, 0, 100, 2400);
+
+ // This requires 4*30000 bytes of memory.
+ gfx::Rect layer2Rect(0, 0, 100, 300);
+
+ // Paint a single tile in layer2 so that it will idle paint.
+ layer1->SetBounds(layer1Rect.size());
+ layer2->SetBounds(layer2Rect.size());
+ calcDrawProps(layer1, layer2);
+ layer1->draw_properties().visible_content_rect = layer1Rect;
+ layer2->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 100);
+ bool needsUpdate = updateAndPush(layer1, layerImpl1,
+ layer2, layerImpl2);
+ // We should need idle-painting for both remaining tiles in layer2.
+ EXPECT_TRUE(needsUpdate);
+
+ // Reduce our memory limits to 1mb.
+ m_resourceManager->setMaxMemoryLimitBytes(1024 * 1024);
+
+ // Now idle paint layer2. We are going to run out of memory though!
+ // Oh well, commit the frame and push.
+ for (int i = 0; i < 4; i++) {
+ needsUpdate = updateAndPush(layer1, layerImpl1,
+ layer2, layerImpl2);
+ }
+
+ // Sanity check, we should have textures for the big layer.
+ EXPECT_TRUE(layerImpl1->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layerImpl1->HasResourceIdForTileAt(0, 23));
+
+ // We should only have the first two tiles from layer2 since
+ // it failed to idle update the last tile.
+ EXPECT_TRUE(layerImpl2->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layerImpl2->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layerImpl2->HasResourceIdForTileAt(0, 1));
+ EXPECT_TRUE(layerImpl2->HasResourceIdForTileAt(0, 1));
+
+ EXPECT_FALSE(needsUpdate);
+ EXPECT_FALSE(layerImpl2->HasResourceIdForTileAt(0, 2));
+}
+
+TEST_F(TiledLayerTest, pushIdlePaintedOccludedTiles)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+ TestOcclusionTracker occluded;
+ m_occlusion = &occluded;
+
+ // The tile size is 100x100, so this invalidates one occluded tile, culls it during paint, but prepaints it.
+ occluded.setOcclusion(gfx::Rect(0, 0, 100, 100));
+
+ layer->SetBounds(gfx::Size(100, 100));
+ calcDrawProps(layer);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 100);
+ updateAndPush(layer, layerImpl);
+
+ // We should have the prepainted tile on the impl side, but culled it during paint.
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 0));
+ EXPECT_EQ(1, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, pushTilesMarkedDirtyDuringPaint)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles.
+ // However, during the paint, we invalidate one of the tiles. This should
+ // not prevent the tile from being pushed.
+ layer->fakeLayerUpdater()->setRectToInvalidate(gfx::Rect(0, 50, 100, 50), layer.get());
+ layer->SetBounds(gfx::Size(100, 200));
+ calcDrawProps(layer);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 200);
+ updateAndPush(layer, layerImpl);
+
+ // We should have both tiles on the impl side.
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 1));
+}
+
+TEST_F(TiledLayerTest, pushTilesLayerMarkedDirtyDuringPaintOnNextLayer)
+{
+ scoped_refptr<FakeTiledLayer> layer1 = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_refptr<FakeTiledLayer> layer2 = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer1Impl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+ scoped_ptr<FakeTiledLayerImpl> layer2Impl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 2));
+
+ // Invalidate a tile on layer1, during update of layer 2.
+ layer2->fakeLayerUpdater()->setRectToInvalidate(gfx::Rect(0, 50, 100, 50), layer1.get());
+ layer1->SetBounds(gfx::Size(100, 200));
+ layer2->SetBounds(gfx::Size(100, 200));
+ calcDrawProps(layer1, layer2);
+ layer1->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 200);
+ layer2->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 200);
+ updateAndPush(layer1, layer1Impl,
+ layer2, layer2Impl);
+
+ // We should have both tiles on the impl side for all layers.
+ EXPECT_TRUE(layer1Impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer1Impl->HasResourceIdForTileAt(0, 1));
+ EXPECT_TRUE(layer2Impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer2Impl->HasResourceIdForTileAt(0, 1));
+}
+
+TEST_F(TiledLayerTest, pushTilesLayerMarkedDirtyDuringPaintOnPreviousLayer)
+{
+ scoped_refptr<FakeTiledLayer> layer1 = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_refptr<FakeTiledLayer> layer2 = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layer1Impl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+ scoped_ptr<FakeTiledLayerImpl> layer2Impl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 2));
+
+ layer1->fakeLayerUpdater()->setRectToInvalidate(gfx::Rect(0, 50, 100, 50), layer2.get());
+ layer1->SetBounds(gfx::Size(100, 200));
+ layer2->SetBounds(gfx::Size(100, 200));
+ calcDrawProps(layer1, layer2);
+ layer1->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 200);
+ layer2->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 200);
+ updateAndPush(layer1, layer1Impl,
+ layer2, layer2Impl);
+
+ // We should have both tiles on the impl side for all layers.
+ EXPECT_TRUE(layer1Impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer1Impl->HasResourceIdForTileAt(0, 1));
+ EXPECT_TRUE(layer2Impl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layer2Impl->HasResourceIdForTileAt(0, 1));
+}
+
+TEST_F(TiledLayerTest, paintSmallAnimatedLayersImmediately)
+{
+ // Create a LayerTreeHost that has the right viewportsize,
+ // so the layer is considered small enough.
+ FakeLayerImplTreeHostClient fakeLayerImplTreeHostClient;
+
+ bool runOutOfMemory[2] = {false, true};
+ for (int i = 0; i < 2; i++) {
+ // Create a layer with 5x5 tiles, with 4x4 size viewport.
+ int viewportWidth = 4 * FakeTiledLayer::tileSize().width();
+ int viewportHeight = 4 * FakeTiledLayer::tileSize().width();
+ int layerWidth = 5 * FakeTiledLayer::tileSize().width();
+ int layerHeight = 5 * FakeTiledLayer::tileSize().height();
+ int memoryForLayer = layerWidth * layerHeight * 4;
+ layer_tree_host_->SetViewportSize(gfx::Size(layerWidth, layerHeight), gfx::Size(layerWidth, layerHeight));
+
+ // Use 10x5 tiles to run out of memory.
+ if (runOutOfMemory[i])
+ layerWidth *= 2;
+
+ m_resourceManager->setMaxMemoryLimitBytes(memoryForLayer);
+
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+
+ // Full size layer with half being visible.
+ layer->SetBounds(gfx::Size(layerWidth, layerHeight));
+ gfx::Rect visibleRect(gfx::Point(), gfx::Size(layerWidth / 2, layerHeight));
+ calcDrawProps(layer);
+
+ // Pretend the layer is animating.
+ layer->draw_properties().target_space_transform_is_animating = true;
+ layer->draw_properties().visible_content_rect = visibleRect;
+ layer->SetLayerTreeHost(layer_tree_host_.get());
+
+ // The layer should paint its entire contents on the first paint
+ // if it is close to the viewport size and has the available memory.
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), 0, NULL);
+ updateTextures();
+ layerPushPropertiesTo(layer.get(), layerImpl.get());
+
+ // We should have all the tiles for the small animated layer.
+ // We should still have the visible tiles when we didn't
+ // have enough memory for all the tiles.
+ if (!runOutOfMemory[i]) {
+ for (int i = 0; i < 5; ++i) {
+ for (int j = 0; j < 5; ++j)
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(i, j));
+ }
+ } else {
+ for (int i = 0; i < 10; ++i) {
+ for (int j = 0; j < 5; ++j)
+ EXPECT_EQ(layerImpl->HasResourceIdForTileAt(i, j), i < 5);
+ }
+ }
+ }
+}
+
+TEST_F(TiledLayerTest, idlePaintOutOfMemory)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+
+ // We have enough memory for only the visible rect, so we will run out of memory in first idle paint.
+ int memoryLimit = 4 * 100 * 100; // 1 tiles, 4 bytes per pixel.
+ m_resourceManager->setMaxMemoryLimitBytes(memoryLimit);
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles.
+ bool needsUpdate = false;
+ layer->SetBounds(gfx::Size(300, 300));
+ calcDrawProps(layer);
+ layer->draw_properties().visible_content_rect = gfx::Rect(100, 100, 100, 100);
+ for (int i = 0; i < 2; i++)
+ needsUpdate = updateAndPush(layer, layerImpl);
+
+ // Idle-painting should see no more priority tiles for painting.
+ EXPECT_FALSE(needsUpdate);
+
+ // We should have one tile on the impl side.
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(1, 1));
+}
+
+TEST_F(TiledLayerTest, idlePaintZeroSizedLayer)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+
+ bool animating[2] = {false, true};
+ for (int i = 0; i < 2; i++) {
+ // Pretend the layer is animating.
+ layer->draw_properties().target_space_transform_is_animating = animating[i];
+
+ // The layer's bounds are empty.
+ // Empty layers don't paint or idle-paint.
+ layer->SetBounds(gfx::Size());
+ calcDrawProps(layer);
+ layer->draw_properties().visible_content_rect = gfx::Rect();
+ bool needsUpdate = updateAndPush(layer, layerImpl);
+
+ // Empty layers don't have tiles.
+ EXPECT_EQ(0u, layer->NumPaintedTiles());
+
+ // Empty layers don't need prepaint.
+ EXPECT_FALSE(needsUpdate);
+
+ // Empty layers don't have tiles.
+ EXPECT_FALSE(layerImpl->HasResourceIdForTileAt(0, 0));
+ }
+}
+
+TEST_F(TiledLayerTest, idlePaintNonVisibleLayers)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+
+ // Alternate between not visible and visible.
+ gfx::Rect v(0, 0, 100, 100);
+ gfx::Rect nv(0, 0, 0, 0);
+ gfx::Rect visibleRect[10] = {nv, nv, v, v, nv, nv, v, v, nv, nv};
+ bool invalidate[10] = {true, true, true, true, true, true, true, true, false, false };
+
+ // We should not have any tiles except for when the layer was visible
+ // or after the layer was visible and we didn't invalidate.
+ bool haveTile[10] = { false, false, true, true, false, false, true, true, true, true };
+
+ for (int i = 0; i < 10; i++) {
+ layer->SetBounds(gfx::Size(100, 100));
+ calcDrawProps(layer);
+ layer->draw_properties().visible_content_rect = visibleRect[i];
+
+ if (invalidate[i])
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 100, 100));
+ bool needsUpdate = updateAndPush(layer, layerImpl);
+
+ // We should never signal idle paint, as we painted the entire layer
+ // or the layer was not visible.
+ EXPECT_FALSE(needsUpdate);
+ EXPECT_EQ(layerImpl->HasResourceIdForTileAt(0, 0), haveTile[i]);
+ }
+}
+
+TEST_F(TiledLayerTest, invalidateFromPrepare)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles.
+ layer->SetBounds(gfx::Size(100, 200));
+ calcDrawProps(layer);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 200);
+ updateAndPush(layer, layerImpl);
+
+ // We should have both tiles on the impl side.
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 1));
+
+ layer->fakeLayerUpdater()->clearPrepareCount();
+ // Invoke update again. As the layer is valid update shouldn't be invoked on
+ // the LayerUpdater.
+ updateAndPush(layer, layerImpl);
+ EXPECT_EQ(0, layer->fakeLayerUpdater()->prepareCount());
+
+ // setRectToInvalidate triggers InvalidateContentRect() being invoked from update.
+ layer->fakeLayerUpdater()->setRectToInvalidate(gfx::Rect(25, 25, 50, 50), layer.get());
+ layer->fakeLayerUpdater()->clearPrepareCount();
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 50, 50));
+ updateAndPush(layer, layerImpl);
+ EXPECT_EQ(1, layer->fakeLayerUpdater()->prepareCount());
+ layer->fakeLayerUpdater()->clearPrepareCount();
+
+ // The layer should still be invalid as update invoked invalidate.
+ updateAndPush(layer, layerImpl); // visible
+ EXPECT_EQ(1, layer->fakeLayerUpdater()->prepareCount());
+}
+
+TEST_F(TiledLayerTest, verifyUpdateRectWhenContentBoundsAreScaled)
+{
+ // The updateRect (that indicates what was actually painted) should be in
+ // layer space, not the content space.
+ scoped_refptr<FakeTiledLayerWithScaledBounds> layer = make_scoped_refptr(new FakeTiledLayerWithScaledBounds(m_resourceManager.get()));
+
+ gfx::Rect layerBounds(0, 0, 300, 200);
+ gfx::Rect contentBounds(0, 0, 200, 250);
+
+ layer->SetBounds(layerBounds.size());
+ layer->setContentBounds(contentBounds.size());
+ layer->draw_properties().visible_content_rect = contentBounds;
+
+ // On first update, the updateRect includes all tiles, even beyond the boundaries of the layer.
+ // However, it should still be in layer space, not content space.
+ layer->InvalidateContentRect(contentBounds);
+
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), 0, NULL);
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 300, 300 * 0.8), layer->updateRect());
+ updateTextures();
+
+ // After the tiles are updated once, another invalidate only needs to update the bounds of the layer.
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->InvalidateContentRect(contentBounds);
+ layer->Update(m_queue.get(), 0, NULL);
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(layerBounds), layer->updateRect());
+ updateTextures();
+
+ // Partial re-paint should also be represented by the updateRect in layer space, not content space.
+ gfx::Rect partialDamage(30, 100, 10, 10);
+ layer->InvalidateContentRect(partialDamage);
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), 0, NULL);
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(45, 80, 15, 8), layer->updateRect());
+}
+
+TEST_F(TiledLayerTest, verifyInvalidationWhenContentsScaleChanges)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+
+ // Create a layer with one tile.
+ layer->SetBounds(gfx::Size(100, 100));
+ calcDrawProps(layer);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 100, 100);
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 100, 100), layer->lastNeedsDisplayRect());
+
+ // Push the tiles to the impl side and check that there is exactly one.
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), 0, NULL);
+ updateTextures();
+ layerPushPropertiesTo(layer.get(), layerImpl.get());
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 0));
+ EXPECT_FALSE(layerImpl->HasResourceIdForTileAt(0, 1));
+ EXPECT_FALSE(layerImpl->HasResourceIdForTileAt(1, 0));
+ EXPECT_FALSE(layerImpl->HasResourceIdForTileAt(1, 1));
+
+ layer->SetNeedsDisplayRect(gfx::Rect());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(), layer->lastNeedsDisplayRect());
+
+ // Change the contents scale.
+ layer->updateContentsScale(2);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 200, 200);
+
+ // The impl side should get 2x2 tiles now.
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), 0, NULL);
+ updateTextures();
+ layerPushPropertiesTo(layer.get(), layerImpl.get());
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 0));
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(0, 1));
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(1, 0));
+ EXPECT_TRUE(layerImpl->HasResourceIdForTileAt(1, 1));
+
+ // Verify that changing the contents scale caused invalidation, and
+ // that the layer-space rectangle requiring painting is not scaled.
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 100, 100), layer->lastNeedsDisplayRect());
+
+ // Invalidate the entire layer again, but do not paint. All tiles should be gone now from the
+ // impl side.
+ layer->SetNeedsDisplay();
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+
+ layerPushPropertiesTo(layer.get(), layerImpl.get());
+ EXPECT_FALSE(layerImpl->HasResourceIdForTileAt(0, 0));
+ EXPECT_FALSE(layerImpl->HasResourceIdForTileAt(0, 1));
+ EXPECT_FALSE(layerImpl->HasResourceIdForTileAt(1, 0));
+ EXPECT_FALSE(layerImpl->HasResourceIdForTileAt(1, 1));
+}
+
+TEST_F(TiledLayerTest, skipsDrawGetsReset)
+{
+ // Create two 300 x 300 tiled layers.
+ gfx::Size contentBounds(300, 300);
+ gfx::Rect contentRect(gfx::Point(), contentBounds);
+
+ // We have enough memory for only one of the two layers.
+ int memoryLimit = 4 * 300 * 300; // 4 bytes per pixel.
+
+ scoped_refptr<FakeTiledLayer> root_layer = make_scoped_refptr(new FakeTiledLayer(layer_tree_host_->contents_texture_manager()));
+ scoped_refptr<FakeTiledLayer> childLayer = make_scoped_refptr(new FakeTiledLayer(layer_tree_host_->contents_texture_manager()));
+ root_layer->AddChild(childLayer);
+
+ root_layer->SetBounds(contentBounds);
+ root_layer->draw_properties().visible_content_rect = contentRect;
+ root_layer->SetPosition(gfx::PointF(0, 0));
+ childLayer->SetBounds(contentBounds);
+ childLayer->draw_properties().visible_content_rect = contentRect;
+ childLayer->SetPosition(gfx::PointF(0, 0));
+ root_layer->InvalidateContentRect(contentRect);
+ childLayer->InvalidateContentRect(contentRect);
+
+ layer_tree_host_->SetRootLayer(root_layer);
+ layer_tree_host_->SetViewportSize(gfx::Size(300, 300), gfx::Size(300, 300));
+
+ layer_tree_host_->UpdateLayers(m_queue.get(), memoryLimit);
+
+ // We'll skip the root layer.
+ EXPECT_TRUE(root_layer->SkipsDraw());
+ EXPECT_FALSE(childLayer->SkipsDraw());
+
+ layer_tree_host_->CommitComplete();
+
+ // Remove the child layer.
+ root_layer->RemoveAllChildren();
+
+ layer_tree_host_->UpdateLayers(m_queue.get(), memoryLimit);
+ EXPECT_FALSE(root_layer->SkipsDraw());
+
+ resourceManagerClearAllMemory(layer_tree_host_->contents_texture_manager(), m_resourceProvider.get());
+ layer_tree_host_->SetRootLayer(NULL);
+}
+
+TEST_F(TiledLayerTest, resizeToSmaller)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+
+ layer->SetBounds(gfx::Size(700, 700));
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 700, 700);
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 700, 700));
+
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), 0, NULL);
+
+ layer->SetBounds(gfx::Size(200, 200));
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 200, 200));
+}
+
+TEST_F(TiledLayerTest, hugeLayerUpdateCrash)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+
+ int size = 1 << 30;
+ layer->SetBounds(gfx::Size(size, size));
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 700, 700);
+ layer->InvalidateContentRect(gfx::Rect(0, 0, size, size));
+
+ // Ensure no crash for bounds where size * size would overflow an int.
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), 0, NULL);
+}
+
+class TiledLayerPartialUpdateTest : public TiledLayerTest {
+public:
+ TiledLayerPartialUpdateTest()
+ {
+ m_settings.maxPartialTextureUpdates = 4;
+ }
+};
+
+TEST_F(TiledLayerPartialUpdateTest, partialUpdates)
+{
+ // Create one 300 x 200 tiled layer with 3 x 2 tiles.
+ gfx::Size contentBounds(300, 200);
+ gfx::Rect contentRect(gfx::Point(), contentBounds);
+
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(layer_tree_host_->contents_texture_manager()));
+ layer->SetBounds(contentBounds);
+ layer->SetPosition(gfx::PointF(0, 0));
+ layer->draw_properties().visible_content_rect = contentRect;
+ layer->InvalidateContentRect(contentRect);
+
+ layer_tree_host_->SetRootLayer(layer);
+ layer_tree_host_->SetViewportSize(gfx::Size(300, 200), gfx::Size(300, 200));
+
+ // Full update of all 6 tiles.
+ layer_tree_host_->UpdateLayers(m_queue.get(),
+ std::numeric_limits<size_t>::max());
+ {
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+ EXPECT_EQ(6, m_queue->fullUploadSize());
+ EXPECT_EQ(0, m_queue->partialUploadSize());
+ updateTextures();
+ EXPECT_EQ(6, layer->fakeLayerUpdater()->updateCount());
+ EXPECT_FALSE(m_queue->hasMoreUpdates());
+ layer->fakeLayerUpdater()->clearUpdateCount();
+ layerPushPropertiesTo(layer.get(), layerImpl.get());
+ }
+ layer_tree_host_->CommitComplete();
+
+ // Full update of 3 tiles and partial update of 3 tiles.
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 300, 150));
+ layer_tree_host_->UpdateLayers(m_queue.get(), std::numeric_limits<size_t>::max());
+ {
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+ EXPECT_EQ(3, m_queue->fullUploadSize());
+ EXPECT_EQ(3, m_queue->partialUploadSize());
+ updateTextures();
+ EXPECT_EQ(6, layer->fakeLayerUpdater()->updateCount());
+ EXPECT_FALSE(m_queue->hasMoreUpdates());
+ layer->fakeLayerUpdater()->clearUpdateCount();
+ layerPushPropertiesTo(layer.get(), layerImpl.get());
+ }
+ layer_tree_host_->CommitComplete();
+
+ // Partial update of 6 tiles.
+ layer->InvalidateContentRect(gfx::Rect(50, 50, 200, 100));
+ {
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+ layer_tree_host_->UpdateLayers(m_queue.get(), std::numeric_limits<size_t>::max());
+ EXPECT_EQ(2, m_queue->fullUploadSize());
+ EXPECT_EQ(4, m_queue->partialUploadSize());
+ updateTextures();
+ EXPECT_EQ(6, layer->fakeLayerUpdater()->updateCount());
+ EXPECT_FALSE(m_queue->hasMoreUpdates());
+ layer->fakeLayerUpdater()->clearUpdateCount();
+ layerPushPropertiesTo(layer.get(), layerImpl.get());
+ }
+ layer_tree_host_->CommitComplete();
+
+ // Checkerboard all tiles.
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 300, 200));
+ {
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+ layerPushPropertiesTo(layer.get(), layerImpl.get());
+ }
+ layer_tree_host_->CommitComplete();
+
+ // Partial update of 6 checkerboard tiles.
+ layer->InvalidateContentRect(gfx::Rect(50, 50, 200, 100));
+ {
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+ layer_tree_host_->UpdateLayers(m_queue.get(), std::numeric_limits<size_t>::max());
+ EXPECT_EQ(6, m_queue->fullUploadSize());
+ EXPECT_EQ(0, m_queue->partialUploadSize());
+ updateTextures();
+ EXPECT_EQ(6, layer->fakeLayerUpdater()->updateCount());
+ EXPECT_FALSE(m_queue->hasMoreUpdates());
+ layer->fakeLayerUpdater()->clearUpdateCount();
+ layerPushPropertiesTo(layer.get(), layerImpl.get());
+ }
+ layer_tree_host_->CommitComplete();
+
+ // Partial update of 4 tiles.
+ layer->InvalidateContentRect(gfx::Rect(50, 50, 100, 100));
+ {
+ scoped_ptr<FakeTiledLayerImpl> layerImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), 1));
+ layer_tree_host_->UpdateLayers(m_queue.get(), std::numeric_limits<size_t>::max());
+ EXPECT_EQ(0, m_queue->fullUploadSize());
+ EXPECT_EQ(4, m_queue->partialUploadSize());
+ updateTextures();
+ EXPECT_EQ(4, layer->fakeLayerUpdater()->updateCount());
+ EXPECT_FALSE(m_queue->hasMoreUpdates());
+ layer->fakeLayerUpdater()->clearUpdateCount();
+ layerPushPropertiesTo(layer.get(), layerImpl.get());
+ }
+ layer_tree_host_->CommitComplete();
+
+ resourceManagerClearAllMemory(layer_tree_host_->contents_texture_manager(), m_resourceProvider.get());
+ layer_tree_host_->SetRootLayer(NULL);
+}
+
+TEST_F(TiledLayerTest, tilesPaintedWithoutOcclusion)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles.
+ layer->SetBounds(gfx::Size(100, 200));
+ calcDrawProps(layer);
+
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), 0, NULL);
+ EXPECT_EQ(2, layer->fakeLayerUpdater()->updateCount());
+}
+
+TEST_F(TiledLayerTest, tilesPaintedWithOcclusion)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ TestOcclusionTracker occluded;
+ m_occlusion = &occluded;
+
+ // The tile size is 100x100.
+
+ layer_tree_host_->SetViewportSize(gfx::Size(600, 600), gfx::Size(600, 600));
+ layer->SetBounds(gfx::Size(600, 600));
+ calcDrawProps(layer);
+
+ occluded.setOcclusion(gfx::Rect(200, 200, 300, 100));
+ layer->draw_properties().drawable_content_rect = gfx::Rect(gfx::Point(), layer->content_bounds());
+ layer->draw_properties().visible_content_rect = gfx::Rect(gfx::Point(), layer->content_bounds());
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ EXPECT_EQ(36-3, layer->fakeLayerUpdater()->updateCount());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 330000, 1);
+ EXPECT_EQ(3, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ layer->fakeLayerUpdater()->clearUpdateCount();
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+
+ occluded.setOcclusion(gfx::Rect(250, 200, 300, 100));
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->Update(m_queue.get(), &occluded, NULL);
+ EXPECT_EQ(36-2, layer->fakeLayerUpdater()->updateCount());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 330000 + 340000, 1);
+ EXPECT_EQ(3 + 2, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ layer->fakeLayerUpdater()->clearUpdateCount();
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+
+ occluded.setOcclusion(gfx::Rect(250, 250, 300, 100));
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->Update(m_queue.get(), &occluded, NULL);
+ EXPECT_EQ(36, layer->fakeLayerUpdater()->updateCount());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 330000 + 340000 + 360000, 1);
+ EXPECT_EQ(3 + 2, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, tilesPaintedWithOcclusionAndVisiblityConstraints)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ TestOcclusionTracker occluded;
+ m_occlusion = &occluded;
+
+ // The tile size is 100x100.
+
+ layer_tree_host_->SetViewportSize(gfx::Size(600, 600), gfx::Size(600, 600));
+ layer->SetBounds(gfx::Size(600, 600));
+ calcDrawProps(layer);
+
+ // The partially occluded tiles (by the 150 occlusion height) are visible beyond the occlusion, so not culled.
+ occluded.setOcclusion(gfx::Rect(200, 200, 300, 150));
+ layer->draw_properties().drawable_content_rect = gfx::Rect(0, 0, 600, 360);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 600, 360);
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ EXPECT_EQ(24-3, layer->fakeLayerUpdater()->updateCount());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 210000, 1);
+ EXPECT_EQ(3, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ layer->fakeLayerUpdater()->clearUpdateCount();
+
+ // Now the visible region stops at the edge of the occlusion so the partly visible tiles become fully occluded.
+ occluded.setOcclusion(gfx::Rect(200, 200, 300, 150));
+ layer->draw_properties().drawable_content_rect = gfx::Rect(0, 0, 600, 350);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 600, 350);
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ EXPECT_EQ(24-6, layer->fakeLayerUpdater()->updateCount());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 210000 + 180000, 1);
+ EXPECT_EQ(3 + 6, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ layer->fakeLayerUpdater()->clearUpdateCount();
+
+ // Now the visible region is even smaller than the occlusion, it should have the same result.
+ occluded.setOcclusion(gfx::Rect(200, 200, 300, 150));
+ layer->draw_properties().drawable_content_rect = gfx::Rect(0, 0, 600, 340);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 600, 340);
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ EXPECT_EQ(24-6, layer->fakeLayerUpdater()->updateCount());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 210000 + 180000 + 180000, 1);
+ EXPECT_EQ(3 + 6 + 6, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+}
+
+TEST_F(TiledLayerTest, tilesNotPaintedWithoutInvalidation)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ TestOcclusionTracker occluded;
+ m_occlusion = &occluded;
+
+ // The tile size is 100x100.
+
+ layer_tree_host_->SetViewportSize(gfx::Size(600, 600), gfx::Size(600, 600));
+ layer->SetBounds(gfx::Size(600, 600));
+ calcDrawProps(layer);
+
+ occluded.setOcclusion(gfx::Rect(200, 200, 300, 100));
+ layer->draw_properties().drawable_content_rect = gfx::Rect(0, 0, 600, 600);
+ layer->draw_properties().visible_content_rect = gfx::Rect(0, 0, 600, 600);
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ EXPECT_EQ(36-3, layer->fakeLayerUpdater()->updateCount());
+ {
+ updateTextures();
+ }
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 330000, 1);
+ EXPECT_EQ(3, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ layer->fakeLayerUpdater()->clearUpdateCount();
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+
+ // Repaint without marking it dirty. The 3 culled tiles will be pre-painted now.
+ layer->Update(m_queue.get(), &occluded, NULL);
+ EXPECT_EQ(3, layer->fakeLayerUpdater()->updateCount());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 330000, 1);
+ EXPECT_EQ(6, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, tilesPaintedWithOcclusionAndTransforms)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ TestOcclusionTracker occluded;
+ m_occlusion = &occluded;
+
+ // The tile size is 100x100.
+
+ // This makes sure the painting works when the occluded region (in screen space)
+ // is transformed differently than the layer.
+ layer_tree_host_->SetViewportSize(gfx::Size(600, 600), gfx::Size(600, 600));
+ layer->SetBounds(gfx::Size(600, 600));
+ calcDrawProps(layer);
+ gfx::Transform screenTransform;
+ screenTransform.Scale(0.5, 0.5);
+ layer->draw_properties().screen_space_transform = screenTransform;
+ layer->draw_properties().target_space_transform = screenTransform;
+
+ occluded.setOcclusion(gfx::Rect(100, 100, 150, 50));
+ layer->draw_properties().drawable_content_rect = gfx::Rect(gfx::Point(), layer->content_bounds());
+ layer->draw_properties().visible_content_rect = gfx::Rect(gfx::Point(), layer->content_bounds());
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ EXPECT_EQ(36-3, layer->fakeLayerUpdater()->updateCount());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 330000, 1);
+ EXPECT_EQ(3, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, tilesPaintedWithOcclusionAndScaling)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ TestOcclusionTracker occluded;
+ m_occlusion = &occluded;
+
+ // The tile size is 100x100.
+
+ // This makes sure the painting works when the content space is scaled to
+ // a different layer space. In this case tiles are scaled to be 200x200
+ // pixels, which means none should be occluded.
+ layer_tree_host_->SetViewportSize(gfx::Size(600, 600), gfx::Size(600, 600));
+ layer->SetBounds(gfx::Size(600, 600));
+ layer->SetRasterScale(0.5);
+ calcDrawProps(layer);
+ EXPECT_FLOAT_EQ(layer->contents_scale_x(), layer->contents_scale_y());
+ gfx::Transform drawTransform;
+ double invScaleFactor = 1 / layer->contents_scale_x();
+ drawTransform.Scale(invScaleFactor, invScaleFactor);
+ layer->draw_properties().target_space_transform = drawTransform;
+ layer->draw_properties().screen_space_transform = drawTransform;
+
+ occluded.setOcclusion(gfx::Rect(200, 200, 300, 100));
+ layer->draw_properties().drawable_content_rect = gfx::Rect(gfx::Point(), layer->bounds());
+ layer->draw_properties().visible_content_rect = gfx::Rect(gfx::Point(), layer->content_bounds());
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ // The content is half the size of the layer (so the number of tiles is fewer).
+ // In this case, the content is 300x300, and since the tile size is 100, the
+ // number of tiles 3x3.
+ EXPECT_EQ(9, layer->fakeLayerUpdater()->updateCount());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 90000, 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ layer->fakeLayerUpdater()->clearUpdateCount();
+
+ // This makes sure the painting works when the content space is scaled to
+ // a different layer space. In this case the occluded region catches the
+ // blown up tiles.
+ occluded.setOcclusion(gfx::Rect(200, 200, 300, 200));
+ layer->draw_properties().drawable_content_rect = gfx::Rect(gfx::Point(), layer->bounds());
+ layer->draw_properties().visible_content_rect = gfx::Rect(gfx::Point(), layer->content_bounds());
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ EXPECT_EQ(9-1, layer->fakeLayerUpdater()->updateCount());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 90000 + 80000, 1);
+ EXPECT_EQ(1, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ layer->fakeLayerUpdater()->clearUpdateCount();
+
+ // This makes sure content scaling and transforms work together.
+ gfx::Transform screenTransform;
+ screenTransform.Scale(0.5, 0.5);
+ layer->draw_properties().screen_space_transform = screenTransform;
+ layer->draw_properties().target_space_transform = screenTransform;
+
+ occluded.setOcclusion(gfx::Rect(100, 100, 150, 100));
+
+ gfx::Rect layerBoundsRect(gfx::Point(), layer->bounds());
+ layer->draw_properties().drawable_content_rect = gfx::ToEnclosingRect(gfx::ScaleRect(layerBoundsRect, 0.5));
+ layer->draw_properties().visible_content_rect = gfx::Rect(gfx::Point(), layer->content_bounds());
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 600, 600));
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ EXPECT_EQ(9-1, layer->fakeLayerUpdater()->updateCount());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 90000 + 80000 + 80000, 1);
+ EXPECT_EQ(1 + 1, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, visibleContentOpaqueRegion)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ TestOcclusionTracker occluded;
+ m_occlusion = &occluded;
+ layer_tree_host_->SetViewportSize(gfx::Size(1000, 1000), gfx::Size(1000, 1000));
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles in various ways.
+
+ gfx::Rect opaquePaintRect;
+ Region opaqueContents;
+
+ gfx::Rect contentBounds = gfx::Rect(0, 0, 100, 200);
+ gfx::Rect visibleBounds = gfx::Rect(0, 0, 100, 150);
+
+ layer->SetBounds(contentBounds.size());
+ calcDrawProps(layer);
+ layer->draw_properties().drawable_content_rect = visibleBounds;
+ layer->draw_properties().visible_content_rect = visibleBounds;
+
+ // If the layer doesn't paint opaque content, then the visibleContentOpaqueRegion should be empty.
+ layer->fakeLayerUpdater()->setOpaquePaintRect(gfx::Rect());
+ layer->InvalidateContentRect(contentBounds);
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ opaqueContents = layer->VisibleContentOpaqueRegion();
+ EXPECT_TRUE(opaqueContents.IsEmpty());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_painted(), 20000, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 20000, 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ // visibleContentOpaqueRegion should match the visible part of what is painted opaque.
+ opaquePaintRect = gfx::Rect(10, 10, 90, 190);
+ layer->fakeLayerUpdater()->setOpaquePaintRect(opaquePaintRect);
+ layer->InvalidateContentRect(contentBounds);
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ updateTextures();
+ opaqueContents = layer->VisibleContentOpaqueRegion();
+ EXPECT_EQ(gfx::IntersectRects(opaquePaintRect, visibleBounds).ToString(), opaqueContents.ToString());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_painted(), 20000 * 2, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 17100, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 20000 + 20000 - 17100, 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ // If we paint again without invalidating, the same stuff should be opaque.
+ layer->fakeLayerUpdater()->setOpaquePaintRect(gfx::Rect());
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ updateTextures();
+ opaqueContents = layer->VisibleContentOpaqueRegion();
+ EXPECT_EQ(gfx::IntersectRects(opaquePaintRect, visibleBounds).ToString(), opaqueContents.ToString());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_painted(), 20000 * 2, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 17100, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 20000 + 20000 - 17100, 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ // If we repaint a non-opaque part of the tile, then it shouldn't lose its opaque-ness. And other tiles should
+ // not be affected.
+ layer->fakeLayerUpdater()->setOpaquePaintRect(gfx::Rect());
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 1, 1));
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ updateTextures();
+ opaqueContents = layer->VisibleContentOpaqueRegion();
+ EXPECT_EQ(gfx::IntersectRects(opaquePaintRect, visibleBounds).ToString(), opaqueContents.ToString());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_painted(), 20000 * 2 + 1, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 17100, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 20000 + 20000 - 17100 + 1, 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ // If we repaint an opaque part of the tile, then it should lose its opaque-ness. But other tiles should still
+ // not be affected.
+ layer->fakeLayerUpdater()->setOpaquePaintRect(gfx::Rect());
+ layer->InvalidateContentRect(gfx::Rect(10, 10, 1, 1));
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ updateTextures();
+ opaqueContents = layer->VisibleContentOpaqueRegion();
+ EXPECT_EQ(gfx::IntersectRects(gfx::Rect(10, 100, 90, 100), visibleBounds).ToString(), opaqueContents.ToString());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_painted(), 20000 * 2 + 1 + 1, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 17100, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 20000 + 20000 - 17100 + 1 + 1, 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, pixels_paintedMetrics)
+{
+ scoped_refptr<FakeTiledLayer> layer = make_scoped_refptr(new FakeTiledLayer(m_resourceManager.get()));
+ TestOcclusionTracker occluded;
+ m_occlusion = &occluded;
+ layer_tree_host_->SetViewportSize(gfx::Size(1000, 1000), gfx::Size(1000, 1000));
+
+ // The tile size is 100x100, so this invalidates and then paints two tiles in various ways.
+
+ gfx::Rect opaquePaintRect;
+ Region opaqueContents;
+
+ gfx::Rect contentBounds = gfx::Rect(0, 0, 100, 300);
+ layer->SetBounds(contentBounds.size());
+ calcDrawProps(layer);
+
+ // Invalidates and paints the whole layer.
+ layer->fakeLayerUpdater()->setOpaquePaintRect(gfx::Rect());
+ layer->InvalidateContentRect(contentBounds);
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ updateTextures();
+ opaqueContents = layer->VisibleContentOpaqueRegion();
+ EXPECT_TRUE(opaqueContents.IsEmpty());
+
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_painted(), 30000, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 30000, 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+
+ // Invalidates an area on the top and bottom tile, which will cause us to paint the tile in the middle,
+ // even though it is not dirty and will not be uploaded.
+ layer->fakeLayerUpdater()->setOpaquePaintRect(gfx::Rect());
+ layer->InvalidateContentRect(gfx::Rect(0, 0, 1, 1));
+ layer->InvalidateContentRect(gfx::Rect(50, 200, 10, 10));
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+ layer->Update(m_queue.get(), &occluded, NULL);
+ updateTextures();
+ opaqueContents = layer->VisibleContentOpaqueRegion();
+ EXPECT_TRUE(opaqueContents.IsEmpty());
+
+ // The middle tile was painted even though not invalidated.
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_painted(), 30000 + 60 * 210, 1);
+ // The pixels uploaded will not include the non-invalidated tile in the middle.
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_opaque(), 0, 1);
+ EXPECT_NEAR(occluded.overdraw_metrics()->pixels_uploaded_translucent(), 30000 + 1 + 100, 1);
+ EXPECT_EQ(0, occluded.overdraw_metrics()->tiles_culled_for_upload());
+}
+
+TEST_F(TiledLayerTest, dontAllocateContentsWhenTargetSurfaceCantBeAllocated)
+{
+ // Tile size is 100x100.
+ gfx::Rect rootRect(0, 0, 300, 200);
+ gfx::Rect childRect(0, 0, 300, 100);
+ gfx::Rect child2Rect(0, 100, 300, 100);
+
+ scoped_refptr<FakeTiledLayer> root = make_scoped_refptr(new FakeTiledLayer(layer_tree_host_->contents_texture_manager()));
+ scoped_refptr<Layer> surface = Layer::Create();
+ scoped_refptr<FakeTiledLayer> child = make_scoped_refptr(new FakeTiledLayer(layer_tree_host_->contents_texture_manager()));
+ scoped_refptr<FakeTiledLayer> child2 = make_scoped_refptr(new FakeTiledLayer(layer_tree_host_->contents_texture_manager()));
+
+ root->SetBounds(rootRect.size());
+ root->SetAnchorPoint(gfx::PointF());
+ root->draw_properties().drawable_content_rect = rootRect;
+ root->draw_properties().visible_content_rect = rootRect;
+ root->AddChild(surface);
+
+ surface->SetForceRenderSurface(true);
+ surface->SetAnchorPoint(gfx::PointF());
+ surface->SetOpacity(0.5);
+ surface->AddChild(child);
+ surface->AddChild(child2);
+
+ child->SetBounds(childRect.size());
+ child->SetAnchorPoint(gfx::PointF());
+ child->SetPosition(childRect.origin());
+ child->draw_properties().visible_content_rect = childRect;
+ child->draw_properties().drawable_content_rect = rootRect;
+
+ child2->SetBounds(child2Rect.size());
+ child2->SetAnchorPoint(gfx::PointF());
+ child2->SetPosition(child2Rect.origin());
+ child2->draw_properties().visible_content_rect = child2Rect;
+ child2->draw_properties().drawable_content_rect = rootRect;
+
+ layer_tree_host_->SetRootLayer(root);
+ layer_tree_host_->SetViewportSize(rootRect.size(), rootRect.size());
+
+ // With a huge memory limit, all layers should update and push their textures.
+ root->InvalidateContentRect(rootRect);
+ child->InvalidateContentRect(childRect);
+ child2->InvalidateContentRect(child2Rect);
+ layer_tree_host_->UpdateLayers(m_queue.get(), std::numeric_limits<size_t>::max());
+ {
+ updateTextures();
+ EXPECT_EQ(6, root->fakeLayerUpdater()->updateCount());
+ EXPECT_EQ(3, child->fakeLayerUpdater()->updateCount());
+ EXPECT_EQ(3, child2->fakeLayerUpdater()->updateCount());
+ EXPECT_FALSE(m_queue->hasMoreUpdates());
+
+ root->fakeLayerUpdater()->clearUpdateCount();
+ child->fakeLayerUpdater()->clearUpdateCount();
+ child2->fakeLayerUpdater()->clearUpdateCount();
+
+ scoped_ptr<FakeTiledLayerImpl> rootImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), root->id()));
+ scoped_ptr<FakeTiledLayerImpl> childImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), child->id()));
+ scoped_ptr<FakeTiledLayerImpl> child2Impl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), child2->id()));
+ layerPushPropertiesTo(root.get(), rootImpl.get());
+ layerPushPropertiesTo(child.get(), childImpl.get());
+ layerPushPropertiesTo(child2.get(), child2Impl.get());
+
+ for (unsigned i = 0; i < 3; ++i) {
+ for (unsigned j = 0; j < 2; ++j)
+ EXPECT_TRUE(rootImpl->HasResourceIdForTileAt(i, j));
+ EXPECT_TRUE(childImpl->HasResourceIdForTileAt(i, 0));
+ EXPECT_TRUE(child2Impl->HasResourceIdForTileAt(i, 0));
+ }
+ }
+ layer_tree_host_->CommitComplete();
+
+ // With a memory limit that includes only the root layer (3x2 tiles) and half the surface that
+ // the child layers draw into, the child layers will not be allocated. If the surface isn't
+ // accounted for, then one of the children would fit within the memory limit.
+ root->InvalidateContentRect(rootRect);
+ child->InvalidateContentRect(childRect);
+ child2->InvalidateContentRect(child2Rect);
+ layer_tree_host_->UpdateLayers(m_queue.get(), (3 * 2 + 3 * 1) * (100 * 100) * 4);
+ {
+ updateTextures();
+ EXPECT_EQ(6, root->fakeLayerUpdater()->updateCount());
+ EXPECT_EQ(0, child->fakeLayerUpdater()->updateCount());
+ EXPECT_EQ(0, child2->fakeLayerUpdater()->updateCount());
+ EXPECT_FALSE(m_queue->hasMoreUpdates());
+
+ root->fakeLayerUpdater()->clearUpdateCount();
+ child->fakeLayerUpdater()->clearUpdateCount();
+ child2->fakeLayerUpdater()->clearUpdateCount();
+
+ scoped_ptr<FakeTiledLayerImpl> rootImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), root->id()));
+ scoped_ptr<FakeTiledLayerImpl> childImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), child->id()));
+ scoped_ptr<FakeTiledLayerImpl> child2Impl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), child2->id()));
+ layerPushPropertiesTo(root.get(), rootImpl.get());
+ layerPushPropertiesTo(child.get(), childImpl.get());
+ layerPushPropertiesTo(child2.get(), child2Impl.get());
+
+ for (unsigned i = 0; i < 3; ++i) {
+ for (unsigned j = 0; j < 2; ++j)
+ EXPECT_TRUE(rootImpl->HasResourceIdForTileAt(i, j));
+ EXPECT_FALSE(childImpl->HasResourceIdForTileAt(i, 0));
+ EXPECT_FALSE(child2Impl->HasResourceIdForTileAt(i, 0));
+ }
+ }
+ layer_tree_host_->CommitComplete();
+
+ // With a memory limit that includes only half the root layer, no contents will be
+ // allocated. If render surface memory wasn't accounted for, there is enough space
+ // for one of the children layers, but they draw into a surface that can't be
+ // allocated.
+ root->InvalidateContentRect(rootRect);
+ child->InvalidateContentRect(childRect);
+ child2->InvalidateContentRect(child2Rect);
+ layer_tree_host_->UpdateLayers(m_queue.get(), (3 * 1) * (100 * 100) * 4);
+ {
+ updateTextures();
+ EXPECT_EQ(0, root->fakeLayerUpdater()->updateCount());
+ EXPECT_EQ(0, child->fakeLayerUpdater()->updateCount());
+ EXPECT_EQ(0, child2->fakeLayerUpdater()->updateCount());
+ EXPECT_FALSE(m_queue->hasMoreUpdates());
+
+ root->fakeLayerUpdater()->clearUpdateCount();
+ child->fakeLayerUpdater()->clearUpdateCount();
+ child2->fakeLayerUpdater()->clearUpdateCount();
+
+ scoped_ptr<FakeTiledLayerImpl> rootImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), root->id()));
+ scoped_ptr<FakeTiledLayerImpl> childImpl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), child->id()));
+ scoped_ptr<FakeTiledLayerImpl> child2Impl = make_scoped_ptr(new FakeTiledLayerImpl(m_hostImpl->active_tree(), child2->id()));
+ layerPushPropertiesTo(root.get(), rootImpl.get());
+ layerPushPropertiesTo(child.get(), childImpl.get());
+ layerPushPropertiesTo(child2.get(), child2Impl.get());
+
+ for (unsigned i = 0; i < 3; ++i) {
+ for (unsigned j = 0; j < 2; ++j)
+ EXPECT_FALSE(rootImpl->HasResourceIdForTileAt(i, j));
+ EXPECT_FALSE(childImpl->HasResourceIdForTileAt(i, 0));
+ EXPECT_FALSE(child2Impl->HasResourceIdForTileAt(i, 0));
+ }
+ }
+ layer_tree_host_->CommitComplete();
+
+ resourceManagerClearAllMemory(layer_tree_host_->contents_texture_manager(), m_resourceProvider.get());
+ layer_tree_host_->SetRootLayer(NULL);
+}
+
+class TrackingLayerPainter : public LayerPainter {
+public:
+ static scoped_ptr<TrackingLayerPainter> Create() { return make_scoped_ptr(new TrackingLayerPainter()); }
+
+ virtual void Paint(SkCanvas* canvas, gfx::Rect content_rect, gfx::RectF* opaque) OVERRIDE
+ {
+ m_paintedRect = content_rect;
+ }
+
+ const gfx::Rect& paintedRect() const { return m_paintedRect; }
+ void resetPaintedRect() { m_paintedRect = gfx::Rect(); }
+
+private:
+ TrackingLayerPainter() { }
+
+ gfx::Rect m_paintedRect;
+};
+
+class UpdateTrackingTiledLayer : public FakeTiledLayer {
+public:
+ explicit UpdateTrackingTiledLayer(PrioritizedResourceManager* manager)
+ : FakeTiledLayer(manager)
+ {
+ scoped_ptr<TrackingLayerPainter> trackingLayerPainter(TrackingLayerPainter::Create());
+ m_trackingLayerPainter = trackingLayerPainter.get();
+ m_layerUpdater = BitmapContentLayerUpdater::Create(trackingLayerPainter.PassAs<LayerPainter>());
+ }
+
+ TrackingLayerPainter* trackingLayerPainter() const { return m_trackingLayerPainter; }
+
+protected:
+ virtual ~UpdateTrackingTiledLayer() { }
+
+ virtual LayerUpdater* Updater() const OVERRIDE { return m_layerUpdater.get(); }
+
+private:
+ TrackingLayerPainter* m_trackingLayerPainter;
+ scoped_refptr<BitmapContentLayerUpdater> m_layerUpdater;
+};
+
+TEST_F(TiledLayerTest, nonIntegerContentsScaleIsNotDistortedDuringPaint)
+{
+ scoped_refptr<UpdateTrackingTiledLayer> layer = make_scoped_refptr(new UpdateTrackingTiledLayer(m_resourceManager.get()));
+
+ gfx::Rect layerRect(0, 0, 30, 31);
+ layer->SetPosition(layerRect.origin());
+ layer->SetBounds(layerRect.size());
+ layer->updateContentsScale(1.5);
+
+ gfx::Rect contentRect(0, 0, 45, 47);
+ EXPECT_EQ(contentRect.size(), layer->content_bounds());
+ layer->draw_properties().visible_content_rect = contentRect;
+ layer->draw_properties().drawable_content_rect = contentRect;
+
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+
+ // Update the whole tile.
+ layer->Update(m_queue.get(), 0, NULL);
+ layer->trackingLayerPainter()->resetPaintedRect();
+
+ EXPECT_RECT_EQ(gfx::Rect(), layer->trackingLayerPainter()->paintedRect());
+ updateTextures();
+
+ // Invalidate the entire layer in content space. When painting, the rect given to webkit should match the layer's bounds.
+ layer->InvalidateContentRect(contentRect);
+ layer->Update(m_queue.get(), 0, NULL);
+
+ EXPECT_RECT_EQ(layerRect, layer->trackingLayerPainter()->paintedRect());
+}
+
+TEST_F(TiledLayerTest, nonIntegerContentsScaleIsNotDistortedDuringInvalidation)
+{
+ scoped_refptr<UpdateTrackingTiledLayer> layer = make_scoped_refptr(new UpdateTrackingTiledLayer(m_resourceManager.get()));
+
+ gfx::Rect layerRect(0, 0, 30, 31);
+ layer->SetPosition(layerRect.origin());
+ layer->SetBounds(layerRect.size());
+ layer->updateContentsScale(1.3f);
+
+ gfx::Rect contentRect(gfx::Point(), layer->content_bounds());
+ layer->draw_properties().visible_content_rect = contentRect;
+ layer->draw_properties().drawable_content_rect = contentRect;
+
+ layer->SetTexturePriorities(m_priorityCalculator);
+ m_resourceManager->prioritizeTextures();
+
+ // Update the whole tile.
+ layer->Update(m_queue.get(), 0, NULL);
+ layer->trackingLayerPainter()->resetPaintedRect();
+
+ EXPECT_RECT_EQ(gfx::Rect(), layer->trackingLayerPainter()->paintedRect());
+ updateTextures();
+
+ // Invalidate the entire layer in layer space. When painting, the rect given to webkit should match the layer's bounds.
+ layer->SetNeedsDisplayRect(layerRect);
+ layer->Update(m_queue.get(), 0, NULL);
+
+ EXPECT_RECT_EQ(layerRect, layer->trackingLayerPainter()->paintedRect());
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/video_frame_provider.h b/cc/layers/video_frame_provider.h
new file mode 100644
index 0000000..ecf0024
--- /dev/null
+++ b/cc/layers/video_frame_provider.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 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.
+
+#ifndef CC_LAYERS_VIDEO_FRAME_PROVIDER_H_
+#define CC_LAYERS_VIDEO_FRAME_PROVIDER_H_
+
+#include "base/memory/ref_counted.h"
+
+namespace media {
+class VideoFrame;
+}
+
+namespace cc {
+
+// Threading notes: This class may be used in a multi threaded manner.
+// Specifically, the implementation may call getCurrentFrame() or
+// putCurrentFrame() from the compositor thread. If so, the caller is
+// responsible for making sure Client::didReceiveFrame and
+// Client::didUpdateMatrix are only called from this same thread.
+class VideoFrameProvider {
+ public:
+ virtual ~VideoFrameProvider() {}
+
+ class Client {
+ public:
+ // Provider will call this method to tell the client to stop using it.
+ // StopUsingProvider() may be called from any thread. The client should
+ // block until it has PutCurrentFrame() any outstanding frames.
+ virtual void StopUsingProvider() = 0;
+
+ // Notifies the provider's client that a call to GetCurrentFrame() will
+ // return new data.
+ virtual void DidReceiveFrame() = 0;
+
+ // Notifies the provider's client of a new UV transform matrix to be used.
+ virtual void DidUpdateMatrix(const float* matrix) = 0;
+ };
+
+ // May be called from any thread, but there must be some external guarantee
+ // that the provider is not destroyed before this call returns.
+ virtual void SetVideoFrameProviderClient(Client* client) = 0;
+
+ // This function places a lock on the current frame and returns a pointer to
+ // it. Calls to this method should always be followed with a call to
+ // PutCurrentFrame().
+ // Only the current provider client should call this function.
+ virtual scoped_refptr<media::VideoFrame> GetCurrentFrame() = 0;
+
+ // This function releases the lock on the video frame. It should always be
+ // called after GetCurrentFrame(). Frames passed into this method
+ // should no longer be referenced after the call is made. Only the current
+ // provider client should call this function.
+ virtual void PutCurrentFrame(
+ const scoped_refptr<media::VideoFrame>& frame) = 0;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_VIDEO_FRAME_PROVIDER_H_
diff --git a/cc/layers/video_frame_provider_client_impl.cc b/cc/layers/video_frame_provider_client_impl.cc
new file mode 100644
index 0000000..2f318bf
--- /dev/null
+++ b/cc/layers/video_frame_provider_client_impl.cc
@@ -0,0 +1,87 @@
+// Copyright 2013 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 "cc/layers/video_frame_provider_client_impl.h"
+
+#include "cc/base/math_util.h"
+#include "cc/layers/video_layer_impl.h"
+
+namespace cc {
+
+// static
+scoped_refptr<VideoFrameProviderClientImpl>
+ VideoFrameProviderClientImpl::Create(
+ VideoFrameProvider* provider) {
+ return make_scoped_refptr(
+ new VideoFrameProviderClientImpl(provider));
+}
+
+VideoFrameProviderClientImpl::~VideoFrameProviderClientImpl() {}
+
+VideoFrameProviderClientImpl::VideoFrameProviderClientImpl(
+ VideoFrameProvider* provider)
+ : provider_(provider) {
+ // This only happens during a commit on the compositor thread while the main
+ // thread is blocked. That makes this a thread-safe call to set the video
+ // frame provider client that does not require a lock. The same is true of
+ // the call to Stop().
+ provider_->SetVideoFrameProviderClient(this);
+
+ // This matrix is the default transformation for stream textures, and flips
+ // on the Y axis.
+ stream_texture_matrix_ = gfx::Transform(
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, -1.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+}
+
+void VideoFrameProviderClientImpl::Stop() {
+ if (!provider_)
+ return;
+ provider_->SetVideoFrameProviderClient(NULL);
+ provider_ = NULL;
+}
+
+media::VideoFrame* VideoFrameProviderClientImpl::AcquireLockAndCurrentFrame() {
+ provider_lock_.Acquire(); // Balanced by call to ReleaseLock().
+ if (!provider_)
+ return NULL;
+
+ return provider_->GetCurrentFrame();
+}
+
+void VideoFrameProviderClientImpl::PutCurrentFrame(media::VideoFrame* frame) {
+ provider_lock_.AssertAcquired();
+ provider_->PutCurrentFrame(frame);
+}
+
+void VideoFrameProviderClientImpl::ReleaseLock() {
+ provider_lock_.AssertAcquired();
+ provider_lock_.Release();
+}
+
+void VideoFrameProviderClientImpl::StopUsingProvider() {
+ // Block the provider from shutting down until this client is done
+ // using the frame.
+ base::AutoLock locker(provider_lock_);
+ provider_ = 0;
+}
+
+void VideoFrameProviderClientImpl::DidReceiveFrame() {
+ if (active_video_layer_)
+ active_video_layer_->SetNeedsRedraw();
+}
+
+void VideoFrameProviderClientImpl::DidUpdateMatrix(const float* matrix) {
+ stream_texture_matrix_ = gfx::Transform(
+ matrix[0], matrix[4], matrix[8], matrix[12],
+ matrix[1], matrix[5], matrix[9], matrix[13],
+ matrix[2], matrix[6], matrix[10], matrix[14],
+ matrix[3], matrix[7], matrix[11], matrix[15]);
+ if (active_video_layer_)
+ active_video_layer_->SetNeedsRedraw();
+}
+
+} // namespace cc
diff --git a/cc/layers/video_frame_provider_client_impl.h b/cc/layers/video_frame_provider_client_impl.h
new file mode 100644
index 0000000..c7f332e
--- /dev/null
+++ b/cc/layers/video_frame_provider_client_impl.h
@@ -0,0 +1,63 @@
+// Copyright 2013 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.
+
+#ifndef CC_LAYERS_VIDEO_FRAME_PROVIDER_CLIENT_IMPL_H_
+#define CC_LAYERS_VIDEO_FRAME_PROVIDER_CLIENT_IMPL_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "cc/layers/video_frame_provider.h"
+#include "ui/gfx/transform.h"
+
+namespace media { class VideoFrame; }
+
+namespace cc {
+class VideoLayerImpl;
+
+class VideoFrameProviderClientImpl
+ : public VideoFrameProvider::Client,
+ public base::RefCounted<VideoFrameProviderClientImpl> {
+ public:
+ static scoped_refptr<VideoFrameProviderClientImpl> Create(
+ VideoFrameProvider* provider);
+
+ void set_active_video_layer(VideoLayerImpl* video_layer) {
+ active_video_layer_ = video_layer;
+ }
+
+ void Stop();
+ bool Stopped() const { return !provider_; }
+
+ media::VideoFrame* AcquireLockAndCurrentFrame();
+ void PutCurrentFrame(media::VideoFrame* frame);
+ void ReleaseLock();
+ const gfx::Transform& stream_texture_matrix() const {
+ return stream_texture_matrix_;
+ }
+
+ // VideoFrameProvider::Client implementation. These methods are all callable
+ // on any thread.
+ virtual void StopUsingProvider() OVERRIDE;
+ virtual void DidReceiveFrame() OVERRIDE;
+ virtual void DidUpdateMatrix(const float* matrix) OVERRIDE;
+
+ private:
+ explicit VideoFrameProviderClientImpl(VideoFrameProvider* provider);
+ friend class base::RefCounted<VideoFrameProviderClientImpl>;
+ virtual ~VideoFrameProviderClientImpl();
+
+ VideoLayerImpl* active_video_layer_;
+
+ // Guards the destruction of provider_ and the frame that it provides
+ base::Lock provider_lock_;
+ VideoFrameProvider* provider_;
+
+ gfx::Transform stream_texture_matrix_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameProviderClientImpl);
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_VIDEO_FRAME_PROVIDER_CLIENT_IMPL_H_
diff --git a/cc/layers/video_layer.cc b/cc/layers/video_layer.cc
new file mode 100644
index 0000000..5066835
--- /dev/null
+++ b/cc/layers/video_layer.cc
@@ -0,0 +1,25 @@
+// Copyright 2010 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 "cc/layers/video_layer.h"
+
+#include "cc/layers/video_layer_impl.h"
+
+namespace cc {
+
+scoped_refptr<VideoLayer> VideoLayer::Create(VideoFrameProvider* provider) {
+ return make_scoped_refptr(new VideoLayer(provider));
+}
+
+VideoLayer::VideoLayer(VideoFrameProvider* provider) : provider_(provider) {
+ DCHECK(provider_);
+}
+
+VideoLayer::~VideoLayer() {}
+
+scoped_ptr<LayerImpl> VideoLayer::CreateLayerImpl(LayerTreeImpl* tree_impl) {
+ return VideoLayerImpl::Create(tree_impl, id(), provider_).PassAs<LayerImpl>();
+}
+
+} // namespace cc
diff --git a/cc/layers/video_layer.h b/cc/layers/video_layer.h
new file mode 100644
index 0000000..c9705cd
--- /dev/null
+++ b/cc/layers/video_layer.h
@@ -0,0 +1,38 @@
+// Copyright 2010 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.
+
+#ifndef CC_LAYERS_VIDEO_LAYER_H_
+#define CC_LAYERS_VIDEO_LAYER_H_
+
+#include "base/callback.h"
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer.h"
+
+namespace media { class VideoFrame; }
+
+namespace cc {
+
+class VideoFrameProvider;
+class VideoLayerImpl;
+
+// A Layer that contains a Video element.
+class CC_EXPORT VideoLayer : public Layer {
+ public:
+ static scoped_refptr<VideoLayer> Create(VideoFrameProvider* provider);
+
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+
+ private:
+ explicit VideoLayer(VideoFrameProvider* provider);
+ virtual ~VideoLayer();
+
+ // This pointer is only for passing to VideoLayerImpl's constructor. It should
+ // never be dereferenced by this class.
+ VideoFrameProvider* provider_;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_VIDEO_LAYER_H_
diff --git a/cc/layers/video_layer_impl.cc b/cc/layers/video_layer_impl.cc
new file mode 100644
index 0000000..42d5036
--- /dev/null
+++ b/cc/layers/video_layer_impl.cc
@@ -0,0 +1,482 @@
+// Copyright 2011 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 "cc/layers/video_layer_impl.h"
+
+#include "base/logging.h"
+#include "cc/base/math_util.h"
+#include "cc/layers/quad_sink.h"
+#include "cc/layers/video_frame_provider_client_impl.h"
+#include "cc/output/renderer.h"
+#include "cc/quads/io_surface_draw_quad.h"
+#include "cc/quads/stream_video_draw_quad.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "cc/quads/yuv_video_draw_quad.h"
+#include "cc/resources/resource_provider.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "media/filters/skcanvas_video_renderer.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+
+#if defined(GOOGLE_TV)
+#include "cc/quads/solid_color_draw_quad.h"
+#endif
+
+namespace cc {
+
+// static
+scoped_ptr<VideoLayerImpl> VideoLayerImpl::Create(
+ LayerTreeImpl* tree_impl,
+ int id,
+ VideoFrameProvider* provider) {
+ scoped_ptr<VideoLayerImpl> layer(new VideoLayerImpl(tree_impl, id));
+ layer->SetProviderClientImpl(VideoFrameProviderClientImpl::Create(provider));
+ DCHECK(tree_impl->proxy()->IsImplThread());
+ DCHECK(tree_impl->proxy()->IsMainThreadBlocked());
+ return layer.Pass();
+}
+
+VideoLayerImpl::VideoLayerImpl(LayerTreeImpl* tree_impl, int id)
+ : LayerImpl(tree_impl, id),
+ frame_(NULL),
+ format_(GL_INVALID_VALUE),
+ convert_yuv_(false),
+ external_texture_resource_(0) {}
+
+VideoLayerImpl::~VideoLayerImpl() {
+ if (!provider_client_impl_->Stopped()) {
+ // In impl side painting, we may have a pending and active layer
+ // associated with the video provider at the same time. Both have a ref
+ // on the VideoFrameProviderClientImpl, but we stop when the first
+ // LayerImpl (the one on the pending tree) is destroyed since we know
+ // the main thread is blocked for this commit.
+ DCHECK(layer_tree_impl()->proxy()->IsImplThread());
+ DCHECK(layer_tree_impl()->proxy()->IsMainThreadBlocked());
+ provider_client_impl_->Stop();
+ }
+ FreePlaneData(layer_tree_impl()->resource_provider());
+
+#ifndef NDEBUG
+ for (size_t i = 0; i < media::VideoFrame::kMaxPlanes; ++i)
+ DCHECK(!frame_planes_[i].resource_id);
+ DCHECK(!external_texture_resource_);
+#endif
+}
+
+scoped_ptr<LayerImpl> VideoLayerImpl::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return scoped_ptr<LayerImpl>(new VideoLayerImpl(tree_impl, id()));
+}
+
+void VideoLayerImpl::PushPropertiesTo(LayerImpl* layer) {
+ LayerImpl::PushPropertiesTo(layer);
+
+ VideoLayerImpl* other = static_cast<VideoLayerImpl*>(layer);
+ other->SetProviderClientImpl(provider_client_impl_);
+}
+
+void VideoLayerImpl::DidBecomeActive() {
+ provider_client_impl_->set_active_video_layer(this);
+}
+
+// Convert media::VideoFrame::Format to OpenGL enum values.
+static GLenum ConvertVFCFormatToGLenum(const media::VideoFrame& frame) {
+ switch (frame.format()) {
+ case media::VideoFrame::YV12:
+ case media::VideoFrame::YV16:
+ return GL_LUMINANCE;
+ case media::VideoFrame::NATIVE_TEXTURE:
+ return frame.texture_target();
+#if defined(GOOGLE_TV)
+ case media::VideoFrame::HOLE:
+ return GL_INVALID_VALUE;
+#endif
+ case media::VideoFrame::INVALID:
+ case media::VideoFrame::RGB32:
+ case media::VideoFrame::EMPTY:
+ case media::VideoFrame::I420:
+ NOTREACHED();
+ break;
+ }
+ return GL_INVALID_VALUE;
+}
+
+size_t VideoLayerImpl::NumPlanes() const {
+ if (!frame_)
+ return 0;
+
+ if (convert_yuv_)
+ return 1;
+
+ return media::VideoFrame::NumPlanes(frame_->format());
+}
+
+void VideoLayerImpl::WillDraw(ResourceProvider* resource_provider) {
+ LayerImpl::WillDraw(resource_provider);
+
+
+ // Explicitly acquire and release the provider mutex so it can be held from
+ // willDraw to didDraw. Since the compositor thread is in the middle of
+ // drawing, the layer will not be destroyed before didDraw is called.
+ // Therefore, the only thing that will prevent this lock from being released
+ // is the GPU process locking it. As the GPU process can't cause the
+ // destruction of the provider (calling stopUsingProvider), holding this
+ // lock should not cause a deadlock.
+ frame_ = provider_client_impl_->AcquireLockAndCurrentFrame();
+
+ WillDrawInternal(resource_provider);
+ FreeUnusedPlaneData(resource_provider);
+
+ if (!frame_)
+ provider_client_impl_->ReleaseLock();
+}
+
+void VideoLayerImpl::WillDrawInternal(ResourceProvider* resource_provider) {
+ DCHECK(!external_texture_resource_);
+
+ if (!frame_)
+ return;
+
+#if defined(GOOGLE_TV)
+ if (frame_->format() == media::VideoFrame::HOLE)
+ return;
+#endif
+
+ format_ = ConvertVFCFormatToGLenum(*frame_);
+
+ // If these fail, we'll have to add draw logic that handles offset bitmap/
+ // texture UVs. For now, just expect (0, 0) offset, since all our decoders
+ // so far don't offset.
+ DCHECK_EQ(frame_->visible_rect().x(), 0);
+ DCHECK_EQ(frame_->visible_rect().y(), 0);
+
+ if (format_ == GL_INVALID_VALUE) {
+ provider_client_impl_->PutCurrentFrame(frame_);
+ frame_ = NULL;
+ return;
+ }
+
+ // TODO: If we're in software compositing mode, we do the YUV -> RGB
+ // conversion here. That involves an extra copy of each frame to a bitmap.
+ // Obviously, this is suboptimal and should be addressed once ubercompositor
+ // starts shaping up.
+ convert_yuv_ =
+ resource_provider->default_resource_type() == ResourceProvider::Bitmap &&
+ (frame_->format() == media::VideoFrame::YV12 ||
+ frame_->format() == media::VideoFrame::YV16);
+
+ if (convert_yuv_)
+ format_ = GL_RGBA;
+
+ if (!AllocatePlaneData(resource_provider)) {
+ provider_client_impl_->PutCurrentFrame(frame_);
+ frame_ = NULL;
+ return;
+ }
+
+ if (!CopyPlaneData(resource_provider)) {
+ provider_client_impl_->PutCurrentFrame(frame_);
+ frame_ = NULL;
+ return;
+ }
+
+ if (format_ == GL_TEXTURE_2D) {
+ external_texture_resource_ =
+ resource_provider->CreateResourceFromExternalTexture(
+ frame_->texture_id());
+ }
+}
+
+void VideoLayerImpl::AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) {
+ if (!frame_)
+ return;
+
+ SharedQuadState* shared_quad_state =
+ quad_sink->UseSharedQuadState(CreateSharedQuadState());
+ AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
+
+ // TODO: When we pass quads out of process, we need to double-buffer, or
+ // otherwise synchonize use of all textures in the quad.
+
+ gfx::Rect quad_rect(content_bounds());
+ gfx::Rect opaque_rect(contents_opaque() ? quad_rect : gfx::Rect());
+ gfx::Rect visible_rect = frame_->visible_rect();
+ gfx::Size coded_size = frame_->coded_size();
+
+ // pixels for macroblocked formats.
+ float tex_width_scale =
+ static_cast<float>(visible_rect.width()) / coded_size.width();
+ float tex_height_scale =
+ static_cast<float>(visible_rect.height()) / coded_size.height();
+
+#if defined(GOOGLE_TV)
+ // This block and other blocks wrapped around #if defined(GOOGLE_TV) is not
+ // maintained by the general compositor team. Please contact the following
+ // people instead:
+ //
+ // wonsik@chromium.org
+ // ycheo@chromium.org
+
+ if (frame_->format() == media::VideoFrame::HOLE) {
+ scoped_ptr<SolidColorDrawQuad> solid_color_draw_quad =
+ SolidColorDrawQuad::Create();
+ // Create a solid color quad with transparent black and force no
+ // blending.
+ solid_color_draw_quad->SetAll(
+ shared_quad_state, quad_rect, quad_rect, quad_rect, false,
+ SK_ColorTRANSPARENT);
+ quad_sink->Append(solid_color_draw_quad.PassAs<DrawQuad>(),
+ append_quads_data);
+ return;
+ }
+#endif
+
+ switch (format_) {
+ case GL_LUMINANCE: {
+ // YUV software decoder.
+ const FramePlane& y_plane = frame_planes_[media::VideoFrame::kYPlane];
+ const FramePlane& u_plane = frame_planes_[media::VideoFrame::kUPlane];
+ const FramePlane& v_plane = frame_planes_[media::VideoFrame::kVPlane];
+ gfx::SizeF tex_scale(tex_width_scale, tex_height_scale);
+ scoped_ptr<YUVVideoDrawQuad> yuv_video_quad = YUVVideoDrawQuad::Create();
+ yuv_video_quad->SetNew(shared_quad_state,
+ quad_rect,
+ opaque_rect,
+ tex_scale,
+ y_plane,
+ u_plane,
+ v_plane);
+ quad_sink->Append(yuv_video_quad.PassAs<DrawQuad>(), append_quads_data);
+ break;
+ }
+ case GL_RGBA: {
+ // RGBA software decoder.
+ const FramePlane& plane = frame_planes_[media::VideoFrame::kRGBPlane];
+ bool premultiplied_alpha = true;
+ gfx::PointF uv_top_left(0.f, 0.f);
+ gfx::PointF uv_bottom_right(tex_width_scale, tex_height_scale);
+ float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
+ bool flipped = false;
+ scoped_ptr<TextureDrawQuad> texture_quad = TextureDrawQuad::Create();
+ texture_quad->SetNew(shared_quad_state,
+ quad_rect,
+ opaque_rect,
+ plane.resource_id,
+ premultiplied_alpha,
+ uv_top_left,
+ uv_bottom_right,
+ opacity,
+ flipped);
+ quad_sink->Append(texture_quad.PassAs<DrawQuad>(), append_quads_data);
+ break;
+ }
+ case GL_TEXTURE_2D: {
+ // NativeTexture hardware decoder.
+ bool premultiplied_alpha = true;
+ gfx::PointF uv_top_left(0.f, 0.f);
+ gfx::PointF uv_bottom_right(tex_width_scale, tex_height_scale);
+ float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
+ bool flipped = false;
+ scoped_ptr<TextureDrawQuad> texture_quad = TextureDrawQuad::Create();
+ texture_quad->SetNew(shared_quad_state,
+ quad_rect,
+ opaque_rect,
+ external_texture_resource_,
+ premultiplied_alpha,
+ uv_top_left,
+ uv_bottom_right,
+ opacity,
+ flipped);
+ quad_sink->Append(texture_quad.PassAs<DrawQuad>(), append_quads_data);
+ break;
+ }
+ case GL_TEXTURE_RECTANGLE_ARB: {
+ gfx::Size visible_size(visible_rect.width(), visible_rect.height());
+ scoped_ptr<IOSurfaceDrawQuad> io_surface_quad =
+ IOSurfaceDrawQuad::Create();
+ io_surface_quad->SetNew(shared_quad_state,
+ quad_rect,
+ opaque_rect,
+ visible_size,
+ frame_->texture_id(),
+ IOSurfaceDrawQuad::UNFLIPPED);
+ quad_sink->Append(io_surface_quad.PassAs<DrawQuad>(), append_quads_data);
+ break;
+ }
+ case GL_TEXTURE_EXTERNAL_OES: {
+ // StreamTexture hardware decoder.
+ gfx::Transform transform(provider_client_impl_->stream_texture_matrix());
+ transform.Scale(tex_width_scale, tex_height_scale);
+ scoped_ptr<StreamVideoDrawQuad> stream_video_quad =
+ StreamVideoDrawQuad::Create();
+ stream_video_quad->SetNew(shared_quad_state,
+ quad_rect,
+ opaque_rect,
+ frame_->texture_id(),
+ transform);
+ quad_sink->Append(stream_video_quad.PassAs<DrawQuad>(),
+ append_quads_data);
+ break;
+ }
+ default:
+ // Someone updated ConvertVFCFormatToGLenum() above but update this!
+ NOTREACHED();
+ break;
+ }
+}
+
+void VideoLayerImpl::DidDraw(ResourceProvider* resource_provider) {
+ LayerImpl::DidDraw(resource_provider);
+
+ if (!frame_)
+ return;
+
+ if (format_ == GL_TEXTURE_2D) {
+ DCHECK(external_texture_resource_);
+ // TODO: the following assert will not be true when sending resources to a
+ // parent compositor. We will probably need to hold on to frame_ for
+ // longer, and have several "current frames" in the pipeline.
+ DCHECK(!resource_provider->InUseByConsumer(external_texture_resource_));
+ resource_provider->DeleteResource(external_texture_resource_);
+ external_texture_resource_ = 0;
+ }
+
+ provider_client_impl_->PutCurrentFrame(frame_);
+ frame_ = NULL;
+
+ provider_client_impl_->ReleaseLock();
+}
+
+static gfx::Size VideoFrameDimension(media::VideoFrame* frame, int plane) {
+ gfx::Size dimensions = frame->coded_size();
+ switch (frame->format()) {
+ case media::VideoFrame::YV12:
+ if (plane != media::VideoFrame::kYPlane) {
+ dimensions.set_width(dimensions.width() / 2);
+ dimensions.set_height(dimensions.height() / 2);
+ }
+ break;
+ case media::VideoFrame::YV16:
+ if (plane != media::VideoFrame::kYPlane)
+ dimensions.set_width(dimensions.width() / 2);
+ break;
+ default:
+ break;
+ }
+ return dimensions;
+}
+
+bool VideoLayerImpl::FramePlane::AllocateData(
+ ResourceProvider* resource_provider) {
+ if (resource_id)
+ return true;
+
+ resource_id = resource_provider->CreateResource(
+ size, format, ResourceProvider::TextureUsageAny);
+ return resource_id;
+}
+
+void VideoLayerImpl::FramePlane::FreeData(ResourceProvider* resource_provider) {
+ if (!resource_id)
+ return;
+
+ resource_provider->DeleteResource(resource_id);
+ resource_id = 0;
+}
+
+bool VideoLayerImpl::AllocatePlaneData(ResourceProvider* resource_provider) {
+ int max_texture_size = resource_provider->max_texture_size();
+ size_t plane_count = NumPlanes();
+ for (size_t plane_index = 0; plane_index < plane_count; ++plane_index) {
+ VideoLayerImpl::FramePlane* plane = &frame_planes_[plane_index];
+
+ gfx::Size required_texture_size = VideoFrameDimension(frame_, plane_index);
+ // TODO: Remove the test against max_texture_size when tiled layers are
+ // implemented.
+ if (required_texture_size.IsEmpty() ||
+ required_texture_size.width() > max_texture_size ||
+ required_texture_size.height() > max_texture_size)
+ return false;
+
+ if (plane->size != required_texture_size || plane->format != format_) {
+ plane->FreeData(resource_provider);
+ plane->size = required_texture_size;
+ plane->format = format_;
+ }
+
+ if (!plane->AllocateData(resource_provider))
+ return false;
+ }
+ return true;
+}
+
+bool VideoLayerImpl::CopyPlaneData(ResourceProvider* resource_provider) {
+ size_t plane_count = NumPlanes();
+ if (!plane_count)
+ return true;
+
+ if (convert_yuv_) {
+ if (!video_renderer_)
+ video_renderer_.reset(new media::SkCanvasVideoRenderer);
+ const VideoLayerImpl::FramePlane& plane =
+ frame_planes_[media::VideoFrame::kRGBPlane];
+ ResourceProvider::ScopedWriteLockSoftware lock(resource_provider,
+ plane.resource_id);
+ video_renderer_->Paint(frame_,
+ lock.sk_canvas(),
+ frame_->visible_rect(),
+ 0xff);
+ return true;
+ }
+
+ for (size_t plane_index = 0; plane_index < plane_count; ++plane_index) {
+ const VideoLayerImpl::FramePlane& plane = frame_planes_[plane_index];
+ // Only non-FormatNativeTexture planes should need upload.
+ DCHECK_EQ(plane.format, GL_LUMINANCE);
+ const uint8_t* software_plane_pixels = frame_->data(plane_index);
+ gfx::Rect image_rect(0,
+ 0,
+ frame_->stride(plane_index),
+ plane.size.height());
+ gfx::Rect source_rect(plane.size);
+ resource_provider->SetPixels(plane.resource_id,
+ software_plane_pixels,
+ image_rect,
+ source_rect,
+ gfx::Vector2d());
+ }
+ return true;
+}
+
+void VideoLayerImpl::FreePlaneData(ResourceProvider* resource_provider) {
+ for (size_t i = 0; i < media::VideoFrame::kMaxPlanes; ++i)
+ frame_planes_[i].FreeData(resource_provider);
+}
+
+void VideoLayerImpl::FreeUnusedPlaneData(ResourceProvider* resource_provider) {
+ size_t first_unused_plane = NumPlanes();
+ for (size_t i = first_unused_plane; i < media::VideoFrame::kMaxPlanes; ++i)
+ frame_planes_[i].FreeData(resource_provider);
+}
+
+void VideoLayerImpl::DidLoseOutputSurface() {
+ FreePlaneData(layer_tree_impl()->resource_provider());
+}
+
+void VideoLayerImpl::SetNeedsRedraw() {
+ layer_tree_impl()->SetNeedsRedraw();
+}
+
+void VideoLayerImpl::SetProviderClientImpl(
+ scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl) {
+ provider_client_impl_ = provider_client_impl;
+}
+
+const char* VideoLayerImpl::LayerTypeAsString() const {
+ return "VideoLayer";
+}
+
+} // namespace cc
diff --git a/cc/layers/video_layer_impl.h b/cc/layers/video_layer_impl.h
new file mode 100644
index 0000000..b7969aa
--- /dev/null
+++ b/cc/layers/video_layer_impl.h
@@ -0,0 +1,88 @@
+// Copyright 2012 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.
+
+#ifndef CC_LAYERS_VIDEO_LAYER_IMPL_H_
+#define CC_LAYERS_VIDEO_LAYER_IMPL_H_
+
+#include "base/callback.h"
+#include "base/synchronization/lock.h"
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer_impl.h"
+#include "cc/layers/video_frame_provider.h"
+#include "media/base/video_frame.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "ui/gfx/size.h"
+#include "ui/gfx/transform.h"
+
+namespace media {
+class SkCanvasVideoRenderer;
+}
+
+namespace cc {
+class LayerTreeHostImpl;
+class VideoFrameProviderClientImpl;
+
+class CC_EXPORT VideoLayerImpl : public LayerImpl {
+ public:
+ static scoped_ptr<VideoLayerImpl> Create(LayerTreeImpl* tree_impl,
+ int id,
+ VideoFrameProvider* provider);
+ virtual ~VideoLayerImpl();
+
+ // LayerImpl implementation.
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+ virtual void WillDraw(ResourceProvider* resource_provider) OVERRIDE;
+ virtual void AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) OVERRIDE;
+ virtual void DidDraw(ResourceProvider* resource_provider) OVERRIDE;
+ virtual void DidBecomeActive() OVERRIDE;
+ virtual void DidLoseOutputSurface() OVERRIDE;
+
+ void SetNeedsRedraw();
+
+ void SetProviderClientImpl(
+ scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl);
+
+ struct FramePlane {
+ ResourceProvider::ResourceId resource_id;
+ gfx::Size size;
+ GLenum format;
+
+ FramePlane() : resource_id(0), format(GL_LUMINANCE) {}
+
+ bool AllocateData(ResourceProvider* resource_provider);
+ void FreeData(ResourceProvider* resource_provider);
+ };
+
+ private:
+ VideoLayerImpl(LayerTreeImpl* tree_impl, int id);
+
+ virtual const char* LayerTypeAsString() const OVERRIDE;
+
+ void WillDrawInternal(ResourceProvider* resource_provider);
+ bool AllocatePlaneData(ResourceProvider* resource_provider);
+ bool CopyPlaneData(ResourceProvider* resource_provider);
+ void FreePlaneData(ResourceProvider* resource_provider);
+ void FreeUnusedPlaneData(ResourceProvider* resource_provider);
+ size_t NumPlanes() const;
+
+ scoped_refptr<VideoFrameProviderClientImpl> provider_client_impl_;
+
+ media::VideoFrame* frame_;
+ GLenum format_;
+ bool convert_yuv_;
+ ResourceProvider::ResourceId external_texture_resource_;
+ scoped_ptr<media::SkCanvasVideoRenderer> video_renderer_;
+
+ // Each index in this array corresponds to a plane in media::VideoFrame.
+ FramePlane frame_planes_[media::VideoFrame::kMaxPlanes];
+
+ DISALLOW_COPY_AND_ASSIGN(VideoLayerImpl);
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_VIDEO_LAYER_IMPL_H_