普通文本  |  2232行  |  74.37 KB

// Copyright 2012 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/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/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/test/test_web_graphics_context_3d.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"
#include "gpu/GLES2/gl2extchromium.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using ::testing::Mock;
using ::testing::_;
using ::testing::AtLeast;
using ::testing::AnyNumber;

namespace cc {
namespace {

class MockLayerTreeHost : public LayerTreeHost {
 public:
  explicit MockLayerTreeHost(FakeLayerTreeHostClient* client)
      : LayerTreeHost(client, NULL, LayerTreeSettings()) {
    InitializeSingleThreaded(client);
  }

  MOCK_METHOD0(AcquireLayerTextures, void());
  MOCK_METHOD0(SetNeedsCommit, void());
  MOCK_METHOD0(SetNeedsUpdateLayers, void());
  MOCK_METHOD0(StartRateLimiter, void());
  MOCK_METHOD0(StopRateLimiter, void());
};

class TextureLayerTest : public testing::Test {
 public:
  TextureLayerTest()
      : fake_client_(
          FakeLayerTreeHostClient(FakeLayerTreeHostClient::DIRECT_3D)),
        host_impl_(&proxy_) {}

 protected:
  virtual void SetUp() {
    layer_tree_host_.reset(new MockLayerTreeHost(&fake_client_));
  }

  virtual void TearDown() {
    Mock::VerifyAndClearExpectations(layer_tree_host_.get());
    EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AnyNumber());
    EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AnyNumber());

    layer_tree_host_->SetRootLayer(NULL);
    layer_tree_host_.reset();
  }

  scoped_ptr<MockLayerTreeHost> layer_tree_host_;
  FakeImplProxy proxy_;
  FakeLayerTreeHostClient fake_client_;
  FakeLayerTreeHostImpl host_impl_;
};

TEST_F(TextureLayerTest, SyncImplWhenChangingTextureId) {
  scoped_refptr<TextureLayer> test_layer = TextureLayer::Create(NULL);
  ASSERT_TRUE(test_layer.get());

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AnyNumber());
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AnyNumber());
  layer_tree_host_->SetRootLayer(test_layer);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());
  EXPECT_EQ(test_layer->layer_tree_host(), layer_tree_host_.get());

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
  test_layer->SetTextureId(1);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AtLeast(1));
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
  test_layer->SetTextureId(2);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AtLeast(1));
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
  test_layer->SetTextureId(0);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());
}

TEST_F(TextureLayerTest, SyncImplWhenDrawing) {
  gfx::RectF dirty_rect(0.f, 0.f, 1.f, 1.f);

  scoped_refptr<TextureLayer> test_layer = TextureLayer::Create(NULL);
  ASSERT_TRUE(test_layer.get());
  scoped_ptr<TextureLayerImpl> impl_layer;
  impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, false);
  ASSERT_TRUE(impl_layer);

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AnyNumber());
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AnyNumber());
  layer_tree_host_->SetRootLayer(test_layer);
  test_layer->SetTextureId(1);
  test_layer->SetIsDrawable(true);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());
  EXPECT_EQ(test_layer->layer_tree_host(), layer_tree_host_.get());

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(1);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(0);
  test_layer->WillModifyTexture();
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
  EXPECT_CALL(*layer_tree_host_, SetNeedsUpdateLayers()).Times(1);
  test_layer->SetNeedsDisplayRect(dirty_rect);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(1);
  test_layer->PushPropertiesTo(impl_layer.get());  // fake commit
  test_layer->SetIsDrawable(false);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  // Verify that non-drawable layers don't signal the compositor,
  // except for the first draw after last commit, which must acquire
  // the texture.
  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(1);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(0);
  test_layer->WillModifyTexture();
  test_layer->SetNeedsDisplayRect(dirty_rect);
  test_layer->PushPropertiesTo(impl_layer.get());  // fake commit
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  // Second draw with layer in non-drawable state: no texture
  // acquisition.
  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(0);
  test_layer->WillModifyTexture();
  test_layer->SetNeedsDisplayRect(dirty_rect);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());
}

TEST_F(TextureLayerTest, SyncImplWhenRemovingFromTree) {
  scoped_refptr<Layer> root_layer = Layer::Create();
  ASSERT_TRUE(root_layer.get());
  scoped_refptr<Layer> child_layer = Layer::Create();
  ASSERT_TRUE(child_layer.get());
  root_layer->AddChild(child_layer);
  scoped_refptr<TextureLayer> test_layer = TextureLayer::Create(NULL);
  ASSERT_TRUE(test_layer.get());
  test_layer->SetTextureId(0);
  child_layer->AddChild(test_layer);

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AnyNumber());
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AnyNumber());
  layer_tree_host_->SetRootLayer(root_layer);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
  test_layer->RemoveFromParent();
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
  child_layer->AddChild(test_layer);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
  test_layer->SetTextureId(1);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AtLeast(1));
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
  test_layer->RemoveFromParent();
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());
}

