场景数据的动态更新

时间:2021-08-31 19:17:41
动态更新的核心就是设置数据变度属性DataVariance,它决定了OSG在多线程渲染的过程中的执行策略:只有所有DYNAMIC属性的对象被渲染完毕之后,OSG才会开始执行下一帧的用户更新操作;这样有效地可以避免数据的过快更新造成当前的渲染动作出错,以致系统崩溃。


所有派生自osg::Object的对象都可以设置数据变度的属性,通常设置的时机在新建对象之时,例如:
osg::ref_ptr<osg::Group> node = new osg::Group;
node->setDataVariance(osg::Object::DYNAMIC);
...


这就意味着这个组节点可能在渲染过程中发生变动,可能变动的内容包括:成员属性的改变,新增/替换/删除子节点,或者使用事件/更新/裁减回调实现的自定义变动。


数据变度的设置对于渲染状态和纹理的动态更新同样有重要的意义。而对于osg::Drawable对象,设置为动态更新意味着它可能在渲染过程中发生顶点属性或者图元的变化;这其中尤为要提及osgText::Text,文字属性的变化必须预先设置动态的数据变度属性,否则可能造成系统运行过程中崩溃:
osg::ref_ptr<osgText::Text> text = new osgText::Text;
text->setDataVariance(osg::Object::DYNAMIC);
...


// 渲染过程中可以动态改变文字的位置和内容等属性
{
   text->setPosition( ... );
   text->setText( ... );
}


数据变度不能被自动继承。它事实上只有STATIC和DYNAMIC两个可选值,而UNSPECIFIED意味着系统将自动根据初始化时的情况给对象设置数据变度的属性。默认情况下,任何新建对象都是UNSPECIFIED的,如果系统在第一次执行渲染前发现它附带有更新/事件/裁减回调对象的话,则会自动给它设置setDataVariance(osg::Object::DYNAMIC);因此,我们可以说过多的更新回调会造成系统的渲染效率在一定程度上降低,不过即使在非常极端的情况下,也不会低于单线程模式的效率。

OSG在setDataVariance()方法中提供了解决方法,该方法属于osg::Object类,这是所有场景对象的基类。这可以设置为三个枚举值之一:UNSPECIFIED(默认),STATIC与DYNAMIC。场景图中的DYNAMIC对象必须在绘制遍历的开始进行处理。也就是,渲染后端应确保所有节点以及被指定为DYNAMIC的场景对象在下一帧的更新与裁剪遍历开始之前已完成绘制。然而,STATIC对象,在更新与绘制过程中会保持不变,从而会被稍后渲染且不会阻塞帧速率。

默认情况下,所有新分配的对象都被指定为UNSPECIFIED,包括节点,可绘制元素,状态集以及属性。这允许OSG预测数据变化。另一方面,我们总是可以重置该值并使其由下一帧开始工作,例如:

node->setDataVariance( osg::Object::DYNAMIC );
简单来讲,就是为了线程同步,避免一个正在渲染线程中被渲染的对象在仿真渲染线程中被修改而发生访问冲突。
这也是我从前的理解,于是对所有可能会动态修改的东西,都设置了为DYNAMIC的,包括一些可能会动态添加、删除子节点的组节点,也设置为DYNAMIC。

但是,今天翻看了一下这块的代码,发现在渲染中被优先渲染的“动态”drawable只有两种情况:
1)Drawable本身被指定为DYNAMIC或被指定了回调
2)一个节点的StateSet是DYNAMIC的话,其子树中的所有Drawable被视为“动态”

由此可见,只有需要动态修改StateSet或Drawable时,才需要设置其为DYNAMIC。
而对于节点设置DYNAMIC是没有意义的。

一点认识与大家分享,如果有误还请不吝指出。