Home | History | Annotate | Download | only in bench
      1 // Copyright (c) 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 #if defined(USE_X11)
      6 #include <X11/Xlib.h>
      7 #endif
      8 
      9 #include "base/at_exit.h"
     10 #include "base/bind.h"
     11 #include "base/command_line.h"
     12 #include "base/i18n/icu_util.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/message_loop/message_loop.h"
     15 #include "base/strings/string_split.h"
     16 #include "base/time/time.h"
     17 #include "cc/output/context_provider.h"
     18 #include "gpu/command_buffer/client/gles2_interface.h"
     19 #include "third_party/khronos/GLES2/gl2.h"
     20 #include "third_party/skia/include/core/SkXfermode.h"
     21 #include "ui/aura/client/default_capture_client.h"
     22 #include "ui/aura/env.h"
     23 #include "ui/aura/test/test_focus_client.h"
     24 #include "ui/aura/test/test_screen.h"
     25 #include "ui/aura/window.h"
     26 #include "ui/aura/window_tree_host.h"
     27 #include "ui/base/hit_test.h"
     28 #include "ui/compositor/compositor.h"
     29 #include "ui/compositor/compositor_observer.h"
     30 #include "ui/compositor/debug_utils.h"
     31 #include "ui/compositor/layer.h"
     32 #include "ui/compositor/test/in_process_context_factory.h"
     33 #include "ui/gfx/canvas.h"
     34 #include "ui/gfx/rect.h"
     35 #include "ui/gfx/skia_util.h"
     36 #include "ui/gfx/x/x11_connection.h"
     37 #include "ui/gl/gl_surface.h"
     38 
     39 #ifndef GL_GLEXT_PROTOTYPES
     40 #define GL_GLEXT_PROTOTYPES 1
     41 #endif
     42 #include "third_party/khronos/GLES2/gl2ext.h"
     43 
     44 using base::TimeTicks;
     45 using ui::Compositor;
     46 using ui::Layer;
     47 using ui::LayerDelegate;
     48 
     49 namespace {
     50 
     51 class ColoredLayer : public Layer, public LayerDelegate {
     52  public:
     53   explicit ColoredLayer(SkColor color)
     54       : Layer(ui::LAYER_TEXTURED),
     55         color_(color),
     56         draw_(true) {
     57     set_delegate(this);
     58   }
     59 
     60   virtual ~ColoredLayer() {}
     61 
     62   // Overridden from LayerDelegate:
     63   virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
     64     if (draw_) {
     65       canvas->DrawColor(color_);
     66     }
     67   }
     68 
     69   virtual void OnDelegatedFrameDamage(
     70       const gfx::Rect& damage_rect_in_dip) OVERRIDE {}
     71 
     72   virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {
     73   }
     74 
     75   virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
     76     return base::Closure();
     77   }
     78 
     79   void set_color(SkColor color) { color_ = color; }
     80   void set_draw(bool draw) { draw_ = draw; }
     81 
     82  private:
     83   SkColor color_;
     84   bool draw_;
     85 
     86   DISALLOW_COPY_AND_ASSIGN(ColoredLayer);
     87 };
     88 
     89 const int kFrames = 100;
     90 
     91 // Benchmark base class, hooks up drawing callback and displaying FPS.
     92 class BenchCompositorObserver : public ui::CompositorObserver {
     93  public:
     94   explicit BenchCompositorObserver(int max_frames)
     95       : start_time_(),
     96         frames_(0),
     97         max_frames_(max_frames) {
     98   }
     99 
    100   virtual void OnCompositingDidCommit(ui::Compositor* compositor) OVERRIDE {}
    101 
    102   virtual void OnCompositingStarted(Compositor* compositor,
    103                                     base::TimeTicks start_time) OVERRIDE {}
    104 
    105   virtual void OnCompositingEnded(Compositor* compositor) OVERRIDE {
    106     if (start_time_.is_null()) {
    107       start_time_ = TimeTicks::Now();
    108     } else {
    109       ++frames_;
    110       if (frames_ % kFrames == 0) {
    111         TimeTicks now = TimeTicks::Now();
    112         double ms = (now - start_time_).InMillisecondsF() / kFrames;
    113         LOG(INFO) << "FPS: " << 1000.f / ms << " (" << ms << " ms)";
    114         start_time_ = now;
    115       }
    116     }
    117     if (max_frames_ && frames_ == max_frames_) {
    118       base::MessageLoop::current()->Quit();
    119     } else {
    120       Draw();
    121     }
    122   }
    123 
    124   virtual void OnCompositingAborted(Compositor* compositor) OVERRIDE {}
    125 
    126   virtual void OnCompositingLockStateChanged(
    127       Compositor* compositor) OVERRIDE {}
    128 
    129   virtual void Draw() {}
    130 
    131   int frames() const { return frames_; }
    132 
    133  private:
    134   TimeTicks start_time_;
    135   int frames_;
    136   int max_frames_;
    137 
    138   DISALLOW_COPY_AND_ASSIGN(BenchCompositorObserver);
    139 };
    140 
    141 void ReturnMailbox(scoped_refptr<cc::ContextProvider> context_provider,
    142                    GLuint texture,
    143                    GLuint sync_point,
    144                    bool is_lost) {
    145   gpu::gles2::GLES2Interface* gl = context_provider->ContextGL();
    146   gl->WaitSyncPointCHROMIUM(sync_point);
    147   gl->DeleteTextures(1, &texture);
    148   gl->ShallowFlushCHROMIUM();
    149 }
    150 
    151 // A benchmark that adds a texture layer that is updated every frame.
    152 class WebGLBench : public BenchCompositorObserver {
    153  public:
    154   WebGLBench(ui::ContextFactory* context_factory,
    155              Layer* parent,
    156              Compositor* compositor,
    157              int max_frames)
    158       : BenchCompositorObserver(max_frames),
    159         parent_(parent),
    160         webgl_(ui::LAYER_TEXTURED),
    161         compositor_(compositor),
    162         fbo_(0),
    163         do_draw_(true) {
    164     CommandLine* command_line = CommandLine::ForCurrentProcess();
    165     do_draw_ = !command_line->HasSwitch("disable-draw");
    166 
    167     std::string webgl_size = command_line->GetSwitchValueASCII("webgl-size");
    168     int width = 0;
    169     int height = 0;
    170     if (!webgl_size.empty()) {
    171       std::vector<std::string> split_size;
    172       base::SplitString(webgl_size, 'x', &split_size);
    173       if (split_size.size() == 2) {
    174         width = atoi(split_size[0].c_str());
    175         height = atoi(split_size[1].c_str());
    176       }
    177     }
    178     if (!width || !height) {
    179       width = 800;
    180       height = 600;
    181     }
    182     gfx::Rect bounds(width, height);
    183     webgl_.SetBounds(bounds);
    184     parent_->Add(&webgl_);
    185 
    186     context_provider_ = context_factory->SharedMainThreadContextProvider();
    187     gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
    188     GLuint texture = 0;
    189     gl->GenTextures(1, &texture);
    190     gl->BindTexture(GL_TEXTURE_2D, texture);
    191     gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    192     gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    193     gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    194     gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    195     gl->TexImage2D(GL_TEXTURE_2D,
    196                    0,
    197                    GL_RGBA,
    198                    width,
    199                    height,
    200                    0,
    201                    GL_RGBA,
    202                    GL_UNSIGNED_BYTE,
    203                    NULL);
    204     gpu::Mailbox mailbox;
    205     gl->GenMailboxCHROMIUM(mailbox.name);
    206     gl->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
    207 
    208     gl->GenFramebuffers(1, &fbo_);
    209     gl->BindFramebuffer(GL_FRAMEBUFFER, fbo_);
    210     gl->FramebufferTexture2D(
    211         GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
    212     gl->ClearColor(0.f, 1.f, 0.f, 1.f);
    213     gl->Clear(GL_COLOR_BUFFER_BIT);
    214     gl->Flush();
    215 
    216     GLuint sync_point = gl->InsertSyncPointCHROMIUM();
    217     webgl_.SetTextureMailbox(
    218         cc::TextureMailbox(mailbox, GL_TEXTURE_2D, sync_point),
    219         cc::SingleReleaseCallback::Create(
    220             base::Bind(ReturnMailbox, context_provider_, texture)),
    221         bounds.size());
    222     compositor->AddObserver(this);
    223   }
    224 
    225   virtual ~WebGLBench() {
    226     webgl_.SetShowPaintedContent();
    227     gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
    228     gl->DeleteFramebuffers(1, &fbo_);
    229     compositor_->RemoveObserver(this);
    230   }
    231 
    232   virtual void Draw() OVERRIDE {
    233     if (do_draw_) {
    234       gpu::gles2::GLES2Interface* gl = context_provider_->ContextGL();
    235       gl->ClearColor((frames() % kFrames)*1.0/kFrames, 1.f, 0.f, 1.f);
    236       gl->Clear(GL_COLOR_BUFFER_BIT);
    237       gl->Flush();
    238     }
    239     webgl_.SchedulePaint(gfx::Rect(webgl_.bounds().size()));
    240     compositor_->ScheduleDraw();
    241   }
    242 
    243  private:
    244   Layer* parent_;
    245   Layer webgl_;
    246   Compositor* compositor_;
    247   scoped_refptr<cc::ContextProvider> context_provider_;
    248 
    249   // The FBO that is used to render to the texture.
    250   unsigned int fbo_;
    251 
    252   // Whether or not to draw to the texture every frame.
    253   bool do_draw_;
    254 
    255   DISALLOW_COPY_AND_ASSIGN(WebGLBench);
    256 };
    257 
    258 // A benchmark that paints (in software) all tiles every frame.
    259 class SoftwareScrollBench : public BenchCompositorObserver {
    260  public:
    261   SoftwareScrollBench(ColoredLayer* layer,
    262                       Compositor* compositor,
    263                       int max_frames)
    264       : BenchCompositorObserver(max_frames),
    265         layer_(layer),
    266         compositor_(compositor) {
    267     compositor->AddObserver(this);
    268     layer_->set_draw(
    269         !CommandLine::ForCurrentProcess()->HasSwitch("disable-draw"));
    270   }
    271 
    272   virtual ~SoftwareScrollBench() {
    273     compositor_->RemoveObserver(this);
    274   }
    275 
    276   virtual void Draw() OVERRIDE {
    277     layer_->set_color(
    278         SkColorSetARGBInline(255*(frames() % kFrames)/kFrames, 255, 0, 255));
    279     layer_->SchedulePaint(gfx::Rect(layer_->bounds().size()));
    280   }
    281 
    282  private:
    283   ColoredLayer* layer_;
    284   Compositor* compositor_;
    285 
    286   DISALLOW_COPY_AND_ASSIGN(SoftwareScrollBench);
    287 };
    288 
    289 }  // namespace
    290 
    291 int main(int argc, char** argv) {
    292   CommandLine::Init(argc, argv);
    293 
    294   base::AtExitManager exit_manager;
    295 
    296 #if defined(USE_X11)
    297   // This demo uses InProcessContextFactory which uses X on a separate Gpu
    298   // thread.
    299   gfx::InitializeThreadedX11();
    300 #endif
    301 
    302   gfx::GLSurface::InitializeOneOff();
    303 
    304   // The ContextFactory must exist before any Compositors are created.
    305   scoped_ptr<ui::InProcessContextFactory> context_factory(
    306       new ui::InProcessContextFactory());
    307 
    308   base::i18n::InitializeICU();
    309 
    310   base::MessageLoopForUI message_loop;
    311   aura::Env::CreateInstance(true);
    312   aura::Env::GetInstance()->set_context_factory(context_factory.get());
    313   scoped_ptr<aura::TestScreen> test_screen(
    314       aura::TestScreen::CreateFullscreen());
    315   gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen.get());
    316   scoped_ptr<aura::WindowTreeHost> host(
    317       test_screen->CreateHostForPrimaryDisplay());
    318   aura::client::SetCaptureClient(
    319       host->window(),
    320       new aura::client::DefaultCaptureClient(host->window()));
    321 
    322   scoped_ptr<aura::client::FocusClient> focus_client(
    323       new aura::test::TestFocusClient);
    324   aura::client::SetFocusClient(host->window(), focus_client.get());
    325 
    326   // add layers
    327   ColoredLayer background(SK_ColorRED);
    328   background.SetBounds(host->window()->bounds());
    329   host->window()->layer()->Add(&background);
    330 
    331   ColoredLayer window(SK_ColorBLUE);
    332   window.SetBounds(gfx::Rect(background.bounds().size()));
    333   background.Add(&window);
    334 
    335   Layer content_layer(ui::LAYER_NOT_DRAWN);
    336 
    337   CommandLine* command_line = CommandLine::ForCurrentProcess();
    338   bool force = command_line->HasSwitch("force-render-surface");
    339   content_layer.SetForceRenderSurface(force);
    340   gfx::Rect bounds(window.bounds().size());
    341   bounds.Inset(0, 30, 0, 0);
    342   content_layer.SetBounds(bounds);
    343   window.Add(&content_layer);
    344 
    345   ColoredLayer page_background(SK_ColorWHITE);
    346   page_background.SetBounds(gfx::Rect(content_layer.bounds().size()));
    347   content_layer.Add(&page_background);
    348 
    349   int frames = atoi(command_line->GetSwitchValueASCII("frames").c_str());
    350   scoped_ptr<BenchCompositorObserver> bench;
    351 
    352   if (command_line->HasSwitch("bench-software-scroll")) {
    353     bench.reset(new SoftwareScrollBench(&page_background,
    354                                         host->compositor(),
    355                                         frames));
    356   } else {
    357     bench.reset(new WebGLBench(context_factory.get(),
    358                                &page_background,
    359                                host->compositor(),
    360                                frames));
    361   }
    362 
    363 #ifndef NDEBUG
    364   ui::PrintLayerHierarchy(host->window()->layer(), gfx::Point(100, 100));
    365 #endif
    366 
    367   host->Show();
    368   base::MessageLoopForUI::current()->Run();
    369   focus_client.reset();
    370   host.reset();
    371 
    372   return 0;
    373 }
    374