diff options
-rw-r--r-- | cc/cc_tests.gyp | 3 | ||||
-rw-r--r-- | cc/output/copy_output_request.cc | 25 | ||||
-rw-r--r-- | cc/output/copy_output_request.h | 23 | ||||
-rw-r--r-- | cc/output/gl_renderer.cc | 70 | ||||
-rw-r--r-- | cc/test/test_web_graphics_context_3d.cc | 11 | ||||
-rw-r--r-- | cc/test/test_web_graphics_context_3d.h | 7 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_unittest.cc | 654 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_unittest_copyrequest.cc | 831 |
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 |