渲染流程简析--第二次调试osg最长的一帧心得

前三次只看不调,感觉像看天书。第一次调试osg最长的一帧时,感觉有些机械。第二次调试后,感觉osg最长的一帧真是没一句废话,字字珠玑。好像懂了不少。特将渲染流程的心得记录如下。(不看代码,不看电子书)

一osg渲染流程
1,摄像机添加场景。通过摄像机的图形上下文或者渲染器进行cull,draw或者cull_draw进行对SceneView筛选和绘制。(由不同的线程模型决定)
2,cullvisitor筛选场景,构造渲染树和状态树
3,渲染台遍历其成员渲染叶列表(半透明)和状态树中的渲染叶(不透明),通过state状态机绘制
4,state状态机有渲染属性,模式开关和顶点数据,将顶点数据传给渲染叶。渲染属性可以派生不同的类,进行不同渲染属性的操作
5,渲染台对渲染叶进行了排序。分别处理不同的preRender,正常渲染和postrender。而这些渲染,则是通过不同摄像机进行的。

二,渲染同步(两种,Barrier线程同步和blockCount计数同步)
1,Barrier:主线程和辅线程coorperateWait,左脚踩右脚,而不是you jump, I jump.
以开启graphicThread为例。
(1)主线程开启GraphicThread,并阻塞自己,等待子线程的信号量。
(2)子线程运行后,通过信号量释放主线程,并阻塞自己,等待主线程的事件或者cancel事件。
(3)主线程释放后,发送事件,让子线程结束阻塞。
尽管主线程和辅线程都走coorperateWait函数,但是走的分支不同。这个分支是通过局部存储区获取TLGetValue(),主线程获取的是空,子线程获取不为空。原因是子线程开启后,向局部存储区赋值操作。TLSetValue()
主线程阻塞自己时,将waiter+1,被信号量释放后,waiter-1,子线程释放信号量时,waiter个数目。这就是释放Waiter个线程。

2,blockCount计数同步,这里用到的是dynamic模型的complete,每次绘制完一个complete()则计数-1,直至全部绘制完,则为0,冲开。

三,根据不同的线程模型,进行不同的阻塞
阻塞有三个barrier和1个blockCount。三个barrier是开始绘制同步,结束绘制同步和双缓存同步;一个blockCount是动态数据的同步。
由于不同的线程模型在渲染时,可以等待该帧绘制结束进行下一帧,也可以不等待,由此各个barrier数目也不同,对于culldrawperContext,是严格相等的,也是每一帧绘制完才进行下一帧。
对于另外两种,则动态数据同步即可,只要数据不崩溃就Ok.可以将绘制和筛选放在不同的子线程里。即可以采取cameraThread筛选和GraphicThread绘制,或者只用graphicThread进行筛选和绘制。

这里就遇到一个问题。如果筛选不阻塞,在绘制之前已经准备好怎么办?在绘制时,drawqueue使用SceneView时才起作用。

四,子线程operation队列根据Keep变量进行循环,比如上边说的开始绘制同步,结束绘制同步,双缓存同步,以及相应的筛选(可能在cameraThread,也可能在主线程,如果在主线程时,就不用同步了),绘制,双缓存swap.
也就是说,只要在帧循环中渲染,也就一直keep=true.如果不需要多次使用这些operation,keep=false。

准备再调试第三遍,或许有更多的心得。书读百遍其义自见。

上一篇:查看android app 线程信息的命令


下一篇:写一个软光栅器绘制正方体