summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cc/layers/draw_properties.h7
-rw-r--r--cc/layers/layer_impl.cc10
-rw-r--r--cc/layers/render_surface.cc1
-rw-r--r--cc/layers/render_surface.h12
-rw-r--r--cc/layers/render_surface_impl.cc1
-rw-r--r--cc/layers/render_surface_impl.h12
-rw-r--r--cc/output/copy_output_request.cc4
-rw-r--r--cc/output/copy_output_request.h4
-rw-r--r--cc/output/gl_renderer.cc3
-rw-r--r--cc/trees/layer_tree_host_common.cc125
-rw-r--r--cc/trees/layer_tree_host_common_unittest.cc192
-rw-r--r--cc/trees/layer_tree_host_impl.cc28
-rw-r--r--cc/trees/layer_tree_host_unittest.cc230
-rw-r--r--cc/trees/layer_tree_impl.cc34
-rw-r--r--cc/trees/layer_tree_impl.h6
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_;