summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cc/cc_tests.gyp3
-rw-r--r--cc/output/copy_output_request.cc25
-rw-r--r--cc/output/copy_output_request.h23
-rw-r--r--cc/output/gl_renderer.cc70
-rw-r--r--cc/test/test_web_graphics_context_3d.cc11
-rw-r--r--cc/test/test_web_graphics_context_3d.h7
-rw-r--r--cc/trees/layer_tree_host_unittest.cc654
-rw-r--r--cc/trees/layer_tree_host_unittest_copyrequest.cc831
8 files changed, 935 insertions, 689 deletions
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index a3666ec..9d03b2c 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -89,9 +89,10 @@
'trees/layer_tree_host_pixeltest_masks.cc',
'trees/layer_tree_host_pixeltest_on_demand_raster.cc',
'trees/layer_tree_host_pixeltest_readback.cc',
- 'trees/layer_tree_host_unittest_animation.cc',
'trees/layer_tree_host_unittest.cc',
+ 'trees/layer_tree_host_unittest_animation.cc',
'trees/layer_tree_host_unittest_context.cc',
+ 'trees/layer_tree_host_unittest_copyrequest.cc',
'trees/layer_tree_host_unittest_damage.cc',
'trees/layer_tree_host_unittest_delegated.cc',
'trees/layer_tree_host_unittest_occlusion.cc',
diff --git a/cc/output/copy_output_request.cc b/cc/output/copy_output_request.cc
index 6163d5d..50173d5 100644
--- a/cc/output/copy_output_request.cc
+++ b/cc/output/copy_output_request.cc
@@ -14,6 +14,19 @@
namespace cc {
+// static
+scoped_ptr<CopyOutputRequest> CopyOutputRequest::CreateRelayRequest(
+ const CopyOutputRequest& original_request,
+ const CopyOutputRequestCallback& result_callback) {
+ scoped_ptr<CopyOutputRequest> relay = CreateRequest(result_callback);
+ relay->force_bitmap_result_ = original_request.force_bitmap_result_;
+ relay->has_area_ = original_request.has_area_;
+ relay->area_ = original_request.area_;
+ relay->has_texture_mailbox_ = original_request.has_texture_mailbox_;
+ relay->texture_mailbox_ = original_request.texture_mailbox_;
+ return relay.Pass();
+}
+
CopyOutputRequest::CopyOutputRequest() {}
CopyOutputRequest::CopyOutputRequest(
@@ -21,8 +34,8 @@ CopyOutputRequest::CopyOutputRequest(
const CopyOutputRequestCallback& result_callback)
: force_bitmap_result_(force_bitmap_result),
has_area_(false),
- result_callback_(result_callback) {
-}
+ has_texture_mailbox_(false),
+ result_callback_(result_callback) {}
CopyOutputRequest::~CopyOutputRequest() {
if (!result_callback_.is_null())
@@ -50,4 +63,12 @@ void CopyOutputRequest::SendTextureResult(
size, texture_mailbox, release_callback.Pass()));
}
+void CopyOutputRequest::SetTextureMailbox(
+ const TextureMailbox& texture_mailbox) {
+ DCHECK(!force_bitmap_result_);
+ DCHECK(texture_mailbox.IsTexture());
+ has_texture_mailbox_ = true;
+ texture_mailbox_ = texture_mailbox;
+}
+
} // namespace cc
diff --git a/cc/output/copy_output_request.h b/cc/output/copy_output_request.h
index 60eac5f..4b74b41 100644
--- a/cc/output/copy_output_request.h
+++ b/cc/output/copy_output_request.h
@@ -8,6 +8,7 @@
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
+#include "cc/resources/texture_mailbox.h"
#include "ui/gfx/rect.h"
class SkBitmap;
@@ -15,7 +16,6 @@ class SkBitmap;
namespace cc {
class CopyOutputResult;
class SingleReleaseCallback;
-class TextureMailbox;
class CC_EXPORT CopyOutputRequest {
public:
@@ -35,13 +35,7 @@ class CC_EXPORT CopyOutputRequest {
}
static scoped_ptr<CopyOutputRequest> CreateRelayRequest(
const CopyOutputRequest& original_request,
- const CopyOutputRequestCallback& result_callback) {
- scoped_ptr<CopyOutputRequest> relay = CreateRequest(result_callback);
- relay->force_bitmap_result_ = original_request.force_bitmap_result_;
- relay->has_area_ = original_request.has_area_;
- relay->area_ = original_request.area_;
- return relay.Pass();
- }
+ const CopyOutputRequestCallback& result_callback);
~CopyOutputRequest();
@@ -59,6 +53,13 @@ class CC_EXPORT CopyOutputRequest {
bool has_area() const { return has_area_; }
gfx::Rect area() const { return area_; }
+ // By default copy requests create a new TextureMailbox to return contents
+ // in. This allows a client to provide a TextureMailbox, and the compositor
+ // will place the result inside the TextureMailbox.
+ void SetTextureMailbox(const TextureMailbox& texture_mailbox);
+ bool has_texture_mailbox() const { return has_texture_mailbox_; }
+ const TextureMailbox& texture_mailbox() const { return texture_mailbox_; }
+
void SendEmptyResult();
void SendBitmapResult(scoped_ptr<SkBitmap> bitmap);
void SendTextureResult(gfx::Size size,
@@ -69,12 +70,14 @@ class CC_EXPORT CopyOutputRequest {
private:
CopyOutputRequest();
- explicit CopyOutputRequest(bool force_bitmap_result,
- const CopyOutputRequestCallback& result_callback);
+ CopyOutputRequest(bool force_bitmap_result,
+ const CopyOutputRequestCallback& result_callback);
bool force_bitmap_result_;
bool has_area_;
+ bool has_texture_mailbox_;
gfx::Rect area_;
+ TextureMailbox texture_mailbox_;
CopyOutputRequestCallback result_callback_;
};
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
index 40f2601..3bf0d5b 100644
--- a/cc/output/gl_renderer.cc
+++ b/cc/output/gl_renderer.cc
@@ -2338,36 +2338,62 @@ void GLRenderer::GetFramebufferPixelsAsync(
gfx::Rect window_rect = MoveFromDrawToWindowSpace(rect);
if (!request->force_bitmap_result()) {
+ bool own_mailbox = !request->has_texture_mailbox();
+
unsigned int texture_id = context_->createTexture();
- GLC(context_, context_->bindTexture(GL_TEXTURE_2D, texture_id));
- GLC(context_, context_->texParameteri(
- GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
- GLC(context_, context_->texParameteri(
- GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
- GLC(context_, context_->texParameteri(
- GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
- GLC(context_, context_->texParameteri(
- GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
- GetFramebufferTexture(texture_id, RGBA_8888, window_rect);
gpu::Mailbox mailbox;
- unsigned sync_point = 0;
- GLC(context_, context_->genMailboxCHROMIUM(mailbox.name));
- if (mailbox.IsZero()) {
- context_->deleteTexture(texture_id);
- request->SendEmptyResult();
- return;
+ if (own_mailbox) {
+ GLC(context_, context_->genMailboxCHROMIUM(mailbox.name));
+ if (mailbox.IsZero()) {
+ context_->deleteTexture(texture_id);
+ request->SendEmptyResult();
+ return;
+ }
+ } else {
+ mailbox = request->texture_mailbox().name();
+ DCHECK_EQ(static_cast<unsigned>(GL_TEXTURE_2D),
+ request->texture_mailbox().target());
+ DCHECK(!mailbox.IsZero());
+ unsigned incoming_sync_point = request->texture_mailbox().sync_point();
+ if (incoming_sync_point)
+ GLC(context_, context_->waitSyncPoint(incoming_sync_point));
}
GLC(context_, context_->bindTexture(GL_TEXTURE_2D, texture_id));
- GLC(context_, context_->produceTextureCHROMIUM(
- GL_TEXTURE_2D, mailbox.name));
+ if (own_mailbox) {
+ GLC(context_,
+ context_->texParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ GLC(context_,
+ context_->texParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ GLC(context_,
+ context_->texParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ GLC(context_,
+ context_->texParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+ GLC(context_,
+ context_->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name));
+ } else {
+ GLC(context_,
+ context_->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name));
+ }
+ GetFramebufferTexture(texture_id, RGBA_8888, window_rect);
GLC(context_, context_->bindTexture(GL_TEXTURE_2D, 0));
- sync_point = context_->insertSyncPoint();
+
+ unsigned sync_point = context_->insertSyncPoint();
TextureMailbox texture_mailbox(mailbox, GL_TEXTURE_2D, sync_point);
- scoped_ptr<SingleReleaseCallback> release_callback =
- texture_mailbox_deleter_->GetReleaseCallback(
- output_surface_->context_provider(), texture_id);
+
+ scoped_ptr<SingleReleaseCallback> release_callback;
+ if (own_mailbox) {
+ release_callback = texture_mailbox_deleter_->GetReleaseCallback(
+ output_surface_->context_provider(), texture_id);
+ } else {
+ context_->deleteTexture(texture_id);
+ }
+
request->SendTextureResult(window_rect.size(),
texture_mailbox,
release_callback.Pass());
diff --git a/cc/test/test_web_graphics_context_3d.cc b/cc/test/test_web_graphics_context_3d.cc
index 57183d3..3157e27 100644
--- a/cc/test/test_web_graphics_context_3d.cc
+++ b/cc/test/test_web_graphics_context_3d.cc
@@ -79,6 +79,8 @@ TestWebGraphicsContext3D::TestWebGraphicsContext3D()
scale_factor_(-1.f),
test_support_(NULL),
last_update_type_(NoUpdate),
+ next_insert_sync_point_(1),
+ last_waited_sync_point_(0),
bound_buffer_(0),
peak_transfer_buffer_memory_used_bytes_(0),
weak_ptr_factory_(this) {
@@ -605,6 +607,15 @@ void TestWebGraphicsContext3D::unmapImageCHROMIUM(
DCHECK_GT(namespace_->images.count(image_id), 0u);
}
+unsigned TestWebGraphicsContext3D::insertSyncPoint() {
+ return next_insert_sync_point_++;
+}
+
+void TestWebGraphicsContext3D::waitSyncPoint(unsigned sync_point) {
+ if (sync_point)
+ last_waited_sync_point_ = sync_point;
+}
+
size_t TestWebGraphicsContext3D::NumTextures() const {
base::AutoLock lock(namespace_->lock);
return namespace_->textures.Size();
diff --git a/cc/test/test_web_graphics_context_3d.h b/cc/test/test_web_graphics_context_3d.h
index f8d17c7..54cf543 100644
--- a/cc/test/test_web_graphics_context_3d.h
+++ b/cc/test/test_web_graphics_context_3d.h
@@ -158,6 +158,11 @@ class TestWebGraphicsContext3D : public FakeWebGraphicsContext3D {
blink::WGC3Denum access);
virtual void unmapImageCHROMIUM(blink::WGC3Duint image_id);
+ virtual unsigned insertSyncPoint() OVERRIDE;
+ virtual void waitSyncPoint(unsigned sync_point) OVERRIDE;
+
+ unsigned last_waited_sync_point() const { return last_waited_sync_point_; }
+
const ContextProvider::Capabilities& test_capabilities() const {
return test_capabilities_;
}
@@ -352,6 +357,8 @@ class TestWebGraphicsContext3D : public FakeWebGraphicsContext3D {
TestContextSupport* test_support_;
gfx::Rect update_rect_;
UpdateType last_update_type_;
+ unsigned next_insert_sync_point_;
+ unsigned last_waited_sync_point_;
unsigned bound_buffer_;
TextureTargets texture_targets_;
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index fa70d95..9b301e2 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -2927,660 +2927,6 @@ class LayerTreeHostTestIOSurfaceDrawing : public LayerTreeHostTest {
SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
LayerTreeHostTestIOSurfaceDrawing);
-class LayerTreeHostTestAsyncReadback : public LayerTreeHostTest {
- protected:
- virtual void SetupTree() OVERRIDE {
- root = FakeContentLayer::Create(&client_);
- root->SetBounds(gfx::Size(20, 20));
-
- child = FakeContentLayer::Create(&client_);
- child->SetBounds(gfx::Size(10, 10));
- root->AddChild(child);
-
- layer_tree_host()->SetRootLayer(root);
- LayerTreeHostTest::SetupTree();
- }
-
- virtual void BeginTest() OVERRIDE {
- PostSetNeedsCommitToMainThread();
- }
-
- virtual void DidCommitAndDrawFrame() OVERRIDE {
- WaitForCallback();
- }
-
- void WaitForCallback() {
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(
- &LayerTreeHostTestAsyncReadback::NextStep,
- base::Unretained(this)));
- }
-
- void NextStep() {
- int frame = layer_tree_host()->source_frame_number();
- switch (frame) {
- case 1:
- child->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
- base::Bind(&LayerTreeHostTestAsyncReadback::CopyOutputCallback,
- base::Unretained(this))));
- EXPECT_EQ(0u, callbacks_.size());
- break;
- case 2:
- if (callbacks_.size() < 1u) {
- WaitForCallback();
- return;
- }
- EXPECT_EQ(1u, callbacks_.size());
- EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[0].ToString());
-
- child->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
- base::Bind(&LayerTreeHostTestAsyncReadback::CopyOutputCallback,
- base::Unretained(this))));
- root->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
- base::Bind(&LayerTreeHostTestAsyncReadback::CopyOutputCallback,
- base::Unretained(this))));
- child->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
- base::Bind(&LayerTreeHostTestAsyncReadback::CopyOutputCallback,
- base::Unretained(this))));
- EXPECT_EQ(1u, callbacks_.size());
- break;
- case 3:
- if (callbacks_.size() < 4u) {
- WaitForCallback();
- return;
- }
- EXPECT_EQ(4u, callbacks_.size());
- // The child was copied to a bitmap and passed back twice.
- EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[1].ToString());
- EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[2].ToString());
- // The root was copied to a bitmap and passed back also.
- EXPECT_EQ(gfx::Size(20, 20).ToString(), callbacks_[3].ToString());
- EndTest();
- break;
- }
- }
-
- void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) {
- EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread());
- EXPECT_TRUE(result->HasBitmap());
- scoped_ptr<SkBitmap> bitmap = result->TakeBitmap().Pass();
- EXPECT_EQ(result->size().ToString(),
- gfx::Size(bitmap->width(), bitmap->height()).ToString());
- callbacks_.push_back(result->size());
- }
-
- virtual void AfterTest() OVERRIDE {
- EXPECT_EQ(4u, callbacks_.size());
- }
-
- virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback)
- OVERRIDE {
- scoped_ptr<FakeOutputSurface> output_surface;
- if (use_gl_renderer_) {
- output_surface = FakeOutputSurface::Create3d().Pass();
- } else {
- output_surface = FakeOutputSurface::CreateSoftware(
- make_scoped_ptr(new SoftwareOutputDevice)).Pass();
- }
- return output_surface.PassAs<OutputSurface>();
- }
-
- bool use_gl_renderer_;
- std::vector<gfx::Size> callbacks_;
- FakeContentLayerClient client_;
- scoped_refptr<FakeContentLayer> root;
- scoped_refptr<FakeContentLayer> child;
-};
-
-// Readback can't be done with a delegating renderer.
-TEST_F(LayerTreeHostTestAsyncReadback, GLRenderer_RunSingleThread) {
- use_gl_renderer_ = true;
- RunTest(false, false, false);
-}
-
-TEST_F(LayerTreeHostTestAsyncReadback,
- GLRenderer_RunMultiThread_MainThreadPainting) {
- use_gl_renderer_ = true;
- RunTest(true, false, false);
-}
-
-TEST_F(LayerTreeHostTestAsyncReadback, SoftwareRenderer_RunSingleThread) {
- use_gl_renderer_ = false;
- RunTest(false, false, false);
-}
-
-TEST_F(LayerTreeHostTestAsyncReadback,
- SoftwareRenderer_RunMultiThread_MainThreadPainting) {
- use_gl_renderer_ = false;
- RunTest(true, false, false);
-}
-
-class LayerTreeHostTestAsyncReadbackLayerDestroyed : public LayerTreeHostTest {
- protected:
- virtual void SetupTree() OVERRIDE {
- root_ = FakeContentLayer::Create(&client_);
- root_->SetBounds(gfx::Size(20, 20));
-
- main_destroyed_ = FakeContentLayer::Create(&client_);
- main_destroyed_->SetBounds(gfx::Size(15, 15));
- root_->AddChild(main_destroyed_);
-
- impl_destroyed_ = FakeContentLayer::Create(&client_);
- impl_destroyed_->SetBounds(gfx::Size(10, 10));
- root_->AddChild(impl_destroyed_);
-
- layer_tree_host()->SetRootLayer(root_);
- LayerTreeHostTest::SetupTree();
- }
-
- virtual void BeginTest() OVERRIDE {
- callback_count_ = 0;
- PostSetNeedsCommitToMainThread();
- }
-
- virtual void DidCommit() OVERRIDE {
- int frame = layer_tree_host()->source_frame_number();
- switch (frame) {
- case 1:
- main_destroyed_->RequestCopyOfOutput(
- CopyOutputRequest::CreateBitmapRequest(base::Bind(
- &LayerTreeHostTestAsyncReadbackLayerDestroyed::
- CopyOutputCallback,
- base::Unretained(this))));
- impl_destroyed_->RequestCopyOfOutput(
- CopyOutputRequest::CreateBitmapRequest(base::Bind(
- &LayerTreeHostTestAsyncReadbackLayerDestroyed::
- CopyOutputCallback,
- base::Unretained(this))));
- EXPECT_EQ(0, callback_count_);
-
- // Destroy the main thread layer right away.
- main_destroyed_->RemoveFromParent();
- main_destroyed_ = NULL;
-
- // Should callback with a NULL bitmap.
- EXPECT_EQ(1, callback_count_);
-
- // Prevent drawing so we can't make a copy of the impl_destroyed layer.
- layer_tree_host()->SetViewportSize(gfx::Size());
- break;
- case 2:
- // Flush the message loops and make sure the callbacks run.
- layer_tree_host()->SetNeedsCommit();
- break;
- case 3:
- // No drawing means no readback yet.
- EXPECT_EQ(1, callback_count_);
-
- // Destroy the impl thread layer.
- impl_destroyed_->RemoveFromParent();
- impl_destroyed_ = NULL;
-
- // No callback yet because it's on the impl side.
- EXPECT_EQ(1, callback_count_);
- break;
- case 4:
- // Flush the message loops and make sure the callbacks run.
- layer_tree_host()->SetNeedsCommit();
- break;
- case 5:
- // We should get another callback with a NULL bitmap.
- EXPECT_EQ(2, callback_count_);
- EndTest();
- break;
- }
- }
-
- void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) {
- EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread());
- EXPECT_TRUE(result->IsEmpty());
- ++callback_count_;
- }
-
- virtual void AfterTest() OVERRIDE {}
-
- int callback_count_;
- FakeContentLayerClient client_;
- scoped_refptr<FakeContentLayer> root_;
- scoped_refptr<FakeContentLayer> main_destroyed_;
- scoped_refptr<FakeContentLayer> impl_destroyed_;
-};
-
-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_NOIMPL_TEST_F(
- LayerTreeHostTestAsyncReadbackInHiddenSubtree);
-
-class LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest
- : 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));
- 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->HasAllocatedResourcesForTesting(
- parent->render_surface()->RenderPassId()));
-
- // |copy_layer| should have been rendered to a texture since it was needed
- // for a copy request.
- EXPECT_TRUE(renderer->HasAllocatedResourcesForTesting(
- 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 LayerTreeHostTestAsyncTwoReadbacksWithoutDraw : public LayerTreeHostTest {
- protected:
- virtual void SetupTree() OVERRIDE {
- root_ = FakeContentLayer::Create(&client_);
- root_->SetBounds(gfx::Size(20, 20));
-
- copy_layer_ = FakeContentLayer::Create(&client_);
- copy_layer_->SetBounds(gfx::Size(10, 10));
- root_->AddChild(copy_layer_);
-
- layer_tree_host()->SetRootLayer(root_);
- LayerTreeHostTest::SetupTree();
- }
-
- void AddCopyRequest(Layer* layer) {
- layer->RequestCopyOfOutput(
- CopyOutputRequest::CreateBitmapRequest(base::Bind(
- &LayerTreeHostTestAsyncTwoReadbacksWithoutDraw::CopyOutputCallback,
- base::Unretained(this))));
- }
-
- virtual void BeginTest() OVERRIDE {
- saw_copy_request_ = false;
- callback_count_ = 0;
- PostSetNeedsCommitToMainThread();
-
- // Prevent drawing.
- layer_tree_host()->SetViewportSize(gfx::Size(0, 0));
-
- AddCopyRequest(copy_layer_.get());
- }
-
- virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
- if (impl->active_tree()->source_frame_number() == 0) {
- LayerImpl* root = impl->active_tree()->root_layer();
- EXPECT_TRUE(root->children()[0]->HasCopyRequest());
- saw_copy_request_ = true;
- }
- }
-
- virtual void DidCommit() OVERRIDE {
- if (layer_tree_host()->source_frame_number() == 1) {
- // Allow drawing.
- layer_tree_host()->SetViewportSize(gfx::Size(root_->bounds()));
-
- 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_;
-
- if (callback_count_ == 2)
- EndTest();
- }
-
- virtual void AfterTest() OVERRIDE { EXPECT_TRUE(saw_copy_request_); }
-
- bool saw_copy_request_;
- int callback_count_;
- FakeContentLayerClient client_;
- scoped_refptr<FakeContentLayer> root_;
- scoped_refptr<FakeContentLayer> copy_layer_;
-};
-
-// No output to copy for delegated renderers.
-SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(
- LayerTreeHostTestAsyncTwoReadbacksWithoutDraw);
-
-class LayerTreeHostTestAsyncReadbackLostOutputSurface
- : public LayerTreeHostTest {
- protected:
- virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback)
- OVERRIDE {
- if (!first_context_provider_.get()) {
- first_context_provider_ = TestContextProvider::Create();
- return FakeOutputSurface::Create3d(first_context_provider_)
- .PassAs<OutputSurface>();
- }
-
- EXPECT_FALSE(second_context_provider_.get());
- second_context_provider_ = TestContextProvider::Create();
- return FakeOutputSurface::Create3d(second_context_provider_)
- .PassAs<OutputSurface>();
- }
-
- virtual void SetupTree() OVERRIDE {
- root_ = FakeContentLayer::Create(&client_);
- root_->SetBounds(gfx::Size(20, 20));
-
- copy_layer_ = FakeContentLayer::Create(&client_);
- copy_layer_->SetBounds(gfx::Size(10, 10));
- root_->AddChild(copy_layer_);
-
- layer_tree_host()->SetRootLayer(root_);
- LayerTreeHostTest::SetupTree();
- }
-
- virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); }
-
- void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) {
- EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread());
- EXPECT_EQ(gfx::Size(10, 10).ToString(), result->size().ToString());
- EXPECT_TRUE(result->HasTexture());
-
- // Save the result for later.
- EXPECT_FALSE(result_);
- result_ = result.Pass();
-
- // Post a commit to lose the output surface.
- layer_tree_host()->SetNeedsCommit();
- }
-
- virtual void DidCommitAndDrawFrame() OVERRIDE {
- switch (layer_tree_host()->source_frame_number()) {
- case 1:
- // The layers have been pushed to the impl side. The layer textures have
- // been allocated.
-
- // Request a copy of the layer. This will use another texture.
- copy_layer_->RequestCopyOfOutput(
- CopyOutputRequest::CreateRequest(base::Bind(
- &LayerTreeHostTestAsyncReadbackLostOutputSurface::
- CopyOutputCallback,
- base::Unretained(this))));
- break;
- case 4:
- // With SingleThreadProxy it takes two commits to finally swap after a
- // context loss.
- case 5:
- // Now destroy the CopyOutputResult, releasing the texture inside back
- // to the compositor.
- EXPECT_TRUE(result_);
- result_.reset();
-
- // Check that it is released.
- ImplThreadTaskRunner()->PostTask(
- FROM_HERE,
- base::Bind(&LayerTreeHostTestAsyncReadbackLostOutputSurface::
- CheckNumTextures,
- base::Unretained(this),
- num_textures_after_loss_ - 1));
- break;
- }
- }
-
- virtual void SwapBuffersOnThread(LayerTreeHostImpl *impl, bool result)
- OVERRIDE {
- switch (impl->active_tree()->source_frame_number()) {
- case 0:
- // The layers have been drawn, so their textures have been allocated.
- EXPECT_FALSE(result_);
- num_textures_without_readback_ =
- first_context_provider_->TestContext3d()->NumTextures();
- break;
- case 1:
- // We did a readback, so there will be a readback texture around now.
- EXPECT_LT(num_textures_without_readback_,
- first_context_provider_->TestContext3d()->NumTextures());
- break;
- case 2:
- // The readback texture is collected.
- EXPECT_TRUE(result_);
-
- // Lose the output surface.
- first_context_provider_->TestContext3d()->loseContextCHROMIUM(
- GL_GUILTY_CONTEXT_RESET_ARB,
- GL_INNOCENT_CONTEXT_RESET_ARB);
- break;
- case 3:
- // With SingleThreadProxy it takes two commits to finally swap after a
- // context loss.
- case 4:
- // The output surface has been recreated.
- EXPECT_TRUE(second_context_provider_.get());
-
- num_textures_after_loss_ =
- first_context_provider_->TestContext3d()->NumTextures();
- break;
- }
- }
-
- void CheckNumTextures(size_t expected_num_textures) {
- EXPECT_EQ(expected_num_textures,
- first_context_provider_->TestContext3d()->NumTextures());
- EndTest();
- }
-
- virtual void AfterTest() OVERRIDE {}
-
- scoped_refptr<TestContextProvider> first_context_provider_;
- scoped_refptr<TestContextProvider> second_context_provider_;
- size_t num_textures_without_readback_;
- size_t num_textures_after_loss_;
- FakeContentLayerClient client_;
- scoped_refptr<FakeContentLayer> root_;
- scoped_refptr<FakeContentLayer> copy_layer_;
- scoped_ptr<CopyOutputResult> result_;
-};
-
-// No output to copy for delegated renderers.
-SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(
- LayerTreeHostTestAsyncReadbackLostOutputSurface);
-
class LayerTreeHostTestNumFramesPending : public LayerTreeHostTest {
public:
virtual void BeginTest() OVERRIDE {
diff --git a/cc/trees/layer_tree_host_unittest_copyrequest.cc b/cc/trees/layer_tree_host_unittest_copyrequest.cc
new file mode 100644
index 0000000..a7fad10
--- /dev/null
+++ b/cc/trees/layer_tree_host_unittest_copyrequest.cc
@@ -0,0 +1,831 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/output/copy_output_request.h"
+#include "cc/output/copy_output_result.h"
+#include "cc/test/fake_content_layer.h"
+#include "cc/test/fake_content_layer_client.h"
+#include "cc/test/fake_output_surface.h"
+#include "cc/test/layer_tree_test.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "gpu/GLES2/gl2extchromium.h"
+
+namespace cc {
+namespace {
+
+// These tests only use direct rendering, as there is no output to copy for
+// delegated renderers.
+class LayerTreeHostCopyRequestTest : public LayerTreeTest {};
+
+class LayerTreeHostCopyRequestTestMultipleRequests
+ : public LayerTreeHostCopyRequestTest {
+ protected:
+ virtual void SetupTree() OVERRIDE {
+ root = FakeContentLayer::Create(&client_);
+ root->SetBounds(gfx::Size(20, 20));
+
+ child = FakeContentLayer::Create(&client_);
+ child->SetBounds(gfx::Size(10, 10));
+ root->AddChild(child);
+
+ layer_tree_host()->SetRootLayer(root);
+ LayerTreeHostCopyRequestTest::SetupTree();
+ }
+
+ virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); }
+
+ virtual void DidCommitAndDrawFrame() OVERRIDE { WaitForCallback(); }
+
+ void WaitForCallback() {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&LayerTreeHostCopyRequestTestMultipleRequests::NextStep,
+ base::Unretained(this)));
+ }
+
+ void NextStep() {
+ int frame = layer_tree_host()->source_frame_number();
+ switch (frame) {
+ case 1:
+ child->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
+ base::Bind(&LayerTreeHostCopyRequestTestMultipleRequests::
+ CopyOutputCallback,
+ base::Unretained(this))));
+ EXPECT_EQ(0u, callbacks_.size());
+ break;
+ case 2:
+ if (callbacks_.size() < 1u) {
+ WaitForCallback();
+ return;
+ }
+ EXPECT_EQ(1u, callbacks_.size());
+ EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[0].ToString());
+
+ child->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
+ base::Bind(&LayerTreeHostCopyRequestTestMultipleRequests::
+ CopyOutputCallback,
+ base::Unretained(this))));
+ root->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
+ base::Bind(&LayerTreeHostCopyRequestTestMultipleRequests::
+ CopyOutputCallback,
+ base::Unretained(this))));
+ child->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
+ base::Bind(&LayerTreeHostCopyRequestTestMultipleRequests::
+ CopyOutputCallback,
+ base::Unretained(this))));
+ EXPECT_EQ(1u, callbacks_.size());
+ break;
+ case 3:
+ if (callbacks_.size() < 4u) {
+ WaitForCallback();
+ return;
+ }
+ EXPECT_EQ(4u, callbacks_.size());
+ // The child was copied to a bitmap and passed back twice.
+ EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[1].ToString());
+ EXPECT_EQ(gfx::Size(10, 10).ToString(), callbacks_[2].ToString());
+ // The root was copied to a bitmap and passed back also.
+ EXPECT_EQ(gfx::Size(20, 20).ToString(), callbacks_[3].ToString());
+ EndTest();
+ break;
+ }
+ }
+
+ void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) {
+ EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread());
+ EXPECT_TRUE(result->HasBitmap());
+ scoped_ptr<SkBitmap> bitmap = result->TakeBitmap().Pass();
+ EXPECT_EQ(result->size().ToString(),
+ gfx::Size(bitmap->width(), bitmap->height()).ToString());
+ callbacks_.push_back(result->size());
+ }
+
+ virtual void AfterTest() OVERRIDE { EXPECT_EQ(4u, callbacks_.size()); }
+
+ virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback)
+ OVERRIDE {
+ scoped_ptr<FakeOutputSurface> output_surface;
+ if (use_gl_renderer_) {
+ output_surface = FakeOutputSurface::Create3d().Pass();
+ } else {
+ output_surface = FakeOutputSurface::CreateSoftware(
+ make_scoped_ptr(new SoftwareOutputDevice)).Pass();
+ }
+ return output_surface.PassAs<OutputSurface>();
+ }
+
+ bool use_gl_renderer_;
+ std::vector<gfx::Size> callbacks_;
+ FakeContentLayerClient client_;
+ scoped_refptr<FakeContentLayer> root;
+ scoped_refptr<FakeContentLayer> child;
+};
+
+// Readback can't be done with a delegating renderer.
+TEST_F(LayerTreeHostCopyRequestTestMultipleRequests,
+ GLRenderer_RunSingleThread) {
+ use_gl_renderer_ = true;
+ RunTest(false, false, false);
+}
+
+TEST_F(LayerTreeHostCopyRequestTestMultipleRequests,
+ GLRenderer_RunMultiThread_MainThreadPainting) {
+ use_gl_renderer_ = true;
+ RunTest(true, false, false);
+}
+
+TEST_F(LayerTreeHostCopyRequestTestMultipleRequests,
+ SoftwareRenderer_RunSingleThread) {
+ use_gl_renderer_ = false;
+ RunTest(false, false, false);
+}
+
+TEST_F(LayerTreeHostCopyRequestTestMultipleRequests,
+ SoftwareRenderer_RunMultiThread_MainThreadPainting) {
+ use_gl_renderer_ = false;
+ RunTest(true, false, false);
+}
+
+class LayerTreeHostCopyRequestTestLayerDestroyed
+ : public LayerTreeHostCopyRequestTest {
+ protected:
+ virtual void SetupTree() OVERRIDE {
+ root_ = FakeContentLayer::Create(&client_);
+ root_->SetBounds(gfx::Size(20, 20));
+
+ main_destroyed_ = FakeContentLayer::Create(&client_);
+ main_destroyed_->SetBounds(gfx::Size(15, 15));
+ root_->AddChild(main_destroyed_);
+
+ impl_destroyed_ = FakeContentLayer::Create(&client_);
+ impl_destroyed_->SetBounds(gfx::Size(10, 10));
+ root_->AddChild(impl_destroyed_);
+
+ layer_tree_host()->SetRootLayer(root_);
+ LayerTreeHostCopyRequestTest::SetupTree();
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ callback_count_ = 0;
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void DidCommit() OVERRIDE {
+ int frame = layer_tree_host()->source_frame_number();
+ switch (frame) {
+ case 1:
+ main_destroyed_->RequestCopyOfOutput(
+ CopyOutputRequest::CreateBitmapRequest(base::Bind(
+ &LayerTreeHostCopyRequestTestLayerDestroyed::CopyOutputCallback,
+ base::Unretained(this))));
+ impl_destroyed_->RequestCopyOfOutput(
+ CopyOutputRequest::CreateBitmapRequest(base::Bind(
+ &LayerTreeHostCopyRequestTestLayerDestroyed::CopyOutputCallback,
+ base::Unretained(this))));
+ EXPECT_EQ(0, callback_count_);
+
+ // Destroy the main thread layer right away.
+ main_destroyed_->RemoveFromParent();
+ main_destroyed_ = NULL;
+
+ // Should callback with a NULL bitmap.
+ EXPECT_EQ(1, callback_count_);
+
+ // Prevent drawing so we can't make a copy of the impl_destroyed layer.
+ layer_tree_host()->SetViewportSize(gfx::Size());
+ break;
+ case 2:
+ // Flush the message loops and make sure the callbacks run.
+ layer_tree_host()->SetNeedsCommit();
+ break;
+ case 3:
+ // No drawing means no readback yet.
+ EXPECT_EQ(1, callback_count_);
+
+ // Destroy the impl thread layer.
+ impl_destroyed_->RemoveFromParent();
+ impl_destroyed_ = NULL;
+
+ // No callback yet because it's on the impl side.
+ EXPECT_EQ(1, callback_count_);
+ break;
+ case 4:
+ // Flush the message loops and make sure the callbacks run.
+ layer_tree_host()->SetNeedsCommit();
+ break;
+ case 5:
+ // We should get another callback with a NULL bitmap.
+ EXPECT_EQ(2, callback_count_);
+ EndTest();
+ break;
+ }
+ }
+
+ void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) {
+ EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread());
+ EXPECT_TRUE(result->IsEmpty());
+ ++callback_count_;
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ int callback_count_;
+ FakeContentLayerClient client_;
+ scoped_refptr<FakeContentLayer> root_;
+ scoped_refptr<FakeContentLayer> main_destroyed_;
+ scoped_refptr<FakeContentLayer> impl_destroyed_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCopyRequestTestLayerDestroyed);
+
+class LayerTreeHostCopyRequestTestInHiddenSubtree
+ : public LayerTreeHostCopyRequestTest {
+ 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_);
+ LayerTreeHostCopyRequestTest::SetupTree();
+ }
+
+ void AddCopyRequest(Layer* layer) {
+ layer->RequestCopyOfOutput(
+ CopyOutputRequest::CreateBitmapRequest(base::Bind(
+ &LayerTreeHostCopyRequestTestInHiddenSubtree::CopyOutputCallback,
+ base::Unretained(this))));
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ callback_count_ = 0;
+ PostSetNeedsCommitToMainThread();
+
+ AddCopyRequest(copy_layer_.get());
+ }
+
+ void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) {
+ ++callback_count_;
+ 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_;
+};
+
+SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(
+ LayerTreeHostCopyRequestTestInHiddenSubtree);
+
+class LayerTreeHostTestHiddenSurfaceNotAllocatedForSubtreeCopyRequest
+ : public LayerTreeHostCopyRequestTest {
+ 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));
+ 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_);
+ LayerTreeHostCopyRequestTest::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->HasAllocatedResourcesForTesting(
+ parent->render_surface()->RenderPassId()));
+
+ // |copy_layer| should have been rendered to a texture since it was needed
+ // for a copy request.
+ EXPECT_TRUE(renderer->HasAllocatedResourcesForTesting(
+ 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 LayerTreeHostCopyRequestTestClippedOut
+ : public LayerTreeHostCopyRequestTest {
+ 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_);
+ LayerTreeHostCopyRequestTest::SetupTree();
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ PostSetNeedsCommitToMainThread();
+
+ copy_layer_->RequestCopyOfOutput(CopyOutputRequest::CreateBitmapRequest(
+ base::Bind(&LayerTreeHostCopyRequestTestClippedOut::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_;
+};
+
+SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
+ LayerTreeHostCopyRequestTestClippedOut);
+
+class LayerTreeHostTestAsyncTwoReadbacksWithoutDraw
+ : public LayerTreeHostCopyRequestTest {
+ protected:
+ virtual void SetupTree() OVERRIDE {
+ root_ = FakeContentLayer::Create(&client_);
+ root_->SetBounds(gfx::Size(20, 20));
+
+ copy_layer_ = FakeContentLayer::Create(&client_);
+ copy_layer_->SetBounds(gfx::Size(10, 10));
+ root_->AddChild(copy_layer_);
+
+ layer_tree_host()->SetRootLayer(root_);
+ LayerTreeHostCopyRequestTest::SetupTree();
+ }
+
+ void AddCopyRequest(Layer* layer) {
+ layer->RequestCopyOfOutput(
+ CopyOutputRequest::CreateBitmapRequest(base::Bind(
+ &LayerTreeHostTestAsyncTwoReadbacksWithoutDraw::CopyOutputCallback,
+ base::Unretained(this))));
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ saw_copy_request_ = false;
+ callback_count_ = 0;
+ PostSetNeedsCommitToMainThread();
+
+ // Prevent drawing.
+ layer_tree_host()->SetViewportSize(gfx::Size(0, 0));
+
+ AddCopyRequest(copy_layer_.get());
+ }
+
+ virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ if (impl->active_tree()->source_frame_number() == 0) {
+ LayerImpl* root = impl->active_tree()->root_layer();
+ EXPECT_TRUE(root->children()[0]->HasCopyRequest());
+ saw_copy_request_ = true;
+ }
+ }
+
+ virtual void DidCommit() OVERRIDE {
+ if (layer_tree_host()->source_frame_number() == 1) {
+ // Allow drawing.
+ layer_tree_host()->SetViewportSize(gfx::Size(root_->bounds()));
+
+ 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_;
+
+ if (callback_count_ == 2)
+ EndTest();
+ }
+
+ virtual void AfterTest() OVERRIDE { EXPECT_TRUE(saw_copy_request_); }
+
+ bool saw_copy_request_;
+ int callback_count_;
+ FakeContentLayerClient client_;
+ scoped_refptr<FakeContentLayer> root_;
+ scoped_refptr<FakeContentLayer> copy_layer_;
+};
+
+SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(
+ LayerTreeHostTestAsyncTwoReadbacksWithoutDraw);
+
+class LayerTreeHostCopyRequestTestLostOutputSurface
+ : public LayerTreeHostCopyRequestTest {
+ protected:
+ virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback)
+ OVERRIDE {
+ if (!first_context_provider_.get()) {
+ first_context_provider_ = TestContextProvider::Create();
+ return FakeOutputSurface::Create3d(first_context_provider_)
+ .PassAs<OutputSurface>();
+ }
+
+ EXPECT_FALSE(second_context_provider_.get());
+ second_context_provider_ = TestContextProvider::Create();
+ return FakeOutputSurface::Create3d(second_context_provider_)
+ .PassAs<OutputSurface>();
+ }
+
+ virtual void SetupTree() OVERRIDE {
+ root_ = FakeContentLayer::Create(&client_);
+ root_->SetBounds(gfx::Size(20, 20));
+
+ copy_layer_ = FakeContentLayer::Create(&client_);
+ copy_layer_->SetBounds(gfx::Size(10, 10));
+ root_->AddChild(copy_layer_);
+
+ layer_tree_host()->SetRootLayer(root_);
+ LayerTreeHostCopyRequestTest::SetupTree();
+ }
+
+ virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); }
+
+ void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) {
+ EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread());
+ EXPECT_EQ(gfx::Size(10, 10).ToString(), result->size().ToString());
+ EXPECT_TRUE(result->HasTexture());
+
+ // Save the result for later.
+ EXPECT_FALSE(result_);
+ result_ = result.Pass();
+
+ // Post a commit to lose the output surface.
+ layer_tree_host()->SetNeedsCommit();
+ }
+
+ virtual void DidCommitAndDrawFrame() OVERRIDE {
+ switch (layer_tree_host()->source_frame_number()) {
+ case 1:
+ // The layers have been pushed to the impl side. The layer textures have
+ // been allocated.
+
+ // Request a copy of the layer. This will use another texture.
+ copy_layer_->RequestCopyOfOutput(CopyOutputRequest::CreateRequest(
+ base::Bind(&LayerTreeHostCopyRequestTestLostOutputSurface::
+ CopyOutputCallback,
+ base::Unretained(this))));
+ break;
+ case 4:
+ // With SingleThreadProxy it takes two commits to finally swap after a
+ // context loss.
+ case 5:
+ // Now destroy the CopyOutputResult, releasing the texture inside back
+ // to the compositor.
+ EXPECT_TRUE(result_);
+ result_.reset();
+
+ // Check that it is released.
+ ImplThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::Bind(&LayerTreeHostCopyRequestTestLostOutputSurface::
+ CheckNumTextures,
+ base::Unretained(this),
+ num_textures_after_loss_ - 1));
+ break;
+ }
+ }
+
+ virtual void SwapBuffersOnThread(LayerTreeHostImpl* impl,
+ bool result) OVERRIDE {
+ switch (impl->active_tree()->source_frame_number()) {
+ case 0:
+ // The layers have been drawn, so their textures have been allocated.
+ EXPECT_FALSE(result_);
+ num_textures_without_readback_ =
+ first_context_provider_->TestContext3d()->NumTextures();
+ break;
+ case 1:
+ // We did a readback, so there will be a readback texture around now.
+ EXPECT_LT(num_textures_without_readback_,
+ first_context_provider_->TestContext3d()->NumTextures());
+ break;
+ case 2:
+ // The readback texture is collected.
+ EXPECT_TRUE(result_);
+
+ // Lose the output surface.
+ first_context_provider_->TestContext3d()->loseContextCHROMIUM(
+ GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB);
+ break;
+ case 3:
+ // With SingleThreadProxy it takes two commits to finally swap after a
+ // context loss.
+ case 4:
+ // The output surface has been recreated.
+ EXPECT_TRUE(second_context_provider_.get());
+
+ num_textures_after_loss_ =
+ first_context_provider_->TestContext3d()->NumTextures();
+ break;
+ }
+ }
+
+ void CheckNumTextures(size_t expected_num_textures) {
+ EXPECT_EQ(expected_num_textures,
+ first_context_provider_->TestContext3d()->NumTextures());
+ EndTest();
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ scoped_refptr<TestContextProvider> first_context_provider_;
+ scoped_refptr<TestContextProvider> second_context_provider_;
+ size_t num_textures_without_readback_;
+ size_t num_textures_after_loss_;
+ FakeContentLayerClient client_;
+ scoped_refptr<FakeContentLayer> root_;
+ scoped_refptr<FakeContentLayer> copy_layer_;
+ scoped_ptr<CopyOutputResult> result_;
+};
+
+SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(
+ LayerTreeHostCopyRequestTestLostOutputSurface);
+
+class LayerTreeHostCopyRequestTestCountTextures
+ : public LayerTreeHostCopyRequestTest {
+ protected:
+ virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback)
+ OVERRIDE {
+ context_provider_ = TestContextProvider::Create();
+ return FakeOutputSurface::Create3d(context_provider_)
+ .PassAs<OutputSurface>();
+ }
+
+ virtual void SetupTree() OVERRIDE {
+ root_ = FakeContentLayer::Create(&client_);
+ root_->SetBounds(gfx::Size(20, 20));
+
+ copy_layer_ = FakeContentLayer::Create(&client_);
+ copy_layer_->SetBounds(gfx::Size(10, 10));
+ root_->AddChild(copy_layer_);
+
+ layer_tree_host()->SetRootLayer(root_);
+ LayerTreeHostCopyRequestTest::SetupTree();
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ num_textures_without_readback_ = 0;
+ num_textures_with_readback_ = 0;
+ waited_sync_point_after_readback_ = 0;
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void RequestCopy(Layer* layer) = 0;
+
+ virtual void DidCommitAndDrawFrame() OVERRIDE {
+ switch (layer_tree_host()->source_frame_number()) {
+ case 1:
+ // The layers have been pushed to the impl side. The layer textures have
+ // been allocated.
+ RequestCopy(copy_layer_.get());
+ break;
+ }
+ }
+
+ virtual void SwapBuffersOnThread(LayerTreeHostImpl* impl,
+ bool result) OVERRIDE {
+ switch (impl->active_tree()->source_frame_number()) {
+ case 0:
+ // The layers have been drawn, so their textures have been allocated.
+ num_textures_without_readback_ =
+ context_provider_->TestContext3d()->NumTextures();
+ break;
+ case 1:
+ // We did a readback, so there will be a readback texture around now.
+ num_textures_with_readback_ =
+ context_provider_->TestContext3d()->NumTextures();
+ waited_sync_point_after_readback_ =
+ context_provider_->TestContext3d()->last_waited_sync_point();
+
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::Bind(&LayerTreeHostCopyRequestTestCountTextures::DoEndTest,
+ base::Unretained(this)));
+ break;
+ }
+ }
+
+ virtual void DoEndTest() { EndTest(); }
+
+ scoped_refptr<TestContextProvider> context_provider_;
+ size_t num_textures_without_readback_;
+ size_t num_textures_with_readback_;
+ unsigned waited_sync_point_after_readback_;
+ FakeContentLayerClient client_;
+ scoped_refptr<FakeContentLayer> root_;
+ scoped_refptr<FakeContentLayer> copy_layer_;
+};
+
+class LayerTreeHostCopyRequestTestCreatesTexture
+ : public LayerTreeHostCopyRequestTestCountTextures {
+ protected:
+ virtual void RequestCopy(Layer* layer) OVERRIDE {
+ // Request a normal texture copy. This should create a new texture.
+ copy_layer_->RequestCopyOfOutput(
+ CopyOutputRequest::CreateRequest(base::Bind(
+ &LayerTreeHostCopyRequestTestCreatesTexture::CopyOutputCallback,
+ base::Unretained(this))));
+ }
+
+ void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) {
+ EXPECT_FALSE(result->IsEmpty());
+ EXPECT_TRUE(result->HasTexture());
+
+ TextureMailbox mailbox;
+ scoped_ptr<SingleReleaseCallback> release;
+ result->TakeTexture(&mailbox, &release);
+ EXPECT_TRUE(release);
+
+ release->Run(0, false);
+ }
+
+ virtual void AfterTest() OVERRIDE {
+ // No sync point was needed.
+ EXPECT_EQ(0u, waited_sync_point_after_readback_);
+ // Except the copy to have made another texture.
+ EXPECT_EQ(num_textures_without_readback_ + 1, num_textures_with_readback_);
+ }
+};
+
+SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(
+ LayerTreeHostCopyRequestTestCreatesTexture);
+
+class LayerTreeHostCopyRequestTestProvideTexture
+ : public LayerTreeHostCopyRequestTestCountTextures {
+ protected:
+ virtual void BeginTest() OVERRIDE {
+ external_context_provider_ = TestContextProvider::Create();
+ EXPECT_TRUE(external_context_provider_->BindToCurrentThread());
+ LayerTreeHostCopyRequestTestCountTextures::BeginTest();
+ }
+
+ void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) {
+ EXPECT_FALSE(result->IsEmpty());
+ EXPECT_TRUE(result->HasTexture());
+
+ TextureMailbox mailbox;
+ scoped_ptr<SingleReleaseCallback> release;
+ result->TakeTexture(&mailbox, &release);
+ EXPECT_FALSE(release);
+ }
+
+ virtual void RequestCopy(Layer* layer) OVERRIDE {
+ // Request a copy to a provided texture. This should not create a new
+ // texture.
+ scoped_ptr<CopyOutputRequest> request =
+ CopyOutputRequest::CreateRequest(base::Bind(
+ &LayerTreeHostCopyRequestTestProvideTexture::CopyOutputCallback,
+ base::Unretained(this)));
+
+ gpu::Mailbox mailbox;
+ external_context_provider_->Context3d()->genMailboxCHROMIUM(mailbox.name);
+ sync_point_ = external_context_provider_->Context3d()->insertSyncPoint();
+ request->SetTextureMailbox(TextureMailbox(mailbox, sync_point_));
+ EXPECT_TRUE(request->has_texture_mailbox());
+
+ copy_layer_->RequestCopyOfOutput(request.Pass());
+ }
+
+ virtual void AfterTest() OVERRIDE {
+ // Expect the compositor to have waited for the sync point in the provided
+ // TextureMailbox.
+ EXPECT_EQ(sync_point_, waited_sync_point_after_readback_);
+ // Except the copy to have *not* made another texture.
+ EXPECT_EQ(num_textures_without_readback_, num_textures_with_readback_);
+ }
+
+ scoped_refptr<TestContextProvider> external_context_provider_;
+ unsigned sync_point_;
+};
+
+SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_NOIMPL_TEST_F(
+ LayerTreeHostCopyRequestTestProvideTexture);
+
+} // namespace
+} // namespace cc