Home | History | Annotate | Download | only in compositor_model_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 // This tool is used to benchmark the render model used by the compositor
      6 
      7 // Most of this file is derived from the source of the tile_render_bench tool,
      8 // and has been changed to  support running a sequence of independent
      9 // simulations for our different render models and test cases.
     10 
     11 #include <stdio.h>
     12 #include <sys/dir.h>
     13 #include <sys/file.h>
     14 #include <sys/stat.h>
     15 #include <sys/types.h>
     16 #include <X11/keysym.h>
     17 #include <X11/Xlib.h>
     18 #include <X11/Xutil.h>
     19 
     20 #include <queue>
     21 #include <string>
     22 #include <vector>
     23 
     24 #include "base/at_exit.h"
     25 #include "base/basictypes.h"
     26 #include "base/bind.h"
     27 #include "base/command_line.h"
     28 #include "base/file_util.h"
     29 #include "base/files/file_enumerator.h"
     30 #include "base/files/file_path.h"
     31 #include "base/memory/scoped_ptr.h"
     32 #include "base/message_loop/message_loop.h"
     33 #include "base/time/time.h"
     34 
     35 #include "gpu/tools/compositor_model_bench/render_model_utils.h"
     36 #include "gpu/tools/compositor_model_bench/render_models.h"
     37 #include "gpu/tools/compositor_model_bench/render_tree.h"
     38 
     39 
     40 using base::TimeTicks;
     41 using file_util::CloseFile;
     42 using base::DirectoryExists;
     43 using file_util::OpenFile;
     44 using base::PathExists;
     45 using std::queue;
     46 using std::string;
     47 
     48 struct SimulationSpecification {
     49   string simulation_name;
     50   base::FilePath input_path;
     51   RenderModel model_under_test;
     52   TimeTicks simulation_start_time;
     53   int frames_rendered;
     54 };
     55 
     56 // Forward declarations
     57 class Simulator;
     58 void _process_events(Simulator* sim);
     59 void _update_loop(Simulator* sim);
     60 
     61 class Simulator {
     62  public:
     63   Simulator(int seconds_per_test, const base::FilePath& output_path)
     64      : current_sim_(NULL),
     65        output_path_(output_path),
     66        seconds_per_test_(seconds_per_test),
     67        weak_factory_(this),
     68        display_(NULL),
     69        window_(0),
     70        gl_context_(NULL),
     71        window_width_(WINDOW_WIDTH),
     72        window_height_(WINDOW_HEIGHT) {
     73   }
     74 
     75   ~Simulator() {
     76     // Cleanup GL.
     77     glXMakeCurrent(display_, 0, NULL);
     78     glXDestroyContext(display_, gl_context_);
     79 
     80     // Destroy window and display.
     81     XDestroyWindow(display_, window_);
     82     XCloseDisplay(display_);
     83   }
     84 
     85   void QueueTest(const base::FilePath& path) {
     86     SimulationSpecification spec;
     87 
     88     // To get a std::string, we'll try to get an ASCII simulation name.
     89     // If the name of the file wasn't ASCII, this will give an empty simulation
     90     //  name, but that's not really harmful (we'll still warn about it though.)
     91     spec.simulation_name = path.BaseName().RemoveExtension().MaybeAsASCII();
     92     if (spec.simulation_name == "") {
     93       LOG(WARNING) << "Simulation for path " << path.LossyDisplayName() <<
     94         " will have a blank simulation name, since the file name isn't ASCII";
     95     }
     96     spec.input_path = path;
     97     spec.model_under_test = ForwardRenderModel;
     98     spec.frames_rendered = 0;
     99 
    100     sims_remaining_.push(spec);
    101 
    102     // The following lines are commented out pending the addition
    103     // of the new render model once this version gets fully checked in.
    104     //
    105     //  spec.model_under_test = KDTreeRenderModel;
    106     //  sims_remaining_.push(spec);
    107   }
    108 
    109   void Run() {
    110     if (!sims_remaining_.size()) {
    111       LOG(WARNING) << "No configuration files loaded.";
    112       return;
    113     }
    114 
    115     base::AtExitManager at_exit;
    116     base::MessageLoop loop;
    117     if (!InitX11() || !InitGLContext()) {
    118       LOG(FATAL) << "Failed to set up GUI.";
    119     }
    120 
    121     InitBuffers();
    122 
    123     LOG(INFO) << "Running " << sims_remaining_.size() << " simulations.";
    124 
    125     loop.PostTask(FROM_HERE,
    126                   base::Bind(&Simulator::ProcessEvents,
    127                              weak_factory_.GetWeakPtr()));
    128     loop.Run();
    129   }
    130 
    131   void ProcessEvents() {
    132     // Consume all the X events.
    133     while (XPending(display_)) {
    134       XEvent e;
    135       XNextEvent(display_, &e);
    136       switch (e.type) {
    137         case Expose:
    138           UpdateLoop();
    139           break;
    140         case ConfigureNotify:
    141           Resize(e.xconfigure.width, e.xconfigure.height);
    142           break;
    143         default:
    144           break;
    145       }
    146     }
    147   }
    148 
    149   void UpdateLoop() {
    150     if (UpdateTestStatus())
    151       UpdateCurrentTest();
    152   }
    153 
    154  private:
    155   // Initialize X11. Returns true if successful. This method creates the
    156   // X11 window. Further initialization is done in X11VideoRenderer.
    157   bool InitX11() {
    158     display_ = XOpenDisplay(NULL);
    159     if (!display_) {
    160       LOG(FATAL) << "Cannot open display";
    161       return false;
    162     }
    163 
    164     // Get properties of the screen.
    165     int screen = DefaultScreen(display_);
    166     int root_window = RootWindow(display_, screen);
    167 
    168     // Creates the window.
    169     window_ = XCreateSimpleWindow(display_,
    170                                   root_window,
    171                                   1,
    172                                   1,
    173                                   window_width_,
    174                                   window_height_,
    175                                   0,
    176                                   BlackPixel(display_, screen),
    177                                   BlackPixel(display_, screen));
    178     XStoreName(display_, window_, "Compositor Model Bench");
    179 
    180     XSelectInput(display_, window_,
    181                  ExposureMask | KeyPressMask | StructureNotifyMask);
    182     XMapWindow(display_, window_);
    183 
    184     XResizeWindow(display_, window_, WINDOW_WIDTH, WINDOW_HEIGHT);
    185 
    186     return true;
    187   }
    188 
    189   // Initialize the OpenGL context.
    190   bool InitGLContext() {
    191     if (!InitializeGLBindings(gfx::kGLImplementationDesktopGL)) {
    192       LOG(FATAL) << "InitializeGLBindings failed";
    193       return false;
    194     }
    195 
    196     XWindowAttributes attributes;
    197     XGetWindowAttributes(display_, window_, &attributes);
    198     XVisualInfo visual_info_template;
    199     visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
    200     int visual_info_count = 0;
    201     XVisualInfo* visual_info_list = XGetVisualInfo(display_, VisualIDMask,
    202                                                    &visual_info_template,
    203                                                    &visual_info_count);
    204 
    205     for (int i = 0; i < visual_info_count && !gl_context_; ++i) {
    206       gl_context_ = glXCreateContext(display_, visual_info_list + i, 0,
    207                                      True /* Direct rendering */);
    208     }
    209 
    210     XFree(visual_info_list);
    211     if (!gl_context_) {
    212       return false;
    213     }
    214 
    215     if (!glXMakeCurrent(display_, window_, gl_context_)) {
    216       glXDestroyContext(display_, gl_context_);
    217       gl_context_ = NULL;
    218       return false;
    219     }
    220 
    221     return true;
    222   }
    223 
    224   bool InitializeNextTest() {
    225     SimulationSpecification& spec = sims_remaining_.front();
    226     LOG(INFO) << "Initializing test for " << spec.simulation_name <<
    227         "(" << ModelToString(spec.model_under_test) << ")";
    228     const base::FilePath& path = spec.input_path;
    229 
    230     RenderNode* root = NULL;
    231     if (!(root = BuildRenderTreeFromFile(path))) {
    232       LOG(ERROR) << "Couldn't parse test configuration file " <<
    233           path.LossyDisplayName();
    234       return false;
    235     }
    236 
    237     current_sim_ = ConstructSimulationModel(spec.model_under_test,
    238                                             root,
    239                                             window_width_,
    240                                             window_height_);
    241     if (!current_sim_)
    242       return false;
    243 
    244     return true;
    245   }
    246 
    247   void CleanupCurrentTest() {
    248     LOG(INFO) << "Finished test " << sims_remaining_.front().simulation_name;
    249 
    250     delete current_sim_;
    251     current_sim_ = NULL;
    252   }
    253 
    254   void UpdateCurrentTest() {
    255     ++sims_remaining_.front().frames_rendered;
    256 
    257     if (current_sim_)
    258       current_sim_->Update();
    259 
    260     glXSwapBuffers(display_, window_);
    261 
    262     XExposeEvent ev = { Expose, 0, 1, display_, window_,
    263                         0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0 };
    264     XSendEvent(display_,
    265       window_,
    266       False,
    267       ExposureMask,
    268       reinterpret_cast<XEvent*>(&ev));
    269 
    270     base::MessageLoop::current()->PostTask(
    271         FROM_HERE,
    272         base::Bind(&Simulator::UpdateLoop, weak_factory_.GetWeakPtr()));
    273   }
    274 
    275   void DumpOutput() {
    276     LOG(INFO) << "Successfully ran " << sims_completed_.size() << " tests";
    277 
    278     FILE* f = OpenFile(output_path_, "w");
    279 
    280     if (!f) {
    281       LOG(ERROR) << "Failed to open output file " <<
    282         output_path_.LossyDisplayName();
    283       exit(-1);
    284     }
    285 
    286     LOG(INFO) << "Writing results to " << output_path_.LossyDisplayName();
    287 
    288     fputs("{\n\t\"results\": [\n", f);
    289 
    290     while (sims_completed_.size()) {
    291       SimulationSpecification i = sims_completed_.front();
    292       fprintf(f,
    293         "\t\t{\"simulation_name\":\"%s\",\n"
    294         "\t\t\t\"render_model\":\"%s\",\n"
    295         "\t\t\t\"frames_drawn\":%d\n"
    296         "\t\t},\n",
    297         i.simulation_name.c_str(),
    298         ModelToString(i.model_under_test),
    299         i.frames_rendered);
    300       sims_completed_.pop();
    301     }
    302 
    303     fputs("\t]\n}", f);
    304     CloseFile(f);
    305   }
    306 
    307   bool UpdateTestStatus() {
    308     TimeTicks& current_start = sims_remaining_.front().simulation_start_time;
    309     base::TimeDelta d = TimeTicks::Now() - current_start;
    310     if (!current_start.is_null() && d.InSeconds() > seconds_per_test_) {
    311       CleanupCurrentTest();
    312       sims_completed_.push(sims_remaining_.front());
    313       sims_remaining_.pop();
    314     }
    315 
    316     if (sims_remaining_.size() &&
    317       sims_remaining_.front().simulation_start_time.is_null()) {
    318       while (sims_remaining_.size() && !InitializeNextTest()) {
    319         sims_remaining_.pop();
    320       }
    321       if (sims_remaining_.size()) {
    322         sims_remaining_.front().simulation_start_time = TimeTicks::Now();
    323       }
    324     }
    325 
    326     if (!sims_remaining_.size()) {
    327       DumpOutput();
    328       base::MessageLoop::current()->Quit();
    329       return false;
    330     }
    331 
    332     return true;
    333   }
    334 
    335   void Resize(int width, int height) {
    336     window_width_ = width;
    337     window_height_ = height;
    338     if (current_sim_)
    339       current_sim_->Resize(window_width_, window_height_);
    340   }
    341 
    342   // Simulation task list for this execution
    343   RenderModelSimulator* current_sim_;
    344   queue<SimulationSpecification> sims_remaining_;
    345   queue<SimulationSpecification> sims_completed_;
    346   base::FilePath output_path_;
    347   // Amount of time to run each simulation
    348   int seconds_per_test_;
    349   // GUI data
    350   base::WeakPtrFactory<Simulator> weak_factory_;
    351   Display* display_;
    352   Window window_;
    353   GLXContext gl_context_;
    354   int window_width_;
    355   int window_height_;
    356 };
    357 
    358 int main(int argc, char* argv[]) {
    359   CommandLine::Init(argc, argv);
    360   const CommandLine* cl = CommandLine::ForCurrentProcess();
    361 
    362   if (argc != 3 && argc != 4) {
    363     LOG(INFO) << "Usage: \n" <<
    364       cl->GetProgram().BaseName().LossyDisplayName() <<
    365       "--in=[input path] --out=[output path] (duration=[seconds])\n"
    366       "The input path specifies either a JSON configuration file or\n"
    367       "a directory containing only these files\n"
    368       "(if a directory is specified, simulations will be run for\n"
    369       "all files in that directory and subdirectories)\n"
    370       "The optional duration parameter specifies the (integer)\n"
    371       "number of seconds to be spent on each simulation.\n"
    372       "Performance measurements for the specified simulation(s) are\n"
    373       "written to the output path.";
    374     return -1;
    375   }
    376 
    377   int seconds_per_test = 1;
    378   if (cl->HasSwitch("duration")) {
    379     seconds_per_test = atoi(cl->GetSwitchValueASCII("duration").c_str());
    380   }
    381 
    382   Simulator sim(seconds_per_test, cl->GetSwitchValuePath("out"));
    383   base::FilePath inPath = cl->GetSwitchValuePath("in");
    384 
    385   if (!PathExists(inPath)) {
    386     LOG(FATAL) << "Path does not exist: " << inPath.LossyDisplayName();
    387     return -1;
    388   }
    389 
    390   if (DirectoryExists(inPath)) {
    391     LOG(INFO) << "(input path is a directory)";
    392     base::FileEnumerator dirItr(inPath, true, base::FileEnumerator::FILES);
    393     for (base::FilePath f = dirItr.Next(); !f.empty(); f = dirItr.Next()) {
    394       sim.QueueTest(f);
    395     }
    396   } else {
    397     LOG(INFO) << "(input path is a file)";
    398     sim.QueueTest(inPath);
    399   }
    400 
    401   sim.Run();
    402 
    403   return 0;
    404 }
    405