Home | History | Annotate | Download | only in trees
      1 // Copyright 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "cc/trees/layer_tree_host.h"
      6 
      7 #include <sstream>
      8 
      9 #include "base/file_util.h"
     10 #include "base/files/file_path.h"
     11 #include "base/path_service.h"
     12 #include "base/strings/string_piece.h"
     13 #include "base/time/time.h"
     14 #include "cc/layers/content_layer.h"
     15 #include "cc/layers/nine_patch_layer.h"
     16 #include "cc/layers/solid_color_layer.h"
     17 #include "cc/layers/texture_layer.h"
     18 #include "cc/resources/texture_mailbox.h"
     19 #include "cc/test/fake_content_layer_client.h"
     20 #include "cc/test/lap_timer.h"
     21 #include "cc/test/layer_tree_json_parser.h"
     22 #include "cc/test/layer_tree_test.h"
     23 #include "cc/test/paths.h"
     24 #include "cc/trees/layer_tree_impl.h"
     25 #include "testing/perf/perf_test.h"
     26 
     27 namespace cc {
     28 namespace {
     29 
     30 static const int kTimeLimitMillis = 2000;
     31 static const int kWarmupRuns = 5;
     32 static const int kTimeCheckInterval = 10;
     33 
     34 class LayerTreeHostPerfTest : public LayerTreeTest {
     35  public:
     36   LayerTreeHostPerfTest()
     37       : draw_timer_(kWarmupRuns,
     38                     base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
     39                     kTimeCheckInterval),
     40         commit_timer_(0, base::TimeDelta(), 1),
     41         full_damage_each_frame_(false),
     42         animation_driven_drawing_(false),
     43         measure_commit_cost_(false) {
     44     fake_content_layer_client_.set_paint_all_opaque(true);
     45   }
     46 
     47   virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE {
     48     settings->throttle_frame_production = false;
     49   }
     50 
     51   virtual void BeginTest() OVERRIDE {
     52     BuildTree();
     53     PostSetNeedsCommitToMainThread();
     54   }
     55 
     56   virtual void Animate(base::TimeTicks monotonic_time) OVERRIDE {
     57     if (animation_driven_drawing_ && !TestEnded())
     58       layer_tree_host()->SetNeedsAnimate();
     59   }
     60 
     61   virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
     62     if (measure_commit_cost_)
     63       commit_timer_.Start();
     64   }
     65 
     66   virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
     67     if (measure_commit_cost_ && draw_timer_.IsWarmedUp()) {
     68       commit_timer_.NextLap();
     69     }
     70   }
     71 
     72   virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE {
     73     if (TestEnded() || CleanUpStarted())
     74       return;
     75     draw_timer_.NextLap();
     76     if (draw_timer_.HasTimeLimitExpired()) {
     77       CleanUpAndEndTest(impl);
     78       return;
     79     }
     80     if (!animation_driven_drawing_)
     81       impl->SetNeedsRedraw();
     82     if (full_damage_each_frame_)
     83       impl->SetFullRootLayerDamage();
     84   }
     85 
     86   virtual void CleanUpAndEndTest(LayerTreeHostImpl* host_impl) { EndTest(); }
     87 
     88   virtual bool CleanUpStarted() { return false; }
     89 
     90   virtual void BuildTree() {}
     91 
     92   virtual void AfterTest() OVERRIDE {
     93     CHECK(!test_name_.empty()) << "Must SetTestName() before AfterTest().";
     94     perf_test::PrintResult("layer_tree_host_frame_time", "", test_name_,
     95                            1000 * draw_timer_.MsPerLap(), "us", true);
     96     if (measure_commit_cost_) {
     97       perf_test::PrintResult("layer_tree_host_commit_time", "", test_name_,
     98                              1000 * commit_timer_.MsPerLap(), "us", true);
     99     }
    100   }
    101 
    102  protected:
    103   LapTimer draw_timer_;
    104   LapTimer commit_timer_;
    105 
    106   std::string test_name_;
    107   FakeContentLayerClient fake_content_layer_client_;
    108   bool full_damage_each_frame_;
    109   bool animation_driven_drawing_;
    110 
    111   bool measure_commit_cost_;
    112 };
    113 
    114 
    115 class LayerTreeHostPerfTestJsonReader : public LayerTreeHostPerfTest {
    116  public:
    117   LayerTreeHostPerfTestJsonReader()
    118       : LayerTreeHostPerfTest() {
    119   }
    120 
    121   void SetTestName(const std::string& name) {
    122     test_name_ = name;
    123   }
    124 
    125   void ReadTestFile(const std::string& name) {
    126     base::FilePath test_data_dir;
    127     ASSERT_TRUE(PathService::Get(CCPaths::DIR_TEST_DATA, &test_data_dir));
    128     base::FilePath json_file = test_data_dir.AppendASCII(name + ".json");
    129     ASSERT_TRUE(base::ReadFileToString(json_file, &json_));
    130   }
    131 
    132   virtual void BuildTree() OVERRIDE {
    133     gfx::Size viewport = gfx::Size(720, 1038);
    134     layer_tree_host()->SetViewportSize(viewport);
    135     scoped_refptr<Layer> root = ParseTreeFromJson(json_,
    136                                                   &fake_content_layer_client_);
    137     ASSERT_TRUE(root.get());
    138     layer_tree_host()->SetRootLayer(root);
    139   }
    140 
    141  private:
    142   std::string json_;
    143 };
    144 
    145 // Simulates a tab switcher scene with two stacks of 10 tabs each.
    146 TEST_F(LayerTreeHostPerfTestJsonReader, TenTenSingleThread) {
    147   SetTestName("10_10_single_thread");
    148   ReadTestFile("10_10_layer_tree");
    149   RunTest(false, false, false);
    150 }
    151 
    152 TEST_F(LayerTreeHostPerfTestJsonReader, TenTenThreadedImplSide) {
    153   SetTestName("10_10_threaded_impl_side");
    154   ReadTestFile("10_10_layer_tree");
    155   RunTestWithImplSidePainting();
    156 }
    157 
    158 // Simulates a tab switcher scene with two stacks of 10 tabs each.
    159 TEST_F(LayerTreeHostPerfTestJsonReader,
    160        TenTenSingleThread_FullDamageEachFrame) {
    161   full_damage_each_frame_ = true;
    162   SetTestName("10_10_single_thread_full_damage_each_frame");
    163   ReadTestFile("10_10_layer_tree");
    164   RunTest(false, false, false);
    165 }
    166 
    167 TEST_F(LayerTreeHostPerfTestJsonReader,
    168        TenTenThreadedImplSide_FullDamageEachFrame) {
    169   full_damage_each_frame_ = true;
    170   SetTestName("10_10_threaded_impl_side_full_damage_each_frame");
    171   ReadTestFile("10_10_layer_tree");
    172   RunTestWithImplSidePainting();
    173 }
    174 
    175 // Invalidates a leaf layer in the tree on the main thread after every commit.
    176 class LayerTreeHostPerfTestLeafInvalidates
    177     : public LayerTreeHostPerfTestJsonReader {
    178  public:
    179   virtual void BuildTree() OVERRIDE {
    180     LayerTreeHostPerfTestJsonReader::BuildTree();
    181 
    182     // Find a leaf layer.
    183     for (layer_to_invalidate_ = layer_tree_host()->root_layer();
    184          layer_to_invalidate_->children().size();
    185          layer_to_invalidate_ = layer_to_invalidate_->children()[0]) {}
    186   }
    187 
    188   virtual void DidCommitAndDrawFrame() OVERRIDE {
    189     if (TestEnded())
    190       return;
    191 
    192     static bool flip = true;
    193     layer_to_invalidate_->SetOpacity(flip ? 1.f : 0.5f);
    194     flip = !flip;
    195   }
    196 
    197  protected:
    198   Layer* layer_to_invalidate_;
    199 };
    200 
    201 // Simulates a tab switcher scene with two stacks of 10 tabs each. Invalidate a
    202 // property on a leaf layer in the tree every commit.
    203 TEST_F(LayerTreeHostPerfTestLeafInvalidates, TenTenSingleThread) {
    204   SetTestName("10_10_single_thread_leaf_invalidates");
    205   ReadTestFile("10_10_layer_tree");
    206   RunTest(false, false, false);
    207 }
    208 
    209 TEST_F(LayerTreeHostPerfTestLeafInvalidates, TenTenThreadedImplSide) {
    210   SetTestName("10_10_threaded_impl_side_leaf_invalidates");
    211   ReadTestFile("10_10_layer_tree");
    212   RunTestWithImplSidePainting();
    213 }
    214 
    215 // Simulates main-thread scrolling on each frame.
    216 class ScrollingLayerTreePerfTest : public LayerTreeHostPerfTestJsonReader {
    217  public:
    218   ScrollingLayerTreePerfTest()
    219       : LayerTreeHostPerfTestJsonReader() {
    220   }
    221 
    222   virtual void BuildTree() OVERRIDE {
    223     LayerTreeHostPerfTestJsonReader::BuildTree();
    224     scrollable_ = layer_tree_host()->root_layer()->children()[1];
    225     ASSERT_TRUE(scrollable_.get());
    226   }
    227 
    228   virtual void Layout() OVERRIDE {
    229     static const gfx::Vector2d delta = gfx::Vector2d(0, 10);
    230     scrollable_->SetScrollOffset(scrollable_->scroll_offset() + delta);
    231   }
    232 
    233  private:
    234   scoped_refptr<Layer> scrollable_;
    235 };
    236 
    237 TEST_F(ScrollingLayerTreePerfTest, LongScrollablePageSingleThread) {
    238   SetTestName("long_scrollable_page");
    239   ReadTestFile("long_scrollable_page");
    240   RunTest(false, false, false);
    241 }
    242 
    243 TEST_F(ScrollingLayerTreePerfTest, LongScrollablePageThreadedImplSide) {
    244   SetTestName("long_scrollable_page_threaded_impl_side");
    245   ReadTestFile("long_scrollable_page");
    246   RunTestWithImplSidePainting();
    247 }
    248 
    249 static void EmptyReleaseCallback(unsigned sync_point, bool lost_resource) {}
    250 
    251 // Simulates main-thread scrolling on each frame.
    252 class BrowserCompositorInvalidateLayerTreePerfTest
    253     : public LayerTreeHostPerfTestJsonReader {
    254  public:
    255   BrowserCompositorInvalidateLayerTreePerfTest()
    256       : next_sync_point_(1), clean_up_started_(false) {}
    257 
    258   virtual void BuildTree() OVERRIDE {
    259     LayerTreeHostPerfTestJsonReader::BuildTree();
    260     tab_contents_ =
    261         static_cast<TextureLayer*>(
    262             layer_tree_host()->root_layer()->children()[0]->
    263                                              children()[0]->
    264                                              children()[0]->
    265                                              children()[0].get());
    266     ASSERT_TRUE(tab_contents_.get());
    267   }
    268 
    269   virtual void WillCommit() OVERRIDE {
    270     gpu::Mailbox gpu_mailbox;
    271     std::ostringstream name_stream;
    272     name_stream << "name" << next_sync_point_;
    273     gpu_mailbox.SetName(
    274         reinterpret_cast<const int8*>(name_stream.str().c_str()));
    275     scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create(
    276         base::Bind(&EmptyReleaseCallback));
    277     TextureMailbox mailbox(gpu_mailbox, next_sync_point_);
    278     next_sync_point_++;
    279 
    280     tab_contents_->SetTextureMailbox(mailbox, callback.Pass());
    281   }
    282 
    283   virtual void DidCommit() OVERRIDE {
    284     if (CleanUpStarted())
    285       return;
    286     layer_tree_host()->SetNeedsCommit();
    287   }
    288 
    289   virtual void DidCommitAndDrawFrame() OVERRIDE {
    290     if (CleanUpStarted())
    291       EndTest();
    292   }
    293 
    294   virtual void CleanUpAndEndTest(LayerTreeHostImpl* host_impl) OVERRIDE {
    295     clean_up_started_ = true;
    296     MainThreadTaskRunner()->PostTask(
    297         FROM_HERE,
    298         base::Bind(&BrowserCompositorInvalidateLayerTreePerfTest::
    299                         CleanUpAndEndTestOnMainThread,
    300                    base::Unretained(this)));
    301   }
    302 
    303   void CleanUpAndEndTestOnMainThread() {
    304     tab_contents_->SetTextureMailbox(TextureMailbox(),
    305                                      scoped_ptr<SingleReleaseCallback>());
    306   }
    307 
    308   virtual bool CleanUpStarted() OVERRIDE { return clean_up_started_; }
    309 
    310  private:
    311   scoped_refptr<TextureLayer> tab_contents_;
    312   unsigned next_sync_point_;
    313   bool clean_up_started_;
    314 };
    315 
    316 TEST_F(BrowserCompositorInvalidateLayerTreePerfTest, DenseBrowserUI) {
    317   SetTestName("dense_layer_tree");
    318   ReadTestFile("dense_layer_tree");
    319   RunTestWithImplSidePainting();
    320 }
    321 
    322 // Simulates a page with several large, transformed and animated layers.
    323 TEST_F(LayerTreeHostPerfTestJsonReader, HeavyPageThreadedImplSide) {
    324   animation_driven_drawing_ = true;
    325   measure_commit_cost_ = true;
    326   SetTestName("heavy_page");
    327   ReadTestFile("heavy_layer_tree");
    328   RunTestWithImplSidePainting();
    329 }
    330 
    331 class PageScaleImplSidePaintingPerfTest
    332     : public LayerTreeHostPerfTestJsonReader {
    333  public:
    334   PageScaleImplSidePaintingPerfTest()
    335       : max_scale_(16.f), min_scale_(1.f / max_scale_) {}
    336 
    337   virtual void SetupTree() OVERRIDE {
    338     layer_tree_host()->SetPageScaleFactorAndLimits(1.f, min_scale_, max_scale_);
    339   }
    340 
    341   virtual void ApplyScrollAndScale(gfx::Vector2d scroll_delta,
    342                                    float scale_delta) OVERRIDE {
    343     float page_scale_factor = layer_tree_host()->page_scale_factor();
    344     page_scale_factor *= scale_delta;
    345     layer_tree_host()->SetPageScaleFactorAndLimits(
    346         page_scale_factor, min_scale_, max_scale_);
    347   }
    348 
    349   virtual void AnimateLayers(LayerTreeHostImpl* host_impl,
    350                              base::TimeTicks monotonic_time) OVERRIDE {
    351     if (!host_impl->pinch_gesture_active()) {
    352       host_impl->PinchGestureBegin();
    353       start_time_ = monotonic_time;
    354     }
    355     gfx::Point anchor(200, 200);
    356 
    357     float seconds = (monotonic_time - start_time_).InSecondsF();
    358 
    359     // Every half second, zoom from min scale to max scale.
    360     float interval = 0.5f;
    361 
    362     // Start time in the middle of the interval when zoom = 1.
    363     seconds += interval / 2.f;
    364 
    365     // Stack two ranges together to go up from min to max and down from
    366     // max to min in the next so as not to have a zoom discrepancy.
    367     float time_in_two_intervals = fmod(seconds, 2.f * interval) / interval;
    368 
    369     // Map everything to go from min to max between 0 and 1.
    370     float time_in_one_interval =
    371         time_in_two_intervals > 1.f ? 2.f - time_in_two_intervals
    372                                     : time_in_two_intervals;
    373     // Normalize time to -1..1.
    374     float normalized = 2.f * time_in_one_interval - 1.f;
    375     float scale_factor = std::abs(normalized) * (max_scale_ - 1.f) + 1.f;
    376     float total_scale = normalized < 0.f ? 1.f / scale_factor : scale_factor;
    377 
    378     float desired_delta =
    379         total_scale / host_impl->active_tree()->total_page_scale_factor();
    380     host_impl->PinchGestureUpdate(desired_delta, anchor);
    381   }
    382 
    383  private:
    384   float max_scale_;
    385   float min_scale_;
    386   base::TimeTicks start_time_;
    387 };
    388 
    389 TEST_F(PageScaleImplSidePaintingPerfTest, HeavyPage) {
    390   measure_commit_cost_ = true;
    391   SetTestName("heavy_page_page_scale");
    392   ReadTestFile("heavy_layer_tree");
    393   RunTestWithImplSidePainting();
    394 }
    395 
    396 }  // namespace
    397 }  // namespace cc
    398