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