TEST_F(TextureLayerTest, CheckPropertyChangeCausesCorrectBehavior) {
  scoped_refptr<TextureLayer> test_layer = TextureLayer::Create(NULL);
  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.
  EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetFlipped(false));
  EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetUV(
      gfx::PointF(0.25f, 0.25f), gfx::PointF(0.75f, 0.75f)));
  EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetVertexOpacity(
      0.5f, 0.5f, 0.5f, 0.5f));
  EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetPremultipliedAlpha(false));
  EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetBlendBackgroundColor(true));
  EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetTextureId(1));

  // Calling SetTextureId can call AcquireLayerTextures.
  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(AnyNumber());
}

TEST_F(TextureLayerTest, VisibleContentOpaqueRegion) {
  const gfx::Size layer_bounds(100, 100);
  const gfx::Rect layer_rect(layer_bounds);
  const Region layer_region(layer_rect);

  scoped_refptr<TextureLayer> layer = TextureLayer::Create(NULL);
  layer->SetBounds(layer_bounds);
  layer->draw_properties().visible_content_rect = layer_rect;
  layer->SetBlendBackgroundColor(true);

  // Verify initial conditions.
  EXPECT_FALSE(layer->contents_opaque());
  EXPECT_EQ(0u, layer->background_color());
  EXPECT_EQ(Region().ToString(),
            layer->VisibleContentOpaqueRegion().ToString());

  // Opaque background.
  layer->SetBackgroundColor(SK_ColorWHITE);
  EXPECT_EQ(layer_region.ToString(),
            layer->VisibleContentOpaqueRegion().ToString());

  // Transparent background.
  layer->SetBackgroundColor(SkColorSetARGB(100, 255, 255, 255));
  EXPECT_EQ(Region().ToString(),
            layer->VisibleContentOpaqueRegion().ToString());
}

class FakeTextureLayerClient : public TextureLayerClient {
 public:
  FakeTextureLayerClient() {}

  virtual unsigned PrepareTexture() OVERRIDE {
    return 0;
  }

  virtual bool PrepareTextureMailbox(
      TextureMailbox* mailbox,
      scoped_ptr<SingleReleaseCallback>* release_callback,
      bool use_shared_memory) OVERRIDE {
    *mailbox = TextureMailbox();
    *release_callback = scoped_ptr<SingleReleaseCallback>();
    return true;
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(FakeTextureLayerClient);
};

TEST_F(TextureLayerTest, RateLimiter) {
  FakeTextureLayerClient client;
  scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(
      &client);
  test_layer->SetIsDrawable(true);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AnyNumber());
  layer_tree_host_->SetRootLayer(test_layer);

  // Don't rate limit until we invalidate.
  EXPECT_CALL(*layer_tree_host_, StartRateLimiter()).Times(0);
  test_layer->SetRateLimitContext(true);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  // Do rate limit after we invalidate.
  EXPECT_CALL(*layer_tree_host_, StartRateLimiter());
  test_layer->SetNeedsDisplay();
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  // Stop rate limiter when we don't want it any more.
  EXPECT_CALL(*layer_tree_host_, StopRateLimiter());
  test_layer->SetRateLimitContext(false);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  // Or we clear the client.
  test_layer->SetRateLimitContext(true);
  EXPECT_CALL(*layer_tree_host_, StopRateLimiter());
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AnyNumber());
  test_layer->ClearClient();
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  // Reset to a layer with a client, that started the rate limiter.
  test_layer = TextureLayer::CreateForMailbox(
      &client);
  test_layer->SetIsDrawable(true);
  test_layer->SetRateLimitContext(true);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AnyNumber());
  layer_tree_host_->SetRootLayer(test_layer);
  EXPECT_CALL(*layer_tree_host_, StartRateLimiter()).Times(0);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());
  EXPECT_CALL(*layer_tree_host_, StartRateLimiter());
  test_layer->SetNeedsDisplay();
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  // Stop rate limiter when we're removed from the tree.
  EXPECT_CALL(*layer_tree_host_, StopRateLimiter());
  layer_tree_host_->SetRootLayer(NULL);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());
}

