对于几个多线程渲染中的成员变量
继续抄一抄,
Block阻塞器
BlockCount;计数器类。它与阻塞器类的使用方法基本相同。block()阻塞线程
release()释放线程
不过除此之外,BlockCount的构造函数还可以设置一个阻塞计数值。
计数的作用是,每当阻塞器的completed()函数被执行一次,计数器就减1,直至减到0就释放被阻塞的线程。
Barrier类,线程栅栏类。这是一个对于线程颇为重要的阻塞器接口。
它的构造函数与BlockCount类似,可以设置为一个整数值,我们可以把这个值理解为栅栏的强度。
每个执行了Barrier::block()函数的线程都将被阻塞。当被阻塞在栅栏处的线程达到指定的数目时,就好比栅栏无法支撑那么大的强度一样,展览将被冲开,所有的线程将被释放。重要的是,这些线程是几乎同时释放的,也就保证了线程执行的同步性。
从代码中,也很容易看到。BlockCount是由其他任意线程执行指定次数的completed()函数,即可释放被阻塞的线程;而Barrier则是必须阻塞指定个数的线程之后,所有的线程才会同时被释放。
其实这些都不用死记硬背,看看源码就一目了然了。
OSG多线程渲染实现的关键在于以下三个变量,用与对线程阻塞与同步的操作,
osg::BarrierOperation类在定义时会传入两个参数。整型参数定义了这个栅栏可阻塞的线程数,或者说它的强度(达到这一数值时将自动释放所有线程),另一个则定义是否需要在阻塞前执行固定的操作(通常不用,即BarrierOperation::NO_OPERATION)
osg::EndOfDynamicDrawBlock在定义时会传入一个参数,表示阻塞的最大计数值。当阻塞器对象的completed()函数执行次数达到这一数值时,才会释放被阻塞的线程。
这边都+1,是因为在主进程中block一下,以便释放所有block的线程。如不需要等待所有线程完成即可下一帧,则只需要主进程的这1个block即可。在渲染开始时,需要同步其他线程,设定为contexts.size()+1或者camera.size()+1即可。主线程的为1,其他线程为context.size()或者camera.size();
上面两个开启线程的方法,会在后面的电子书介绍,所以暂时放在这里,不扩展。
在这两者之间,其实就是单线程渲染方式顺序执行各个图形设备的筛选和绘制工作,概括起来就是
在CullDrawThreadPerContext和DrawThreadContext方式下,由于gc->getGraphicsThread()为真,因此不再执行GraphicsContext::runOperations(),也就是不再从主进程的renderingTraversals函数中执行场景绘制;而在更加高效也更加复杂的cullThreadPercameraDrawThreadPercontext方式下,不只场景绘制不再由主进程来进行,由于camera->getCameraThread()也为真,因此筛选动作也是由摄像机线程来完成的。
这点可以验证下,看看走哪里
可见绘制是摄像机线程完成的。具体不展开,因为电子书后面还有,到时候再根据电子书内容慢慢调试。