We started redesigning GpuMemoryBuffer interface to handle multiple buffers [0].
[chromium-blink-merge.git] / gpu / tools / compositor_model_bench / compositor_model_bench.cc
blob1160ff70891e85597b35b9f794998f23d5a776a0
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/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"
39 using base::TimeTicks;
40 using base::DirectoryExists;
41 using base::PathExists;
42 using std::queue;
43 using std::string;
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;
53 // Forward declarations
54 class Simulator;
55 void _process_events(Simulator* sim);
56 void _update_loop(Simulator* sim);
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 display_(NULL),
65 window_(0),
66 gl_context_(NULL),
67 window_width_(WINDOW_WIDTH),
68 window_height_(WINDOW_HEIGHT),
69 weak_factory_(this) {
72 ~Simulator() {
73 // Cleanup GL.
74 glXMakeCurrent(display_, 0, NULL);
75 glXDestroyContext(display_, gl_context_);
77 // Destroy window and display.
78 XDestroyWindow(display_, window_);
79 XCloseDisplay(display_);
82 void QueueTest(const base::FilePath& path) {
83 SimulationSpecification spec;
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.empty()) {
90 LOG(WARNING) << "Simulation for path " << path.LossyDisplayName() <<
91 " will have a blank simulation name, since the file name isn't ASCII";
93 spec.input_path = path;
94 spec.model_under_test = ForwardRenderModel;
95 spec.frames_rendered = 0;
97 sims_remaining_.push(spec);
99 // The following lines are commented out pending the addition
100 // of the new render model once this version gets fully checked in.
102 // spec.model_under_test = KDTreeRenderModel;
103 // sims_remaining_.push(spec);
106 void Run() {
107 if (!sims_remaining_.size()) {
108 LOG(WARNING) << "No configuration files loaded.";
109 return;
112 base::AtExitManager at_exit;
113 base::MessageLoop loop;
114 if (!InitX11() || !InitGLContext()) {
115 LOG(FATAL) << "Failed to set up GUI.";
118 InitBuffers();
120 LOG(INFO) << "Running " << sims_remaining_.size() << " simulations.";
122 loop.PostTask(FROM_HERE,
123 base::Bind(&Simulator::ProcessEvents,
124 weak_factory_.GetWeakPtr()));
125 loop.Run();
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;
146 void UpdateLoop() {
147 if (UpdateTestStatus())
148 UpdateCurrentTest();
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;
161 // Get properties of the screen.
162 int screen = DefaultScreen(display_);
163 int root_window = RootWindow(display_, screen);
165 // Creates the window.
166 window_ = XCreateSimpleWindow(display_,
167 root_window,
170 window_width_,
171 window_height_,
173 BlackPixel(display_, screen),
174 BlackPixel(display_, screen));
175 XStoreName(display_, window_, "Compositor Model Bench");
177 XSelectInput(display_, window_,
178 ExposureMask | KeyPressMask | StructureNotifyMask);
179 XMapWindow(display_, window_);
181 XResizeWindow(display_, window_, WINDOW_WIDTH, WINDOW_HEIGHT);
183 return true;
186 // Initialize the OpenGL context.
187 bool InitGLContext() {
188 if (!gfx::GLSurface::InitializeOneOff()) {
189 LOG(FATAL) << "gfx::GLSurface::InitializeOneOff failed";
190 return false;
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);
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 */);
207 XFree(visual_info_list);
208 if (!gl_context_) {
209 return false;
212 if (!glXMakeCurrent(display_, window_, gl_context_)) {
213 glXDestroyContext(display_, gl_context_);
214 gl_context_ = NULL;
215 return false;
218 return true;
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;
227 RenderNode* root = NULL;
228 if (!(root = BuildRenderTreeFromFile(path))) {
229 LOG(ERROR) << "Couldn't parse test configuration file " <<
230 path.LossyDisplayName();
231 return false;
234 current_sim_ = ConstructSimulationModel(spec.model_under_test,
235 root,
236 window_width_,
237 window_height_);
238 if (!current_sim_)
239 return false;
241 return true;
244 void CleanupCurrentTest() {
245 LOG(INFO) << "Finished test " << sims_remaining_.front().simulation_name;
247 delete current_sim_;
248 current_sim_ = NULL;
251 void UpdateCurrentTest() {
252 ++sims_remaining_.front().frames_rendered;
254 if (current_sim_)
255 current_sim_->Update();
257 glXSwapBuffers(display_, window_);
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));
267 base::MessageLoop::current()->PostTask(
268 FROM_HERE,
269 base::Bind(&Simulator::UpdateLoop, weak_factory_.GetWeakPtr()));
272 void DumpOutput() {
273 LOG(INFO) << "Successfully ran " << sims_completed_.size() << " tests";
275 FILE* f = base::OpenFile(output_path_, "w");
277 if (!f) {
278 LOG(ERROR) << "Failed to open output file " <<
279 output_path_.LossyDisplayName();
280 exit(-1);
283 LOG(INFO) << "Writing results to " << output_path_.LossyDisplayName();
285 fputs("{\n\t\"results\": [\n", f);
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();
300 fputs("\t]\n}", f);
301 base::CloseFile(f);
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();
313 if (sims_remaining_.size() &&
314 sims_remaining_.front().simulation_start_time.is_null()) {
315 while (sims_remaining_.size() && !InitializeNextTest()) {
316 sims_remaining_.pop();
318 if (sims_remaining_.size()) {
319 sims_remaining_.front().simulation_start_time = TimeTicks::Now();
323 if (!sims_remaining_.size()) {
324 DumpOutput();
325 base::MessageLoop::current()->Quit();
326 return false;
329 return true;
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_);
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 Display* display_;
348 Window window_;
349 GLXContext gl_context_;
350 int window_width_;
351 int window_height_;
352 base::WeakPtrFactory<Simulator> weak_factory_;
355 int main(int argc, char* argv[]) {
356 base::CommandLine::Init(argc, argv);
357 const base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
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;
374 int seconds_per_test = 1;
375 if (cl->HasSwitch("duration")) {
376 seconds_per_test = atoi(cl->GetSwitchValueASCII("duration").c_str());
379 Simulator sim(seconds_per_test, cl->GetSwitchValuePath("out"));
380 base::FilePath inPath = cl->GetSwitchValuePath("in");
382 if (!PathExists(inPath)) {
383 LOG(FATAL) << "Path does not exist: " << inPath.LossyDisplayName();
384 return -1;
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);
393 } else {
394 LOG(INFO) << "(input path is a file)";
395 sim.QueueTest(inPath);
398 sim.Run();
400 return 0;