class MockMailboxCallback {
 public:
  MOCK_METHOD3(Release, void(const std::string& mailbox,
                             unsigned sync_point,
                             bool lost_resource));
  MOCK_METHOD3(Release2, void(base::SharedMemory* shared_memory,
                              unsigned sync_point,
                              bool lost_resource));
};

struct CommonMailboxObjects {
  CommonMailboxObjects()
      : mailbox_name1_(64, '1'),
        mailbox_name2_(64, '2'),
        sync_point1_(1),
        sync_point2_(2),
        shared_memory_(new base::SharedMemory) {
    release_mailbox1_ = base::Bind(&MockMailboxCallback::Release,
                                   base::Unretained(&mock_callback_),
                                   mailbox_name1_);
    release_mailbox2_ = base::Bind(&MockMailboxCallback::Release,
                                   base::Unretained(&mock_callback_),
                                   mailbox_name2_);
    gpu::Mailbox m1;
    m1.SetName(reinterpret_cast<const int8*>(mailbox_name1_.data()));
    mailbox1_ = TextureMailbox(m1, sync_point1_);
    gpu::Mailbox m2;
    m2.SetName(reinterpret_cast<const int8*>(mailbox_name2_.data()));
    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);
  }

  std::string mailbox_name1_;
  std::string mailbox_name2_;
  MockMailboxCallback mock_callback_;
  ReleaseCallback release_mailbox1_;
  ReleaseCallback release_mailbox2_;
  ReleaseCallback release_mailbox3_;
  TextureMailbox mailbox1_;
  TextureMailbox mailbox2_;
  TextureMailbox mailbox3_;
  unsigned sync_point1_;
  unsigned sync_point2_;
  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() {
    Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
    EXPECT_CALL(test_data_.mock_callback_,
                Release(test_data_.mailbox_name1_,
                        test_data_.sync_point1_,
                        false)).Times(1);
    TextureLayerTest::TearDown();
  }

  CommonMailboxObjects test_data_;
};

TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) {
  scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL);
  ASSERT_TRUE(test_layer.get());

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AnyNumber());
  layer_tree_host_->SetRootLayer(test_layer);
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
  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);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
  EXPECT_CALL(test_data_.mock_callback_,
              Release(test_data_.mailbox_name1_,
                      test_data_.sync_point1_,
                      false))
      .Times(1);
  test_layer->SetTextureMailbox(
      test_data_.mailbox2_,
      SingleReleaseCallback::Create(test_data_.release_mailbox2_));
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());
  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
  EXPECT_CALL(test_data_.mock_callback_,
              Release(test_data_.mailbox_name2_,
                      test_data_.sync_point2_,
                      false))
      .Times(1);
  test_layer->SetTextureMailbox(TextureMailbox(),
                                scoped_ptr<SingleReleaseCallback>());
  Mock::VerifyAndClearExpectations(layer_tree_host_.get());
  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);

  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_);

  EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
  EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
  EXPECT_CALL(test_data_.mock_callback_,
              Release2(test_data_.shared_memory_.get(),
                       0, false))
      .Times(1);
  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_,
      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 {
 public:
  TextureLayerImplWithMailboxThreadedCallback()
      : callback_count_(0),
        commit_count_(0) {}

  // 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, main_thread_.CalledOnValidThread());
    EXPECT_FALSE(lost_resource);
    ++callback_count_;
  }

  void SetMailbox(char 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, callback.Pass());
  }

  virtual void BeginTest() OVERRIDE {
    EXPECT_EQ(true, main_thread_.CalledOnValidThread());

    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');
    EXPECT_EQ(0, callback_count_);

    // Case #1: change mailbox before the commit. The old mailbox should be
    // released immediately.
    SetMailbox('2');
    EXPECT_EQ(1, callback_count_);
    PostSetNeedsCommitToMainThread();
  }

  virtual void DidCommit() OVERRIDE {
    ++commit_count_;
    switch (commit_count_) {
      case 1:
        // Case #2: change mailbox after the commit (and draw), where the
        // layer draws. The old mailbox should be released during the next
        // commit.
        SetMailbox('3');
        EXPECT_EQ(1, callback_count_);
        break;
      case 2:
        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 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(),
                                  scoped_ptr<SingleReleaseCallback>());
        break;
      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 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 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(),
                                  scoped_ptr<SingleReleaseCallback>());
        EXPECT_EQ(5, callback_count_);
        EndTest();
        break;
      default:
        NOTREACHED();
        break;
    }
  }

  virtual void AfterTest() OVERRIDE {}

 private:
  base::ThreadChecker main_thread_;
  int callback_count_;
  int commit_count_;
  scoped_refptr<Layer> root_;
  scoped_refptr<TextureLayer> layer_;
};

SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
    TextureLayerImplWithMailboxThreadedCallback);


class TextureLayerNoMailboxIsActivatedDuringCommit : public LayerTreeTest,
                                                     public TextureLayerClient {
 protected:
  TextureLayerNoMailboxIsActivatedDuringCommit()
      : wait_thread_("WAIT"),
        wait_event_(false, false),
        texture_(0u) {
    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();
  }

  virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback)
      OVERRIDE {
    scoped_refptr<TestContextProvider> provider = TestContextProvider::Create();
    texture_ = provider->UnboundTestContext3d()->createExternalTexture();
    return FakeOutputSurface::Create3d(provider).PassAs<OutputSurface>();
  }

  // TextureLayerClient implementation.
  virtual unsigned PrepareTexture() OVERRIDE {
    return texture_;
  }
  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_;
  unsigned texture_;
  int activate_count_;
  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()
      : fake_client_(
          FakeLayerTreeHostClient(FakeLayerTreeHostClient::DIRECT_3D)) {}

  virtual void SetUp() {
    TextureLayerTest::SetUp();
    layer_tree_host_.reset(new MockLayerTreeHost(&fake_client_));
    EXPECT_TRUE(host_impl_.InitializeRenderer(CreateFakeOutputSurface()));
  }

  bool WillDraw(TextureLayerImpl* layer, DrawMode mode) {
    bool will_draw = layer->WillDraw(
        mode, host_impl_.active_tree()->resource_provider());
    if (will_draw)
      layer->DidDraw(host_impl_.active_tree()->resource_provider());
    return will_draw;
  }

  CommonMailboxObjects test_data_;
  FakeLayerTreeHostClient fake_client_;
};

// Test conditions for results of TextureLayerImpl::WillDraw under
// different configurations of different mailbox, texture_id, and draw_mode.
TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) {
  EXPECT_CALL(test_data_.mock_callback_,
              Release(test_data_.mailbox_name1_,
                      test_data_.sync_point1_,
                      false))
      .Times(AnyNumber());
  EXPECT_CALL(test_data_.mock_callback_,
              Release2(test_data_.shared_memory_.get(), 0, false))
      .Times(AnyNumber());
  // Hardware mode.
  {
    scoped_ptr<TextureLayerImpl> impl_layer =
        TextureLayerImpl::Create(host_impl_.active_tree(), 1, true);
    impl_layer->SetDrawsContent(true);
    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->SetDrawsContent(true);
    impl_layer->SetTextureMailbox(TextureMailbox(),
                                  scoped_ptr<SingleReleaseCallback>());
    EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE));
  }

  {
    // Software resource.
    scoped_ptr<TextureLayerImpl> impl_layer =
        TextureLayerImpl::Create(host_impl_.active_tree(), 1, true);
    impl_layer->SetDrawsContent(true);
    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);
    impl_layer->SetDrawsContent(true);
    ContextProvider* context_provider =
        host_impl_.output_surface()->context_provider();
    unsigned texture =
        context_provider->Context3d()->createTexture();
    impl_layer->set_texture_id(texture);
    EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE));
  }

  {
    scoped_ptr<TextureLayerImpl> impl_layer =
        TextureLayerImpl::Create(host_impl_.active_tree(), 1, false);
    impl_layer->SetDrawsContent(true);
    impl_layer->set_texture_id(0);
    EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE));
  }

  // Software mode.
  {
    scoped_ptr<TextureLayerImpl> impl_layer =
        TextureLayerImpl::Create(host_impl_.active_tree(), 1, true);
    impl_layer->SetDrawsContent(true);
    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->SetDrawsContent(true);
    impl_layer->SetTextureMailbox(TextureMailbox(),
                                  scoped_ptr<SingleReleaseCallback>());
    EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE));
  }

  {
    // Software resource.
    scoped_ptr<TextureLayerImpl> impl_layer =
        TextureLayerImpl::Create(host_impl_.active_tree(), 1, true);
    impl_layer->SetDrawsContent(true);
    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);
    impl_layer->SetDrawsContent(true);
    ContextProvider* context_provider =
        host_impl_.output_surface()->context_provider();
    unsigned texture =
        context_provider->Context3d()->createTexture();
    impl_layer->set_texture_id(texture);
    EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE));
  }

  {
    scoped_ptr<TextureLayerImpl> impl_layer =
        TextureLayerImpl::Create(host_impl_.active_tree(), 1, false);
    impl_layer->SetDrawsContent(true);
    impl_layer->set_texture_id(0);
    EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE));
  }

  // Resourceless software mode.
  {
    scoped_ptr<TextureLayerImpl> impl_layer =
        TextureLayerImpl::Create(host_impl_.active_tree(), 1, true);
    impl_layer->SetDrawsContent(true);
    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);
    impl_layer->SetDrawsContent(true);
    ContextProvider* context_provider =
        host_impl_.output_surface()->context_provider();
    unsigned texture =
        context_provider->Context3d()->createTexture();
    impl_layer->set_texture_id(texture);
    EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_RESOURCELESS_SOFTWARE));
  }
}

TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) {
  host_impl_.CreatePendingTree();
  scoped_ptr<TextureLayerImpl> pending_layer;
  pending_layer = TextureLayerImpl::Create(host_impl_.pending_tree(), 1, true);
  ASSERT_TRUE(pending_layer);

  scoped_ptr<LayerImpl> active_layer(
      pending_layer->CreateLayerImpl(host_impl_.active_tree()));
  ASSERT_TRUE(active_layer);

  pending_layer->SetTextureMailbox(
      test_data_.mailbox1_,
      SingleReleaseCallback::Create(test_data_.release_mailbox1_));

  // Test multiple commits without an activation.
  EXPECT_CALL(test_data_.mock_callback_,
              Release(test_data_.mailbox_name1_,
                      test_data_.sync_point1_,
                      false))
      .Times(1);
  pending_layer->SetTextureMailbox(
      test_data_.mailbox2_,
      SingleReleaseCallback::Create(test_data_.release_mailbox2_));
  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);

  // Test callback after activation.
  pending_layer->PushPropertiesTo(active_layer.get());
  active_layer->DidBecomeActive();

  EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0);
  pending_layer->SetTextureMailbox(
      test_data_.mailbox1_,
      SingleReleaseCallback::Create(test_data_.release_mailbox1_));
  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);

  EXPECT_CALL(test_data_.mock_callback_,
              Release(test_data_.mailbox_name2_, _, false))
      .Times(1);
  pending_layer->PushPropertiesTo(active_layer.get());
  active_layer->DidBecomeActive();
  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);

  // Test resetting the mailbox.
  EXPECT_CALL(test_data_.mock_callback_,
              Release(test_data_.mailbox_name1_, _, false))
      .Times(1);
  pending_layer->SetTextureMailbox(TextureMailbox(),
                                   scoped_ptr<SingleReleaseCallback>());
  pending_layer->PushPropertiesTo(active_layer.get());
  active_layer->DidBecomeActive();
  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);

  // Test destructor.
  EXPECT_CALL(test_data_.mock_callback_,
              Release(test_data_.mailbox_name1_,
                      test_data_.sync_point1_,
                      false))
      .Times(1);
  pending_layer->SetTextureMailbox(
      test_data_.mailbox1_,
      SingleReleaseCallback::Create(test_data_.release_mailbox1_));
}

TEST_F(TextureLayerImplWithMailboxTest,
       TestDestructorCallbackOnCreatedResource) {
  scoped_ptr<TextureLayerImpl> impl_layer;
  impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1, true);
  ASSERT_TRUE(impl_layer);

  EXPECT_CALL(test_data_.mock_callback_,
              Release(test_data_.mailbox_name1_, _, false))
      .Times(1);
  impl_layer->SetTextureMailbox(
      test_data_.mailbox1_,
      SingleReleaseCallback::Create(test_data_.release_mailbox1_));
  impl_layer->SetDrawsContent(true);
  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(),
                                scoped_ptr<SingleReleaseCallback>());
}

TEST_F(TextureLayerImplWithMailboxTest, TestCallbackOnInUseResource) {
  ResourceProvider* provider = host_impl_.active_tree()->resource_provider();
  ResourceProvider::ResourceId id =
      provider->CreateResourceFromTextureMailbox(
          test_data_.mailbox1_,
          SingleReleaseCallback::Create(test_data_.release_mailbox1_));
  provider->AllocateForTesting(id);

  // Transfer some resources to the parent.
  ResourceProvider::ResourceIdArray resource_ids_to_transfer;
  resource_ids_to_transfer.push_back(id);
  TransferableResourceArray list;
  provider->PrepareSendToParent(resource_ids_to_transfer, &list);
  EXPECT_TRUE(provider->InUseByConsumer(id));
  EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0);
  provider->DeleteResource(id);
  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
  EXPECT_CALL(test_data_.mock_callback_,
              Release(test_data_.mailbox_name1_, _, false))
      .Times(1);
  ReturnedResourceArray returned;
  TransferableResource::ReturnResources(list, &returned);
  provider->ReceiveReturnsFromParent(returned);
}

