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.
15 #include <sys/types.h>
16 #include <X11/keysym.h>
18 #include <X11/Xutil.h>
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
;
45 struct SimulationSpecification
{
46 string simulation_name
;
47 base::FilePath input_path
;
48 RenderModel model_under_test
;
49 TimeTicks simulation_start_time
;
53 // Forward declarations
55 void _process_events(Simulator
* sim
);
56 void _update_loop(Simulator
* sim
);
60 Simulator(int seconds_per_test
, const base::FilePath
& output_path
)
62 output_path_(output_path
),
63 seconds_per_test_(seconds_per_test
),
67 window_width_(WINDOW_WIDTH
),
68 window_height_(WINDOW_HEIGHT
),
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);
107 if (!sims_remaining_
.size()) {
108 LOG(WARNING
) << "No configuration files loaded.";
112 base::AtExitManager at_exit
;
113 base::MessageLoop loop
;
114 if (!InitX11() || !InitGLContext()) {
115 LOG(FATAL
) << "Failed to set up GUI.";
120 LOG(INFO
) << "Running " << sims_remaining_
.size() << " simulations.";
122 loop
.PostTask(FROM_HERE
,
123 base::Bind(&Simulator::ProcessEvents
,
124 weak_factory_
.GetWeakPtr()));
128 void ProcessEvents() {
129 // Consume all the X events.
130 while (XPending(display_
)) {
132 XNextEvent(display_
, &e
);
137 case ConfigureNotify
:
138 Resize(e
.xconfigure
.width
, e
.xconfigure
.height
);
147 if (UpdateTestStatus())
152 // Initialize X11. Returns true if successful. This method creates the
153 // X11 window. Further initialization is done in X11VideoRenderer.
155 display_
= XOpenDisplay(NULL
);
157 LOG(FATAL
) << "Cannot open display";
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_
,
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
);
186 // Initialize the OpenGL context.
187 bool InitGLContext() {
188 if (!gfx::GLSurface::InitializeOneOff()) {
189 LOG(FATAL
) << "gfx::GLSurface::InitializeOneOff failed";
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
,
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
);
212 if (!glXMakeCurrent(display_
, window_
, gl_context_
)) {
213 glXDestroyContext(display_
, gl_context_
);
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();
234 current_sim_
= ConstructSimulationModel(spec
.model_under_test
,
244 void CleanupCurrentTest() {
245 LOG(INFO
) << "Finished test " << sims_remaining_
.front().simulation_name
;
251 void UpdateCurrentTest() {
252 ++sims_remaining_
.front().frames_rendered
;
255 current_sim_
->Update();
257 glXSwapBuffers(display_
, window_
);
259 XExposeEvent ev
= { Expose
, 0, 1, display_
, window_
,
260 0, 0, WINDOW_WIDTH
, WINDOW_HEIGHT
, 0 };
265 reinterpret_cast<XEvent
*>(&ev
));
267 base::MessageLoop::current()->PostTask(
269 base::Bind(&Simulator::UpdateLoop
, weak_factory_
.GetWeakPtr()));
273 LOG(INFO
) << "Successfully ran " << sims_completed_
.size() << " tests";
275 FILE* f
= base::OpenFile(output_path_
, "w");
278 LOG(ERROR
) << "Failed to open output file " <<
279 output_path_
.LossyDisplayName();
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();
290 "\t\t{\"simulation_name\":\"%s\",\n"
291 "\t\t\t\"render_model\":\"%s\",\n"
292 "\t\t\t\"frames_drawn\":%d\n"
294 i
.simulation_name
.c_str(),
295 ModelToString(i
.model_under_test
),
297 sims_completed_
.pop();
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()) {
325 base::MessageLoop::current()->Quit();
332 void Resize(int width
, int height
) {
333 window_width_
= width
;
334 window_height_
= height
;
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_
;
349 GLXContext gl_context_
;
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.";
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();
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()) {
394 LOG(INFO
) << "(input path is a file)";
395 sim
.QueueTest(inPath
);