查看: 1843|回复: 4

一天一个类之:Referenced

[复制链接]

该用户从未签到

发表于 2011-5-9 18:27:11 | 显示全部楼层 |阅读模式
前段时间想写写关于osg中一些类的认识的东东,就把想法发上来了,没想到会有好几个盟友响应,看来这个东东还是有点用。但从上次发了“大话”后一直没写什么,没办法,工作太忙了,但想想总也不能没有一点输出吧,^_^,所以今天决定无论如何也要种下这第一颗种子,整个osg那么多类,不可能一个人全写下去,希望大家踊跃一点也沿着这个标题写下去,为我们的osg社区添砖加瓦。
    大家看到这个标题后可能觉得很好笑甚至无聊,“靠,这么简单的东东也值得拿出来说!”,其实我也有同感,但是,俗话说,万事开头难,为了让开头不那么难,所以本人就特意找了个最简单的类,但也是最基本最关键的类。
用过osg的朋友都知道osg的一大特点就是内存管理采用自动管理的方式,用户无需显示释放对象内存,而这一实现的基础就源于这个最简单的类Referenced,大家都能看到osg中除了辅助功能类之外,几乎所有的“对象”类头上都有一个Referenced的帽子,如果没有这个帽子,那你只有你自己去负责管理你的对象内存哦。哎,说实话这个类实在是简单的不得了,没什么可说的,最主要的也就是有一个引用计数成员_refCount,引用计数大家都晓得,在此不再废话。
    有几个与引用计数相关的接口,这些接口一般来说用户是不用去调用的,是让另外一个模板类ref_ptr来调用的。在写这个类的时候就很纠结,到底是写ref_ptr还是Referenced,因为这两个类实在没法单独来说,但后来想想Referenced还有一点点特殊的地方,所以就以它为基础来说了。ref_ptr也就是智能指针了,自动化内存管理基本上就是通过ref_ptr操纵Referenced来完成的,一般都叫智能指针为auto_ptr或smart_ptr,osg大师起了个别名ref_ptr,呵呵。ref_ptr就不多说了,它也就是包装了一个"Referenced"而已,然后调用它的几个简单接口完成对象的创建和销毁工作。所以我们接下来继续说Referenced。还要说?^_^。刚才说到Referenced有一点点特别的地方,第一,它引用观察者模式,用户可以给Reference设定若干观察者,当改对象要被销毁时会自动通知所有的观察者,至于观察者知道后干什么那就要取悦于您老人家了。干什么呢?我也不知道,至少我还没发现在osg其它什么地方用到了这个功能,我自己琢磨了半天也没发现在自己的项目中什么地方可以用得上的,呵呵。
    当然,你要试验的话很简单,你坐上飞机,当飞机要爆炸销毁的时候你要跳伞的,所以你要监控飞机哦,否则自己的小命儿就不保了,而且跳伞要快。不知道这个是不是能说明一点该功能的使用场合。你是不是要问,还来得及跳伞吗?答案是来得及,但你必须使用另外一个特殊的地方,那就是对象删除处理器DeleteHandler,否则它告诉你飞机爆炸跟飞机爆炸这个事情本身几乎是同时的,你是不可能有机会跳伞的。osg可以给Referenced指定一个对象删除处理器,让它来统一管理所有Referenced的销毁工作,怎么销毁,具体什么时候都由它说了算(重写doDelete函数即可)。记住,所有的Referenced对象公用一个对象删除处理器,不能你用这个他用那个。默认DeleteHandler是保留两帧后销毁,你也可以指定保留10帧,20帧后销毁,看你的需要了。这样以来,osg不但能自己管理对象,但它并不霸道,它还可以让你来决定具体怎么管理,很够意思!
    是不是该结尾了?别着急,还有很重要的一点,你一定感兴趣。我们都知道osg的另外一大特点就是多线程渲染处理。在设定多线程渲染后,osg可以在上一帧还没有渲染结束就开始下一帧的更新操作,(下面的我就不说了,array的最长的一帧中有详细解说,这里就直接拷过来了)也就是说我们采用 DrawThreadPerContext 或者 CullThreadPerCameraDrawThreadPerContext 线程模型时,这两种模式中存在“上次的渲染工作与下次的更新工作交叠”这一情形。假设我们在更新工作中立即将这个节点删除,而上次渲染工作可能正要将这个节点中的数据送往 OpenGL 图形渲染管线,那么灾难就发生了……看到这里,读者您一定已经想到了一种解决方案。对,就是在渲染后台也使用ref_ptr来引用(ref)图形节点,然后在渲染结束取消引用(unref),这样不就可以避免无谓的牺牲了吗?也省却用户的很多麻烦。说得有道理,不过这其中恐怕忽视了一个核心的问题:渲染效率。是的,假设我们要渲染成千上万个这样的几何体节点(这对您来说也许简直是家常便饭),如果每个节点的渲染都要多执行一次 ref/unref 的话,效率的损失将是无法被忽略的。事实上经过测算,CPU 时间的流失大概可以达到 6%,对于一个实时渲染系统来说,这的确值得斟酌。因此,OSG        的新版本中提出了 DeleteHandler 的概念,也就是“垃圾收集”,把那些引用计数已经为零的对象统一收集起来,确保它们不会再被渲染线程用到之后,再在适当的地方予以释放。DeleteHandler 有一个重要的参数_numFramesToRetainObjects,它的意义是,垃圾对象被收集之后,再经过多少帧(默认设置是 2),方予以释放。因此,OSG 的垃圾收集器同样需要使用 DeleteHandler::setFrameNumber 来记录当前的帧数。这个概念提出的时间并不长,也许还需要一段时间的测试,也许会有更好的方案来替代它。目前,OSG 的发行版本仍然采用第一种方式,也就是渲染后台采用ref_ptr 引用计数的方式来避免删除对象造成的问题;如果您想要尝试使用和帮助调试 DeleteHandler 的话,可以在自己的程序中(main 函数之前)加入:
