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

本文详细探讨了OpenSceneGraph(osg)的渲染流程,从摄像机添加场景、筛选与绘制、状态机工作原理到渲染同步的 Barrier 线程同步和 blockCount 计数同步机制。在渲染流程中,讲解了如何通过cullvisitor构造渲染树,以及state状态机如何处理渲染属性。此外,还分析了不同线程模型下的同步策略,包括开始绘制、结束绘制和双缓存同步的 Barrier,以及动态数据同步的 blockCount。最后,作者分享了调试过程中的体会,强调了多轮调试对理解的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前三次只看不调,感觉像看天书。第一次调试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。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值