diff options
author | danakj <danakj@chromium.org> | 2015-02-13 14:12:16 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-13 22:12:46 +0000 |
commit | 4902c30c928ceb9f14366d9a6cad5ef85968a058 (patch) | |
tree | d8591afefe165b48f62adb971acd104d58de1c62 /cc | |
parent | 984bf74de879dde74718e1107fa945b944abd36c (diff) | |
download | chromium_src-4902c30c928ceb9f14366d9a6cad5ef85968a058.zip chromium_src-4902c30c928ceb9f14366d9a6cad5ef85968a058.tar.gz chromium_src-4902c30c928ceb9f14366d9a6cad5ef85968a058.tar.bz2 |
cc: Make occlusion a draw property.
We were computing occlusion twice, once in UpdateDrawProperties
and once in CalculateRenderPasses. Now just do it once in
UpdateDrawProperties, which means we don't have to do that work
at all if we draw again without dirtying the render surface layer
list.
This moves the call to UpdateTiles out of the loop for occlusion,
instead calling it directly on each of the picture layers on the
tree, meaning it does not have to be virtual anymore.
This makes it possible for us to call UpdateTiles on picture layers
that were not part of the RenderSurfaceLayerList. Though for now
I left the behaviour as is with a TODO.
Also removed all the old LayerTreeHostOcclusionTests. Added a few
new ones to ensure occlusion gets saved correctly in layers and
surfaces. The test cases that would be missing, I moved to be
OcclusionTrackerTests.
R=enne, vmpstr
BUG=446751
Review URL: https://codereview.chromium.org/915083004
Cr-Commit-Position: refs/heads/master@{#316306}
Diffstat (limited to 'cc')
-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()); } }; |