root/cc/trees/layer_tree_host_unittest_damage.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. SetupTree
  2. BeginTest
  3. DidCommitAndDrawFrame
  4. PrepareToDrawOnThread
  5. AfterTest
  6. SetupTree
  7. BeginTest
  8. DidCommitAndDrawFrame
  9. PrepareToDrawOnThread
  10. AfterTest
  11. BeginTest
  12. SetupTree
  13. PrepareToDrawOnThread
  14. SwapBuffersOnThread
  15. DidCommit
  16. AfterTest
  17. BeginTest
  18. SetupTree
  19. PrepareToDrawOnThread
  20. DidCommitAndDrawFrame
  21. AfterTest
  22. BeginTest
  23. SetupTree
  24. PrepareToDrawOnThread
  25. DidCommitAndDrawFrame
  26. AfterTest
  27. SetupTree
  28. BeginTest
  29. PrepareToDrawOnThread
  30. SwapBuffersOnThread
  31. AfterTest
  32. BeginTest
  33. PrepareToDrawOnThread
  34. SwapBuffersOnThread
  35. AfterTest
  36. InitializeSettings
  37. BeginTest
  38. SetupTree
  39. PrepareToDrawOnThread
  40. UpdateVisibleTilesOnThread
  41. SwapBuffersOnThread
  42. AfterTest

// 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/trees/layer_tree_host.h"

#include "base/bind.h"
#include "base/callback.h"
#include "base/location.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/time/time.h"
#include "cc/test/fake_content_layer.h"
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_painted_scrollbar_layer.h"
#include "cc/test/fake_picture_layer.h"
#include "cc/test/layer_tree_test.h"
#include "cc/trees/damage_tracker.h"
#include "cc/trees/layer_tree_impl.h"