// Check that ClearClient correctly clears the state so that the impl side
// doesn't try to use a texture that could have been destroyed.
class TextureLayerClientTest
    : public LayerTreeTest,
      public TextureLayerClient {
 public:
  TextureLayerClientTest()
      : texture_(0),
        commit_count_(0),
        expected_used_textures_on_draw_(0),
        expected_used_textures_on_commit_(0) {}

  virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback)
      OVERRIDE {
    scoped_refptr<TestContextProvider> provider = TestContextProvider::Create();
    texture_ = provider->UnboundTestContext3d()->createExternalTexture();
    return FakeOutputSurface::Create3d(provider).PassAs<OutputSurface>();
  }

  virtual unsigned PrepareTexture() OVERRIDE { return texture_; }

  virtual bool PrepareTextureMailbox(
      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);

    texture_layer_ = TextureLayer::Create(this);
    texture_layer_->SetBounds(gfx::Size(10, 10));
    texture_layer_->SetAnchorPoint(gfx::PointF());
    texture_layer_->SetIsDrawable(true);
    root->AddChild(texture_layer_);

    layer_tree_host()->SetRootLayer(root);
    LayerTreeTest::SetupTree();
    {
      base::AutoLock lock(lock_);
      expected_used_textures_on_commit_ = 1;
    }
  }

  virtual void BeginTest() OVERRIDE {
    PostSetNeedsCommitToMainThread();
  }

  virtual void DidCommitAndDrawFrame() OVERRIDE {
    ++commit_count_;
    switch (commit_count_) {
      case 1:
        texture_layer_->ClearClient();
        texture_layer_->SetNeedsDisplay();
        {
          base::AutoLock lock(lock_);
          expected_used_textures_on_commit_ = 0;
        }
        break;
      case 2:
        EndTest();
        break;
      default:
        NOTREACHED();
        break;
    }
  }

  virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
    base::AutoLock lock(lock_);
    expected_used_textures_on_draw_ = expected_used_textures_on_commit_;
  }

  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);
    EXPECT_EQ(expected_used_textures_on_draw_,
              ContextForImplThread(host_impl)->NumUsedTextures());
  }

  virtual void AfterTest() OVERRIDE {}

 private:
  TestWebGraphicsContext3D* ContextForImplThread(LayerTreeHostImpl* host_impl) {
    return static_cast<TestWebGraphicsContext3D*>(
        host_impl->output_surface()->context_provider()->Context3d());
  }

  scoped_refptr<TextureLayer> texture_layer_;
  unsigned texture_;
  int commit_count_;

  // Used only on thread.
  unsigned expected_used_textures_on_draw_;

  // Used on either thread, protected by lock_.
  base::Lock lock_;
  unsigned expected_used_textures_on_commit_;
};

// The TextureLayerClient does not use mailboxes, so can't use a delegating
// 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()
      : texture_(0u),
        prepare_called_(0),
        commit_count_(0),
        expected_texture_on_draw_(0) {}

  virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback)
      OVERRIDE {
    scoped_refptr<TestContextProvider> provider = TestContextProvider::Create();
    texture_ = provider->UnboundTestContext3d()->createExternalTexture();
    return FakeOutputSurface::Create3d(provider).PassAs<OutputSurface>();
  }

  // TextureLayerClient implementation.
  virtual unsigned PrepareTexture() OVERRIDE {
    ++prepare_called_;
    return texture_;
  }
  virtual bool PrepareTextureMailbox(
      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_);
        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_);
        // 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();
        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_;

  // Used on the main thread, and on the impl thread while the main thread is
  // blocked.
  unsigned texture_;

  // Used on the main thread.
  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 TextureLayer::Update does not cause an extra commit when setting
