main函数流程
main函数的主要工作有:
- 解析输入参数
- 初始化log,appname
- 打印帮助 if (Options::show_help) Options::print_help();
- 设置窗口大小Options::size = (800, 600)
- 实例化canvas,配置相关参数
- 实例化SceneCollection,在定义scenes时就通过构造函数,调用add_scenes,将各个scene实例添加到vector中。
- register_scenes将scene再导入到sceneMap_中,时名字和scene的map类型
- if (Options::list_scenes) list_scenes(); 打印列出scenes
- canvas.init(),窗口初始化,初始化OpenGL和默认配置
native_state_.init_display()
gl_state_.init_display(native_state_.display(), visual_config_);
reset(); - canvas.visible(true);
- 执行benchmark,加载benchmarks并循环执行测试。
总结下,main函数中主要是做canvas的创建,和初始化,然后是scene的初始化,到do_benchmark中开始加载和创建测试的benchmark链。
canvas初始化
窗口初始化部分的主要工作:
CanvasGeneric canvas(native_state, gl_state, Options::size.first, Options::size.second);
canvas.offscreen(Options::offscreen);
canvas.visual_config(Options::visual_config);
canvas.init()
canvas.visible(true);
scene初始化
scene初始部分的主要工作有:
SceneCollection scenes(canvas);
scenes.register_scenes();
执行过程
benchmark的执行流程:
分别是do_validation和do_benchmark,两个的整体流程一致:
- 通过 benchmark_collection.populate_from_options() 加载具体测试项 benchmark;
- 根据 Option 定义 Loop 类型,然后循环执行loop中的测试项:while (loop->step());
- do_validation 只是验证下这个bench是不是可以执行,很快会结束,不统计得分
- do_benchmark 是会按照配置参数完全执行bench,会计算得分
三个mainloop的区别
这里相当是 Options 的参数会走出三个 MainLoop 的分支:
- MainLoopValidation:
-继承自MainLoop,跑一个快速的验证,只跑第一帧。
重构 draw 方法,在 canvas_.update() 之后设置运行结束 scene_->running(false)
重构 log_scene_result 方法,打印 validate 的结果,在MainLoop的 step 中会调用 log_scene_result,log_scene_result 中会调用 validate 函数,每一个 scene 都会定义validate函数。 - MainLoopDecoration:
装饰作用,重点是会打印帧率和title,重构的draw、before_scene_setup、after_scene_setup中添加了判断 if (show_fps_) 和 if (show_title_) 的处理。
这两个mainloop根据needs_decoration区分,判断 benchmarks_ 中只要一个bench是 needs_decoration 的,就返回true, bench的needs_decoration是看option中如果设置show-fps为true或者title为空。 - MainLoop:
这里有一个思想:从过重构mainloop将条件分散到不同的类中执行,然后到scene的时候又回到相同的调用里。
testbench的加载populate_from_options
testbench的加载在 BenchmarkCollection::populate_from_options 中执行,分三种情况依次判断:
- 输入参数 benchmark 非空,用参数生成 Benchmark 对象,添加进 benchmark_ 中;
- 输入参数 benchmark_files 非空,调用 add_benchmarks_from_files 从文件种读取配置,生成Benchmark 添加进 benchmark_ 中;
- 如果 benchmark_ 中没有一个scene(判断 scene name 是不是空),调用 DefaultBenchmarks 的 populate 添加默认的 option 配置,生成 Benchmark 添加进 benchmark_ 中;
void
BenchmarkCollection::populate_from_options()
{
if (!Options::benchmarks.empty())
add(Options::benchmarks);
if (!Options::benchmark_files.empty())
add_benchmarks_from_files();
if (!benchmarks_contain_normal_scenes())
add(DefaultBenchmarks::get());
}
mainloop的step执行
- 遍历 benchmarks_ 找到一个有效的 scene,判断有效方法为:scene的name非空。
- 找到一个有效的scene之后,调用 setup_scene ,设置option、shader精度和开始的时间戳。同时前后调用 before_scene_setup 和 after_scene_setup,这两个只有在decoration中有操作。
- 判断是否中止,如果继续运行,调用draw函数进行canvas和scene的真正绘制。draw中会update canvas和scene的状态和参数。
- 再次判断运行状态是否是running,如果运行结束,记录log和result,teardown scene并切换next benchmark。
问题:scene_setup_status_ == SceneSetupStatusSuccess之后才计算分值
可作为代码模板:
while (bench_iter_ != benchmarks_.end()) {
scene_ = &(*bench_iter_)->scene();
if (scene_->name().empty())
(*bench_iter_)->setup_scene(); //从option中拿配置参数,将参数写到scene中,生成一个scene的
else
break;
next_benchmark();
}
//操作为给遍历指针++,如果到底了或者有forever运行,将指针重新指到开始。
void
MainLoop::next_benchmark()
{
bench_iter_++;
if (bench_iter_ == benchmarks_.end() && Options::run_forever)
bench_iter_ = benchmarks_.begin();
}
MainLoop的draw函数
void
MainLoop::draw()
{
canvas_.clear(); //调用到两个Canvas子类中的clear,时glClearColor、glClearDepth和glClear
scene_->draw(); //调用到具体scene的draw函数
scene_->update(); //更新帧数和事件
canvas_.update(); //判断当前帧的状态,有swap、finish、readpixel
}
整体循环流程
main函数中:while (loop->step());
MainLoop::step函数中:判断中间变量 scene_ 是否为空,从 benchmarks_ 中逐个取出给 scene_
然后调用scene的draw进行渲染测试,结束后指针查找下一个 next_benchmark
总结
总结下:scene是运行的各个用例的类型,根据option参数可以配置不同的测试点。在scenecollection中将各个scene对象串起来做成链表保存在SceneMap中,和scene的name形成一个map表,方便后面的查询;到populate_from_options中根据参数(scene:options)取出scene和option,创建成Benchmark组成具体的测试项链,这里用get_scene_by_name通过name获取在SceneMap中存的scene对象。整个过程中相当于多了一个SceneMap对scene对象进行管理。所以,总结两点:
1、测试的benchmark是可以通过option配置,保证scene复用;
2、用map对scene对象管理,为第一点提供支持。