#undef OSGUTIL_RENDERBACKEND_USE_REF_PTR 以请求使用 DeleteHandler。
完了,这次真没什么可说的了。下一个类,你来接?

该用户从未签到

发表于 2011-5-10 08:49:52 | 显示全部楼层
谢谢搂主的用心,语言也比较风趣。个人的建议是,开发者教程本身还是应该有一些简单但是明确的代码,用来说明一些关键方法的用法和注意事项,比如您这里提到的Observer类。此外DeleteHandler本身并不是必选的,通常我们还是直接在引用计数到0后立即删除对象。

另一个建议是,您不妨了解和介绍一下observer_ptr,它和ref_ptr的意义不同,但是同样对于开发者有很大的好处,可以让开发工作变得更为安全可靠~~

该用户从未签到

 楼主| 发表于 2011-5-10 09:04:24 | 显示全部楼层
多谢array的建议,DeleteHandler是不是必须的,在后面引用你的那段就说明了这个问题。其实写这些倒不是让它作为教程的(也称不上教程,本人就还没学好不可能写教程了,^_^),只是感觉不知道放到哪个版面了,呵呵。此外,本人对osg学艺不精,说出来的东西难免漏洞百出,希望大家能够谅解。只是希望能抛砖引玉。

该用户从未签到

发表于 2011-5-10 11:18:38 | 显示全部楼层
楼主的第一个类,支持啊

该用户从未签到

发表于 2011-5-13 20:54:04 | 显示全部楼层
学习了。我会天天来向您学习的
您需要登录后才可以回帖 登录 | 注册

本版积分规则

OSG中国官方论坛-有您OSG在中国才更好

网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

联系我们

  • 工作时间:09:00--18:00
  • 反馈邮箱:1315785073@qq.com
快速回复 返回顶部 返回列表