diff options
-rw-r--r-- | cc/layers/draw_properties.h | 4 | ||||
-rw-r--r-- | cc/layers/layer_impl.cc | 5 | ||||
-rw-r--r-- | cc/layers/layer_impl.h | 5 | ||||
-rw-r--r-- | cc/layers/picture_layer_impl.h | 4 | ||||
-rw-r--r-- | cc/layers/render_surface_impl.h | 9 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_impl.cc | 75 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_unittest_occlusion.cc | 743 | ||||
-rw-r--r-- | cc/trees/layer_tree_impl.cc | 148 | ||||
-rw-r--r-- | cc/trees/layer_tree_impl.h | 4 | ||||
-rw-r--r-- | cc/trees/occlusion.cc | 12 | ||||
-rw-r--r-- | cc/trees/occlusion.h | 5 | ||||
-rw-r--r-- | cc/trees/occlusion_tracker_unittest.cc | 59 |
12 files changed, 407 insertions, 666 deletions
diff --git a/cc/layers/draw_properties.h b/cc/layers/draw_properties.h index 5c78000..7cf89e7 100644 --- a/cc/layers/draw_properties.h +++ b/cc/layers/draw_properties.h @@ -6,6 +6,7 @@ #define CC_LAYERS_DRAW_PROPERTIES_H_ #include "base/memory/scoped_ptr.h" +#include "cc/trees/occlusion.h" #include "third_party/skia/include/core/SkXfermode.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/transform.h" @@ -50,6 +51,9 @@ struct CC_EXPORT DrawProperties { // Transforms objects from content space to screen space (viewport space). gfx::Transform screen_space_transform; + // Known occlusion above the layer mapped to the content space of the layer. + Occlusion occlusion_in_content_space; + // 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. diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc index 3795bb4..6e11644 100644 --- a/cc/layers/layer_impl.cc +++ b/cc/layers/layer_impl.cc @@ -378,11 +378,6 @@ RenderPassId LayerImpl::NextContributingRenderPassId(RenderPassId id) const { return RenderPassId(0, 0); } -bool LayerImpl::UpdateTiles(const Occlusion& occlusion_in_layer_space, - bool resourceless_software_draw) { - return false; -} - void LayerImpl::GetContentsResourceId(ResourceProvider::ResourceId* resource_id, gfx::Size* resource_size) const { NOTREACHED(); diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h index aa8dba2..3ba68e38 100644 --- a/cc/layers/layer_impl.h +++ b/cc/layers/layer_impl.h @@ -220,11 +220,6 @@ class CC_EXPORT LayerImpl : public LayerAnimationValueObserver, virtual RenderPassId FirstContributingRenderPassId() const; virtual RenderPassId NextContributingRenderPassId(RenderPassId id) const; - // Updates the layer's tiles. This should return true if meaningful work was - // done. That is, if an early-out was hit and as a result the internal state - // of tiles didn't change, this function should return false. - virtual bool UpdateTiles(const Occlusion& occlusion_in_layer_space, - bool resourceless_software_draw); virtual void NotifyTileStateChanged(const Tile* tile) {} virtual ScrollbarLayerImplBase* ToScrollbarLayer(); diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h index e2dfea1..dbc3804 100644 --- a/cc/layers/picture_layer_impl.h +++ b/cc/layers/picture_layer_impl.h @@ -57,8 +57,6 @@ class CC_EXPORT PictureLayerImpl void AppendQuads(RenderPass* render_pass, const Occlusion& occlusion_in_content_space, AppendQuadsData* append_quads_data) override; - bool UpdateTiles(const Occlusion& occlusion_in_content_space, - bool resourceless_software_draw) override; void NotifyTileStateChanged(const Tile* tile) override; void DidBeginTracing() override; void ReleaseResources() override; @@ -81,6 +79,8 @@ class CC_EXPORT PictureLayerImpl void UpdateRasterSource(scoped_refptr<RasterSource> raster_source, Region* new_invalidation, const PictureLayerTilingSet* pending_set); + bool UpdateTiles(const Occlusion& occlusion_in_content_space, + bool resourceless_software_draw); // Mask-related functions. void GetContentsResourceId(ResourceProvider::ResourceId* resource_id, diff --git a/cc/layers/render_surface_impl.h b/cc/layers/render_surface_impl.h index 1cc2de8..2035e28 100644 --- a/cc/layers/render_surface_impl.h +++ b/cc/layers/render_surface_impl.h @@ -14,6 +14,7 @@ #include "cc/layers/layer_lists.h" #include "cc/quads/render_pass.h" #include "cc/quads/shared_quad_state.h" +#include "cc/trees/occlusion.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/transform.h" @@ -125,6 +126,13 @@ class CC_EXPORT RenderSurfaceImpl { void SetContentRect(const gfx::Rect& content_rect); gfx::Rect content_rect() const { return content_rect_; } + const Occlusion& occlusion_in_content_space() const { + return occlusion_in_content_space_; + } + void set_occlusion_in_content_space(const Occlusion& occlusion) { + occlusion_in_content_space_ = occlusion; + } + LayerImplList& layer_list() { return layer_list_; } void AddContributingDelegatedRenderPassLayer(LayerImpl* layer); void ClearLayerLists(); @@ -174,6 +182,7 @@ class CC_EXPORT RenderSurfaceImpl { LayerImplList layer_list_; std::vector<DelegatedRendererLayerImpl*> contributing_delegated_render_pass_layer_list_; + Occlusion occlusion_in_content_space_; // The nearest ancestor target surface that will contain the contents of this // surface, and that ignores outside occlusion. This can point to itself. diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 95e1ab1..047e068 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -68,7 +68,6 @@ #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_host_common.h" #include "cc/trees/layer_tree_impl.h" -#include "cc/trees/occlusion_tracker.h" #include "cc/trees/single_thread_proxy.h" #include "cc/trees/tree_synchronizer.h" #include "gpu/command_buffer/client/gles2_interface.h" @@ -592,25 +591,20 @@ DrawMode LayerTreeHostImpl::GetDrawMode() const { static void AppendQuadsForLayer( RenderPass* target_render_pass, LayerImpl* layer, - const OcclusionTracker<LayerImpl>& occlusion_tracker, AppendQuadsData* append_quads_data) { - layer->AppendQuads( - target_render_pass, - occlusion_tracker.GetCurrentOcclusionForLayer(layer->draw_transform()), - append_quads_data); + layer->AppendQuads(target_render_pass, + layer->draw_properties().occlusion_in_content_space, + append_quads_data); } static void AppendQuadsForRenderSurfaceLayer( RenderPass* target_render_pass, LayerImpl* layer, const RenderPass* contributing_render_pass, - const OcclusionTracker<LayerImpl>& occlusion_tracker, AppendQuadsData* append_quads_data) { RenderSurfaceImpl* surface = layer->render_surface(); const gfx::Transform& draw_transform = surface->draw_transform(); - const Occlusion& occlusion = - occlusion_tracker.GetCurrentOcclusionForContributingSurface( - draw_transform); + const Occlusion& occlusion = surface->occlusion_in_content_space(); SkColor debug_border_color = surface->GetDebugBorderColor(); float debug_border_width = surface->GetDebugBorderWidth(); LayerImpl* mask_layer = layer->mask_layer(); @@ -623,9 +617,8 @@ static void AppendQuadsForRenderSurfaceLayer( if (layer->has_replica()) { const gfx::Transform& replica_draw_transform = surface->replica_draw_transform(); - const Occlusion& replica_occlusion = - occlusion_tracker.GetCurrentOcclusionForContributingSurface( - replica_draw_transform); + Occlusion replica_occlusion = occlusion.GetOcclusionWithGivenDrawTransform( + surface->replica_draw_transform()); SkColor replica_debug_border_color = surface->GetReplicaDebugBorderColor(); float replica_debug_border_width = surface->GetReplicaDebugBorderWidth(); // TODO(danakj): By using the same RenderSurfaceImpl for both the @@ -645,16 +638,13 @@ static void AppendQuadsForRenderSurfaceLayer( } } -static void AppendQuadsToFillScreen( - const gfx::Rect& root_scroll_layer_rect, - RenderPass* target_render_pass, - LayerImpl* root_layer, - SkColor screen_background_color, - const OcclusionTracker<LayerImpl>& occlusion_tracker) { +static void AppendQuadsToFillScreen(const gfx::Rect& root_scroll_layer_rect, + RenderPass* target_render_pass, + LayerImpl* root_layer, + SkColor screen_background_color, + const Region& fill_region) { if (!root_layer || !SkColorGetA(screen_background_color)) return; - - Region fill_region = occlusion_tracker.ComputeVisibleRegionInScreen(); if (fill_region.IsEmpty()) return; @@ -758,14 +748,14 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses( root_pass->damage_rect = root_pass->output_rect; } - OcclusionTracker<LayerImpl> occlusion_tracker( - active_tree_->root_layer()->render_surface()->content_rect()); - occlusion_tracker.set_minimum_tracking_size( - settings_.minimum_occlusion_tracking_size); - - // Add quads to the Render passes in front-to-back order to allow for testing - // occlusion and performing culling during the tree walk. - typedef LayerIterator<LayerImpl> LayerIteratorType; + // Grab this region here before iterating layers. Taking copy requests from + // the layers while constructing the render passes will dirty the render + // surface layer list and this unoccluded region, flipping the dirty bit to + // true, and making us able to query for it without doing + // UpdateDrawProperties again. The value inside the Region is not actually + // changed until UpdateDrawProperties happens, so a reference to it is safe. + const Region& unoccluded_screen_space_region = + active_tree_->UnoccludedScreenSpaceRegion(); // Typically when we are missing a texture and use a checkerboard quad, we // still draw the frame. However when the layer being checkerboarded is moving @@ -783,19 +773,15 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses( int num_missing_tiles = 0; int num_incomplete_tiles = 0; - LayerIteratorType end = - LayerIteratorType::End(frame->render_surface_layer_list); - for (LayerIteratorType it = - LayerIteratorType::Begin(frame->render_surface_layer_list); - it != end; - ++it) { + auto end = LayerIterator<LayerImpl>::End(frame->render_surface_layer_list); + for (auto it = + LayerIterator<LayerImpl>::Begin(frame->render_surface_layer_list); + it != end; ++it) { RenderPassId target_render_pass_id = it.target_render_surface_layer()->render_surface()->GetRenderPassId(); RenderPass* target_render_pass = frame->render_passes_by_id[target_render_pass_id]; - occlusion_tracker.EnterLayer(it); - AppendQuadsData append_quads_data; if (it.represents_target_render_surface()) { @@ -813,13 +799,12 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses( AppendQuadsForRenderSurfaceLayer(target_render_pass, *it, contributing_render_pass, - occlusion_tracker, &append_quads_data); } else if (it.represents_itself() && !it->visible_content_rect().IsEmpty()) { bool occluded = - occlusion_tracker.GetCurrentOcclusionForLayer(it->draw_transform()) - .IsOccluded(it->visible_content_rect()); + it->draw_properties().occlusion_in_content_space.IsOccluded( + it->visible_content_rect()); if (!occluded && it->WillDraw(draw_mode, resource_provider_.get())) { DCHECK_EQ(active_tree_, it->layer_tree_impl()); @@ -835,7 +820,6 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses( AppendQuadsForLayer(render_pass, *it, - occlusion_tracker, &append_quads_data); contributing_render_pass_id = @@ -845,7 +829,6 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses( AppendQuadsForLayer(target_render_pass, *it, - occlusion_tracker, &append_quads_data); // For layers that represent themselves, add composite frame timing @@ -885,8 +868,6 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses( if (RequiresHighResToDraw()) draw_result = DRAW_ABORTED_MISSING_HIGH_RES_CONTENT; } - - occlusion_tracker.LeaveLayer(it); } if (have_copy_request || @@ -907,10 +888,8 @@ DrawResult LayerTreeHostImpl::CalculateRenderPasses( frame->render_passes.back()->has_transparent_background = false; AppendQuadsToFillScreen( active_tree_->RootScrollLayerDeviceViewportBounds(), - frame->render_passes.back(), - active_tree_->root_layer(), - active_tree_->background_color(), - occlusion_tracker); + frame->render_passes.back(), active_tree_->root_layer(), + active_tree_->background_color(), unoccluded_screen_space_region); } RemoveRenderPasses(CullRenderPassesWithNoQuads(), frame); diff --git a/cc/trees/layer_tree_host_unittest_occlusion.cc b/cc/trees/layer_tree_host_unittest_occlusion.cc index fe87e44..9974ae4 100644 --- a/cc/trees/layer_tree_host_unittest_occlusion.cc +++ b/cc/trees/layer_tree_host_unittest_occlusion.cc @@ -5,623 +5,266 @@ #include "cc/trees/layer_tree_host.h" #include "cc/layers/layer.h" -#include "cc/output/copy_output_request.h" -#include "cc/output/copy_output_result.h" +#include "cc/layers/picture_layer.h" +#include "cc/test/fake_content_layer_client.h" #include "cc/test/layer_tree_test.h" -#include "cc/test/test_occlusion_tracker.h" +#include "cc/trees/layer_tree_impl.h" namespace cc { namespace { -class TestLayer : public Layer { - public: - static scoped_refptr<TestLayer> Create() { - return make_scoped_refptr(new TestLayer()); - } - - bool Update(ResourceUpdateQueue* update_queue, - const OcclusionTracker<Layer>* occlusion) override { - if (!occlusion) - return false; - - const TestOcclusionTracker<Layer>* test_occlusion = - static_cast<const TestOcclusionTracker<Layer>*>(occlusion); - occlusion_ = UnionSimpleEnclosedRegions( - test_occlusion->occlusion_from_inside_target(), - test_occlusion->occlusion_from_outside_target()); - return false; - } - - const SimpleEnclosedRegion& occlusion() const { return occlusion_; } - const SimpleEnclosedRegion& expected_occlusion() const { - return expected_occlusion_; - } - void set_expected_occlusion(const gfx::Rect& occlusion) { - expected_occlusion_ = SimpleEnclosedRegion(occlusion); - } - - private: - TestLayer() : Layer() { - SetIsDrawable(true); - } - ~TestLayer() override {} - - SimpleEnclosedRegion occlusion_; - SimpleEnclosedRegion expected_occlusion_; -}; +#define EXPECT_OCCLUSION_EQ(expected, actual) \ + EXPECT_TRUE(expected.IsEqual(actual)) \ + << " Expected: " << expected.ToString() << std::endl \ + << " Actual: " << actual.ToString(); class LayerTreeHostOcclusionTest : public LayerTreeTest { - public: - LayerTreeHostOcclusionTest() - : root_(TestLayer::Create()), - child_(TestLayer::Create()), - child2_(TestLayer::Create()), - grand_child_(TestLayer::Create()), - mask_(TestLayer::Create()) { - } - - void BeginTest() override { PostSetNeedsCommitToMainThread(); } - - void DidCommit() override { - TestLayer* root = static_cast<TestLayer*>(layer_tree_host()->root_layer()); - VerifyOcclusion(root); - - EndTest(); - } - - void AfterTest() override {} - - void VerifyOcclusion(TestLayer* layer) const { - EXPECT_EQ(layer->expected_occlusion().ToString(), - layer->occlusion().ToString()); - - for (size_t i = 0; i < layer->children().size(); ++i) { - TestLayer* child = static_cast<TestLayer*>(layer->children()[i].get()); - VerifyOcclusion(child); - } - } - - void SetLayerPropertiesForTesting(TestLayer* layer, - TestLayer* parent, - const gfx::Transform& transform, - const gfx::PointF& position, - const gfx::Size& bounds, - bool opaque) const { - layer->RemoveAllChildren(); - if (parent) - parent->AddChild(layer); - layer->SetTransform(transform); - layer->SetPosition(position); - layer->SetBounds(bounds); - layer->SetContentsOpaque(opaque); - } - protected: void InitializeSettings(LayerTreeSettings* settings) override { settings->minimum_occlusion_tracking_size = gfx::Size(); } - - scoped_refptr<TestLayer> root_; - scoped_refptr<TestLayer> child_; - scoped_refptr<TestLayer> child2_; - scoped_refptr<TestLayer> grand_child_; - scoped_refptr<TestLayer> mask_; - - gfx::Transform identity_matrix_; }; - -class LayerTreeHostOcclusionTestOcclusionSurfaceClipping +// Verify occlusion is set on the layer draw properties. +class LayerTreeHostOcclusionTestDrawPropertiesOnLayer : public LayerTreeHostOcclusionTest { public: void SetupTree() override { - // The child layer is a surface and the grand_child is opaque, but clipped - // to the child and root - SetLayerPropertiesForTesting( - root_.get(), NULL, identity_matrix_, - gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); - SetLayerPropertiesForTesting( - child_.get(), root_.get(), identity_matrix_, - gfx::PointF(10.f, 10.f), gfx::Size(500, 500), false); - SetLayerPropertiesForTesting( - grand_child_.get(), child_.get(), identity_matrix_, - gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); - - child_->SetMasksToBounds(true); - child_->SetForceRenderSurface(true); - - child_->set_expected_occlusion(gfx::Rect(0, 0, 10, 190)); - root_->set_expected_occlusion(gfx::Rect(10, 10, 10, 190)); - - layer_tree_host()->SetRootLayer(root_); + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(gfx::Size(100, 100)); + root->SetIsDrawable(true); + + scoped_refptr<Layer> child = Layer::Create(); + child->SetBounds(gfx::Size(50, 60)); + child->SetPosition(gfx::PointF(10.f, 5.5f)); + child->SetContentsOpaque(true); + child->SetIsDrawable(true); + root->AddChild(child); + + layer_tree_host()->SetRootLayer(root); LayerTreeTest::SetupTree(); } -}; -SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeHostOcclusionTestOcclusionSurfaceClipping); + void BeginTest() override { PostSetNeedsCommitToMainThread(); } -class LayerTreeHostOcclusionTestOcclusionSurfaceClippingOpaque - : public LayerTreeHostOcclusionTest { - public: - void SetupTree() override { - // If the child layer is opaque, then it adds to the occlusion seen by the - // root_. - SetLayerPropertiesForTesting( - root_.get(), NULL, identity_matrix_, - gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); - SetLayerPropertiesForTesting( - child_.get(), root_.get(), identity_matrix_, - gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); - SetLayerPropertiesForTesting( - grand_child_.get(), child_.get(), identity_matrix_, - gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); - - child_->SetMasksToBounds(true); - child_->SetForceRenderSurface(true); - - child_->set_expected_occlusion(gfx::Rect(0, 0, 10, 190)); - root_->set_expected_occlusion(gfx::Rect(10, 10, 190, 190)); - - layer_tree_host()->SetRootLayer(root_); - LayerTreeTest::SetupTree(); + void DrawLayersOnThread(LayerTreeHostImpl* impl) override { + LayerImpl* root = impl->active_tree()->root_layer(); + LayerImpl* child = root->children()[0]; + + // Verify the draw properties are valid. + EXPECT_TRUE(root->IsDrawnRenderSurfaceLayerListMember()); + EXPECT_TRUE(child->IsDrawnRenderSurfaceLayerListMember()); + + EXPECT_OCCLUSION_EQ( + Occlusion(child->draw_transform(), SimpleEnclosedRegion(), + SimpleEnclosedRegion()), + child->draw_properties().occlusion_in_content_space); + EXPECT_OCCLUSION_EQ( + Occlusion(root->draw_transform(), SimpleEnclosedRegion(), + SimpleEnclosedRegion(gfx::Rect(10, 6, 50, 59))), + root->draw_properties().occlusion_in_content_space); + EndTest(); } -}; -SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeHostOcclusionTestOcclusionSurfaceClippingOpaque); - -class LayerTreeHostOcclusionTestOcclusionTwoChildren - : public LayerTreeHostOcclusionTest { - public: - void SetupTree() override { - // Add a second child to the root layer and the regions should merge - SetLayerPropertiesForTesting( - root_.get(), NULL, identity_matrix_, - gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); - SetLayerPropertiesForTesting( - child_.get(), root_.get(), identity_matrix_, - gfx::PointF(10.f, 10.f), gfx::Size(500, 500), false); - SetLayerPropertiesForTesting( - grand_child_.get(), child_.get(), identity_matrix_, - gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); - SetLayerPropertiesForTesting( - child2_.get(), root_.get(), identity_matrix_, - gfx::PointF(20.f, 10.f), gfx::Size(10, 500), true); - - child_->SetMasksToBounds(true); - child_->SetForceRenderSurface(true); - - grand_child_->set_expected_occlusion(gfx::Rect(10, 0, 10, 190)); - child_->set_expected_occlusion(gfx::Rect(0, 0, 20, 190)); - root_->set_expected_occlusion(gfx::Rect(10, 10, 20, 190)); - - layer_tree_host()->SetRootLayer(root_); - LayerTreeTest::SetupTree(); - } + void AfterTest() override {} }; -SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeHostOcclusionTestOcclusionTwoChildren); +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostOcclusionTestDrawPropertiesOnLayer); -class LayerTreeHostOcclusionTestOcclusionMask +// Verify occlusion is set on the render surfaces. +class LayerTreeHostOcclusionTestDrawPropertiesOnSurface : public LayerTreeHostOcclusionTest { public: void SetupTree() override { - // If the child layer has a mask on it, then it shouldn't contribute to - // occlusion on stuff below it. - SetLayerPropertiesForTesting( - root_.get(), NULL, identity_matrix_, - gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); - SetLayerPropertiesForTesting( - child2_.get(), root_.get(), identity_matrix_, - gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); - SetLayerPropertiesForTesting( - child_.get(), root_.get(), identity_matrix_, - gfx::PointF(20.f, 20.f), gfx::Size(500, 500), true); - SetLayerPropertiesForTesting( - grand_child_.get(), child_.get(), identity_matrix_, - gfx::PointF(-10.f, -10.f), gfx::Size(500, 500), true); - - child_->SetMasksToBounds(true); - child_->SetForceRenderSurface(true); - child_->SetMaskLayer(mask_.get()); - - child_->set_expected_occlusion(gfx::Rect(0, 0, 180, 180)); - root_->set_expected_occlusion(gfx::Rect(10, 10, 190, 190)); - - layer_tree_host()->SetRootLayer(root_); + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(gfx::Size(100, 100)); + root->SetIsDrawable(true); + + scoped_refptr<Layer> child = Layer::Create(); + child->SetBounds(gfx::Size(1, 1)); + child->SetPosition(gfx::PointF(10.f, 5.5f)); + child->SetIsDrawable(true); + child->SetForceRenderSurface(true); + root->AddChild(child); + + scoped_refptr<Layer> child2 = Layer::Create(); + child2->SetBounds(gfx::Size(10, 12)); + child2->SetPosition(gfx::PointF(13.f, 8.5f)); + child2->SetContentsOpaque(true); + child2->SetIsDrawable(true); + root->AddChild(child2); + + layer_tree_host()->SetRootLayer(root); LayerTreeTest::SetupTree(); } -}; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostOcclusionTestOcclusionMask); + void BeginTest() override { PostSetNeedsCommitToMainThread(); } -class LayerTreeHostOcclusionTestOcclusionMaskBelowOcclusion - : public LayerTreeHostOcclusionTest { - public: - void SetupTree() override { - // If the child layer with a mask is below child2, then child2 should - // contribute to occlusion on everything, and child shouldn't contribute - // to the root_. - SetLayerPropertiesForTesting( - root_.get(), NULL, identity_matrix_, - gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); - SetLayerPropertiesForTesting( - child_.get(), root_.get(), identity_matrix_, - gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); - SetLayerPropertiesForTesting( - grand_child_.get(), child_.get(), identity_matrix_, - gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); - SetLayerPropertiesForTesting( - child2_.get(), root_.get(), identity_matrix_, - gfx::PointF(20.f, 10.f), gfx::Size(10, 500), true); - - child_->SetMasksToBounds(true); - child_->SetForceRenderSurface(true); - child_->SetMaskLayer(mask_.get()); - - grand_child_->set_expected_occlusion(gfx::Rect(10, 0, 10, 190)); - child_->set_expected_occlusion(gfx::Rect(0, 0, 20, 190)); - root_->set_expected_occlusion(gfx::Rect(20, 10, 10, 190)); - - layer_tree_host()->SetRootLayer(root_); - LayerTreeTest::SetupTree(); - } -}; + void DrawLayersOnThread(LayerTreeHostImpl* impl) override { + LayerImpl* root = impl->active_tree()->root_layer(); + LayerImpl* child = root->children()[0]; + RenderSurfaceImpl* surface = child->render_surface(); -SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeHostOcclusionTestOcclusionMaskBelowOcclusion); + // Verify the draw properties are valid. + EXPECT_TRUE(root->IsDrawnRenderSurfaceLayerListMember()); + EXPECT_TRUE(child->IsDrawnRenderSurfaceLayerListMember()); + EXPECT_EQ(child, child->render_target()); -class LayerTreeHostOcclusionTestOcclusionOpacity - : public LayerTreeHostOcclusionTest { - public: - void SetupTree() override { - // If the child layer has a non-opaque opacity, then it shouldn't - // contribute to occlusion on stuff below it - SetLayerPropertiesForTesting( - root_.get(), NULL, identity_matrix_, - gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); - SetLayerPropertiesForTesting( - child2_.get(), root_.get(), identity_matrix_, - gfx::PointF(20.f, 10.f), gfx::Size(10, 500), true); - SetLayerPropertiesForTesting( - child_.get(), root_.get(), identity_matrix_, - gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); - SetLayerPropertiesForTesting( - grand_child_.get(), child_.get(), identity_matrix_, - gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); - - child_->SetMasksToBounds(true); - child_->SetForceRenderSurface(true); - child_->SetOpacity(0.5f); - - child_->set_expected_occlusion(gfx::Rect(0, 0, 10, 190)); - root_->set_expected_occlusion(gfx::Rect(20, 10, 10, 190)); - - layer_tree_host()->SetRootLayer(root_); - LayerTreeTest::SetupTree(); + EXPECT_OCCLUSION_EQ( + Occlusion(surface->draw_transform(), SimpleEnclosedRegion(), + SimpleEnclosedRegion(gfx::Rect(13, 9, 10, 11))), + surface->occlusion_in_content_space()); + EndTest(); } -}; - -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostOcclusionTestOcclusionOpacity); -class LayerTreeHostOcclusionTestOcclusionOpacityBelowOcclusion - : public LayerTreeHostOcclusionTest { - public: - void SetupTree() override { - // If the child layer with non-opaque opacity is below child2, then - // child2 should contribute to occlusion on everything, and child shouldn't - // contribute to the root_. - SetLayerPropertiesForTesting( - root_.get(), NULL, identity_matrix_, - gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); - SetLayerPropertiesForTesting( - child_.get(), root_.get(), identity_matrix_, - gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); - SetLayerPropertiesForTesting( - grand_child_.get(), child_.get(), identity_matrix_, - gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); - SetLayerPropertiesForTesting( - child2_.get(), root_.get(), identity_matrix_, - gfx::PointF(20.f, 10.f), gfx::Size(10, 500), true); - - child_->SetMasksToBounds(true); - child_->SetForceRenderSurface(true); - child_->SetOpacity(0.5f); - - grand_child_->set_expected_occlusion(gfx::Rect(10, 0, 10, 190)); - child_->set_expected_occlusion(gfx::Rect(0, 0, 20, 190)); - root_->set_expected_occlusion(gfx::Rect(20, 10, 10, 190)); - - layer_tree_host()->SetRootLayer(root_); - LayerTreeTest::SetupTree(); - } + void AfterTest() override {} }; SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeHostOcclusionTestOcclusionOpacityBelowOcclusion); + LayerTreeHostOcclusionTestDrawPropertiesOnSurface); -class LayerTreeHostOcclusionTestOcclusionBlending +// Verify occlusion is set on mask layers. +class LayerTreeHostOcclusionTestDrawPropertiesOnMask : public LayerTreeHostOcclusionTest { public: void SetupTree() override { - // If the child layer has a blend mode, then it shouldn't - // contribute to occlusion on stuff below it - SetLayerPropertiesForTesting( - root_.get(), NULL, identity_matrix_, - gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); - SetLayerPropertiesForTesting( - child2_.get(), root_.get(), identity_matrix_, - gfx::PointF(20.f, 10.f), gfx::Size(10, 500), true); - SetLayerPropertiesForTesting( - child_.get(), root_.get(), identity_matrix_, - gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); - SetLayerPropertiesForTesting( - grand_child_.get(), child_.get(), identity_matrix_, - gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); - - child_->SetMasksToBounds(true); - child_->SetBlendMode(SkXfermode::kMultiply_Mode); - child_->SetForceRenderSurface(true); - - child_->set_expected_occlusion(gfx::Rect(0, 0, 10, 190)); - root_->set_expected_occlusion(gfx::Rect(20, 10, 10, 190)); - - layer_tree_host()->SetRootLayer(root_); + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(gfx::Size(100, 100)); + root->SetIsDrawable(true); + + scoped_refptr<Layer> child = Layer::Create(); + child->SetBounds(gfx::Size(30, 40)); + child->SetPosition(gfx::PointF(10.f, 5.5f)); + child->SetIsDrawable(true); + root->AddChild(child); + + scoped_refptr<Layer> make_surface_bigger = Layer::Create(); + make_surface_bigger->SetBounds(gfx::Size(100, 100)); + make_surface_bigger->SetPosition(gfx::PointF(-10.f, -15.f)); + make_surface_bigger->SetIsDrawable(true); + child->AddChild(make_surface_bigger); + + scoped_refptr<Layer> mask = PictureLayer::Create(&client_); + mask->SetBounds(gfx::Size(30, 40)); + mask->SetIsDrawable(true); + child->SetMaskLayer(mask.get()); + + scoped_refptr<Layer> child2 = Layer::Create(); + child2->SetBounds(gfx::Size(10, 12)); + child2->SetPosition(gfx::PointF(13.f, 8.5f)); + child2->SetContentsOpaque(true); + child2->SetIsDrawable(true); + root->AddChild(child2); + + layer_tree_host()->SetRootLayer(root); LayerTreeTest::SetupTree(); } -}; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostOcclusionTestOcclusionBlending); + void BeginTest() override { PostSetNeedsCommitToMainThread(); } -class LayerTreeHostOcclusionTestOcclusionBlendingBelowOcclusion - : public LayerTreeHostOcclusionTest { - public: - void SetupTree() override { - // If the child layer with a blend mode is below child2, then - // child2 should contribute to occlusion on everything, and child shouldn't - // contribute to the root_. - SetLayerPropertiesForTesting( - root_.get(), NULL, identity_matrix_, - gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); - SetLayerPropertiesForTesting( - child_.get(), root_.get(), identity_matrix_, - gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); - SetLayerPropertiesForTesting( - grand_child_.get(), child_.get(), identity_matrix_, - gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); - SetLayerPropertiesForTesting( - child2_.get(), root_.get(), identity_matrix_, - gfx::PointF(20.f, 10.f), gfx::Size(10, 500), true); - - child_->SetMasksToBounds(true); - child_->SetBlendMode(SkXfermode::kMultiply_Mode); - - grand_child_->set_expected_occlusion(gfx::Rect(10, 0, 10, 190)); - child_->set_expected_occlusion(gfx::Rect(0, 0, 20, 190)); - root_->set_expected_occlusion(gfx::Rect(20, 10, 10, 190)); - - layer_tree_host()->SetRootLayer(root_); - LayerTreeTest::SetupTree(); - } -}; + void DrawLayersOnThread(LayerTreeHostImpl* impl) override { + LayerImpl* root = impl->active_tree()->root_layer(); + LayerImpl* child = root->children()[0]; + RenderSurfaceImpl* surface = child->render_surface(); + LayerImpl* mask = child->mask_layer(); -SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeHostOcclusionTestOcclusionBlendingBelowOcclusion); + // Verify the draw properties are valid. + EXPECT_TRUE(root->IsDrawnRenderSurfaceLayerListMember()); + EXPECT_TRUE(child->IsDrawnRenderSurfaceLayerListMember()); + EXPECT_EQ(child, child->render_target()); -class LayerTreeHostOcclusionTestOcclusionOpacityFilter - : public LayerTreeHostOcclusionTest { - public: - void SetupTree() override { - FilterOperations filters; - filters.Append(FilterOperation::CreateOpacityFilter(0.5f)); - - // If the child layer has a filter that changes alpha values, and is below - // child2, then child2 should contribute to occlusion on everything, - // and child shouldn't contribute to the root - SetLayerPropertiesForTesting( - root_.get(), NULL, identity_matrix_, - gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); - SetLayerPropertiesForTesting(child_.get(), - root_.get(), - identity_matrix_, - gfx::PointF(0.f, 0.f), - gfx::Size(500, 500), - true); - SetLayerPropertiesForTesting(grand_child_.get(), - child_.get(), - identity_matrix_, - gfx::PointF(0.f, 0.f), - gfx::Size(500, 500), - true); - SetLayerPropertiesForTesting(child2_.get(), - root_.get(), - identity_matrix_, - gfx::PointF(10.f, 10.f), - gfx::Size(30, 30), - true); - - child_->SetMasksToBounds(true); - child_->SetFilters(filters); - - // child2_ occludes grand_child_, showing it does occlude inside child_'s - // subtree. - grand_child_->set_expected_occlusion(gfx::Rect(10, 10, 30, 30)); - // grand_child_ occludes child_, showing there is more occlusion in - // child_'s subtree. - child_->set_expected_occlusion(gfx::Rect(0, 0, 200, 200)); - // child2_'s occlusion reaches the root, but child_'s subtree does not. - root_->set_expected_occlusion(gfx::Rect(10, 10, 30, 30)); - - layer_tree_host()->SetRootLayer(root_); - LayerTreeTest::SetupTree(); + gfx::Transform transform = surface->draw_transform(); + transform.PreconcatTransform(child->draw_transform()); + + EXPECT_OCCLUSION_EQ( + Occlusion(transform, SimpleEnclosedRegion(), + SimpleEnclosedRegion(gfx::Rect(13, 9, 10, 11))), + mask->draw_properties().occlusion_in_content_space); + EndTest(); } -}; -SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeHostOcclusionTestOcclusionOpacityFilter); + void AfterTest() override {} -class LayerTreeHostOcclusionTestOcclusionBlurFilter - : public LayerTreeHostOcclusionTest { - public: - void SetupTree() override { - gfx::Transform child_transform; - child_transform.Translate(250.0, 250.0); - child_transform.Rotate(90.0); - child_transform.Translate(-250.0, -250.0); - - FilterOperations filters; - filters.Append(FilterOperation::CreateBlurFilter(10.f)); - - // If the child layer has a filter that moves pixels/changes alpha, and is - // below child2, then child should not inherit occlusion from outside its - // subtree, and should not contribute to the root - SetLayerPropertiesForTesting( - root_.get(), NULL, identity_matrix_, - gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); - SetLayerPropertiesForTesting( - child_.get(), root_.get(), child_transform, - gfx::PointF(30.f, 30.f), gfx::Size(500, 500), true); - SetLayerPropertiesForTesting( - grand_child_.get(), child_.get(), identity_matrix_, - gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); - SetLayerPropertiesForTesting( - child2_.get(), root_.get(), identity_matrix_, - gfx::PointF(10.f, 70.f), gfx::Size(500, 500), true); - - child_->SetMasksToBounds(true); - child_->SetFilters(filters); - - child_->set_expected_occlusion(gfx::Rect(10, 330, 160, 170)); - root_->set_expected_occlusion(gfx::Rect(10, 70, 190, 130)); - - layer_tree_host()->SetRootLayer(root_); - LayerTreeTest::SetupTree(); - } + FakeContentLayerClient client_; }; -SINGLE_AND_MULTI_THREAD_TEST_F( - LayerTreeHostOcclusionTestOcclusionBlurFilter); +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostOcclusionTestDrawPropertiesOnMask); -class LayerTreeHostOcclusionTestOcclusionCopyRequest +// Verify occlusion is set to empty inside the subtree of a replica. This is +// done because the tile system does not know about replicas, and so would not +// know that something is unoccluded on the replica even though it's occluded on +// the original. +class LayerTreeHostOcclusionTestDrawPropertiesInsideReplica : public LayerTreeHostOcclusionTest { public: - static void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) {} - void SetupTree() override { - // If the child layer has copy request, and is below child2, - // then child should not inherit occlusion from outside its subtree. - // The child layer will still receive occlusion from inside, and - // the root layer will recive occlusion from child. - SetLayerPropertiesForTesting( - root_.get(), NULL, identity_matrix_, - gfx::PointF(), gfx::Size(100, 100), true); - SetLayerPropertiesForTesting( - child_.get(), root_.get(), identity_matrix_, - gfx::PointF(), gfx::Size(75, 75), true); - SetLayerPropertiesForTesting( - grand_child_.get(), child_.get(), identity_matrix_, - gfx::PointF(), gfx::Size(75, 50), true); - SetLayerPropertiesForTesting( - child2_.get(), root_.get(), identity_matrix_, - gfx::PointF(0.f, 25.f), gfx::Size(75, 75), true); - - child_->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest( - base::Bind(&CopyOutputCallback))); - EXPECT_TRUE(child_->HasCopyRequest()); - - child_->set_expected_occlusion(gfx::Rect(0, 0, 75, 50)); - root_->set_expected_occlusion(gfx::Rect(0, 0, 75, 100)); - - layer_tree_host()->SetRootLayer(root_); + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(gfx::Size(100, 100)); + root->SetIsDrawable(true); + + scoped_refptr<Layer> child = Layer::Create(); + child->SetBounds(gfx::Size(1, 1)); + child->SetPosition(gfx::PointF(10.f, 5.5f)); + child->SetIsDrawable(true); + child->SetForceRenderSurface(true); + root->AddChild(child); + + scoped_refptr<Layer> replica = Layer::Create(); + gfx::Transform translate; + translate.Translate(20.f, 4.f); + replica->SetTransform(translate); + child->SetReplicaLayer(replica.get()); + + scoped_refptr<Layer> mask = PictureLayer::Create(&client_); + mask->SetBounds(gfx::Size(30, 40)); + mask->SetIsDrawable(true); + child->SetMaskLayer(mask.get()); + + scoped_refptr<Layer> child2 = Layer::Create(); + child2->SetBounds(gfx::Size(10, 12)); + child2->SetPosition(gfx::PointF(13.f, 8.5f)); + child2->SetContentsOpaque(true); + child2->SetIsDrawable(true); + root->AddChild(child2); + + layer_tree_host()->SetRootLayer(root); LayerTreeTest::SetupTree(); } -}; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostOcclusionTestOcclusionCopyRequest); + void BeginTest() override { PostSetNeedsCommitToMainThread(); } -class LayerTreeHostOcclusionTestOcclusionReplica - : public LayerTreeHostOcclusionTest { - public: - void SetupTree() override { - // If the child layer has copy request, and is below child2, - // then child should not inherit occlusion from outside its subtree. - // The child layer will still receive occlusion from inside, and - // the root layer will recive occlusion from child. - SetLayerPropertiesForTesting( - root_.get(), NULL, identity_matrix_, - gfx::PointF(), gfx::Size(100, 100), true); - SetLayerPropertiesForTesting( - child_.get(), root_.get(), identity_matrix_, - gfx::PointF(), gfx::Size(75, 75), true); - SetLayerPropertiesForTesting( - grand_child_.get(), child_.get(), identity_matrix_, - gfx::PointF(), gfx::Size(75, 50), true); - SetLayerPropertiesForTesting( - child2_.get(), root_.get(), identity_matrix_, - gfx::PointF(0.f, 25.f), gfx::Size(75, 75), true); - - scoped_refptr<Layer> replica_layer(Layer::Create()); - child_->SetReplicaLayer(replica_layer.get()); - EXPECT_TRUE(child_->has_replica()); - - child_->set_expected_occlusion(gfx::Rect(0, 0, 75, 50)); - root_->set_expected_occlusion(gfx::Rect(0, 0, 75, 100)); - - layer_tree_host()->SetRootLayer(root_); - LayerTreeTest::SetupTree(); + void DrawLayersOnThread(LayerTreeHostImpl* impl) override { + LayerImpl* root = impl->active_tree()->root_layer(); + LayerImpl* child = root->children()[0]; + RenderSurfaceImpl* surface = child->render_surface(); + LayerImpl* mask = child->mask_layer(); + + // Verify the draw properties are valid. + EXPECT_TRUE(root->IsDrawnRenderSurfaceLayerListMember()); + EXPECT_TRUE(child->IsDrawnRenderSurfaceLayerListMember()); + EXPECT_EQ(child, child->render_target()); + + // No occlusion from on child, which is part of the replica. + EXPECT_OCCLUSION_EQ(Occlusion(), + child->draw_properties().occlusion_in_content_space); + // Occlusion on the surface is okay. + EXPECT_OCCLUSION_EQ( + Occlusion(surface->draw_transform(), SimpleEnclosedRegion(), + SimpleEnclosedRegion(gfx::Rect(13, 9, 10, 11))), + surface->occlusion_in_content_space()); + // No occlusion on the replica'd mask. + EXPECT_OCCLUSION_EQ(Occlusion(), + mask->draw_properties().occlusion_in_content_space); + EndTest(); } -}; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostOcclusionTestOcclusionReplica); + void AfterTest() override {} -class LayerTreeHostOcclusionTestManySurfaces - : public LayerTreeHostOcclusionTest { - public: - void SetupTree() override { - // We create enough RenderSurfaces that it will trigger Vector reallocation - // while computing occlusion. - std::vector<scoped_refptr<TestLayer>> layers; - int num_surfaces = 200; - int root_width = 400; - int root_height = 400; - - for (int i = 0; i < num_surfaces; ++i) { - layers.push_back(TestLayer::Create()); - if (i == 0) { - SetLayerPropertiesForTesting( - layers.back().get(), NULL, identity_matrix_, - gfx::PointF(0.f, 0.f), - gfx::Size(root_width, root_height), true); - } else { - SetLayerPropertiesForTesting( - layers.back().get(), layers[layers.size() - 2].get(), - identity_matrix_, - gfx::PointF(1.f, 1.f), - gfx::Size(root_width-i, root_height-i), true); - layers.back()->SetForceRenderSurface(true); - } - } - - for (int i = 1; i < num_surfaces; ++i) { - scoped_refptr<TestLayer> child = TestLayer::Create(); - SetLayerPropertiesForTesting( - child.get(), layers[i].get(), identity_matrix_, - gfx::PointF(0.f, 0.f), gfx::Size(root_width, root_height), false); - } - - for (int i = 0; i < num_surfaces-1; ++i) { - gfx::Rect expected_occlusion(1, 1, root_width-i-1, root_height-i-1); - layers[i]->set_expected_occlusion(expected_occlusion); - } - - layer_tree_host()->SetRootLayer(layers[0]); - LayerTreeTest::SetupTree(); - } + FakeContentLayerClient client_; }; -SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostOcclusionTestManySurfaces); +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeHostOcclusionTestDrawPropertiesInsideReplica); } // namespace } // namespace cc diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc index 6806d9c..443308b 100644 --- a/cc/trees/layer_tree_impl.cc +++ b/cc/trees/layer_tree_impl.cc @@ -549,12 +549,9 @@ bool LayerTreeImpl::UpdateDrawProperties() { render_surface_layer_list_.clear(); { - TRACE_EVENT2("cc", - "LayerTreeImpl::UpdateDrawProperties", - "IsActive", - IsActiveTree(), - "SourceFrameNumber", - source_frame_number_); + TRACE_EVENT2( + "cc", "LayerTreeImpl::UpdateDrawProperties::CalculateDrawProperties", + "IsActive", IsActiveTree(), "SourceFrameNumber", source_frame_number_); LayerImpl* page_scale_layer = page_scale_layer_ ? page_scale_layer_ : InnerViewportContainerLayer(); bool can_render_to_separate_surface = @@ -577,66 +574,100 @@ bool LayerTreeImpl::UpdateDrawProperties() { } { - TRACE_EVENT_BEGIN2("cc", "LayerTreeImpl::UpdateTilePriorities", "IsActive", - IsActiveTree(), "SourceFrameNumber", - source_frame_number_); - scoped_ptr<OcclusionTracker<LayerImpl>> occlusion_tracker; - if (settings().use_occlusion_for_tile_prioritization) { - occlusion_tracker.reset(new OcclusionTracker<LayerImpl>( - root_layer()->render_surface()->content_rect())); - occlusion_tracker->set_minimum_tracking_size( - settings().minimum_occlusion_tracking_size); - } - - bool resourceless_software_draw = (layer_tree_host_impl_->GetDrawMode() == - DRAW_MODE_RESOURCELESS_SOFTWARE); + TRACE_EVENT2("cc", "LayerTreeImpl::UpdateDrawProperties::Occlusion", + "IsActive", IsActiveTree(), "SourceFrameNumber", + source_frame_number_); + OcclusionTracker<LayerImpl> occlusion_tracker( + root_layer()->render_surface()->content_rect()); + occlusion_tracker.set_minimum_tracking_size( + settings().minimum_occlusion_tracking_size); // LayerIterator is used here instead of CallFunctionForSubtree to only // UpdateTilePriorities on layers that will be visible (and thus have valid // draw properties) and not because any ordering is required. - typedef LayerIterator<LayerImpl> LayerIteratorType; - LayerIteratorType end = LayerIteratorType::End(&render_surface_layer_list_); - size_t layers_updated_count = 0; - bool tile_priorities_updated = false; - for (LayerIteratorType it = - LayerIteratorType::Begin(&render_surface_layer_list_); - it != end; - ++it) { - if (occlusion_tracker) - occlusion_tracker->EnterLayer(it); - - LayerImpl* layer = *it; - const Occlusion& occlusion_in_content_space = - occlusion_tracker ? occlusion_tracker->GetCurrentOcclusionForLayer( - layer->draw_transform()) - : Occlusion(); + auto end = LayerIterator<LayerImpl>::End(&render_surface_layer_list_); + for (auto it = LayerIterator<LayerImpl>::Begin(&render_surface_layer_list_); + it != end; ++it) { + occlusion_tracker.EnterLayer(it); + + // There are very few render targets so this should be cheap to do for + // each layer instead of something more complicated. + bool inside_replica = false; + LayerImpl* layer = it->render_target(); + while (layer && !inside_replica) { + if (layer->render_target()->has_replica()) + inside_replica = true; + layer = layer->render_target()->parent(); + } + + // Don't use occlusion if a layer will appear in a replica, since the + // tile raster code does not know how to look for the replica and would + // consider it occluded even though the replica is visible. + // Since occlusion is only used for browser compositor (i.e. + // use_occlusion_for_tile_prioritization) and it won't use replicas, + // this should matter not. if (it.represents_itself()) { - tile_priorities_updated |= layer->UpdateTiles( - occlusion_in_content_space, resourceless_software_draw); - ++layers_updated_count; + Occlusion occlusion = + inside_replica ? Occlusion() + : occlusion_tracker.GetCurrentOcclusionForLayer( + it->draw_transform()); + it->draw_properties().occlusion_in_content_space = occlusion; } - if (!it.represents_contributing_render_surface()) { - if (occlusion_tracker) - occlusion_tracker->LeaveLayer(it); - continue; + if (it.represents_contributing_render_surface()) { + // Surfaces aren't used by the tile raster code, so they can have + // occlusion regardless of replicas. + Occlusion occlusion = + occlusion_tracker.GetCurrentOcclusionForContributingSurface( + it->render_surface()->draw_transform()); + it->render_surface()->set_occlusion_in_content_space(occlusion); + // Masks are used to draw the contributing surface, so should have + // the same occlusion as the surface (nothing inside the surface + // occludes them). + if (LayerImpl* mask = it->mask_layer()) { + Occlusion mask_occlusion = + inside_replica + ? Occlusion() + : occlusion_tracker.GetCurrentOcclusionForContributingSurface( + it->render_surface()->draw_transform() * + it->draw_transform()); + mask->draw_properties().occlusion_in_content_space = mask_occlusion; + } + if (LayerImpl* replica = it->replica_layer()) { + if (LayerImpl* mask = replica->mask_layer()) + mask->draw_properties().occlusion_in_content_space = Occlusion(); + } } - if (layer->mask_layer()) { - tile_priorities_updated |= layer->mask_layer()->UpdateTiles( - occlusion_in_content_space, resourceless_software_draw); - ++layers_updated_count; - } - if (layer->replica_layer() && layer->replica_layer()->mask_layer()) { - tile_priorities_updated |= - layer->replica_layer()->mask_layer()->UpdateTiles( - occlusion_in_content_space, resourceless_software_draw); - ++layers_updated_count; - } + occlusion_tracker.LeaveLayer(it); + } + + unoccluded_screen_space_region_ = + occlusion_tracker.ComputeVisibleRegionInScreen(); + } - if (occlusion_tracker) - occlusion_tracker->LeaveLayer(it); + { + TRACE_EVENT_BEGIN2("cc", "LayerTreeImpl::UpdateDrawProperties::UpdateTiles", + "IsActive", IsActiveTree(), "SourceFrameNumber", + source_frame_number_); + const bool resourceless_software_draw = + (layer_tree_host_impl_->GetDrawMode() == + DRAW_MODE_RESOURCELESS_SOFTWARE); + static const Occlusion kEmptyOcclusion; + size_t layers_updated_count = 0; + bool tile_priorities_updated = false; + for (PictureLayerImpl* layer : picture_layers_) { + // TODO(danakj): Remove this to fix crbug.com/446751 + if (!layer->IsDrawnRenderSurfaceLayerListMember()) + continue; + ++layers_updated_count; + const Occlusion& occlusion = + settings().use_occlusion_for_tile_prioritization + ? layer->draw_properties().occlusion_in_content_space + : kEmptyOcclusion; + tile_priorities_updated |= + layer->UpdateTiles(occlusion, resourceless_software_draw); } if (tile_priorities_updated) @@ -657,6 +688,13 @@ const LayerImplList& LayerTreeImpl::RenderSurfaceLayerList() const { return render_surface_layer_list_; } +const Region& LayerTreeImpl::UnoccludedScreenSpaceRegion() const { + // If this assert triggers, then the render_surface_layer_list_ is dirty, so + // the unoccluded_screen_space_region_ is not valid anymore. + DCHECK(!needs_update_draw_properties_); + return unoccluded_screen_space_region_; +} + gfx::Size LayerTreeImpl::ScrollableSize() const { LayerImpl* root_scroll_layer = OuterViewportScrollLayer() ? OuterViewportScrollLayer() diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h index 6c37147..54c8660 100644 --- a/cc/trees/layer_tree_impl.h +++ b/cc/trees/layer_tree_impl.h @@ -220,6 +220,7 @@ class CC_EXPORT LayerTreeImpl { void set_ui_resource_request_queue(const UIResourceRequestQueue& queue); const LayerImplList& RenderSurfaceLayerList() const; + const Region& UnoccludedScreenSpaceRegion() const; // These return the size of the root scrollable area and the size of // the user-visible scrolling viewport, in CSS layout coordinates. @@ -381,6 +382,9 @@ class CC_EXPORT LayerTreeImpl { // List of visible layers for the most recently prepared frame. LayerImplList render_surface_layer_list_; + // After drawing the |render_surface_layer_list_| the areas in this region + // would not be fully covered by opaque content. + Region unoccluded_screen_space_region_; bool contents_textures_purged_; bool viewport_size_invalid_; diff --git a/cc/trees/occlusion.cc b/cc/trees/occlusion.cc index 14168f9..6a71496 100644 --- a/cc/trees/occlusion.cc +++ b/cc/trees/occlusion.cc @@ -85,4 +85,16 @@ gfx::Rect Occlusion::GetUnoccludedRectInTargetSurface( return unoccluded_rect_in_target_surface; } +bool Occlusion::IsEqual(const Occlusion& other) const { + return draw_transform_ == other.draw_transform_ && + occlusion_from_inside_target_ == other.occlusion_from_inside_target_ && + occlusion_from_outside_target_ == other.occlusion_from_outside_target_; +} + +std::string Occlusion::ToString() const { + return draw_transform_.ToString() + "outside(" + + occlusion_from_outside_target_.ToString() + ") inside(" + + occlusion_from_inside_target_.ToString() + ")"; +} + } // namespace cc diff --git a/cc/trees/occlusion.h b/cc/trees/occlusion.h index 56f9962d..fec4915 100644 --- a/cc/trees/occlusion.h +++ b/cc/trees/occlusion.h @@ -5,6 +5,8 @@ #ifndef CC_TREES_OCCLUSION_H_ #define CC_TREES_OCCLUSION_H_ +#include <string> + #include "base/basictypes.h" #include "cc/base/cc_export.h" #include "cc/base/simple_enclosed_region.h" @@ -26,6 +28,9 @@ class CC_EXPORT Occlusion { bool IsOccluded(const gfx::Rect& content_rect) const; gfx::Rect GetUnoccludedContentRect(const gfx::Rect& content_rect) const; + bool IsEqual(const Occlusion& other) const; + std::string ToString() const; + private: gfx::Rect GetUnoccludedRectInTargetSurface( const gfx::Rect& content_rect) const; diff --git a/cc/trees/occlusion_tracker_unittest.cc b/cc/trees/occlusion_tracker_unittest.cc index 847d60c..e3639ad 100644 --- a/cc/trees/occlusion_tracker_unittest.cc +++ b/cc/trees/occlusion_tracker_unittest.cc @@ -2672,6 +2672,53 @@ ALL_OCCLUSIONTRACKER_TEST( OcclusionTrackerTestReduceOcclusionWhenBackgroundFilterIsPartiallyOccluded); template <class Types> +class OcclusionTrackerTestBlendModeDoesNotOcclude + : public OcclusionTrackerTest<Types> { + protected: + explicit OcclusionTrackerTestBlendModeDoesNotOcclude(bool opaque_layers) + : OcclusionTrackerTest<Types>(opaque_layers) {} + void RunMyTest() override { + typename Types::ContentLayerType* parent = this->CreateRoot( + this->identity_matrix, gfx::PointF(), gfx::Size(100, 100)); + typename Types::LayerType* blend_mode_layer = this->CreateDrawingLayer( + parent, this->identity_matrix, gfx::PointF(0.f, 0.f), + gfx::Size(100, 100), true); + typename Types::LayerType* top_layer = this->CreateDrawingLayer( + parent, this->identity_matrix, gfx::PointF(10.f, 12.f), + gfx::Size(20, 22), true); + + // Blend mode makes the layer own a surface. + Types::SetForceRenderSurface(blend_mode_layer, true); + blend_mode_layer->SetBlendMode(SkXfermode::kMultiply_Mode); + + this->CalcDrawEtc(parent); + + TestOcclusionTrackerWithClip<typename Types::LayerType> occlusion( + gfx::Rect(0, 0, 1000, 1000)); + + this->VisitLayer(top_layer, &occlusion); + // |top_layer| occludes. + EXPECT_EQ(gfx::Rect(10, 12, 20, 22).ToString(), + occlusion.occlusion_from_inside_target().ToString()); + EXPECT_TRUE(occlusion.occlusion_from_outside_target().IsEmpty()); + + this->VisitLayer(blend_mode_layer, &occlusion); + // |top_layer| occludes but not |blend_mode_layer|. + EXPECT_EQ(gfx::Rect(10, 12, 20, 22).ToString(), + occlusion.occlusion_from_outside_target().ToString()); + EXPECT_TRUE(occlusion.occlusion_from_inside_target().IsEmpty()); + + this->VisitContributingSurface(blend_mode_layer, &occlusion); + // |top_layer| occludes but not |blend_mode_layer|. + EXPECT_EQ(gfx::Rect(10, 12, 20, 22).ToString(), + occlusion.occlusion_from_inside_target().ToString()); + EXPECT_TRUE(occlusion.occlusion_from_outside_target().IsEmpty()); + } +}; + +ALL_OCCLUSIONTRACKER_TEST(OcclusionTrackerTestBlendModeDoesNotOcclude); + +template <class Types> class OcclusionTrackerTestMinimumTrackingSize : public OcclusionTrackerTest<Types> { protected: @@ -2817,12 +2864,22 @@ class OcclusionTrackerTestCopyRequestDoesOcclude gfx::PointF(), gfx::Size(200, 400), true); + typename Types::LayerType* top_layer = + this->CreateDrawingLayer(root, this->identity_matrix, + gfx::PointF(50, 0), gfx::Size(50, 400), true); this->CalcDrawEtc(root); TestOcclusionTrackerWithClip<typename Types::LayerType> occlusion( gfx::Rect(0, 0, 1000, 1000)); + this->VisitLayer(top_layer, &occlusion); + EXPECT_EQ(gfx::Rect().ToString(), + occlusion.occlusion_from_outside_target().ToString()); + EXPECT_EQ(gfx::Rect(50, 0, 50, 400).ToString(), + occlusion.occlusion_from_inside_target().ToString()); + this->VisitLayer(copy_child, &occlusion); + // Layers outside the copy request do not occlude. EXPECT_EQ(gfx::Rect().ToString(), occlusion.occlusion_from_outside_target().ToString()); EXPECT_EQ(gfx::Rect(200, 400).ToString(), @@ -2834,7 +2891,7 @@ class OcclusionTrackerTestCopyRequestDoesOcclude // The occlusion from the copy should be kept. EXPECT_EQ(gfx::Rect().ToString(), occlusion.occlusion_from_outside_target().ToString()); - EXPECT_EQ(gfx::Rect(100, 0, 200, 400).ToString(), + EXPECT_EQ(gfx::Rect(50, 0, 250, 400).ToString(), occlusion.occlusion_from_inside_target().ToString()); } }; |