summaryrefslogtreecommitdiffstats
path: root/cc
diff options
context:
space:
mode:
authordanakj@chromium.org <danakj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-04 00:54:45 +0000
committerdanakj@chromium.org <danakj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-04 00:54:45 +0000
commit30fe19ff40762f1cc3771fb705bd85a736f20515 (patch)
tree3995542f4366d5ba92a94779e33fe490ae37092b /cc
parent7cdf718fc246843a381a48041c697355e314a563 (diff)
downloadchromium_src-30fe19ff40762f1cc3771fb705bd85a736f20515.zip
chromium_src-30fe19ff40762f1cc3771fb705bd85a736f20515.tar.gz
chromium_src-30fe19ff40762f1cc3771fb705bd85a736f20515.tar.bz2
cc: Allow readbacks from hidden layers.
If a layer or any of its ancestors has hide_layer_and_subtree() set to true, the layer will not be part of the compositor's output. But if we want to service a CopyOutputRequest on a layer in the hidden subtree, we need to draw it. This CL tracks visibility recursively in CalcDropProperties, so that when an output request exists in the subtree, we can avoid skipping the subtree, but make note that the layers in it are not visible (at least, until we recurse into the copy output requested layer). If a layer with an output request can not be drawn (it is clipped away/empty), then we abort the copy request and send an empty result. This is done for any remaining copy requests in the layer tree after we have taken requests that will be used and moved them onto RenderPasses. Tests: LayerTreeHostCommonTest.SubtreeHiddenWithCopyRequest LayerTreeHostCommonTest.ClippedOutCopyRequest LayerTreeHostTestAsyncReadbackInHiddenSubtree.RunSingleThread_DirectRenderer LayerTreeHostTestAsyncReadbackInHiddenSubtree.RunMultiThread_DirectRenderer_MainThreadPaint LayerTreeHostTestAsyncReadbackInHiddenSubtree.RunMultiThread_DirectRenderer_ImplSidePaint LayerTreeHostTestAsyncReadbackClippedOut.RunSingleThread_DirectRenderer LayerTreeHostTestAsyncReadbackClippedOut.RunMultiThread_DirectRenderer_MainThreadPaint LayerTreeHostTestAsyncReadbackClippedOut.RunMultiThread_DirectRenderer_ImplSidePaint LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest.RunSingleThread_DirectRenderer LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest.RunMultiThread_DirectRenderer_MainThreadPaint LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest.RunMultiThread_DirectRenderer_ImplSidePaint R=enne BUG=242572 Review URL: https://chromiumcodereview.appspot.com/17619004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@210090 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc')
-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_;