15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This tool is used to benchmark the render model used by the compositor
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Most of this file is derived from the source of the tile_render_bench tool,
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// and has been changed to  support running a sequence of independent
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// simulations for our different render models and test cases.
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdio.h>
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/dir.h>
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/file.h>
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/stat.h>
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/types.h>
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <X11/keysym.h>
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <X11/Xlib.h>
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <X11/Xutil.h>
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <queue>
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/at_exit.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h"
28868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/files/file_enumerator.h"
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
329ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
33eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/tools/compositor_model_bench/render_model_utils.h"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/tools/compositor_model_bench/render_models.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "gpu/tools/compositor_model_bench/render_tree.h"
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gl/gl_surface.h"
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::TimeTicks;
407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochusing base::DirectoryExists;
417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochusing base::PathExists;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using std::queue;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using std::string;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct SimulationSpecification {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string simulation_name;
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath input_path;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RenderModel model_under_test;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TimeTicks simulation_start_time;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int frames_rendered;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Forward declarations
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Simulator;
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void _process_events(Simulator* sim);
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void _update_loop(Simulator* sim);
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Simulator {
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Simulator(int seconds_per_test, const base::FilePath& output_path)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     : current_sim_(NULL),
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       output_path_(output_path),
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       seconds_per_test_(seconds_per_test),
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       weak_factory_(this),
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       display_(NULL),
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       window_(0),
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       gl_context_(NULL),
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       window_width_(WINDOW_WIDTH),
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       window_height_(WINDOW_HEIGHT) {
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ~Simulator() {
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Cleanup GL.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    glXMakeCurrent(display_, 0, NULL);
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    glXDestroyContext(display_, gl_context_);
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Destroy window and display.
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    XDestroyWindow(display_, window_);
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    XCloseDisplay(display_);
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void QueueTest(const base::FilePath& path) {
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SimulationSpecification spec;
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // To get a std::string, we'll try to get an ASCII simulation name.
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If the name of the file wasn't ASCII, this will give an empty simulation
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //  name, but that's not really harmful (we'll still warn about it though.)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    spec.simulation_name = path.BaseName().RemoveExtension().MaybeAsASCII();
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (spec.simulation_name == "") {
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(WARNING) << "Simulation for path " << path.LossyDisplayName() <<
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        " will have a blank simulation name, since the file name isn't ASCII";
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    spec.input_path = path;
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    spec.model_under_test = ForwardRenderModel;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    spec.frames_rendered = 0;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sims_remaining_.push(spec);
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The following lines are commented out pending the addition
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // of the new render model once this version gets fully checked in.
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //  spec.model_under_test = KDTreeRenderModel;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //  sims_remaining_.push(spec);
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Run() {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!sims_remaining_.size()) {
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(WARNING) << "No configuration files loaded.";
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::AtExitManager at_exit;
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    base::MessageLoop loop;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!InitX11() || !InitGLContext()) {
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(FATAL) << "Failed to set up GUI.";
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    InitBuffers();
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(INFO) << "Running " << sims_remaining_.size() << " simulations.";
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    loop.PostTask(FROM_HERE,
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  base::Bind(&Simulator::ProcessEvents,
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             weak_factory_.GetWeakPtr()));
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    loop.Run();
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void ProcessEvents() {
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Consume all the X events.
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (XPending(display_)) {
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      XEvent e;
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      XNextEvent(display_, &e);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      switch (e.type) {
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        case Expose:
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          UpdateLoop();
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        case ConfigureNotify:
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          Resize(e.xconfigure.width, e.xconfigure.height);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        default:
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void UpdateLoop() {
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (UpdateTestStatus())
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      UpdateCurrentTest();
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Initialize X11. Returns true if successful. This method creates the
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // X11 window. Further initialization is done in X11VideoRenderer.
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool InitX11() {
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    display_ = XOpenDisplay(NULL);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!display_) {
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(FATAL) << "Cannot open display";
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Get properties of the screen.
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int screen = DefaultScreen(display_);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int root_window = RootWindow(display_, screen);
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Creates the window.
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    window_ = XCreateSimpleWindow(display_,
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  root_window,
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  1,
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  1,
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  window_width_,
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  window_height_,
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  0,
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  BlackPixel(display_, screen),
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  BlackPixel(display_, screen));
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    XStoreName(display_, window_, "Compositor Model Bench");
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    XSelectInput(display_, window_,
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 ExposureMask | KeyPressMask | StructureNotifyMask);
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    XMapWindow(display_, window_);
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    XResizeWindow(display_, window_, WINDOW_WIDTH, WINDOW_HEIGHT);
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Initialize the OpenGL context.
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool InitGLContext() {
1885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (!gfx::GLSurface::InitializeOneOff()) {
1895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      LOG(FATAL) << "gfx::GLSurface::InitializeOneOff failed";
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    XWindowAttributes attributes;
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    XGetWindowAttributes(display_, window_, &attributes);
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    XVisualInfo visual_info_template;
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    visual_info_template.visualid = XVisualIDFromVisual(attributes.visual);
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int visual_info_count = 0;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    XVisualInfo* visual_info_list = XGetVisualInfo(display_, VisualIDMask,
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                   &visual_info_template,
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                   &visual_info_count);
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = 0; i < visual_info_count && !gl_context_; ++i) {
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gl_context_ = glXCreateContext(display_, visual_info_list + i, 0,
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     True /* Direct rendering */);
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    XFree(visual_info_list);
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!gl_context_) {
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!glXMakeCurrent(display_, window_, gl_context_)) {
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      glXDestroyContext(display_, gl_context_);
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gl_context_ = NULL;
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool InitializeNextTest() {
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SimulationSpecification& spec = sims_remaining_.front();
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(INFO) << "Initializing test for " << spec.simulation_name <<
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "(" << ModelToString(spec.model_under_test) << ")";
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& path = spec.input_path;
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RenderNode* root = NULL;
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!(root = BuildRenderTreeFromFile(path))) {
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "Couldn't parse test configuration file " <<
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          path.LossyDisplayName();
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_sim_ = ConstructSimulationModel(spec.model_under_test,
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            root,
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            window_width_,
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                            window_height_);
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!current_sim_)
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void CleanupCurrentTest() {
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(INFO) << "Finished test " << sims_remaining_.front().simulation_name;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delete current_sim_;
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    current_sim_ = NULL;
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void UpdateCurrentTest() {
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++sims_remaining_.front().frames_rendered;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (current_sim_)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_sim_->Update();
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    glXSwapBuffers(display_, window_);
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    XExposeEvent ev = { Expose, 0, 1, display_, window_,
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0 };
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    XSendEvent(display_,
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      window_,
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      False,
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ExposureMask,
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<XEvent*>(&ev));
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
267c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    base::MessageLoop::current()->PostTask(
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        FROM_HERE,
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Bind(&Simulator::UpdateLoop, weak_factory_.GetWeakPtr()));
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void DumpOutput() {
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(INFO) << "Successfully ran " << sims_completed_.size() << " tests";
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    FILE* f = base::OpenFile(output_path_, "w");
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!f) {
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "Failed to open output file " <<
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        output_path_.LossyDisplayName();
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit(-1);
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(INFO) << "Writing results to " << output_path_.LossyDisplayName();
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fputs("{\n\t\"results\": [\n", f);
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (sims_completed_.size()) {
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SimulationSpecification i = sims_completed_.front();
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      fprintf(f,
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "\t\t{\"simulation_name\":\"%s\",\n"
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "\t\t\t\"render_model\":\"%s\",\n"
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "\t\t\t\"frames_drawn\":%d\n"
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "\t\t},\n",
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        i.simulation_name.c_str(),
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ModelToString(i.model_under_test),
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        i.frames_rendered);
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sims_completed_.pop();
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fputs("\t]\n}", f);
301a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::CloseFile(f);
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool UpdateTestStatus() {
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TimeTicks& current_start = sims_remaining_.front().simulation_start_time;
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::TimeDelta d = TimeTicks::Now() - current_start;
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!current_start.is_null() && d.InSeconds() > seconds_per_test_) {
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CleanupCurrentTest();
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sims_completed_.push(sims_remaining_.front());
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sims_remaining_.pop();
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (sims_remaining_.size() &&
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sims_remaining_.front().simulation_start_time.is_null()) {
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while (sims_remaining_.size() && !InitializeNextTest()) {
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        sims_remaining_.pop();
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (sims_remaining_.size()) {
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        sims_remaining_.front().simulation_start_time = TimeTicks::Now();
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!sims_remaining_.size()) {
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DumpOutput();
325c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::MessageLoop::current()->Quit();
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Resize(int width, int height) {
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    window_width_ = width;
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    window_height_ = height;
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (current_sim_)
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_sim_->Resize(window_width_, window_height_);
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Simulation task list for this execution
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RenderModelSimulator* current_sim_;
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  queue<SimulationSpecification> sims_remaining_;
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  queue<SimulationSpecification> sims_completed_;
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath output_path_;
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Amount of time to run each simulation
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int seconds_per_test_;
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // GUI data
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::WeakPtrFactory<Simulator> weak_factory_;
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Display* display_;
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Window window_;
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GLXContext gl_context_;
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int window_width_;
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int window_height_;
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int main(int argc, char* argv[]) {
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CommandLine::Init(argc, argv);
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const CommandLine* cl = CommandLine::ForCurrentProcess();
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (argc != 3 && argc != 4) {
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(INFO) << "Usage: \n" <<
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cl->GetProgram().BaseName().LossyDisplayName() <<
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "--in=[input path] --out=[output path] (duration=[seconds])\n"
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "The input path specifies either a JSON configuration file or\n"
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "a directory containing only these files\n"
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "(if a directory is specified, simulations will be run for\n"
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "all files in that directory and subdirectories)\n"
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "The optional duration parameter specifies the (integer)\n"
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "number of seconds to be spent on each simulation.\n"
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "Performance measurements for the specified simulation(s) are\n"
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "written to the output path.";
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return -1;
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int seconds_per_test = 1;
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (cl->HasSwitch("duration")) {
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    seconds_per_test = atoi(cl->GetSwitchValueASCII("duration").c_str());
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Simulator sim(seconds_per_test, cl->GetSwitchValuePath("out"));
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath inPath = cl->GetSwitchValuePath("in");
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!PathExists(inPath)) {
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(FATAL) << "Path does not exist: " << inPath.LossyDisplayName();
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return -1;
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (DirectoryExists(inPath)) {
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(INFO) << "(input path is a directory)";
389868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    base::FileEnumerator dirItr(inPath, true, base::FileEnumerator::FILES);
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (base::FilePath f = dirItr.Next(); !f.empty(); f = dirItr.Next()) {
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sim.QueueTest(f);
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(INFO) << "(input path is a file)";
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sim.QueueTest(inPath);
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sim.Run();
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0;
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
402