// the texture mailbox.
class TextureLayerNoExtraCommitForMailboxTest
    : public LayerTreeTest,
      public TextureLayerClient {
 public:
  // TextureLayerClient implementation.
  virtual unsigned PrepareTexture() OVERRIDE {
    NOTREACHED();
    return 0;
  }
  virtual bool PrepareTextureMailbox(
      TextureMailbox* mailbox,
      scoped_ptr<SingleReleaseCallback>* release_callback,
      bool use_shared_memory) OVERRIDE {
    if (layer_tree_host()->source_frame_number() == 1) {
      *mailbox = TextureMailbox();
      return true;
    }

    *mailbox = TextureMailbox(std::string(64, '1'));
    *release_callback = SingleReleaseCallback::Create(
        base::Bind(&TextureLayerNoExtraCommitForMailboxTest::MailboxReleased,
                   base::Unretained(this)));
    return true;
  }

  void MailboxReleased(unsigned sync_point, bool lost_resource) {
    EXPECT_EQ(2, layer_tree_host()->source_frame_number());
    EndTest();
  }

  virtual void SetupTree() OVERRIDE {
    scoped_refptr<Layer> root = Layer::Create();
    root->SetBounds(gfx::Size(10, 10));
    root->SetAnchorPoint(gfx::PointF());
    root->SetIsDrawable(true);

    texture_layer_ = TextureLayer::CreateForMailbox(this);
    texture_layer_->SetBounds(gfx::Size(10, 10));
    texture_layer_->SetAnchorPoint(gfx::PointF());
    texture_layer_->SetIsDrawable(true);
    root->AddChild(texture_layer_);

    layer_tree_host()->SetRootLayer(root);
    LayerTreeTest::SetupTree();
  }

  virtual void BeginTest() OVERRIDE {
    PostSetNeedsCommitToMainThread();
  }

  virtual void DidCommitAndDrawFrame() OVERRIDE {
    switch (layer_tree_host()->source_frame_number()) {
      case 1:
        EXPECT_FALSE(proxy()->CommitPendingForTesting());
        // Invalidate the texture layer to clear the mailbox before
        // ending the test.
        texture_layer_->SetNeedsDisplay();
        break;
      case 2:
        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<TextureLayer> texture_layer_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(TextureLayerNoExtraCommitForMailboxTest);

// 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;
  }

  virtual bool PrepareTextureMailbox(
      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 that TextureLayerImpl::ReleaseResources can be called which releases
// the mailbox back to TextureLayerClient.
class TextureLayerReleaseResourcesBase
    : public LayerTreeTest,
      public TextureLayerClient {
 public:
  // TextureLayerClient implementation.
  virtual unsigned PrepareTexture() OVERRIDE {
    NOTREACHED();
    return 0;
  }
  virtual bool PrepareTextureMailbox(
      TextureMailbox* mailbox,
      scoped_ptr<SingleReleaseCallback>* release_callback,
      bool use_shared_memory) OVERRIDE {
    *mailbox = TextureMailbox(std::string(64, '1'));
    *release_callback = SingleReleaseCallback::Create(
        base::Bind(&TextureLayerReleaseResourcesBase::MailboxReleased,
                   base::Unretained(this)));
    return true;
  }

  void MailboxReleased(unsigned sync_point, bool lost_resource) {
    mailbox_released_ = true;
  }

  virtual void SetupTree() OVERRIDE {
    LayerTreeTest::SetupTree();

    scoped_refptr<TextureLayer> texture_layer =
        TextureLayer::CreateForMailbox(this);
    texture_layer->SetBounds(gfx::Size(10, 10));
    texture_layer->SetAnchorPoint(gfx::PointF());
    texture_layer->SetIsDrawable(true);

    layer_tree_host()->root_layer()->AddChild(texture_layer);
  }

  virtual void BeginTest() OVERRIDE {
    mailbox_released_ = false;
    PostSetNeedsCommitToMainThread();
  }

  virtual void DidCommitAndDrawFrame() OVERRIDE {
    EndTest();
  }

  virtual void AfterTest() OVERRIDE {
    EXPECT_TRUE(mailbox_released_);
  }

 private:
  bool mailbox_released_;
};

class TextureLayerReleaseResourcesAfterCommit
    : public TextureLayerReleaseResourcesBase {
 public:
  virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
    LayerTreeImpl* tree = NULL;
    if (host_impl->settings().impl_side_painting)
      tree = host_impl->pending_tree();
    else
      tree = host_impl->active_tree();
    tree->root_layer()->children()[0]->ReleaseResources();
  }
};

SINGLE_AND_MULTI_THREAD_TEST_F(TextureLayerReleaseResourcesAfterCommit);

class TextureLayerReleaseResourcesAfterActivate
    : public TextureLayerReleaseResourcesBase {
 public:
  virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
    host_impl->active_tree()->root_layer()->children()[0]->ReleaseResources();
  }
};

SINGLE_AND_MULTI_THREAD_TEST_F(TextureLayerReleaseResourcesAfterActivate);

// Test recovering from a lost context.
class TextureLayerLostContextTest
    : public LayerTreeTest,
      public TextureLayerClient {
 public:
  TextureLayerLostContextTest()
      : context_lost_(false),
        draw_count_(0) {}

  virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback)
      OVERRIDE {
    return CreateFakeOutputSurface();
  }

  virtual unsigned PrepareTexture() OVERRIDE {
    if (draw_count_ == 0)
      context_lost_ = true;
    if (context_lost_)
      return 0u;
    return 1u;
  }

  virtual bool PrepareTextureMailbox(
      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->SetIsDrawable(true);

    texture_layer_ = TextureLayer::Create(this);
    texture_layer_->SetBounds(gfx::Size(10, 10));
    texture_layer_->SetIsDrawable(true);
    root->AddChild(texture_layer_);

    layer_tree_host()->SetRootLayer(root);
    LayerTreeTest::SetupTree();
  }

  virtual void BeginTest() OVERRIDE {
    PostSetNeedsCommitToMainThread();
  }

  virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
                                     LayerTreeHostImpl::FrameData* frame_data,
                                     bool result) OVERRIDE {
    LayerImpl* root = host_impl->RootLayer();
    TextureLayerImpl* texture_layer =
        static_cast<TextureLayerImpl*>(root->children()[0]);
    if (++draw_count_ == 1)
      EXPECT_EQ(0u, texture_layer->texture_id());
    else
      EXPECT_EQ(1u, texture_layer->texture_id());
    return true;
  }

  virtual void DidCommitAndDrawFrame() OVERRIDE {
    EndTest();
  }

  virtual void AfterTest() OVERRIDE {}

 private:
  scoped_refptr<TextureLayer> texture_layer_;
  bool context_lost_;
  int draw_count_;
};

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, main_thread_.CalledOnValidThread());
    EXPECT_FALSE(lost_resource);
    ++callback_count_;
    EndTest();
  }

  void SetMailbox(char 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, callback.Pass());
  }

  virtual void SetupTree() OVERRIDE {
    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);
  }

  virtual void BeginTest() OVERRIDE {
    EXPECT_EQ(true, main_thread_.CalledOnValidThread());

    callback_count_ = 0;

    // Set the mailbox on the main thread.
    SetMailbox('1');
    EXPECT_EQ(0, callback_count_);

    PostSetNeedsCommitToMainThread();
  }

  virtual void DidCommitAndDrawFrame() OVERRIDE {
    switch (layer_tree_host()->source_frame_number()) {
      case 1:
        // Delete the TextureLayer on the main thread while the mailbox is in
        // the impl tree.
        layer_->RemoveFromParent();
        layer_ = NULL;
        break;
    }
  }

  virtual void AfterTest() OVERRIDE {
    EXPECT_EQ(1, callback_count_);
  }

 private:
  base::ThreadChecker main_thread_;
  int callback_count_;
  scoped_refptr<Layer> root_;
  scoped_refptr<TextureLayer> layer_;
};

SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
    TextureLayerWithMailboxMainThreadDeleted);

class TextureLayerWithMailboxImplThreadDeleted : public LayerTreeTest {
 public:
  void ReleaseCallback(unsigned sync_point, bool lost_resource) {
    EXPECT_EQ(true, main_thread_.CalledOnValidThread());
    EXPECT_FALSE(lost_resource);
    ++callback_count_;
    EndTest();
  }

  void SetMailbox(char 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, callback.Pass());
  }

  virtual void SetupTree() OVERRIDE {
    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);
  }

  virtual void BeginTest() OVERRIDE {
    EXPECT_EQ(true, main_thread_.CalledOnValidThread());

    callback_count_ = 0;

    // Set the mailbox on the main thread.
    SetMailbox('1');
    EXPECT_EQ(0, callback_count_);

    PostSetNeedsCommitToMainThread();
  }

  virtual void DidCommitAndDrawFrame() OVERRIDE {
    switch (layer_tree_host()->source_frame_number()) {
      case 1:
        // Remove the TextureLayer on the main thread while the mailbox is in
        // the impl tree, but don't delete the TextureLayer until after the impl
        // tree side is deleted.
        layer_->RemoveFromParent();
        break;
      case 2:
        layer_ = NULL;
        break;
    }
  }

  virtual void AfterTest() OVERRIDE {
    EXPECT_EQ(1, callback_count_);
  }

 private:
  base::ThreadChecker main_thread_;
  int callback_count_;
  scoped_refptr<Layer> root_;
  scoped_refptr<TextureLayer> layer_;
};

SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
    TextureLayerWithMailboxImplThreadDeleted);

}  // namespace
}  // namespace cc