namespace cc {
namespace {

// These tests deal with damage tracking.
class LayerTreeHostDamageTest : public LayerTreeTest {};

// LayerTreeHost::SetNeedsRedraw should damage the whole viewport.
class LayerTreeHostDamageTestSetNeedsRedraw
    : public LayerTreeHostDamageTest {
  virtual void SetupTree() OVERRIDE {
    // Viewport is 10x10.
    scoped_refptr<FakeContentLayer> root = FakeContentLayer::Create(&client_);
    root->SetBounds(gfx::Size(10, 10));

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

  virtual void BeginTest() OVERRIDE {
    draw_count_ = 0;
    PostSetNeedsCommitToMainThread();
  }

  virtual void DidCommitAndDrawFrame() OVERRIDE {
    switch (layer_tree_host()->source_frame_number()) {
      case 1:
        layer_tree_host()->SetNeedsRedraw();
        break;
    }
  }

  virtual DrawSwapReadbackResult::DrawResult PrepareToDrawOnThread(
      LayerTreeHostImpl* impl,
      LayerTreeHostImpl::FrameData* frame_data,
      DrawSwapReadbackResult::DrawResult draw_result) OVERRIDE {
    EXPECT_EQ(DrawSwapReadbackResult::DRAW_SUCCESS, draw_result);

    RenderSurfaceImpl* root_surface =
        impl->active_tree()->root_layer()->render_surface();
    gfx::RectF root_damage =
        root_surface->damage_tracker()->current_damage_rect();

    switch (draw_count_) {
      case 0:
        // The first frame has full damage.
        EXPECT_EQ(gfx::RectF(10.f, 10.f).ToString(), root_damage.ToString());
        break;
      case 1:
        // The second frame has full damage.
        EXPECT_EQ(gfx::RectF(10.f, 10.f).ToString(), root_damage.ToString());
        EndTest();
        break;
      case 2:
        NOTREACHED();
    }

    ++draw_count_;
    return draw_result;
  }

  virtual void AfterTest() OVERRIDE {}

  int draw_count_;
  FakeContentLayerClient client_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDamageTestSetNeedsRedraw);

// LayerTreeHost::SetViewportSize should damage the whole viewport.
class LayerTreeHostDamageTestSetViewportSize
    : public LayerTreeHostDamageTest {
  virtual void SetupTree() OVERRIDE {
    // Viewport is 10x10.
    scoped_refptr<FakeContentLayer> root = FakeContentLayer::Create(&client_);
    root->SetBounds(gfx::Size(10, 10));

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

  virtual void BeginTest() OVERRIDE {
    draw_count_ = 0;
    PostSetNeedsCommitToMainThread();
  }

  virtual void DidCommitAndDrawFrame() OVERRIDE {
    switch (layer_tree_host()->source_frame_number()) {
      case 1:
        layer_tree_host()->SetViewportSize(gfx::Size(15, 15));
        break;
    }
  }

  virtual DrawSwapReadbackResult::DrawResult PrepareToDrawOnThread(
      LayerTreeHostImpl* impl,
      LayerTreeHostImpl::FrameData* frame_data,
      DrawSwapReadbackResult::DrawResult draw_result) OVERRIDE {
    EXPECT_EQ(DrawSwapReadbackResult::DRAW_SUCCESS, draw_result);

    RenderSurfaceImpl* root_surface =
        impl->active_tree()->root_layer()->render_surface();
    gfx::RectF root_damage =
        root_surface->damage_tracker()->current_damage_rect();

    switch (draw_count_) {
      case 0:
        // The first frame has full damage.
        EXPECT_EQ(gfx::RectF(10.f, 10.f).ToString(), root_damage.ToString());
        break;
      case 1:
        // The second frame has full damage.
        EXPECT_EQ(gfx::RectF(15.f, 15.f).ToString(), root_damage.ToString());
        EndTest();
        break;
      case 2:
        NOTREACHED();
    }

    ++draw_count_;
    return draw_result;
  }

  virtual void AfterTest() OVERRIDE {}

  int draw_count_;
  FakeContentLayerClient client_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDamageTestSetViewportSize);

class LayerTreeHostDamageTestNoDamageDoesNotSwap
    : public LayerTreeHostDamageTest {
  virtual void BeginTest() OVERRIDE {
    expect_swap_and_succeed_ = 0;
    did_swaps_ = 0;
    did_swap_and_succeed_ = 0;
    PostSetNeedsCommitToMainThread();
  }

  virtual void SetupTree() OVERRIDE {
    scoped_refptr<FakeContentLayer> root = FakeContentLayer::Create(&client_);
    root->SetBounds(gfx::Size(10, 10));

    // Most of the layer isn't visible.
    content_ = FakeContentLayer::Create(&client_);
    content_->SetBounds(gfx::Size(2000, 100));
    root->AddChild(content_);

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

  virtual DrawSwapReadbackResult::DrawResult PrepareToDrawOnThread(
      LayerTreeHostImpl* host_impl,
      LayerTreeHostImpl::FrameData* frame_data,
      DrawSwapReadbackResult::DrawResult draw_result) OVERRIDE {
    EXPECT_EQ(DrawSwapReadbackResult::DRAW_SUCCESS, draw_result);

    int source_frame = host_impl->active_tree()->source_frame_number();
    switch (source_frame) {
      case 0:
        // The first frame has damage, so we should draw and swap.
        ++expect_swap_and_succeed_;
        break;
      case 1:
        // The second frame has no damage, so we should not draw and swap.
        break;
      case 2:
        // The third frame has damage again, so we should draw and swap.
        ++expect_swap_and_succeed_;
        break;
      case 3:
        // The fourth frame has no visible damage, so we should not draw and
        // swap.
        EndTest();
        break;
    }
    return draw_result;
  }

  virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
                                   bool result) OVERRIDE {
    ++did_swaps_;
    if (result)
      ++did_swap_and_succeed_;
    EXPECT_EQ(expect_swap_and_succeed_, did_swap_and_succeed_);
  }

  virtual void DidCommit() OVERRIDE {
    int next_frame = layer_tree_host()->source_frame_number();
    switch (next_frame) {
      case 1:
        layer_tree_host()->SetNeedsCommit();
        break;
      case 2:
        // Cause visible damage.
        content_->SetNeedsDisplayRect(
            gfx::Rect(layer_tree_host()->device_viewport_size()));
        break;
      case 3:
        // Cause non-visible damage.
        content_->SetNeedsDisplayRect(gfx::Rect(1990, 1990, 10, 10));
        layer_tree_host()->SetNeedsCommit();
        break;
    }
  }

  virtual void AfterTest() OVERRIDE {
    EXPECT_EQ(4, did_swaps_);
    EXPECT_EQ(2, expect_swap_and_succeed_);
    EXPECT_EQ(expect_swap_and_succeed_, did_swap_and_succeed_);
  }

  FakeContentLayerClient client_;
  scoped_refptr<FakeContentLayer> content_;
  int expect_swap_and_succeed_;
  int did_swaps_;
  int did_swap_and_succeed_;
};

SINGLE_AND_MULTI_THREAD_NOIMPL_TEST_F(
    LayerTreeHostDamageTestNoDamageDoesNotSwap);

class LayerTreeHostDamageTestNoDamageReadbackDoesDraw
    : public LayerTreeHostDamageTest {
  virtual void BeginTest() OVERRIDE {
    PostSetNeedsCommitToMainThread();
  }

  virtual void SetupTree() OVERRIDE {
    scoped_refptr<FakeContentLayer> root = FakeContentLayer::Create(&client_);
    root->SetBounds(gfx::Size(10, 10));

    // Most of the layer isn't visible.
    content_ = FakeContentLayer::Create(&client_);
    content_->SetBounds(gfx::Size(100, 100));
    root->AddChild(content_);

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

  virtual DrawSwapReadbackResult::DrawResult PrepareToDrawOnThread(
      LayerTreeHostImpl* host_impl,
      LayerTreeHostImpl::FrameData* frame_data,
      DrawSwapReadbackResult::DrawResult draw_result) OVERRIDE {
    EXPECT_EQ(DrawSwapReadbackResult::DRAW_SUCCESS, draw_result);

    int source_frame = host_impl->active_tree()->source_frame_number();
    switch (source_frame) {
      case 0:
        // The first frame draws and clears any damage.
        break;
      case 1: {
        // The second frame is a readback, we should have damage in the readback
        // rect, but not swap.
        RenderSurfaceImpl* root_surface =
            host_impl->active_tree()->root_layer()->render_surface();
        gfx::RectF root_damage =
            root_surface->damage_tracker()->current_damage_rect();
        root_damage.Intersect(root_surface->content_rect());
        EXPECT_TRUE(root_damage.Contains(gfx::Rect(3, 3, 1, 1)));
        break;
      }
      case 2:
        // CompositeAndReadback causes a follow-up commit.
        break;
      case 3:
        NOTREACHED();
        break;
    }
    return draw_result;
  }

  virtual void DidCommitAndDrawFrame() OVERRIDE {
    int next_frame = layer_tree_host()->source_frame_number();
    switch (next_frame) {
      case 1: {
        char pixels[4];
        layer_tree_host()->CompositeAndReadback(static_cast<void*>(&pixels),
                                                gfx::Rect(3, 3, 1, 1));
        EndTest();
        break;
      }
    }
  }

  virtual void AfterTest() OVERRIDE {}

  FakeContentLayerClient client_;
  scoped_refptr<FakeContentLayer> content_;
};

SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDamageTestNoDamageReadbackDoesDraw);

class LayerTreeHostDamageTestForcedFullDamage : public LayerTreeHostDamageTest {
  virtual void BeginTest() OVERRIDE {
    PostSetNeedsCommitToMainThread();
  }

  virtual void SetupTree() OVERRIDE {
    root_ = FakeContentLayer::Create(&client_);
    child_ = FakeContentLayer::Create(&client_);

    root_->SetBounds(gfx::Size(500, 500));
    child_->SetPosition(gfx::Point(100, 100));
    child_->SetBounds(gfx::Size(30, 30));

    root_->AddChild(child_);
    layer_tree_host()->SetRootLayer(root_);
    LayerTreeHostDamageTest::SetupTree();
  }

  virtual DrawSwapReadbackResult::DrawResult PrepareToDrawOnThread(
      LayerTreeHostImpl* host_impl,
      LayerTreeHostImpl::FrameData* frame_data,
      DrawSwapReadbackResult::DrawResult draw_result) OVERRIDE {
    EXPECT_EQ(DrawSwapReadbackResult::DRAW_SUCCESS, draw_result);

    RenderSurfaceImpl* root_surface =
        host_impl->active_tree()->root_layer()->render_surface();
    gfx::RectF root_damage =
        root_surface->damage_tracker()->current_damage_rect();
    root_damage.Intersect(root_surface->content_rect());

    int source_frame = host_impl->active_tree()->source_frame_number();
    switch (source_frame) {
      case 0:
        // The first frame draws and clears any damage.
        EXPECT_EQ(gfx::RectF(root_surface->content_rect()).ToString(),
                  root_damage.ToString());
        EXPECT_FALSE(frame_data->has_no_damage);
        break;
      case 1:
        // If we get a frame without damage then we don't draw.
        EXPECT_EQ(gfx::RectF().ToString(), root_damage.ToString());
        EXPECT_TRUE(frame_data->has_no_damage);

        // Then we set full damage for the next frame.
        host_impl->SetFullRootLayerDamage();
        break;
      case 2:
        // The whole frame should be damaged as requested.
        EXPECT_EQ(gfx::RectF(root_surface->content_rect()).ToString(),
                  root_damage.ToString());
        EXPECT_FALSE(frame_data->has_no_damage);

        // Just a part of the next frame should be damaged.
        child_damage_rect_ = gfx::RectF(10, 11, 12, 13);
        break;
      case 3:
        // The update rect in the child should be damaged and the damaged area
        // should match the invalidation.
        EXPECT_EQ(gfx::RectF(100+10, 100+11, 12, 13).ToString(),
                  root_damage.ToString());

        // TODO(danakj): Remove this when impl side painting is always on.
        if (delegating_renderer() ||
            host_impl->settings().impl_side_painting) {
          // When using a delegating renderer, or using impl side painting, the
          // entire child is considered damaged as we need to replace its
          // resources with newly created ones. The damaged area is kept as it
          // is, but entire child is painted.

          // The paint rect should match the layer bounds.
          gfx::RectF paint_rect = child_->LastPaintRect();
          paint_rect.set_origin(child_->position());
          EXPECT_EQ(gfx::RectF(100, 100, 30, 30).ToString(),
                    paint_rect.ToString());
        }
        EXPECT_FALSE(frame_data->has_no_damage);

        // If we damage part of the frame, but also damage the full
        // frame, then the whole frame should be damaged.
        child_damage_rect_ = gfx::RectF(10, 11, 12, 13);
        host_impl->SetFullRootLayerDamage();
        break;
      case 4:
        // The whole frame is damaged.
        EXPECT_EQ(gfx::RectF(root_surface->content_rect()).ToString(),
                  root_damage.ToString());
        EXPECT_FALSE(frame_data->has_no_damage);

        EndTest();
        break;
    }
    return draw_result;
  }

  virtual void DidCommitAndDrawFrame() OVERRIDE {
    if (!TestEnded())
      layer_tree_host()->SetNeedsCommit();

    if (!child_damage_rect_.IsEmpty()) {
      child_->SetNeedsDisplayRect(child_damage_rect_);
      child_damage_rect_ = gfx::RectF();
    }
  }

  virtual void AfterTest() OVERRIDE {}

  FakeContentLayerClient client_;
  scoped_refptr<FakeContentLayer> root_;
  scoped_refptr<FakeContentLayer> child_;
  gfx::RectF child_damage_rect_;
};

SINGLE_AND_MULTI_THREAD_NOIMPL_TEST_F(LayerTreeHostDamageTestForcedFullDamage);

class LayerTreeHostScrollbarDamageTest : public LayerTreeHostDamageTest {
  virtual void SetupTree() OVERRIDE {
    scoped_refptr<Layer> root_layer = Layer::Create();
    root_layer->SetBounds(gfx::Size(400, 400));
    root_layer->SetMasksToBounds(true);
    layer_tree_host()->SetRootLayer(root_layer);

    scoped_refptr<Layer> scroll_clip_layer = Layer::Create();
    scoped_refptr<Layer> content_layer = FakeContentLayer::Create(&client_);
    content_layer->SetScrollClipLayerId(scroll_clip_layer->id());
    content_layer->SetScrollOffset(gfx::Vector2d(10, 20));
    content_layer->SetBounds(gfx::Size(100, 200));
    scroll_clip_layer->SetBounds(
        gfx::Size(content_layer->bounds().width() - 30,
                  content_layer->bounds().height() - 50));
    scroll_clip_layer->AddChild(content_layer);
    root_layer->AddChild(scroll_clip_layer);

    scoped_refptr<Layer> scrollbar_layer =
        FakePaintedScrollbarLayer::Create(false, true, content_layer->id());
    scrollbar_layer->SetPosition(gfx::Point(300, 300));
    scrollbar_layer->SetBounds(gfx::Size(10, 100));
    scrollbar_layer->ToScrollbarLayer()->SetClipLayer(scroll_clip_layer->id());
    scrollbar_layer->ToScrollbarLayer()->SetScrollLayer(content_layer->id());
    root_layer->AddChild(scrollbar_layer);

    gfx::RectF content_rect(content_layer->position(),
                            content_layer->bounds());
    gfx::RectF scrollbar_rect(scrollbar_layer->position(),
                              scrollbar_layer->bounds());
    EXPECT_FALSE(content_rect.Intersects(scrollbar_rect));

    LayerTreeHostDamageTest::SetupTree();
  }

 private:
  FakeContentLayerClient client_;
};

class LayerTreeHostDamageTestScrollbarDoesDamage
    : public LayerTreeHostScrollbarDamageTest {
  virtual void BeginTest() OVERRIDE {
    did_swaps_ = 0;
    PostSetNeedsCommitToMainThread();
  }

  virtual DrawSwapReadbackResult::DrawResult PrepareToDrawOnThread(
      LayerTreeHostImpl* host_impl,
      LayerTreeHostImpl::FrameData* frame_data,
      DrawSwapReadbackResult::DrawResult draw_result) OVERRIDE {
    EXPECT_EQ(DrawSwapReadbackResult::DRAW_SUCCESS, draw_result);
    RenderSurfaceImpl* root_surface =
        host_impl->active_tree()->root_layer()->render_surface();
    gfx::RectF root_damage =
        root_surface->damage_tracker()->current_damage_rect();
    root_damage.Intersect(root_surface->content_rect());
    switch (did_swaps_) {
      case 0:
        // The first frame has damage, so we should draw and swap.
        break;
      case 1:
        // The second frame should not damage the scrollbars.
        EXPECT_FALSE(root_damage.Intersects(gfx::Rect(300, 300, 10, 100)));
        break;
      case 2:
        // The third frame should damage the scrollbars.
        EXPECT_TRUE(root_damage.Contains(gfx::Rect(300, 300, 10, 100)));
        break;
      case 3:
        // The fourth frame should damage the scrollbars.
        EXPECT_TRUE(root_damage.Contains(gfx::Rect(300, 300, 10, 100)));
        EndTest();
        break;
    }
    return draw_result;
  }

  virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
                                   bool result) OVERRIDE {
    ++did_swaps_;
    EXPECT_TRUE(result);
    LayerImpl* root = host_impl->active_tree()->root_layer();
    LayerImpl* scroll_clip_layer = root->children()[0];
    LayerImpl* scroll_layer = scroll_clip_layer->children()[0];
    switch (did_swaps_) {
      case 1:
        // Test that modifying the position of the content layer (not
        // scrolling) won't damage the scrollbar.
        scroll_layer->SetPosition(gfx::Point(1, 1));
        scroll_layer->SetScrollOffset(scroll_layer->scroll_offset());
        host_impl->SetNeedsRedraw();
        break;
      case 2:
        scroll_layer->ScrollBy(gfx::Vector2dF(10.f, 10.f));
        host_impl->SetNeedsRedraw();
        break;
      case 3:
        scroll_layer->SetBounds(gfx::Size(root->bounds().width() + 60,
                                          root->bounds().height() + 100));
        host_impl->SetNeedsRedraw();
        break;
    }
  }

  virtual void AfterTest() OVERRIDE {
    EXPECT_EQ(4, did_swaps_);
  }

  int did_swaps_;
};

MULTI_THREAD_TEST_F(LayerTreeHostDamageTestScrollbarDoesDamage);

class LayerTreeHostDamageTestScrollbarCommitDoesNoDamage
    : public LayerTreeHostScrollbarDamageTest {
  virtual void BeginTest() OVERRIDE {
    did_swaps_ = 0;
    PostSetNeedsCommitToMainThread();
  }

  virtual DrawSwapReadbackResult::DrawResult PrepareToDrawOnThread(
      LayerTreeHostImpl* host_impl,
      LayerTreeHostImpl::FrameData* frame_data,
      DrawSwapReadbackResult::DrawResult draw_result) OVERRIDE {
    EXPECT_EQ(DrawSwapReadbackResult::DRAW_SUCCESS, draw_result);
    RenderSurfaceImpl* root_surface =
        host_impl->active_tree()->root_layer()->render_surface();
    gfx::RectF root_damage =
        root_surface->damage_tracker()->current_damage_rect();
    root_damage.Intersect(root_surface->content_rect());
    int frame = host_impl->active_tree()->source_frame_number();
    switch (did_swaps_) {
      case 0:
        // The first frame has damage, so we should draw and swap.
        EXPECT_EQ(0, frame);
        break;
      case 1:
        // The second frame has scrolled, so the scrollbar should be damaged.
        EXPECT_EQ(0, frame);
        EXPECT_TRUE(root_damage.Contains(gfx::Rect(300, 300, 10, 100)));
        break;
      case 2:
        // The third frame (after the commit) has no changes, so it shouldn't.
        EXPECT_EQ(1, frame);
        EXPECT_FALSE(root_damage.Intersects(gfx::Rect(300, 300, 10, 100)));
        break;
      default:
        NOTREACHED();
        break;
    }
    return draw_result;
  }

  virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
                                   bool result) OVERRIDE {
    ++did_swaps_;
    EXPECT_TRUE(result);
    LayerImpl* root = host_impl->active_tree()->root_layer();
    LayerImpl* scroll_clip_layer = root->children()[0];
    LayerImpl* scroll_layer = scroll_clip_layer->children()[0];
    switch (did_swaps_) {
      case 1:
        // Scroll on the thread.  This should damage the scrollbar for the
        // next draw on the thread.
        scroll_layer->ScrollBy(gfx::Vector2dF(10.f, 10.f));
        host_impl->SetNeedsRedraw();
        break;
      case 2:
        // Forcibly send the scroll to the main thread.
        PostSetNeedsCommitToMainThread();
        break;
      case 3:
        // First swap after second commit.
        EndTest();
        break;
      default:
        NOTREACHED();
        break;
    }
  }

  virtual void AfterTest() OVERRIDE {
    EXPECT_EQ(3, did_swaps_);
  }

  int did_swaps_;
};

MULTI_THREAD_TEST_F(LayerTreeHostDamageTestScrollbarCommitDoesNoDamage);

class LayerTreeHostDamageTestVisibleTilesStillTriggerDraws
    : public LayerTreeHostDamageTest {

  virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE {
    settings->impl_side_painting = true;
  }

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

  virtual void SetupTree() OVERRIDE {
    scoped_refptr<FakePictureLayer> root = FakePictureLayer::Create(&client_);
    root->SetBounds(gfx::Size(500, 500));
    layer_tree_host()->SetRootLayer(root);
    LayerTreeHostDamageTest::SetupTree();

    swap_count_ = 0;
    prepare_to_draw_count_ = 0;
    update_visible_tile_count_ = 0;
  }

  virtual DrawSwapReadbackResult::DrawResult PrepareToDrawOnThread(
      LayerTreeHostImpl* host_impl,
      LayerTreeHostImpl::FrameData* frame_data,
      DrawSwapReadbackResult::DrawResult draw_result) OVERRIDE {
    EXPECT_EQ(DrawSwapReadbackResult::DRAW_SUCCESS, draw_result);
    prepare_to_draw_count_++;
    switch (prepare_to_draw_count_) {
      case 1:
        // Detect that we have an incomplete tile, during the first frame.
        // The first frame should have damage.
        frame_data->contains_incomplete_tile = true;
        DCHECK(!frame_data->has_no_damage);
        break;
      case 2:
        // Make a no-damage frame. We early out and can't detect
        // incomplete tiles, even if they still exist.
        frame_data->contains_incomplete_tile = false;
        frame_data->has_no_damage = true;
        break;
      case 3:
        // Trigger the last swap for the completed tile.
        frame_data->contains_incomplete_tile = false;
        frame_data->has_no_damage = false;
        EndTest();
        break;
      default:
        NOTREACHED();
        break;
    }

    return draw_result;
  }

  virtual void UpdateVisibleTilesOnThread(
      LayerTreeHostImpl* host_impl) OVERRIDE {
    // Simulate creating some visible tiles (that trigger prepare-to-draws).
    // The first we make into a no-damage-frame during prepare-to-draw (see
    // above). This is to ensure we still get UpdateVisibleTiles calls after
    // a no-damage or aborted frame.
    update_visible_tile_count_++;
    switch (update_visible_tile_count_) {
      case 3:
      case 6:
        host_impl->DidInitializeVisibleTileForTesting();
        break;
      case 7:
        NOTREACHED();
        break;
    }
  }

  virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
                                   bool didSwap) OVERRIDE {
    if (!didSwap)
      return;
    ++swap_count_;
  }

  virtual void AfterTest() OVERRIDE {
    // We should keep getting update-visible-tiles calls
    // until we report there are no more incomplete-tiles.
    EXPECT_EQ(update_visible_tile_count_, 6);
    // First frame, plus two triggered by DidInitializeVisibleTile()
    EXPECT_EQ(prepare_to_draw_count_, 3);
    // First swap, plus final swap (contained damage).
    EXPECT_EQ(swap_count_, 2);
  }

  FakeContentLayerClient client_;
  int swap_count_;
  int prepare_to_draw_count_;
  int update_visible_tile_count_;
};

MULTI_THREAD_TEST_F(LayerTreeHostDamageTestVisibleTilesStillTriggerDraws);

}  // namespace
}  // namespace cc

/* [<][>][^][v][top][bottom][index][help] */