diff options
Diffstat (limited to 'chromium/cc/layers/texture_layer_unittest.cc')
-rw-r--r-- | chromium/cc/layers/texture_layer_unittest.cc | 1120 |
1 files changed, 1036 insertions, 84 deletions
diff --git a/chromium/cc/layers/texture_layer_unittest.cc b/chromium/cc/layers/texture_layer_unittest.cc index aa08e9f81ee..239c5b8fa22 100644 --- a/chromium/cc/layers/texture_layer_unittest.cc +++ b/chromium/cc/layers/texture_layer_unittest.cc @@ -4,16 +4,28 @@ #include "cc/layers/texture_layer.h" +#include <algorithm> #include <string> +#include "base/bind.h" #include "base/callback.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "base/time/time.h" +#include "cc/debug/test_web_graphics_context_3d.h" +#include "cc/layers/solid_color_layer.h" #include "cc/layers/texture_layer_client.h" #include "cc/layers/texture_layer_impl.h" +#include "cc/output/compositor_frame_ack.h" +#include "cc/output/context_provider.h" +#include "cc/resources/returned_resource.h" #include "cc/test/fake_impl_proxy.h" #include "cc/test/fake_layer_tree_host_client.h" #include "cc/test/fake_layer_tree_host_impl.h" +#include "cc/test/fake_output_surface.h" #include "cc/test/layer_test_common.h" #include "cc/test/layer_tree_test.h" +#include "cc/trees/blocking_task_runner.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/single_thread_proxy.h" @@ -187,7 +199,7 @@ TEST_F(TextureLayerTest, SyncImplWhenRemovingFromTree) { TEST_F(TextureLayerTest, CheckPropertyChangeCausesCorrectBehavior) { scoped_refptr<TextureLayer> test_layer = TextureLayer::Create(NULL); - layer_tree_host_->SetRootLayer(test_layer); + EXPECT_SET_NEEDS_COMMIT(1, layer_tree_host_->SetRootLayer(test_layer)); // Test properties that should call SetNeedsCommit. All properties need to // be set to new values in order for SetNeedsCommit to be called. @@ -243,9 +255,12 @@ class FakeTextureLayerClient : public TextureLayerClient { return context_.get(); } - virtual bool PrepareTextureMailbox(TextureMailbox* mailbox, - bool use_shared_memory) OVERRIDE { + virtual bool PrepareTextureMailbox( + TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { *mailbox = TextureMailbox(); + *release_callback = scoped_ptr<SingleReleaseCallback>(); return true; } @@ -328,25 +343,25 @@ struct CommonMailboxObjects { mailbox_name2_); gpu::Mailbox m1; m1.SetName(reinterpret_cast<const int8*>(mailbox_name1_.data())); - mailbox1_ = TextureMailbox(m1, release_mailbox1_, sync_point1_); + mailbox1_ = TextureMailbox(m1, sync_point1_); gpu::Mailbox m2; m2.SetName(reinterpret_cast<const int8*>(mailbox_name2_.data())); - mailbox2_ = TextureMailbox(m2, release_mailbox2_, sync_point2_); + mailbox2_ = TextureMailbox(m2, sync_point2_); gfx::Size size(128, 128); EXPECT_TRUE(shared_memory_->CreateAndMapAnonymous(4 * size.GetArea())); release_mailbox3_ = base::Bind(&MockMailboxCallback::Release2, base::Unretained(&mock_callback_), shared_memory_.get()); - mailbox3_ = TextureMailbox(shared_memory_.get(), size, release_mailbox3_); + mailbox3_ = TextureMailbox(shared_memory_.get(), size); } std::string mailbox_name1_; std::string mailbox_name2_; MockMailboxCallback mock_callback_; - TextureMailbox::ReleaseCallback release_mailbox1_; - TextureMailbox::ReleaseCallback release_mailbox2_; - TextureMailbox::ReleaseCallback release_mailbox3_; + ReleaseCallback release_mailbox1_; + ReleaseCallback release_mailbox2_; + ReleaseCallback release_mailbox3_; TextureMailbox mailbox1_; TextureMailbox mailbox2_; TextureMailbox mailbox3_; @@ -355,6 +370,14 @@ struct CommonMailboxObjects { scoped_ptr<base::SharedMemory> shared_memory_; }; +class TestMailboxHolder : public TextureLayer::MailboxHolder { + public: + using TextureLayer::MailboxHolder::Create; + + protected: + virtual ~TestMailboxHolder() {} +}; + class TextureLayerWithMailboxTest : public TextureLayerTest { protected: virtual void TearDown() { @@ -380,7 +403,9 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) { EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0); EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1)); - test_layer->SetTextureMailbox(test_data_.mailbox1_); + test_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0); @@ -390,7 +415,9 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) { test_data_.sync_point1_, false)) .Times(1); - test_layer->SetTextureMailbox(test_data_.mailbox2_); + test_layer->SetTextureMailbox( + test_data_.mailbox2_, + SingleReleaseCallback::Create(test_data_.release_mailbox2_)); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); @@ -401,11 +428,16 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) { test_data_.sync_point2_, false)) .Times(1); - test_layer->SetTextureMailbox(TextureMailbox()); + test_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); - test_layer->SetTextureMailbox(test_data_.mailbox3_); + EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0); + EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1)); + test_layer->SetTextureMailbox( + test_data_.mailbox3_, + SingleReleaseCallback::Create(test_data_.release_mailbox3_)); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); @@ -415,13 +447,309 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) { Release2(test_data_.shared_memory_.get(), 0, false)) .Times(1); - test_layer->SetTextureMailbox(TextureMailbox()); + test_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); Mock::VerifyAndClearExpectations(layer_tree_host_.get()); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); // Test destructor. EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1)); - test_layer->SetTextureMailbox(test_data_.mailbox1_); + test_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); +} + +class TextureLayerMailboxHolderTest : public TextureLayerTest { + public: + TextureLayerMailboxHolderTest() + : main_thread_("MAIN") { + main_thread_.Start(); + } + + void Wait(const base::Thread& thread) { + bool manual_reset = false; + bool initially_signaled = false; + base::WaitableEvent event(manual_reset, initially_signaled); + thread.message_loop()->PostTask( + FROM_HERE, + base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event))); + event.Wait(); + } + + void CreateMainRef() { + main_ref_ = TestMailboxHolder::Create( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)).Pass(); + } + + void ReleaseMainRef() { + main_ref_.reset(); + } + + void CreateImplRef(scoped_ptr<SingleReleaseCallback>* impl_ref) { + *impl_ref = main_ref_->holder()->GetCallbackForImplThread(); + } + + void CapturePostTasksAndWait(base::WaitableEvent* begin_capture, + base::WaitableEvent* wait_for_capture, + base::WaitableEvent* stop_capture) { + begin_capture->Wait(); + BlockingTaskRunner::CapturePostTasks capture; + wait_for_capture->Signal(); + stop_capture->Wait(); + } + + protected: + scoped_ptr<TestMailboxHolder::MainThreadReference> + main_ref_; + base::Thread main_thread_; + CommonMailboxObjects test_data_; +}; + +TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_BothReleaseThenMain) { + scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL); + ASSERT_TRUE(test_layer.get()); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + // The texture layer is attached to compositor1, and passes a reference to its + // impl tree. + scoped_ptr<SingleReleaseCallback> compositor1; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor1)); + + // Then the texture layer is removed and attached to compositor2, and passes a + // reference to its impl tree. + scoped_ptr<SingleReleaseCallback> compositor2; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor2)); + + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The compositors both destroy their impl trees before the main thread layer + // is destroyed. + compositor1->Run(100, false); + compositor2->Run(200, false); + + Wait(main_thread_); + + EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The main thread ref is the last one, so the mailbox is released back to the + // embedder, with the last sync point provided by the impl trees. + EXPECT_CALL(test_data_.mock_callback_, + Release(test_data_.mailbox_name1_, 200, false)).Times(1); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef, + base::Unretained(this))); + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); +} + +TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_MainReleaseBetween) { + scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL); + ASSERT_TRUE(test_layer.get()); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + // The texture layer is attached to compositor1, and passes a reference to its + // impl tree. + scoped_ptr<SingleReleaseCallback> compositor1; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor1)); + + // Then the texture layer is removed and attached to compositor2, and passes a + // reference to its impl tree. + scoped_ptr<SingleReleaseCallback> compositor2; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor2)); + + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // One compositor destroys their impl tree. + compositor1->Run(100, false); + + // Then the main thread reference is destroyed. + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The second impl reference is destroyed last, causing the mailbox to be + // released back to the embedder with the last sync point from the impl tree. + EXPECT_CALL(test_data_.mock_callback_, + Release(test_data_.mailbox_name1_, 200, true)).Times(1); + + compositor2->Run(200, true); + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); +} + +TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_MainReleasedFirst) { + scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL); + ASSERT_TRUE(test_layer.get()); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + // The texture layer is attached to compositor1, and passes a reference to its + // impl tree. + scoped_ptr<SingleReleaseCallback> compositor1; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor1)); + + // Then the texture layer is removed and attached to compositor2, and passes a + // reference to its impl tree. + scoped_ptr<SingleReleaseCallback> compositor2; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor2)); + + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The main thread reference is destroyed first. + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef, + base::Unretained(this))); + + // One compositor destroys their impl tree. + compositor2->Run(200, false); + + Wait(main_thread_); + + EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The second impl reference is destroyed last, causing the mailbox to be + // released back to the embedder with the last sync point from the impl tree. + EXPECT_CALL(test_data_.mock_callback_, + Release(test_data_.mailbox_name1_, 100, true)).Times(1); + + compositor1->Run(100, true); + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); +} + +TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_SecondImplRefShortcut) { + scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL); + ASSERT_TRUE(test_layer.get()); + + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef, + base::Unretained(this))); + + Wait(main_thread_); + + // The texture layer is attached to compositor1, and passes a reference to its + // impl tree. + scoped_ptr<SingleReleaseCallback> compositor1; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor1)); + + // Then the texture layer is removed and attached to compositor2, and passes a + // reference to its impl tree. + scoped_ptr<SingleReleaseCallback> compositor2; + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef, + base::Unretained(this), + &compositor2)); + + Wait(main_thread_); + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); + + // The main thread reference is destroyed first. + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef, + base::Unretained(this))); + + EXPECT_CALL(test_data_.mock_callback_, + Release(test_data_.mailbox_name1_, 200, true)).Times(1); + + bool manual_reset = false; + bool initially_signaled = false; + base::WaitableEvent begin_capture(manual_reset, initially_signaled); + base::WaitableEvent wait_for_capture(manual_reset, initially_signaled); + base::WaitableEvent stop_capture(manual_reset, initially_signaled); + + // Post a task to start capturing tasks on the main thread. This will block + // the main thread until we signal the |stop_capture| event. + main_thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxHolderTest::CapturePostTasksAndWait, + base::Unretained(this), + &begin_capture, + &wait_for_capture, + &stop_capture)); + + // Before the main thread capturing starts, one compositor destroys their + // impl reference. Since capturing did not start, this gets post-tasked to + // the main thread. + compositor1->Run(100, false); + + // Start capturing on the main thread. + begin_capture.Signal(); + wait_for_capture.Wait(); + + // Meanwhile, the second compositor released its impl reference, but this task + // gets shortcutted directly to the main thread. This means the reference is + // released before compositor1, whose reference will be released later when + // the post-task is serviced. But since it was destroyed _on the impl thread_ + // last, its sync point values should be used. + compositor2->Run(200, true); + + stop_capture.Signal(); + Wait(main_thread_); + + Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); } class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { @@ -432,21 +760,24 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { // Make sure callback is received on main and doesn't block the impl thread. void ReleaseCallback(unsigned sync_point, bool lost_resource) { - EXPECT_EQ(true, proxy()->IsMainThread()); + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); EXPECT_FALSE(lost_resource); ++callback_count_; } void SetMailbox(char mailbox_char) { - TextureMailbox mailbox( - std::string(64, mailbox_char), + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + TextureMailbox mailbox(std::string(64, mailbox_char)); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( base::Bind( &TextureLayerImplWithMailboxThreadedCallback::ReleaseCallback, base::Unretained(this))); - layer_->SetTextureMailbox(mailbox); + layer_->SetTextureMailbox(mailbox, callback.Pass()); } virtual void BeginTest() OVERRIDE { + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + gfx::Size bounds(100, 100); root_ = Layer::Create(); root_->SetAnchorPoint(gfx::PointF()); @@ -481,61 +812,58 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { EXPECT_EQ(1, callback_count_); break; case 2: - // Old mailbox was released, task was posted, but won't execute - // until this DidCommit returns. - // TODO(piman): fix this. - EXPECT_EQ(1, callback_count_); - layer_tree_host()->SetNeedsCommit(); - break; - case 3: EXPECT_EQ(2, callback_count_); // Case #3: change mailbox when the layer doesn't draw. The old // mailbox should be released during the next commit. layer_->SetBounds(gfx::Size()); SetMailbox('4'); break; - case 4: - // Old mailbox was released, task was posted, but won't execute - // until this DidCommit returns. - // TODO(piman): fix this. - EXPECT_EQ(2, callback_count_); - layer_tree_host()->SetNeedsCommit(); - break; - case 5: + case 3: EXPECT_EQ(3, callback_count_); // Case #4: release mailbox that was committed but never drawn. The // old mailbox should be released during the next commit. - layer_->SetTextureMailbox(TextureMailbox()); - break; - case 6: - // Old mailbox was released, task was posted, but won't execute - // until this DidCommit returns. - // TODO(piman): fix this. - EXPECT_EQ(3, callback_count_); - layer_tree_host()->SetNeedsCommit(); + layer_->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); break; - case 7: + case 4: + if (layer_tree_host()->settings().impl_side_painting) { + // With impl painting, the texture mailbox will still be on the impl + // thread when the commit finishes, because the layer is not drawble + // when it has no texture mailbox, and thus does not block the commit + // on activation. So, we wait for activation. + // TODO(danakj): fix this. crbug.com/277953 + layer_tree_host()->SetNeedsCommit(); + break; + } else { + ++commit_count_; + } + case 5: EXPECT_EQ(4, callback_count_); // Restore a mailbox for the next step. SetMailbox('5'); break; - case 8: + case 6: // Case #5: remove layer from tree. Callback should *not* be called, the // mailbox is returned to the main thread. EXPECT_EQ(4, callback_count_); layer_->RemoveFromParent(); break; - case 9: - // Mailbox was released to the main thread, task was posted, but won't - // execute until this DidCommit returns. - // TODO(piman): fix this. - EXPECT_EQ(4, callback_count_); - layer_tree_host()->SetNeedsCommit(); - break; - case 10: + case 7: + if (layer_tree_host()->settings().impl_side_painting) { + // With impl painting, the texture mailbox will still be on the impl + // thread when the commit finishes, because the layer is not around to + // block the commit on activation anymore. So, we wait for activation. + // TODO(danakj): fix this. crbug.com/277953 + layer_tree_host()->SetNeedsCommit(); + break; + } else { + ++commit_count_; + } + case 8: EXPECT_EQ(4, callback_count_); // Resetting the mailbox will call the callback now. - layer_->SetTextureMailbox(TextureMailbox()); + layer_->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); EXPECT_EQ(5, callback_count_); EndTest(); break; @@ -548,6 +876,7 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { virtual void AfterTest() OVERRIDE {} private: + base::ThreadChecker main_thread_; int callback_count_; int commit_count_; scoped_refptr<Layer> root_; @@ -557,6 +886,247 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest { SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( TextureLayerImplWithMailboxThreadedCallback); + +class TextureLayerNoMailboxIsActivatedDuringCommit : public LayerTreeTest, + public TextureLayerClient { + protected: + TextureLayerNoMailboxIsActivatedDuringCommit() + : wait_thread_("WAIT"), + wait_event_(false, false) { + wait_thread_.Start(); + } + + virtual void BeginTest() OVERRIDE { + activate_count_ = 0; + + gfx::Size bounds(100, 100); + root_ = Layer::Create(); + root_->SetAnchorPoint(gfx::PointF()); + root_->SetBounds(bounds); + + layer_ = TextureLayer::Create(this); + layer_->SetIsDrawable(true); + layer_->SetAnchorPoint(gfx::PointF()); + layer_->SetBounds(bounds); + + root_->AddChild(layer_); + layer_tree_host()->SetRootLayer(root_); + layer_tree_host()->SetViewportSize(bounds); + + PostSetNeedsCommitToMainThread(); + } + + // TextureLayerClient implementation. + virtual unsigned PrepareTexture() OVERRIDE { + return OffscreenContextProviderForMainThread() + ->Context3d()->createTexture(); + } + virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE { + return OffscreenContextProviderForMainThread()->Context3d(); + } + virtual bool PrepareTextureMailbox( + TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { + return false; + } + + virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + // Slow down activation so the main thread DidCommit() will run if + // not blocked. + wait_thread_.message_loop()->PostDelayedTask( + FROM_HERE, + base::Bind(&base::WaitableEvent::Signal, + base::Unretained(&wait_event_)), + base::TimeDelta::FromMilliseconds(10)); + wait_event_.Wait(); + + base::AutoLock lock(activate_lock_); + ++activate_count_; + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + // The main thread is awake now, and will run DidCommit() immediately. + // Run DidActivate() afterwards by posting it now. + proxy()->MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerNoMailboxIsActivatedDuringCommit::DidActivate, + base::Unretained(this))); + } + + void DidActivate() { + base::AutoLock lock(activate_lock_); + switch (activate_count_) { + case 1: + // The first texture has been activated. Invalidate the layer so it + // grabs a new texture id from the client. + layer_->SetNeedsDisplay(); + // So this commit number should complete after the second activate. + EXPECT_EQ(1, layer_tree_host()->source_frame_number()); + break; + case 2: + // The second mailbox has been activated. Remove the layer from + // the tree to cause another commit/activation. The commit should + // finish *after* the layer is removed from the active tree. + layer_->RemoveFromParent(); + // So this commit number should complete after the third activate. + EXPECT_EQ(2, layer_tree_host()->source_frame_number()); + break; + case 3: + EndTest(); + break; + } + } + + virtual void DidCommit() OVERRIDE { + switch (layer_tree_host()->source_frame_number()) { + case 2: { + // The activate for the 2nd texture should have happened before now. + base::AutoLock lock(activate_lock_); + EXPECT_EQ(2, activate_count_); + break; + } + case 3: { + // The activate to remove the layer should have happened before now. + base::AutoLock lock(activate_lock_); + EXPECT_EQ(3, activate_count_); + break; + } + } + } + + + virtual void AfterTest() OVERRIDE {} + + base::Thread wait_thread_; + base::WaitableEvent wait_event_; + base::Lock activate_lock_; + int activate_count_; + int activate_commit_; + scoped_refptr<Layer> root_; + scoped_refptr<TextureLayer> layer_; +}; + +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + TextureLayerNoMailboxIsActivatedDuringCommit); + +class TextureLayerMailboxIsActivatedDuringCommit : public LayerTreeTest { + protected: + TextureLayerMailboxIsActivatedDuringCommit() + : wait_thread_("WAIT"), + wait_event_(false, false) { + wait_thread_.Start(); + } + + static void ReleaseCallback(unsigned sync_point, bool lost_resource) {} + + void SetMailbox(char mailbox_char) { + TextureMailbox mailbox(std::string(64, mailbox_char)); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( + base::Bind( + &TextureLayerMailboxIsActivatedDuringCommit::ReleaseCallback)); + layer_->SetTextureMailbox(mailbox, callback.Pass()); + } + + virtual void BeginTest() OVERRIDE { + activate_count_ = 0; + + gfx::Size bounds(100, 100); + root_ = Layer::Create(); + root_->SetAnchorPoint(gfx::PointF()); + root_->SetBounds(bounds); + + layer_ = TextureLayer::CreateForMailbox(NULL); + layer_->SetIsDrawable(true); + layer_->SetAnchorPoint(gfx::PointF()); + layer_->SetBounds(bounds); + + root_->AddChild(layer_); + layer_tree_host()->SetRootLayer(root_); + layer_tree_host()->SetViewportSize(bounds); + SetMailbox('1'); + + PostSetNeedsCommitToMainThread(); + } + + virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + // Slow down activation so the main thread DidCommit() will run if + // not blocked. + wait_thread_.message_loop()->PostDelayedTask( + FROM_HERE, + base::Bind(&base::WaitableEvent::Signal, + base::Unretained(&wait_event_)), + base::TimeDelta::FromMilliseconds(10)); + wait_event_.Wait(); + + base::AutoLock lock(activate_lock_); + ++activate_count_; + } + + virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { + // The main thread is awake now, and will run DidCommit() immediately. + // Run DidActivate() afterwards by posting it now. + proxy()->MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&TextureLayerMailboxIsActivatedDuringCommit::DidActivate, + base::Unretained(this))); + } + + void DidActivate() { + base::AutoLock lock(activate_lock_); + switch (activate_count_) { + case 1: + // The first mailbox has been activated. Set a new mailbox, and + // expect the next commit to finish *after* it is activated. + SetMailbox('2'); + // So this commit number should complete after the second activate. + EXPECT_EQ(1, layer_tree_host()->source_frame_number()); + break; + case 2: + // The second mailbox has been activated. Remove the layer from + // the tree to cause another commit/activation. The commit should + // finish *after* the layer is removed from the active tree. + layer_->RemoveFromParent(); + // So this commit number should complete after the third activate. + EXPECT_EQ(2, layer_tree_host()->source_frame_number()); + break; + case 3: + EndTest(); + break; + } + } + + virtual void DidCommit() OVERRIDE { + switch (layer_tree_host()->source_frame_number()) { + case 2: { + // The activate for the 2nd mailbox should have happened before now. + base::AutoLock lock(activate_lock_); + EXPECT_EQ(2, activate_count_); + break; + } + case 3: { + // The activate to remove the layer should have happened before now. + base::AutoLock lock(activate_lock_); + EXPECT_EQ(3, activate_count_); + break; + } + } + } + + + virtual void AfterTest() OVERRIDE {} + + base::Thread wait_thread_; + base::WaitableEvent wait_event_; + base::Lock activate_lock_; + int activate_count_; + scoped_refptr<Layer> root_; + scoped_refptr<TextureLayer> layer_; +}; + +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( + TextureLayerMailboxIsActivatedDuringCommit); + class TextureLayerImplWithMailboxTest : public TextureLayerTest { protected: TextureLayerImplWithMailboxTest() @@ -596,14 +1166,17 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox1_); + impl_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(TextureMailbox()); + impl_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE)); } @@ -611,15 +1184,19 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { // Software resource. scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox3_); + impl_layer->SetTextureMailbox( + test_data_.mailbox3_, + SingleReleaseCallback::Create(test_data_.release_mailbox3_)); EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, false); + ContextProvider* context_provider = + host_impl_.output_surface()->context_provider(); unsigned texture = - host_impl_.output_surface()->context3d()->createTexture(); + context_provider->Context3d()->createTexture(); impl_layer->set_texture_id(texture); EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE)); } @@ -635,14 +1212,17 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox1_); + impl_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(TextureMailbox()); + impl_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE)); } @@ -650,15 +1230,19 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { // Software resource. scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox3_); + impl_layer->SetTextureMailbox( + test_data_.mailbox3_, + SingleReleaseCallback::Create(test_data_.release_mailbox3_)); EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, false); + ContextProvider* context_provider = + host_impl_.output_surface()->context_provider(); unsigned texture = - host_impl_.output_surface()->context3d()->createTexture(); + context_provider->Context3d()->createTexture(); impl_layer->set_texture_id(texture); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE)); } @@ -674,15 +1258,19 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) { { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true); - impl_layer->SetTextureMailbox(test_data_.mailbox1_); + impl_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_RESOURCELESS_SOFTWARE)); } { scoped_ptr<TextureLayerImpl> impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, false); + ContextProvider* context_provider = + host_impl_.output_surface()->context_provider(); unsigned texture = - host_impl_.output_surface()->context3d()->createTexture(); + context_provider->Context3d()->createTexture(); impl_layer->set_texture_id(texture); EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_RESOURCELESS_SOFTWARE)); } @@ -698,7 +1286,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { pending_layer->CreateLayerImpl(host_impl_.active_tree())); ASSERT_TRUE(active_layer); - pending_layer->SetTextureMailbox(test_data_.mailbox1_); + pending_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); // Test multiple commits without an activation. EXPECT_CALL(test_data_.mock_callback_, @@ -706,7 +1296,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { test_data_.sync_point1_, false)) .Times(1); - pending_layer->SetTextureMailbox(test_data_.mailbox2_); + pending_layer->SetTextureMailbox( + test_data_.mailbox2_, + SingleReleaseCallback::Create(test_data_.release_mailbox2_)); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); // Test callback after activation. @@ -714,7 +1306,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { active_layer->DidBecomeActive(); EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0); - pending_layer->SetTextureMailbox(test_data_.mailbox1_); + pending_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); EXPECT_CALL(test_data_.mock_callback_, @@ -728,7 +1322,8 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { EXPECT_CALL(test_data_.mock_callback_, Release(test_data_.mailbox_name1_, _, false)) .Times(1); - pending_layer->SetTextureMailbox(TextureMailbox()); + pending_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); pending_layer->PushPropertiesTo(active_layer.get()); active_layer->DidBecomeActive(); Mock::VerifyAndClearExpectations(&test_data_.mock_callback_); @@ -739,7 +1334,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) { test_data_.sync_point1_, false)) .Times(1); - pending_layer->SetTextureMailbox(test_data_.mailbox1_); + pending_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); } TEST_F(TextureLayerImplWithMailboxTest, @@ -751,18 +1348,23 @@ TEST_F(TextureLayerImplWithMailboxTest, EXPECT_CALL(test_data_.mock_callback_, Release(test_data_.mailbox_name1_, _, false)) .Times(1); - impl_layer->SetTextureMailbox(test_data_.mailbox1_); + impl_layer->SetTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); impl_layer->DidBecomeActive(); EXPECT_TRUE(impl_layer->WillDraw( DRAW_MODE_HARDWARE, host_impl_.active_tree()->resource_provider())); impl_layer->DidDraw(host_impl_.active_tree()->resource_provider()); - impl_layer->SetTextureMailbox(TextureMailbox()); + impl_layer->SetTextureMailbox(TextureMailbox(), + scoped_ptr<SingleReleaseCallback>()); } TEST_F(TextureLayerImplWithMailboxTest, TestCallbackOnInUseResource) { ResourceProvider* provider = host_impl_.active_tree()->resource_provider(); ResourceProvider::ResourceId id = - provider->CreateResourceFromTextureMailbox(test_data_.mailbox1_); + provider->CreateResourceFromTextureMailbox( + test_data_.mailbox1_, + SingleReleaseCallback::Create(test_data_.release_mailbox1_)); provider->AllocateForTesting(id); // Transfer some resources to the parent. @@ -777,7 +1379,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestCallbackOnInUseResource) { EXPECT_CALL(test_data_.mock_callback_, Release(test_data_.mailbox_name1_, _, false)) .Times(1); - provider->ReceiveFromParent(list); + ReturnedResourceArray returned; + TransferableResource::ReturnResources(list, &returned); + provider->ReceiveReturnsFromParent(returned); } // Check that ClearClient correctly clears the state so that the impl side @@ -799,8 +1403,7 @@ class TextureLayerClientTest TestWebGraphicsContext3D::Create()); context_ = context.get(); texture_ = context->createTexture(); - return FakeOutputSurface::Create3d( - context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>(); + return FakeOutputSurface::Create3d(context.Pass()).PassAs<OutputSurface>(); } virtual unsigned PrepareTexture() OVERRIDE { @@ -812,7 +1415,9 @@ class TextureLayerClientTest } virtual bool PrepareTextureMailbox( - cc::TextureMailbox* mailbox, bool use_shared_memory) OVERRIDE { + TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { return false; } @@ -899,6 +1504,343 @@ class TextureLayerClientTest // renderer. SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(TextureLayerClientTest); + +// Checks that changing a texture in the client for a TextureLayer that's +// invisible correctly works without drawing a deleted texture. See +// crbug.com/266628 +class TextureLayerChangeInvisibleTest + : public LayerTreeTest, + public TextureLayerClient { + public: + TextureLayerChangeInvisibleTest() + : client_context_(TestWebGraphicsContext3D::Create()), + texture_(client_context_->createTexture()), + texture_to_delete_on_next_commit_(0), + prepare_called_(0), + commit_count_(0), + expected_texture_on_draw_(0) {} + + // TextureLayerClient implementation. + virtual unsigned PrepareTexture() OVERRIDE { + ++prepare_called_; + return texture_; + } + + // TextureLayerClient implementation. + virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE { + return client_context_.get(); + } + + // TextureLayerClient implementation. + virtual bool PrepareTextureMailbox( + cc::TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { + return false; + } + + virtual void SetupTree() OVERRIDE { + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(gfx::Size(10, 10)); + root->SetAnchorPoint(gfx::PointF()); + root->SetIsDrawable(true); + + solid_layer_ = SolidColorLayer::Create(); + solid_layer_->SetBounds(gfx::Size(10, 10)); + solid_layer_->SetIsDrawable(true); + solid_layer_->SetBackgroundColor(SK_ColorWHITE); + root->AddChild(solid_layer_); + + parent_layer_ = Layer::Create(); + parent_layer_->SetBounds(gfx::Size(10, 10)); + parent_layer_->SetIsDrawable(true); + root->AddChild(parent_layer_); + + texture_layer_ = TextureLayer::Create(this); + texture_layer_->SetBounds(gfx::Size(10, 10)); + texture_layer_->SetAnchorPoint(gfx::PointF()); + texture_layer_->SetIsDrawable(true); + parent_layer_->AddChild(texture_layer_); + + layer_tree_host()->SetRootLayer(root); + LayerTreeTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + ++commit_count_; + switch (commit_count_) { + case 1: + // We should have updated the layer, committing the texture. + EXPECT_EQ(1, prepare_called_); + // Make layer invisible. + parent_layer_->SetOpacity(0.f); + break; + case 2: { + // Layer shouldn't have been updated. + EXPECT_EQ(1, prepare_called_); + // Change the texture. + texture_to_delete_on_next_commit_ = texture_; + texture_ = client_context_->createTexture(); + texture_layer_->SetNeedsDisplay(); + // Force a change to make sure we draw a frame. + solid_layer_->SetBackgroundColor(SK_ColorGRAY); + break; + } + case 3: + EXPECT_EQ(1, prepare_called_); + client_context_->deleteTexture(texture_to_delete_on_next_commit_); + texture_to_delete_on_next_commit_ = 0; + // Make layer visible again. + parent_layer_->SetOpacity(1.f); + break; + case 4: { + // Layer should have been updated. + EXPECT_EQ(2, prepare_called_); + texture_layer_->ClearClient(); + client_context_->deleteTexture(texture_); + texture_ = 0; + break; + } + case 5: + EndTest(); + break; + default: + NOTREACHED(); + break; + } + } + + virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + ASSERT_TRUE(proxy()->IsMainThreadBlocked()); + // This is the only texture that can be drawn this frame. + expected_texture_on_draw_ = texture_; + } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + ContextForImplThread(host_impl)->ResetUsedTextures(); + return true; + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, + bool result) OVERRIDE { + ASSERT_TRUE(result); + TestWebGraphicsContext3D* context = ContextForImplThread(host_impl); + int used_textures = context->NumUsedTextures(); + switch (host_impl->active_tree()->source_frame_number()) { + case 0: + EXPECT_EQ(1, used_textures); + EXPECT_TRUE(context->UsedTexture(expected_texture_on_draw_)); + break; + case 1: + case 2: + EXPECT_EQ(0, used_textures); + break; + case 3: + EXPECT_EQ(1, used_textures); + EXPECT_TRUE(context->UsedTexture(expected_texture_on_draw_)); + break; + default: + break; + } + } + + virtual void AfterTest() OVERRIDE {} + + private: + TestWebGraphicsContext3D* ContextForImplThread(LayerTreeHostImpl* host_impl) { + return static_cast<TestWebGraphicsContext3D*>( + host_impl->output_surface()->context_provider()->Context3d()); + } + + scoped_refptr<SolidColorLayer> solid_layer_; + scoped_refptr<Layer> parent_layer_; + scoped_refptr<TextureLayer> texture_layer_; + scoped_ptr<TestWebGraphicsContext3D> client_context_; + + // Used on the main thread, and on the impl thread while the main thread is + // blocked. + unsigned texture_; + + // Used on the main thread. + unsigned texture_to_delete_on_next_commit_; + int prepare_called_; + int commit_count_; + + // Used on the compositor thread. + unsigned expected_texture_on_draw_; +}; + +// The TextureLayerChangeInvisibleTest does not use mailboxes, so can't use a +// delegating renderer. +SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(TextureLayerChangeInvisibleTest); + +// Checks that changing a mailbox in the client for a TextureLayer that's +// invisible correctly works and uses the new mailbox as soon as the layer +// becomes visible (and returns the old one). +class TextureLayerChangeInvisibleMailboxTest + : public LayerTreeTest, + public TextureLayerClient { + public: + TextureLayerChangeInvisibleMailboxTest() + : mailbox_changed_(true), + mailbox_returned_(0), + prepare_called_(0), + commit_count_(0) { + mailbox_ = MakeMailbox('1'); + } + + // TextureLayerClient implementation. + virtual unsigned PrepareTexture() OVERRIDE { + NOTREACHED(); + return 0; + } + + // TextureLayerClient implementation. + virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE { + NOTREACHED(); + return NULL; + } + + // TextureLayerClient implementation. + virtual bool PrepareTextureMailbox( + cc::TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { + ++prepare_called_; + if (!mailbox_changed_) + return false; + *mailbox = mailbox_; + *release_callback = SingleReleaseCallback::Create( + base::Bind(&TextureLayerChangeInvisibleMailboxTest::MailboxReleased, + base::Unretained(this))); + return true; + } + + TextureMailbox MakeMailbox(char name) { + return TextureMailbox(std::string(64, name)); + } + + void MailboxReleased(unsigned sync_point, bool lost_resource) { + ++mailbox_returned_; + } + + virtual void SetupTree() OVERRIDE { + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(gfx::Size(10, 10)); + root->SetAnchorPoint(gfx::PointF()); + root->SetIsDrawable(true); + + solid_layer_ = SolidColorLayer::Create(); + solid_layer_->SetBounds(gfx::Size(10, 10)); + solid_layer_->SetIsDrawable(true); + solid_layer_->SetBackgroundColor(SK_ColorWHITE); + root->AddChild(solid_layer_); + + parent_layer_ = Layer::Create(); + parent_layer_->SetBounds(gfx::Size(10, 10)); + parent_layer_->SetIsDrawable(true); + root->AddChild(parent_layer_); + + texture_layer_ = TextureLayer::CreateForMailbox(this); + texture_layer_->SetBounds(gfx::Size(10, 10)); + texture_layer_->SetAnchorPoint(gfx::PointF()); + texture_layer_->SetIsDrawable(true); + parent_layer_->AddChild(texture_layer_); + + layer_tree_host()->SetRootLayer(root); + LayerTreeTest::SetupTree(); + } + + virtual void BeginTest() OVERRIDE { + PostSetNeedsCommitToMainThread(); + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + ++commit_count_; + switch (commit_count_) { + case 1: + // We should have updated the layer, committing the texture. + EXPECT_EQ(1, prepare_called_); + // Make layer invisible. + parent_layer_->SetOpacity(0.f); + break; + case 2: + // Layer shouldn't have been updated. + EXPECT_EQ(1, prepare_called_); + // Change the texture. + mailbox_ = MakeMailbox('2'); + mailbox_changed_ = true; + texture_layer_->SetNeedsDisplay(); + // Force a change to make sure we draw a frame. + solid_layer_->SetBackgroundColor(SK_ColorGRAY); + break; + case 3: + // Layer shouldn't have been updated. + EXPECT_EQ(1, prepare_called_); + // So the old mailbox isn't returned yet. + EXPECT_EQ(0, mailbox_returned_); + // Make layer visible again. + parent_layer_->SetOpacity(1.f); + break; + case 4: + // Layer should have been updated. + EXPECT_EQ(2, prepare_called_); + // So the old mailbox should have been returned already. + EXPECT_EQ(1, mailbox_returned_); + texture_layer_->ClearClient(); + break; + case 5: + EXPECT_EQ(2, mailbox_returned_); + EndTest(); + break; + default: + NOTREACHED(); + break; + } + } + + virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl, + bool result) OVERRIDE { + ASSERT_TRUE(result); + DelegatedFrameData* delegated_frame_data = + output_surface()->last_sent_frame().delegated_frame_data.get(); + if (!delegated_frame_data) + return; + + // Return all resources immediately. + TransferableResourceArray resources_to_return = + output_surface()->resources_held_by_parent(); + + CompositorFrameAck ack; + for (size_t i = 0; i < resources_to_return.size(); ++i) + output_surface()->ReturnResource(resources_to_return[i].id, &ack); + host_impl->ReclaimResources(&ack); + host_impl->OnSwapBuffersComplete(); + } + + virtual void AfterTest() OVERRIDE {} + + private: + scoped_refptr<SolidColorLayer> solid_layer_; + scoped_refptr<Layer> parent_layer_; + scoped_refptr<TextureLayer> texture_layer_; + + // Used on the main thread. + bool mailbox_changed_; + TextureMailbox mailbox_; + int mailbox_returned_; + int prepare_called_; + int commit_count_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(TextureLayerChangeInvisibleMailboxTest); + // Test recovering from a lost context. class TextureLayerLostContextTest : public LayerTreeTest, @@ -928,7 +1870,9 @@ class TextureLayerLostContextTest } virtual bool PrepareTextureMailbox( - cc::TextureMailbox* mailbox, bool use_shared_memory) OVERRIDE { + TextureMailbox* mailbox, + scoped_ptr<SingleReleaseCallback>* release_callback, + bool use_shared_memory) OVERRIDE { return false; } @@ -981,19 +1925,20 @@ SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(TextureLayerLostContextTest); class TextureLayerWithMailboxMainThreadDeleted : public LayerTreeTest { public: void ReleaseCallback(unsigned sync_point, bool lost_resource) { - EXPECT_EQ(true, proxy()->IsMainThread()); + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); EXPECT_FALSE(lost_resource); ++callback_count_; EndTest(); } void SetMailbox(char mailbox_char) { - TextureMailbox mailbox( - std::string(64, mailbox_char), + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + TextureMailbox mailbox(std::string(64, mailbox_char)); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( base::Bind( &TextureLayerWithMailboxMainThreadDeleted::ReleaseCallback, base::Unretained(this))); - layer_->SetTextureMailbox(mailbox); + layer_->SetTextureMailbox(mailbox, callback.Pass()); } virtual void SetupTree() OVERRIDE { @@ -1013,6 +1958,8 @@ class TextureLayerWithMailboxMainThreadDeleted : public LayerTreeTest { } virtual void BeginTest() OVERRIDE { + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + callback_count_ = 0; // Set the mailbox on the main thread. @@ -1038,6 +1985,7 @@ class TextureLayerWithMailboxMainThreadDeleted : public LayerTreeTest { } private: + base::ThreadChecker main_thread_; int callback_count_; scoped_refptr<Layer> root_; scoped_refptr<TextureLayer> layer_; @@ -1049,19 +1997,20 @@ SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F( class TextureLayerWithMailboxImplThreadDeleted : public LayerTreeTest { public: void ReleaseCallback(unsigned sync_point, bool lost_resource) { - EXPECT_EQ(true, proxy()->IsMainThread()); + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); EXPECT_FALSE(lost_resource); ++callback_count_; EndTest(); } void SetMailbox(char mailbox_char) { - TextureMailbox mailbox( - std::string(64, mailbox_char), + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + TextureMailbox mailbox(std::string(64, mailbox_char)); + scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create( base::Bind( &TextureLayerWithMailboxImplThreadDeleted::ReleaseCallback, base::Unretained(this))); - layer_->SetTextureMailbox(mailbox); + layer_->SetTextureMailbox(mailbox, callback.Pass()); } virtual void SetupTree() OVERRIDE { @@ -1081,6 +2030,8 @@ class TextureLayerWithMailboxImplThreadDeleted : public LayerTreeTest { } virtual void BeginTest() OVERRIDE { + EXPECT_EQ(true, main_thread_.CalledOnValidThread()); + callback_count_ = 0; // Set the mailbox on the main thread. @@ -1109,6 +2060,7 @@ class TextureLayerWithMailboxImplThreadDeleted : public LayerTreeTest { } private: + base::ThreadChecker main_thread_; int callback_count_; scoped_refptr<Layer> root_; scoped_refptr<TextureLayer> layer_; |