Upstreaming browser/ui/uikit_ui_util from iOS.
[chromium-blink-merge.git] / gpu / tools / compositor_model_bench / compositor_model_bench.cc
blobb3e209bbb060a9dd5fdf5a8cb0729cbaba9cff26
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.
5 // This tool is used to benchmark the render model used by the compositor
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.
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>
20 #include <queue>
21 #include <string>
22 #include <vector>
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/location.h"
32 #include "base/memory/scoped_ptr.h"
33 #include "base/message_loop/message_loop.h"
34 #include "base/single_thread_task_runner.h"
35 #include "base/thread_task_runner_handle.h"
36 #include "base/time/time.h"
37 #include "gpu/tools/compositor_model_bench/render_model_utils.h"
38 #include "gpu/tools/compositor_model_bench/render_models.h"
39 #include "gpu/tools/compositor_model_bench/render_tree.h"
40 #include "ui/gl/gl_surface.h"
42 using base::TimeTicks;
43 using base::DirectoryExists;
44 using base::PathExists;
45 using std::queue;
46 using std::string;
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;
56 // Forward declarations
57 class Simulator;
58 void _process_events(Simulator* sim);
59 void _update_loop(Simulator* sim);
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 display_(NULL),
68 window_(0),
69 gl_context_(NULL),
70 window_width_(WINDOW_WIDTH),
71 window_height_(WINDOW_HEIGHT),
72 weak_factory_(this) {
75 ~Simulator() {
76 // Cleanup GL.
77 glXMakeCurrent(display_, 0, NULL);
78 glXDestroyContext(display_, gl_context_);
80 // Destroy window and display.
81 XDestroyWindow(display_, window_);
82 XCloseDisplay(display_);
85 void QueueTest(const base::FilePath& path) {
86 SimulationSpecification spec;
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.empty()) {
93 LOG(WARNING) << "Simulation for path " << path.LossyDisplayName() <<
94 " will have a blank simulation name, since the file name isn't ASCII";
96 spec.input_path = path;
97 spec.model_under_test = ForwardRenderModel;
98 spec.frames_rendered = 0;
100 sims_remaining_.push(spec);
102 // The following lines are commented out pending the addition
103 // of the new render model once this version gets fully checked in.
105 // spec.model_under_test = KDTreeRenderModel;
106 // sims_remaining_.push(spec);
109 void Run() {
110 if (!sims_remaining_.size()) {
111 LOG(WARNING) << "No configuration files loaded.";
112 return;
115 base::AtExitManager at_exit;
116 base::MessageLoop loop;
117 if (!InitX11() || !InitGLContext()) {
118 LOG(FATAL) << "Failed to set up GUI.";
121 InitBuffers();
123 LOG(INFO) << "Running " << sims_remaining_.size() << " simulations.";
125 loop.task_runner()->PostTask(
126 FROM_HERE,
127 base::Bind(&Simulator::ProcessEvents, weak_factory_.GetWeakPtr()));
128 loop.Run();
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;
149 void UpdateLoop() {
150 if (UpdateTestStatus())
151 UpdateCurrentTest();
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;
164 // Get properties of the screen.
165 int screen = DefaultScreen(display_);
166 int root_window = RootWindow(display_, screen);
168 // Creates the window.
169 window_ = XCreateSimpleWindow(display_,
170 root_window,
173 window_width_,
174 window_height_,
176 BlackPixel(display_, screen),
177 BlackPixel(display_, screen));
178 XStoreName(display_, window_, "Compositor Model Bench");
180 XSelectInput(display_, window_,
181 ExposureMask | KeyPressMask | StructureNotifyMask);
182 XMapWindow(display_, window_);
184 XResizeWindow(display_, window_, WINDOW_WIDTH, WINDOW_HEIGHT);
186 return true;
189 // Initialize the OpenGL context.
190 bool InitGLContext() {
191 if (!gfx::GLSurface::InitializeOneOff()) {
192 LOG(FATAL) << "gfx::GLSurface::InitializeOneOff failed";
193 return false;
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);
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 */);
210 XFree(visual_info_list);
211 if (!gl_context_) {
212 return false;
215 if (!glXMakeCurrent(display_, window_, gl_context_)) {
216 glXDestroyContext(display_, gl_context_);
217 gl_context_ = NULL;
218 return false;
221 return true;
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;
230 RenderNode* root = NULL;
231 if (!(root = BuildRenderTreeFromFile(path))) {
232 LOG(ERROR) << "Couldn't parse test configuration file " <<
233 path.LossyDisplayName();
234 return false;
237 current_sim_ = ConstructSimulationModel(spec.model_under_test,
238 root,
239 window_width_,
240 window_height_);
241 if (!current_sim_)
242 return false;
244 return true;
247 void CleanupCurrentTest() {
248 LOG(INFO) << "Finished test " << sims_remaining_.front().simulation_name;
250 delete current_sim_;
251 current_sim_ = NULL;
254 void UpdateCurrentTest() {
255 ++sims_remaining_.front().frames_rendered;
257 if (current_sim_)
258 current_sim_->Update();
260 glXSwapBuffers(display_, window_);
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));
270 base::ThreadTaskRunnerHandle::Get()->PostTask(
271 FROM_HERE,
272 base::Bind(&Simulator::UpdateLoop, weak_factory_.GetWeakPtr()));
275 void DumpOutput() {
276 LOG(INFO) << "Successfully ran " << sims_completed_.size() << " tests";
278 FILE* f = base::OpenFile(output_path_, "w");
280 if (!f) {
281 LOG(ERROR) << "Failed to open output file " <<
282 output_path_.LossyDisplayName();
283 exit(-1);
286 LOG(INFO) << "Writing results to " << output_path_.LossyDisplayName();
288 fputs("{\n\t\"results\": [\n", f);
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();
303 fputs("\t]\n}", f);
304 base::CloseFile(f);
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();
316 if (sims_remaining_.size() &&
317 sims_remaining_.front().simulation_start_time.is_null()) {
318 while (sims_remaining_.size() && !InitializeNextTest()) {
319 sims_remaining_.pop();
321 if (sims_remaining_.size()) {
322 sims_remaining_.front().simulation_start_time = TimeTicks::Now();
326 if (!sims_remaining_.size()) {
327 DumpOutput();
328 base::MessageLoop::current()->Quit();
329 return false;
332 return true;
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_);
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 Display* display_;
351 Window window_;
352 GLXContext gl_context_;
353 int window_width_;
354 int window_height_;
355 base::WeakPtrFactory<Simulator> weak_factory_;
358 int main(int argc, char* argv[]) {
359 base::CommandLine::Init(argc, argv);
360 const base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
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;
377 int seconds_per_test = 1;
378 if (cl->HasSwitch("duration")) {
379 seconds_per_test = atoi(cl->GetSwitchValueASCII("duration").c_str());
382 Simulator sim(seconds_per_test, cl->GetSwitchValuePath("out"));
383 base::FilePath inPath = cl->GetSwitchValuePath("in");
385 if (!PathExists(inPath)) {
386 LOG(FATAL) << "Path does not exist: " << inPath.LossyDisplayName();
387 return -1;
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);
396 } else {
397 LOG(INFO) << "(input path is a file)";
398 sim.QueueTest(inPath);
401 sim.Run();
403 return 0;