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/file_util.h"
29 #include "base/files/file_enumerator.h"
30 #include "base/files/file_path.h"
31 #include "base/memory/scoped_ptr.h"
32 #include "base/message_loop/message_loop.h"
33 #include "base/time/time.h"
35 #include "gpu/tools/compositor_model_bench/render_model_utils.h"
36 #include "gpu/tools/compositor_model_bench/render_models.h"
37 #include "gpu/tools/compositor_model_bench/render_tree.h"
40 using base::TimeTicks
;
41 using file_util::CloseFile
;
42 using base::DirectoryExists
;
43 using file_util::OpenFile
;
44 using base::PathExists
;
48 struct SimulationSpecification
{
49 string simulation_name
;
50 base::FilePath input_path
;
51 RenderModel model_under_test
;
52 TimeTicks simulation_start_time
;
56 // Forward declarations
58 void _process_events(Simulator
* sim
);
59 void _update_loop(Simulator
* sim
);
63 Simulator(int seconds_per_test
, const base::FilePath
& output_path
)
65 output_path_(output_path
),
66 seconds_per_test_(seconds_per_test
),
71 window_width_(WINDOW_WIDTH
),
72 window_height_(WINDOW_HEIGHT
) {
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
== "") {
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);
110 if (!sims_remaining_
.size()) {
111 LOG(WARNING
) << "No configuration files loaded.";
115 base::AtExitManager at_exit
;
116 base::MessageLoop loop
;
117 if (!InitX11() || !InitGLContext()) {
118 LOG(FATAL
) << "Failed to set up GUI.";
123 LOG(INFO
) << "Running " << sims_remaining_
.size() << " simulations.";
125 loop
.PostTask(FROM_HERE
,
126 base::Bind(&Simulator::ProcessEvents
,
127 weak_factory_
.GetWeakPtr()));
131 void ProcessEvents() {
132 // Consume all the X events.
133 while (XPending(display_
)) {
135 XNextEvent(display_
, &e
);
140 case ConfigureNotify
:
141 Resize(e
.xconfigure
.width
, e
.xconfigure
.height
);
150 if (UpdateTestStatus())
155 // Initialize X11. Returns true if successful. This method creates the
156 // X11 window. Further initialization is done in X11VideoRenderer.
158 display_
= XOpenDisplay(NULL
);
160 LOG(FATAL
) << "Cannot open display";
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_
,
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
);
189 // Initialize the OpenGL context.
190 bool InitGLContext() {
191 if (!InitializeGLBindings(gfx::kGLImplementationDesktopGL
)) {
192 LOG(FATAL
) << "InitializeGLBindings failed";
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
,
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
);
215 if (!glXMakeCurrent(display_
, window_
, gl_context_
)) {
216 glXDestroyContext(display_
, gl_context_
);
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();
237 current_sim_
= ConstructSimulationModel(spec
.model_under_test
,
247 void CleanupCurrentTest() {
248 LOG(INFO
) << "Finished test " << sims_remaining_
.front().simulation_name
;
254 void UpdateCurrentTest() {
255 ++sims_remaining_
.front().frames_rendered
;
258 current_sim_
->Update();
260 glXSwapBuffers(display_
, window_
);
262 XExposeEvent ev
= { Expose
, 0, 1, display_
, window_
,
263 0, 0, WINDOW_WIDTH
, WINDOW_HEIGHT
, 0 };
268 reinterpret_cast<XEvent
*>(&ev
));
270 base::MessageLoop::current()->PostTask(
272 base::Bind(&Simulator::UpdateLoop
, weak_factory_
.GetWeakPtr()));
276 LOG(INFO
) << "Successfully ran " << sims_completed_
.size() << " tests";
278 FILE* f
= OpenFile(output_path_
, "w");
281 LOG(ERROR
) << "Failed to open output file " <<
282 output_path_
.LossyDisplayName();
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();
293 "\t\t{\"simulation_name\":\"%s\",\n"
294 "\t\t\t\"render_model\":\"%s\",\n"
295 "\t\t\t\"frames_drawn\":%d\n"
297 i
.simulation_name
.c_str(),
298 ModelToString(i
.model_under_test
),
300 sims_completed_
.pop();
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()) {
328 base::MessageLoop::current()->Quit();
335 void Resize(int width
, int height
) {
336 window_width_
= width
;
337 window_height_
= height
;
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_
;
350 base::WeakPtrFactory
<Simulator
> weak_factory_
;
353 GLXContext gl_context_
;
358 int main(int argc
, char* argv
[]) {
359 CommandLine::Init(argc
, argv
);
360 const CommandLine
* cl
= 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.";
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();
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()) {
397 LOG(INFO
) << "(input path is a file)";
398 sim
.QueueTest(inPath
);