diff options
-rw-r--r-- | cc/layers/draw_properties.h | 7 | ||||
-rw-r--r-- | cc/layers/layer_impl.cc | 10 | ||||
-rw-r--r-- | cc/layers/render_surface.cc | 1 | ||||
-rw-r--r-- | cc/layers/render_surface.h | 12 | ||||
-rw-r--r-- | cc/layers/render_surface_impl.cc | 1 | ||||
-rw-r--r-- | cc/layers/render_surface_impl.h | 12 | ||||
-rw-r--r-- | cc/output/copy_output_request.cc | 4 | ||||
-rw-r--r-- | cc/output/copy_output_request.h | 4 | ||||
-rw-r--r-- | cc/output/gl_renderer.cc | 3 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_common.cc | 125 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_common_unittest.cc | 192 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_impl.cc | 28 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_unittest.cc | 230 | ||||
-rw-r--r-- | cc/trees/layer_tree_impl.cc | 34 | ||||
-rw-r--r-- | cc/trees/layer_tree_impl.h | 6 |
15 files changed, 628 insertions, 41 deletions
diff --git a/cc/layers/draw_properties.h b/cc/layers/draw_properties.h index c9bd90c..bb80dce 100644 --- a/cc/layers/draw_properties.h +++ b/cc/layers/draw_properties.h @@ -28,7 +28,8 @@ struct CC_EXPORT DrawProperties { contents_scale_y(1.f), num_descendants_that_draw_content(0), descendants_can_clip_selves(false), - can_draw_directly_to_backbuffer(false) {} + can_draw_directly_to_backbuffer(false), + layer_or_descendant_has_copy_request(false) {} // Transforms objects from content space to target surface space, where // this layer would be drawn. @@ -92,6 +93,10 @@ struct CC_EXPORT DrawProperties { bool descendants_can_clip_selves; bool can_draw_directly_to_backbuffer; + + // If true, the layer or some layer in its sub-tree has a CopyOutputRequest + // present on it. + bool layer_or_descendant_has_copy_request; }; } // namespace cc diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc index b40579b..80c7a35 100644 --- a/cc/layers/layer_impl.cc +++ b/cc/layers/layer_impl.cc @@ -108,9 +108,13 @@ void LayerImpl::PassCopyRequests(ScopedPtrVector<CopyOutputRequest>* requests) { if (requests->empty()) return; + DCHECK(copy_requests_.empty()); + copy_requests_.insert_and_take(copy_requests_.end(), *requests); requests->clear(); + if (layer_tree_impl()->IsActiveTree()) + layer_tree_impl()->AddLayerWithCopyOutputRequest(this); NoteLayerPropertyChangedForSubtree(); } @@ -119,10 +123,11 @@ void LayerImpl::TakeCopyRequestsAndTransformToTarget( if (copy_requests_.empty()) return; + size_t first_inserted_request = requests->size(); requests->insert_and_take(requests->end(), copy_requests_); copy_requests_.clear(); - for (size_t i = 0; i < requests->size(); ++i) { + for (size_t i = first_inserted_request; i < requests->size(); ++i) { CopyOutputRequest* request = requests->at(i); if (!request->has_area()) continue; @@ -134,6 +139,9 @@ void LayerImpl::TakeCopyRequestsAndTransformToTarget( MathUtil::MapClippedRect(draw_properties_.target_space_transform, request_in_content_space)); } + + if (layer_tree_impl()->IsActiveTree()) + layer_tree_impl()->RemoveLayerWithCopyOutputRequest(this); } void LayerImpl::CreateRenderSurface() { diff --git a/cc/layers/render_surface.cc b/cc/layers/render_surface.cc index 4eb5d34..60d9cb2 100644 --- a/cc/layers/render_surface.cc +++ b/cc/layers/render_surface.cc @@ -17,6 +17,7 @@ RenderSurface::RenderSurface(Layer* owning_layer) target_surface_transforms_are_animating_(false), screen_space_transforms_are_animating_(false), is_clipped_(false), + contributes_to_drawn_surface_(false), nearest_ancestor_that_moves_pixels_(NULL) {} RenderSurface::~RenderSurface() {} diff --git a/cc/layers/render_surface.h b/cc/layers/render_surface.h index 11d7d4e..369d8c9 100644 --- a/cc/layers/render_surface.h +++ b/cc/layers/render_surface.h @@ -86,6 +86,17 @@ class CC_EXPORT RenderSurface { gfx::Rect clip_rect() const { return clip_rect_; } void SetClipRect(gfx::Rect clip_rect) { clip_rect_ = clip_rect; } + // When false, the RenderSurface does not contribute to another target + // RenderSurface that is being drawn for the current frame. It could still be + // drawn to as a target, but its output will not be a part of any other + // surface. + bool contributes_to_drawn_surface() const { + return contributes_to_drawn_surface_; + } + void set_contributes_to_drawn_surface(bool contributes_to_drawn_surface) { + contributes_to_drawn_surface_ = contributes_to_drawn_surface; + } + LayerList& layer_list() { return layer_list_; } // A no-op since DelegatedRendererLayers on the main thread don't have any // RenderPasses so they can't contribute to a surface. @@ -117,6 +128,7 @@ class CC_EXPORT RenderSurface { bool screen_space_transforms_are_animating_; bool is_clipped_; + bool contributes_to_drawn_surface_; // Uses the space of the surface's target surface. gfx::Rect clip_rect_; diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc index 5aafb8a..3262e28 100644 --- a/cc/layers/render_surface_impl.cc +++ b/cc/layers/render_surface_impl.cc @@ -33,6 +33,7 @@ RenderSurfaceImpl::RenderSurfaceImpl(LayerImpl* owning_layer) target_surface_transforms_are_animating_(false), screen_space_transforms_are_animating_(false), is_clipped_(false), + contributes_to_drawn_surface_(false), nearest_ancestor_that_moves_pixels_(NULL), target_render_surface_layer_index_history_(0), current_layer_index_history_(0) { diff --git a/cc/layers/render_surface_impl.h b/cc/layers/render_surface_impl.h index 6f3e752..f887a65 100644 --- a/cc/layers/render_surface_impl.h +++ b/cc/layers/render_surface_impl.h @@ -104,6 +104,17 @@ class CC_EXPORT RenderSurfaceImpl { void SetClipRect(gfx::Rect clip_rect); gfx::Rect clip_rect() const { return clip_rect_; } + // When false, the RenderSurface does not contribute to another target + // RenderSurface that is being drawn for the current frame. It could still be + // drawn to as a target, but its output will not be a part of any other + // surface. + bool contributes_to_drawn_surface() const { + return contributes_to_drawn_surface_; + } + void set_contributes_to_drawn_surface(bool contributes_to_drawn_surface) { + contributes_to_drawn_surface_ = contributes_to_drawn_surface; + } + bool ContentsChanged() const; void SetContentRect(gfx::Rect content_rect); @@ -146,6 +157,7 @@ class CC_EXPORT RenderSurfaceImpl { bool screen_space_transforms_are_animating_; bool is_clipped_; + bool contributes_to_drawn_surface_; // Uses the space of the surface's target surface. gfx::Rect clip_rect_; diff --git a/cc/output/copy_output_request.cc b/cc/output/copy_output_request.cc index 341d887..995c3f4 100644 --- a/cc/output/copy_output_request.cc +++ b/cc/output/copy_output_request.cc @@ -32,6 +32,10 @@ void CopyOutputRequest::SendResult(scoped_ptr<CopyOutputResult> result) { base::ResetAndReturn(&result_callback_).Run(result.Pass()); } +void CopyOutputRequest::SendEmptyResult() { + SendResult(CopyOutputResult::CreateEmptyResult().Pass()); +} + void CopyOutputRequest::SendBitmapResult(scoped_ptr<SkBitmap> bitmap) { SendResult(CopyOutputResult::CreateBitmapResult(bitmap.Pass()).Pass()); } diff --git a/cc/output/copy_output_request.h b/cc/output/copy_output_request.h index b7eb0b4..3f4d925 100644 --- a/cc/output/copy_output_request.h +++ b/cc/output/copy_output_request.h @@ -58,11 +58,13 @@ class CC_EXPORT CopyOutputRequest { bool has_area() const { return has_area_; } gfx::Rect area() const { return area_; } - void SendResult(scoped_ptr<CopyOutputResult> result); + void SendEmptyResult(); void SendBitmapResult(scoped_ptr<SkBitmap> bitmap); void SendTextureResult(gfx::Size size, scoped_ptr<TextureMailbox> texture_mailbox); + void SendResult(scoped_ptr<CopyOutputResult> result); + bool Equals(const CopyOutputRequest& other) const { return result_callback_.Equals(other.result_callback_) && force_bitmap_result_ == other.force_bitmap_result_; diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc index f302fdb..f1ac9d7 100644 --- a/cc/output/gl_renderer.cc +++ b/cc/output/gl_renderer.cc @@ -22,7 +22,6 @@ #include "cc/output/compositor_frame_metadata.h" #include "cc/output/context_provider.h" #include "cc/output/copy_output_request.h" -#include "cc/output/copy_output_result.h" #include "cc/output/geometry_binding.h" #include "cc/output/gl_frame_data.h" #include "cc/output/output_surface.h" @@ -2156,7 +2155,7 @@ void GLRenderer::GetFramebufferPixelsAsync( GLC(context_, context_->genMailboxCHROMIUM(mailbox.name)); if (mailbox.IsZero()) { context_->deleteTexture(texture_id); - request->SendResult(CopyOutputResult::CreateEmptyResult()); + request->SendEmptyResult(); return; } diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc index 92cc90c..b58340a 100644 --- a/cc/trees/layer_tree_host_common.cc +++ b/cc/trees/layer_tree_host_common.cc @@ -195,8 +195,10 @@ static inline bool TransformToScreenIsKnown(Layer* layer) { } template <typename LayerType> -static bool LayerShouldBeSkipped(LayerType* layer) { +static bool LayerShouldBeSkipped(LayerType* layer, + bool layer_is_visible) { // Layers can be skipped if any of these conditions are met. + // - is not visible due to it or one of its ancestors being hidden. // - does not draw content. // - is transparent // - has empty bounds @@ -211,6 +213,9 @@ static bool LayerShouldBeSkipped(LayerType* layer) { // transparent, we would have skipped the entire subtree and never made it // into this function, so it is safe to omit this check here. + if (!layer_is_visible) + return true; + if (!layer->DrawsContent() || layer->bounds().IsEmpty()) return true; @@ -231,9 +236,15 @@ static bool LayerShouldBeSkipped(LayerType* layer) { return false; } -static inline bool SubtreeShouldBeSkipped(LayerImpl* layer) { - // The embedder can request to hide the entire layer's subtree. - if (layer->hide_layer_and_subtree()) +static inline bool SubtreeShouldBeSkipped(LayerImpl* layer, + bool layer_is_visible) { + // When we need to do a readback/copy of a layer's output, we can not skip + // it or any of its ancestors. + if (layer->draw_properties().layer_or_descendant_has_copy_request) + return false; + + // If the layer is not visible, then skip it and its subtree. + if (!layer_is_visible) return true; // If layer is on the pending tree and opacity is being animated then @@ -248,9 +259,15 @@ static inline bool SubtreeShouldBeSkipped(LayerImpl* layer) { return !layer->opacity(); } -static inline bool SubtreeShouldBeSkipped(Layer* layer) { - // The embedder can request to hide the entire layer's subtree. - if (layer->hide_layer_and_subtree()) +static inline bool SubtreeShouldBeSkipped(Layer* layer, + bool layer_is_visible) { + // When we need to do a readback/copy of a layer's output, we can not skip + // it or any of its ancestors. + if (layer->draw_properties().layer_or_descendant_has_copy_request) + return false; + + // If the layer is not visible, then skip it and its subtree. + if (!layer_is_visible) return true; // If the opacity is being animated then the opacity on the main thread is @@ -751,45 +768,62 @@ static inline void RemoveSurfaceForEarlyExit( layer_to_remove->ClearRenderSurface(); } +struct PreCalculateMetaInformationRecursiveData { + bool layer_or_descendent_has_copy_request; + + PreCalculateMetaInformationRecursiveData() + : layer_or_descendent_has_copy_request(false) {} +}; + // Recursively walks the layer tree to compute any information that is needed // before doing the main recursion. template <typename LayerType> -static void PreCalculateMetaInformation(LayerType* layer) { - if (layer->HasDelegatedContent()) { - // Layers with delegated content need to be treated as if they have as many - // children as the number of layers they own delegated quads for. Since we - // don't know this number right now, we choose one that acts like infinity - // for our purposes. - layer->draw_properties().num_descendants_that_draw_content = 1000; - layer->draw_properties().descendants_can_clip_selves = false; - return; - } - +static void PreCalculateMetaInformation( + LayerType* layer, + PreCalculateMetaInformationRecursiveData* recursive_data) { + bool has_delegated_content = layer->HasDelegatedContent(); int num_descendants_that_draw_content = 0; bool descendants_can_clip_selves = true; - bool sublayer_transform_prevents_clip = - !layer->sublayer_transform().IsPositiveScaleOrTranslation(); + + if (has_delegated_content) { + // Layers with delegated content need to be treated as if they have as + // many children as the number of layers they own delegated quads for. + // Since we don't know this number right now, we choose one that acts like + // infinity for our purposes. + num_descendants_that_draw_content = 1000; + descendants_can_clip_selves = false; + } for (size_t i = 0; i < layer->children().size(); ++i) { LayerType* child_layer = LayerTreeHostCommon::get_child_as_raw_ptr(layer->children(), i); - PreCalculateMetaInformation<LayerType>(child_layer); + PreCalculateMetaInformation<LayerType>(child_layer, recursive_data); + + if (!has_delegated_content) { + bool sublayer_transform_prevents_clip = + !layer->sublayer_transform().IsPositiveScaleOrTranslation(); - num_descendants_that_draw_content += child_layer->DrawsContent() ? 1 : 0; - num_descendants_that_draw_content += - child_layer->draw_properties().num_descendants_that_draw_content; + num_descendants_that_draw_content += child_layer->DrawsContent() ? 1 : 0; + num_descendants_that_draw_content += + child_layer->draw_properties().num_descendants_that_draw_content; - if ((child_layer->DrawsContent() && !child_layer->CanClipSelf()) || - !child_layer->draw_properties().descendants_can_clip_selves || - sublayer_transform_prevents_clip || - !child_layer->transform().IsPositiveScaleOrTranslation()) - descendants_can_clip_selves = false; + if ((child_layer->DrawsContent() && !child_layer->CanClipSelf()) || + !child_layer->draw_properties().descendants_can_clip_selves || + sublayer_transform_prevents_clip || + !child_layer->transform().IsPositiveScaleOrTranslation()) + descendants_can_clip_selves = false; + } } + if (layer->HasCopyRequest()) + recursive_data->layer_or_descendent_has_copy_request = true; + layer->draw_properties().num_descendants_that_draw_content = num_descendants_that_draw_content; layer->draw_properties().descendants_can_clip_selves = descendants_can_clip_selves; + layer->draw_properties().layer_or_descendant_has_copy_request = + recursive_data->layer_or_descendent_has_copy_request; } static void RoundTranslationComponents(gfx::Transform* transform) { @@ -822,6 +856,7 @@ static void CalculateDrawPropertiesInternal( bool in_subtree_of_page_scale_application_layer, bool subtree_can_use_lcd_text, bool subtree_can_adjust_raster_scales, + bool subtree_is_visible_from_ancestor, gfx::Rect* drawable_content_rect_of_subtree) { // This function computes the new matrix transformations recursively for this // layer and all its descendants. It also computes the appropriate render @@ -953,8 +988,16 @@ static void CalculateDrawPropertiesInternal( // this subtree should be considered empty. *drawable_content_rect_of_subtree = gfx::Rect(); + // Layers with a copy request are always visible, as well as un-hiding their + // subtree. Otherise, layers that are marked as hidden will hide themselves + // and their subtree. + bool layer_is_visible = + subtree_is_visible_from_ancestor && !layer->hide_layer_and_subtree(); + if (layer->HasCopyRequest()) + layer_is_visible = true; + // The root layer cannot skip CalcDrawProperties. - if (!IsRootLayer(layer) && SubtreeShouldBeSkipped(layer)) + if (!IsRootLayer(layer) && SubtreeShouldBeSkipped(layer, layer_is_visible)) return; // As this function proceeds, these are the properties for the current @@ -1129,6 +1172,10 @@ static void CalculateDrawPropertiesInternal( // layer can't directly support non-identity transforms. It should just // forward top-level transforms to the rest of the tree. sublayer_matrix = combined_transform; + + // The root surface does not contribute to any other surface, it has no + // target. + layer->render_surface()->set_contributes_to_drawn_surface(false); } else { // The owning layer's draw transform has a scale from content to layer // space which we do not want; so here we use the combined_transform @@ -1155,6 +1202,9 @@ static void CalculateDrawPropertiesInternal( DCHECK(sublayer_matrix.IsIdentity()); sublayer_matrix.Scale(render_surface_sublayer_scale.x(), render_surface_sublayer_scale.y()); + + layer->render_surface()->set_contributes_to_drawn_surface( + subtree_is_visible_from_ancestor && layer_is_visible); } // The opacity value is moved from the layer to its surface, so that the @@ -1326,7 +1376,7 @@ static void CalculateDrawPropertiesInternal( // and should be included in the sorting process. size_t sorting_start_index = descendants.size(); - if (!LayerShouldBeSkipped(layer)) + if (!LayerShouldBeSkipped(layer, layer_is_visible)) descendants.push_back(layer); gfx::Transform next_scroll_compensation_matrix = @@ -1365,6 +1415,7 @@ static void CalculateDrawPropertiesInternal( in_subtree_of_page_scale_application_layer, subtree_can_use_lcd_text, subtree_can_adjust_raster_scales, + layer_is_visible, &drawable_content_rect_of_child_subtree); if (!drawable_content_rect_of_child_subtree.IsEmpty()) { accumulated_drawable_content_rect_of_children.Union( @@ -1552,11 +1603,14 @@ void LayerTreeHostCommon::CalculateDrawProperties( bool subtree_should_be_clipped = true; gfx::Rect device_viewport_rect(device_viewport_size); bool in_subtree_of_page_scale_application_layer = false; + bool subtree_is_visible = true; // This function should have received a root layer. DCHECK(IsRootLayer(root_layer)); - PreCalculateMetaInformation<Layer>(root_layer); + PreCalculateMetaInformationRecursiveData recursive_data; + PreCalculateMetaInformation<Layer>(root_layer, &recursive_data); + CalculateDrawPropertiesInternal<Layer, LayerList, RenderSurface>( root_layer, scaled_device_transform, @@ -1577,6 +1631,7 @@ void LayerTreeHostCommon::CalculateDrawProperties( in_subtree_of_page_scale_application_layer, can_use_lcd_text, can_adjust_raster_scales, + subtree_is_visible, &total_drawable_content_rect); // The dummy layer list should not have been used. @@ -1609,11 +1664,14 @@ void LayerTreeHostCommon::CalculateDrawProperties( bool subtree_should_be_clipped = true; gfx::Rect device_viewport_rect(device_viewport_size); bool in_subtree_of_page_scale_application_layer = false; + bool subtree_is_visible = true; // This function should have received a root layer. DCHECK(IsRootLayer(root_layer)); - PreCalculateMetaInformation<LayerImpl>(root_layer); + PreCalculateMetaInformationRecursiveData recursive_data; + PreCalculateMetaInformation<LayerImpl>(root_layer, &recursive_data); + CalculateDrawPropertiesInternal<LayerImpl, LayerImplList, RenderSurfaceImpl>( @@ -1636,6 +1694,7 @@ void LayerTreeHostCommon::CalculateDrawProperties( in_subtree_of_page_scale_application_layer, can_use_lcd_text, can_adjust_raster_scales, + subtree_is_visible, &total_drawable_content_rect); // The dummy layer list should not have been used. diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc index 793d5da..f6a1217 100644 --- a/cc/trees/layer_tree_host_common_unittest.cc +++ b/cc/trees/layer_tree_host_common_unittest.cc @@ -13,6 +13,8 @@ #include "cc/layers/layer_impl.h" #include "cc/layers/render_surface.h" #include "cc/layers/render_surface_impl.h" +#include "cc/output/copy_output_request.h" +#include "cc/output/copy_output_result.h" #include "cc/test/animation_test_common.h" #include "cc/test/fake_impl_proxy.h" #include "cc/test/fake_layer_tree_host_impl.h" @@ -8144,5 +8146,195 @@ TEST(LayerTreeHostCommonTest, SubtreeHidden_TwoLayersImpl) { EXPECT_EQ(1, root->render_surface()->layer_list()[0]->id()); } +void EmptyCopyOutputCallback(scoped_ptr<CopyOutputResult> result) {} + +TEST(LayerTreeHostCommonTest, SubtreeHiddenWithCopyRequest) { + FakeImplProxy proxy; + FakeLayerTreeHostImpl host_impl(&proxy); + host_impl.CreatePendingTree(); + const gfx::Transform identity_matrix; + + scoped_refptr<Layer> root = Layer::Create(); + SetLayerPropertiesForTesting(root.get(), + identity_matrix, + identity_matrix, + gfx::PointF(), + gfx::PointF(), + gfx::Size(50, 50), + false); + root->SetIsDrawable(true); + + scoped_refptr<Layer> copy_grand_parent = Layer::Create(); + SetLayerPropertiesForTesting(copy_grand_parent.get(), + identity_matrix, + identity_matrix, + gfx::PointF(), + gfx::PointF(), + gfx::Size(40, 40), + false); + copy_grand_parent->SetIsDrawable(true); + + scoped_refptr<Layer> copy_parent = Layer::Create(); + SetLayerPropertiesForTesting(copy_parent.get(), + identity_matrix, + identity_matrix, + gfx::PointF(), + gfx::PointF(), + gfx::Size(30, 30), + false); + copy_parent->SetIsDrawable(true); + copy_parent->SetForceRenderSurface(true); + + scoped_refptr<Layer> copy_layer = Layer::Create(); + SetLayerPropertiesForTesting(copy_layer.get(), + identity_matrix, + identity_matrix, + gfx::PointF(), + gfx::PointF(), + gfx::Size(20, 20), + false); + copy_layer->SetIsDrawable(true); + + scoped_refptr<Layer> copy_child = Layer::Create(); + SetLayerPropertiesForTesting(copy_child.get(), + identity_matrix, + identity_matrix, + gfx::PointF(), + gfx::PointF(), + gfx::Size(20, 20), + false); + copy_child->SetIsDrawable(true); + + copy_layer->AddChild(copy_child); + copy_parent->AddChild(copy_layer); + copy_grand_parent->AddChild(copy_parent); + root->AddChild(copy_grand_parent); + + // Hide the copy_grand_parent and its subtree. But make a copy request in that + // hidden subtree on copy_layer. + copy_grand_parent->SetHideLayerAndSubtree(true); + copy_layer->RequestCopyOfOutput(CopyOutputRequest::CreateRequest( + base::Bind(&EmptyCopyOutputCallback))); + EXPECT_TRUE(copy_layer->HasCopyRequest()); + + LayerList render_surface_layer_list; + int dummy_max_texture_size = 512; + LayerTreeHostCommon::CalculateDrawProperties(root.get(), + root->bounds(), + gfx::Transform(), + 1.f, + 1.f, + NULL, + dummy_max_texture_size, + false, + true, // can_adjust_raster_scale + &render_surface_layer_list); + + // We should have three render surfaces, one for the root, one for the parent + // since it owns a surface, and one for the copy_layer. + ASSERT_EQ(3u, render_surface_layer_list.size()); + EXPECT_EQ(root->id(), render_surface_layer_list[0]->id()); + EXPECT_EQ(copy_parent->id(), render_surface_layer_list[1]->id()); + EXPECT_EQ(copy_layer->id(), render_surface_layer_list[2]->id()); + + // The root render surface should have 2 contributing layers. The + // copy_grand_parent is hidden, but the copy_parent will appear since + // something in its subtree needs to be drawn for a copy request. + ASSERT_EQ(2u, root->render_surface()->layer_list().size()); + EXPECT_EQ(root->id(), root->render_surface()->layer_list()[0]->id()); + EXPECT_EQ(copy_parent->id(), root->render_surface()->layer_list()[1]->id()); + + // Nothing actually drawns into the copy parent, so only the copy_layer will + // appear in its list, since it needs to be drawn for the copy request. + ASSERT_EQ(1u, copy_parent->render_surface()->layer_list().size()); + EXPECT_EQ(copy_layer->id(), + copy_parent->render_surface()->layer_list()[0]->id()); + + // The copy_layer's render surface should have two contributing layers. + ASSERT_EQ(2u, copy_layer->render_surface()->layer_list().size()); + EXPECT_EQ(copy_layer->id(), + copy_layer->render_surface()->layer_list()[0]->id()); + EXPECT_EQ(copy_child->id(), + copy_layer->render_surface()->layer_list()[1]->id()); +} + +TEST(LayerTreeHostCommonTest, ClippedOutCopyRequest) { + FakeImplProxy proxy; + FakeLayerTreeHostImpl host_impl(&proxy); + host_impl.CreatePendingTree(); + const gfx::Transform identity_matrix; + + scoped_refptr<Layer> root = Layer::Create(); + SetLayerPropertiesForTesting(root.get(), + identity_matrix, + identity_matrix, + gfx::PointF(), + gfx::PointF(), + gfx::Size(50, 50), + false); + root->SetIsDrawable(true); + + scoped_refptr<Layer> copy_parent = Layer::Create(); + SetLayerPropertiesForTesting(copy_parent.get(), + identity_matrix, + identity_matrix, + gfx::PointF(), + gfx::PointF(), + gfx::Size(), + false); + copy_parent->SetIsDrawable(true); + copy_parent->SetMasksToBounds(true); + + scoped_refptr<Layer> copy_layer = Layer::Create(); + SetLayerPropertiesForTesting(copy_layer.get(), + identity_matrix, + identity_matrix, + gfx::PointF(), + gfx::PointF(), + gfx::Size(30, 30), + false); + copy_layer->SetIsDrawable(true); + + scoped_refptr<Layer> copy_child = Layer::Create(); + SetLayerPropertiesForTesting(copy_child.get(), + identity_matrix, + identity_matrix, + gfx::PointF(), + gfx::PointF(), + gfx::Size(20, 20), + false); + copy_child->SetIsDrawable(true); + + copy_layer->AddChild(copy_child); + copy_parent->AddChild(copy_layer); + root->AddChild(copy_parent); + + copy_layer->RequestCopyOfOutput(CopyOutputRequest::CreateRequest( + base::Bind(&EmptyCopyOutputCallback))); + EXPECT_TRUE(copy_layer->HasCopyRequest()); + + LayerList render_surface_layer_list; + int dummy_max_texture_size = 512; + LayerTreeHostCommon::CalculateDrawProperties(root.get(), + root->bounds(), + gfx::Transform(), + 1.f, + 1.f, + NULL, + dummy_max_texture_size, + false, + true, // can_adjust_raster_scale + &render_surface_layer_list); + + // We should have one render surface, as the others are clipped out. + ASSERT_EQ(1u, render_surface_layer_list.size()); + EXPECT_EQ(root->id(), render_surface_layer_list[0]->id()); + + // The root render surface should only have 1 contributing layer, since the + // other layers are empty/clipped away. + ASSERT_EQ(1u, root->render_surface()->layer_list().size()); + EXPECT_EQ(root->id(), root->render_surface()->layer_list()[0]->id()); +} + } // namespace } // namespace cc diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index a2585bc..74b12ba 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -30,6 +30,7 @@ #include "cc/layers/render_surface_impl.h" #include "cc/layers/scrollbar_layer_impl.h" #include "cc/output/compositor_frame_metadata.h" +#include "cc/output/copy_output_request.h" #include "cc/output/delegating_renderer.h" #include "cc/output/gl_renderer.h" #include "cc/output/software_renderer.h" @@ -563,6 +564,9 @@ bool LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { TRACE_EVENT0("cc", "LayerTreeHostImpl::CalculateRenderPasses::EmptyDamageRect"); frame->has_no_damage = true; + // A copy request should cause damage, so we should not have any copy + // requests in this case. + DCHECK_EQ(0u, active_tree_->LayersWithCopyOutputRequest().size()); return true; } @@ -577,7 +581,14 @@ bool LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { --surface_index) { LayerImpl* render_surface_layer = (*frame->render_surface_layer_list)[surface_index]; - render_surface_layer->render_surface()->AppendRenderPasses(frame); + RenderSurfaceImpl* render_surface = render_surface_layer->render_surface(); + + bool should_draw_into_render_pass = + render_surface_layer->parent() == NULL || + render_surface->contributes_to_drawn_surface() || + render_surface_layer->HasCopyRequest(); + if (should_draw_into_render_pass) + render_surface_layer->render_surface()->AppendRenderPasses(frame); } bool record_metrics_for_frame = @@ -633,7 +644,7 @@ bool LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { bool prevent_occlusion = it.target_render_surface_layer()->HasCopyRequest(); occlusion_tracker.EnterLayer(it, prevent_occlusion); - AppendQuadsData append_quads_data(target_render_pass->id); + AppendQuadsData append_quads_data(target_render_pass_id); if (it.represents_target_render_surface()) { if (it->HasCopyRequest()) { @@ -641,7 +652,8 @@ bool LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { it->TakeCopyRequestsAndTransformToTarget( &target_render_pass->copy_requests); } - } else if (it.represents_contributing_render_surface()) { + } else if (it.represents_contributing_render_surface() && + it->render_surface()->contributes_to_drawn_surface()) { RenderPass::Id contributing_render_pass_id = it->render_surface()->RenderPassId(); RenderPass* contributing_render_pass = @@ -751,6 +763,16 @@ bool LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) { RemoveRenderPasses(CullRenderPassesWithCachedTextures(renderer_.get()), frame); + // Any copy requests left in the tree are not going to get serviced, and + // should be aborted. + ScopedPtrVector<CopyOutputRequest> requests_to_abort; + while (!active_tree_->LayersWithCopyOutputRequest().empty()) { + LayerImpl* layer = active_tree_->LayersWithCopyOutputRequest().back(); + layer->TakeCopyRequestsAndTransformToTarget(&requests_to_abort); + } + for (size_t i = 0; i < requests_to_abort.size(); ++i) + requests_to_abort[i]->SendEmptyResult(); + // If we're making a frame to draw, it better have at least one render pass. DCHECK(!frame->render_passes.empty()); return draw_frame; diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index a212971..c2c0e99 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc @@ -2788,6 +2788,236 @@ class LayerTreeHostTestAsyncReadbackLayerDestroyed : public LayerTreeHostTest { SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestAsyncReadbackLayerDestroyed); +class LayerTreeHostTestAsyncReadbackInHiddenSubtree : public LayerTreeHostTest { + protected: + virtual void SetupTree() OVERRIDE { + root_ = FakeContentLayer::Create(&client_); + root_->SetBounds(gfx::Size(20, 20)); + + grand_parent_layer_ = FakeContentLayer::Create(&client_); + grand_parent_layer_->SetBounds(gfx::Size(15, 15)); + root_->AddChild(grand_parent_layer_); + + // parent_layer_ owns a render surface. + parent_layer_ = FakeContentLayer::Create(&client_); + parent_layer_->SetBounds(gfx::Size(15, 15)); + parent_layer_->SetForceRenderSurface(true); + grand_parent_layer_->AddChild(parent_layer_); + + copy_layer_ = FakeContentLayer::Create(&client_); + copy_layer_->SetBounds(gfx::Size(10, 10)); + parent_layer_->AddChild(copy_layer_); + + layer_tree_host()->SetRootLayer(root_); + LayerTreeHostTest::SetupTree(); + } + + void AddCopyRequest(Layer* layer) { + layer->RequestCopyOfOutput( + CopyOutputRequest::CreateBitmapRequest(base::Bind( + &LayerTreeHostTestAsyncReadbackInHiddenSubtree::CopyOutputCallback, + base::Unretained(this)))); + } + + virtual void BeginTest() OVERRIDE { + callback_count_ = 0; + PostSetNeedsCommitToMainThread(); + + AddCopyRequest(copy_layer_.get()); + } + + void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + EXPECT_EQ(copy_layer_->bounds().ToString(), result->size().ToString()); + ++callback_count_; + + switch (callback_count_) { + case 1: + // Hide the copy request layer. + grand_parent_layer_->SetHideLayerAndSubtree(false); + parent_layer_->SetHideLayerAndSubtree(false); + copy_layer_->SetHideLayerAndSubtree(true); + AddCopyRequest(copy_layer_.get()); + break; + case 2: + // Hide the copy request layer's parent only. + grand_parent_layer_->SetHideLayerAndSubtree(false); + parent_layer_->SetHideLayerAndSubtree(true); + copy_layer_->SetHideLayerAndSubtree(false); + AddCopyRequest(copy_layer_.get()); + break; + case 3: + // Hide the copy request layer's grand parent only. + grand_parent_layer_->SetHideLayerAndSubtree(true); + parent_layer_->SetHideLayerAndSubtree(false); + copy_layer_->SetHideLayerAndSubtree(false); + AddCopyRequest(copy_layer_.get()); + break; + case 4: + // Hide the copy request layer's parent and grandparent. + grand_parent_layer_->SetHideLayerAndSubtree(true); + parent_layer_->SetHideLayerAndSubtree(true); + copy_layer_->SetHideLayerAndSubtree(false); + AddCopyRequest(copy_layer_.get()); + break; + case 5: + // Hide the copy request layer as well as its parent and grandparent. + grand_parent_layer_->SetHideLayerAndSubtree(true); + parent_layer_->SetHideLayerAndSubtree(true); + copy_layer_->SetHideLayerAndSubtree(true); + AddCopyRequest(copy_layer_.get()); + break; + case 6: + EndTest(); + break; + } + } + + virtual void AfterTest() OVERRIDE {} + + int callback_count_; + FakeContentLayerClient client_; + scoped_refptr<FakeContentLayer> root_; + scoped_refptr<FakeContentLayer> grand_parent_layer_; + scoped_refptr<FakeContentLayer> parent_layer_; + scoped_refptr<FakeContentLayer> copy_layer_; +}; + +// No output to copy for delegated renderers. +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + LayerTreeHostTestAsyncReadbackInHiddenSubtree); + +class LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest + : public LayerTreeHostTest { + protected: + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + settings->cache_render_pass_contents = true; + } + + virtual void SetupTree() OVERRIDE { + root_ = FakeContentLayer::Create(&client_); + root_->SetBounds(gfx::Size(20, 20)); + + grand_parent_layer_ = FakeContentLayer::Create(&client_); + grand_parent_layer_->SetBounds(gfx::Size(15, 15)); + grand_parent_layer_->SetHideLayerAndSubtree(true); + root_->AddChild(grand_parent_layer_); + + // parent_layer_ owns a render surface. + parent_layer_ = FakeContentLayer::Create(&client_); + parent_layer_->SetBounds(gfx::Size(15, 15)); + parent_layer_->SetForceRenderSurface(true); + grand_parent_layer_->AddChild(parent_layer_); + + copy_layer_ = FakeContentLayer::Create(&client_); + copy_layer_->SetBounds(gfx::Size(10, 10)); + parent_layer_->AddChild(copy_layer_); + + layer_tree_host()->SetRootLayer(root_); + LayerTreeHostTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + did_draw_ = false; + PostSetNeedsCommitToMainThread(); + + copy_layer_->RequestCopyOfOutput( + CopyOutputRequest::CreateBitmapRequest(base::Bind( + &LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest:: + CopyOutputCallback, + base::Unretained(this)))); + } + + void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) { + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + EXPECT_EQ(copy_layer_->bounds().ToString(), result->size().ToString()); + EndTest(); + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + Renderer* renderer = host_impl->renderer(); + + LayerImpl* root = host_impl->active_tree()->root_layer(); + LayerImpl* grand_parent = root->children()[0]; + LayerImpl* parent = grand_parent->children()[0]; + LayerImpl* copy_layer = parent->children()[0]; + + // |parent| owns a surface, but it was hidden and not part of the copy + // request so it should not allocate any resource. + EXPECT_FALSE(renderer->HaveCachedResourcesForRenderPassId( + parent->render_surface()->RenderPassId())); + + // |copy_layer| should have been rendered to a texture since it was needed + // for a copy request. + EXPECT_TRUE(renderer->HaveCachedResourcesForRenderPassId( + copy_layer->render_surface()->RenderPassId())); + + did_draw_ = true; + } + + virtual void AfterTest() OVERRIDE { EXPECT_TRUE(did_draw_); } + + FakeContentLayerClient client_; + bool did_draw_; + scoped_refptr<FakeContentLayer> root_; + scoped_refptr<FakeContentLayer> grand_parent_layer_; + scoped_refptr<FakeContentLayer> parent_layer_; + scoped_refptr<FakeContentLayer> copy_layer_; +}; + +// No output to copy for delegated renderers. +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest); + +class LayerTreeHostTestAsyncReadbackClippedOut : public LayerTreeHostTest { + protected: + virtual void SetupTree() OVERRIDE { + root_ = FakeContentLayer::Create(&client_); + root_->SetBounds(gfx::Size(20, 20)); + + parent_layer_ = FakeContentLayer::Create(&client_); + parent_layer_->SetBounds(gfx::Size(15, 15)); + parent_layer_->SetMasksToBounds(true); + root_->AddChild(parent_layer_); + + copy_layer_ = FakeContentLayer::Create(&client_); + copy_layer_->SetPosition(gfx::Point(15, 15)); + copy_layer_->SetBounds(gfx::Size(10, 10)); + parent_layer_->AddChild(copy_layer_); + + layer_tree_host()->SetRootLayer(root_); + LayerTreeHostTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + + copy_layer_->RequestCopyOfOutput( + CopyOutputRequest::CreateBitmapRequest(base::Bind( + &LayerTreeHostTestAsyncReadbackClippedOut::CopyOutputCallback, + base::Unretained(this)))); + } + + void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) { + // We should still get a callback with no output if the copy requested layer + // was completely clipped away. + EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread()); + EXPECT_EQ(gfx::Size().ToString(), result->size().ToString()); + EndTest(); + } + + virtual void AfterTest() OVERRIDE {} + + FakeContentLayerClient client_; + scoped_refptr<FakeContentLayer> root_; + scoped_refptr<FakeContentLayer> parent_layer_; + scoped_refptr<FakeContentLayer> copy_layer_; +}; + +// No output to copy for delegated renderers. +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + LayerTreeHostTestAsyncReadbackClippedOut); + class LayerTreeHostTestNumFramesPending : public LayerTreeHostTest { public: virtual void BeginTest() OVERRIDE { diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc index 03d0100..067c83a 100644 --- a/cc/trees/layer_tree_impl.cc +++ b/cc/trees/layer_tree_impl.cc @@ -556,4 +556,38 @@ void LayerTreeImpl::WillModifyTilePriorities() { layer_tree_host_impl_->SetNeedsManageTiles(); } +void LayerTreeImpl::AddLayerWithCopyOutputRequest(LayerImpl* layer) { + // Only the active tree needs to know about layers with copy requests, as + // they are aborted if not serviced during draw. + DCHECK(IsActiveTree()); + + DCHECK(std::find(layers_with_copy_output_request_.begin(), + layers_with_copy_output_request_.end(), + layer) == layers_with_copy_output_request_.end()); + layers_with_copy_output_request_.push_back(layer); +} + +void LayerTreeImpl::RemoveLayerWithCopyOutputRequest(LayerImpl* layer) { + // Only the active tree needs to know about layers with copy requests, as + // they are aborted if not serviced during draw. + DCHECK(IsActiveTree()); + + std::vector<LayerImpl*>::iterator it = std::find( + layers_with_copy_output_request_.begin(), + layers_with_copy_output_request_.end(), + layer); + DCHECK(it != layers_with_copy_output_request_.end()); + if (it != layers_with_copy_output_request_.end()) + layers_with_copy_output_request_.erase(it); +} + +const std::vector<LayerImpl*> LayerTreeImpl::LayersWithCopyOutputRequest() + const { + // Only the active tree needs to know about layers with copy requests, as + // they are aborted if not serviced during draw. + DCHECK(IsActiveTree()); + + return layers_with_copy_output_request_; +} + } // namespace cc diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h index 3158e5d..ce31c67 100644 --- a/cc/trees/layer_tree_impl.h +++ b/cc/trees/layer_tree_impl.h @@ -190,6 +190,10 @@ class CC_EXPORT LayerTreeImpl { void WillModifyTilePriorities(); + void AddLayerWithCopyOutputRequest(LayerImpl* layer); + void RemoveLayerWithCopyOutputRequest(LayerImpl* layer); + const std::vector<LayerImpl*> LayersWithCopyOutputRequest() const; + protected: explicit LayerTreeImpl(LayerTreeHostImpl* layer_tree_host_impl); @@ -216,6 +220,8 @@ class CC_EXPORT LayerTreeImpl { typedef base::hash_map<int, LayerImpl*> LayerIdMap; LayerIdMap layer_id_map_; + std::vector<LayerImpl*> layers_with_copy_output_request_; + // Persisted state for non-impl-side-painting. int scrolling_layer_id_from_previous_tree_; |