查看: 5971|回复: 29

关于pagedlod的问题

[复制链接]

该用户从未签到

发表于 2009-12-24 13:46:08 | 显示全部楼层 |阅读模式
我使用的OSG版本是2.8.2,在用pagelod对大范围建筑数据进行处理时发现一个问题:当我将全部的建筑数据切分成1000个IVE文件时,利用pagedlod进行读取时,绘制比较流畅,当我切分成100个IVE文件时,发现绘制比较卡,表现为读取IVE时,会顿一下,读取完了后,就比较流畅。因为我目前还只是初学OSG,不知道从哪里入手解决这个问题,因此我先猜测了下原因:是不是当读取线程读取了IVE文件后,绘制线程还要对这个IVE文件进行某些处理,然后再绘制,这些处理造成了当IVE文件比较大时,绘制就会顿一下的现象?如果是这样的话,请问有没有什么办法,或者从哪里入手能减小这些处理的时间?

该用户从未签到

发表于 2009-12-24 15:06:55 | 显示全部楼层
分成100分文件大,osg是根据设置的距离信息动态加载和卸载的,模型大肯定加载的慢喽

该用户从未签到

 楼主| 发表于 2009-12-24 15:34:10 | 显示全部楼层
我这里的问题不仅是加载慢,是绘制会顿一下,如果只是加载慢那是可以理解的

该用户从未签到

发表于 2009-12-24 16:19:14 | 显示全部楼层
可能是同时需要预编译的数据量(包括首次加载的纹理,显示列表的创建等)过大造成的,这种情况通常是因为您的层次分隔不太合理所致。不过可以通过设置getDatabasePager()->setDoPreCompile(true)来改善

该用户从未签到

 楼主| 发表于 2009-12-24 16:35:27 | 显示全部楼层
我试过setDoPreCompile(true),但是一设置后,从读取到显示一个IVE文件需要7,8秒,不设置之前,虽然绘制会顿,但是读取到显示全部的IVE(10-20个IVE)只需要2-3秒。我想问一下,ARRAY你所说的:预编译数据(包括首次加载的纹理,显示列表的创建等),这个处理一定需要在绘制线程里完成吗?

该用户从未签到

发表于 2009-12-24 16:41:20 | 显示全部楼层
这正是它的代价:setDoPreCompile为false时,预编译数据在绘制线程里完成;而setDoPreCompile(true)则指示在读入数据的线程中进行预编译。此时由于每个模型的载入和编译时间变长,整体的加载时间当然也会被拖长。
总之没有两者兼得的方法,您可以从自己设计的层次结构上多下一些功夫

该用户从未签到

 楼主| 发表于 2009-12-24 16:51:06 | 显示全部楼层
谢谢array这么快的回复,但是我觉得无论预编译数据是在绘制线程里完成,还是在读入数据的线程里完成,编译全部模型的总时间应该是一样的吧。但是setDoPreCompile(true)后,我这里很明显的可以看出,处理所花的总时间变长了很多。这个现象好象说不通吧?难道是读数据的线程的优先度级别的问题,还是读数据线程预编译时要做些额外的处理?

该用户从未签到

发表于 2009-12-25 08:43:34 | 显示全部楼层
不是那么简单的加法,事实上在加载线程中进行编译,所花的时间要更长,因为数据不是一次性传递到OpenGL管线中的;如果您期望对其进行改进,可以自己修改响应的代码。

此外DatabasePager还有一些用于改善效率的函数,例如setMaximumNumOfObjectsToCompilePerFrame等,但是并不是说设置了某个函数就可以一了百了;关键还是自己把结构设计好。

OSG也可以设置专门的编译线程来工作,不过我个人没有试验过

该用户从未签到

发表于 2009-12-25 10:58:25 | 显示全部楼层
这么复杂的东东啊,建模多重要啊

该用户从未签到

 楼主| 发表于 2009-12-25 22:00:42 | 显示全部楼层
谢谢ARRAY,那我只能啃下databasepager的代码,看看能否修改相关的地方了.

该用户从未签到

 楼主| 发表于 2009-12-26 17:53:47 | 显示全部楼层
还想请问ARRAY一下,我查看了下模型加载后的贴图数据,发现里面的image对象是含有_mipmapData的,一共有6级,但是实际上我加载的贴图本身很小,不超过64*64,但是贴图数量比较多,这样应该没必要再生成mipmapdata了吧,况且生成mipmapdata也影响效率,在哪里设置能不自动生成mipmapdata呢?

该用户从未签到

发表于 2009-12-26 19:56:17 | 显示全部楼层
setUseHardwareMipMapGeneration(false)
小声说一句,我的书里面有个自己做mipmap的例子,呵呵

该用户从未签到

发表于 2009-12-27 12:15:10 | 显示全部楼层
array就是活参考手册,还是中文版的~~嘿嘿

该用户从未签到

 楼主| 发表于 2009-12-27 20:17:30 | 显示全部楼层
谢谢ARRAY,但是之前我也遍历了所有的TEXTURE对象,全部都setUseHardwareMipMapGeneration(false),但是加载IVE后,发现IMAGE对象里的MIPMAP还是有数据,然后我将IVE导出成OSG,查看里面的每个TEXTURE里面的HardwareMipMapGeneration参数也确实是FALSE的,这是怎么回事呢?
也小声问一句:请问在哪里可以看(买?)到您的书呢?

该用户从未签到

发表于 2009-12-28 09:37:44 | 显示全部楼层
这样的话,您的Texture::setFilter是如何设置的,除了LINEAR和NEAREST之外,其他所有的值都等同于要求OpenGL生成Mipmaps,那样OSG依然辅助会构建Mipmaps数据的。

此外,有关我的书的信息,请参看置顶贴,全国各大书店和网上书店应当都有

该用户从未签到

 楼主| 发表于 2009-12-28 10:10:55 | 显示全部楼层
谢谢ARRAY,情况是这样的,我最开始有一个IVE,里面的Texture的HardwareMipMapGeneration参数是TRUE,MINFilter是LINEAR_MIPMAP_NEAREST,MAGFilter是LINEAR,这个IVE载入后是有MIPMAP数据的,然后我导入这个IVE,遍历将每个TEXTURE的HardwareMipMapGeneration设为FALSE,MIN和MAG Filter都设为LINEAR,然后再导出,最后载入这个改过的IVE后,发现还是有MIPMAP数据......

该用户从未签到

发表于 2009-12-28 10:36:29 | 显示全部楼层
既然已经生成Mipmaps了,那么自然不会把它再删除掉;这些设置都只是对新创建的对象有效的,如果您不希望自己的数据中有Mipmaps,那么将新建的Image对象赋给Texture,并将旧的Image对象中某一Mipmaps层次的图象的内容拷贝给新对象(注意不要直接拷贝,要自己计算数据偏移量)

该用户从未签到

 楼主| 发表于 2009-12-28 11:16:42 | 显示全部楼层
谢谢ARRAY

该用户从未签到

发表于 2010-1-5 17:26:56 | 显示全部楼层
这正是它的代价:setDoPreCompile为false时,预编译数据在绘制线程里完成;而setDoPreCompile(true)则指示在读入数据的线程中进行预编译。此时由于每个模型的载入和编译时间变长,整体的加载时间当然也会被拖长。
总 ...
array 发表于 2009-12-24 16:41


详细看了一下DatabasePager中关于预编译的实现,从以下这段代码来看,若要执行预编译,只setDoPreCompile(true)似乎是不够的,还要创建CompileContext,使用CompileContext进行预编译。
而CompileContext是在osgViewer::Viewer::realize函数中创建的,并且默认是不创建的,如下面第二段代码。
因此若要使用预编译功能是否需要osg:isplaySettings::instance()->setCompileContextsHint(true)?

另外,从第一段代码中可以看到,如果CompileContext的GraphicsThread存在,DatabasePager只是向这个GraphicsThread添加一个CompileOperation,真正的预编译操作应该是在这个GraphicsThread中执行的。只有在GraphicsThread不存在时,才会在分页数据库线程中执行预编译操作。
而从第二段代码可以看到,缺省情况下,创建CompileContext的同时也会为其创建GraphicsThread。因此如果使用setDoPreCompile(true)和setCompileContextsHint(true),则预编译应该是在CompileContext的线程中执行。

  1. if (loadedObjectsNeedToBeCompiled && !_pager->_activeGraphicsContexts.empty())
  2. {
  3. for(ActiveGraphicsContexts::iterator itr = _pager->_activeGraphicsContexts.begin();
  4. itr != _pager->_activeGraphicsContexts.end();
  5. ++itr)
  6. {
  7. osg::GraphicsContext* gc = osg::GraphicsContext::getCompileContext(*itr);
  8. if (gc)
  9. {
  10. osg::GraphicsThread* gt = gc->getGraphicsThread();
  11. if (gt)
  12. {
  13. gt->add(new DatabasePager::CompileOperation(_pager));
  14. }
  15. else
  16. {
  17. gc->makeCurrent();

  18. _pager->compileAllGLObjects(*(gc->getState()));

  19. gc->releaseContext();
  20. }
  21. }
  22. }

  23. // osg::notify(osg::NOTICE)<<"Done compiling in paging thread"<<std::endl;
  24. }
复制代码


  1. if (osg::DisplaySettings::instance()->getCompileContextsHint())
  2. {
  3. int numProcessors = OpenThreads::GetNumberOfProcessors();
  4. int processNum = 0;

  5. for(unsigned int i=0; i<= osg::GraphicsContext::getMaxContextID(); ++i)
  6. {
  7. osg::GraphicsContext* gc = osg::GraphicsContext::getOrCreateCompileContext(i);

  8. if (gc)
  9. {
  10. gc->createGraphicsThread();
  11. gc->getGraphicsThread()->setProcessorAffinity(processNum % numProcessors);
  12. gc->getGraphicsThread()->startThread();

  13. ++processNum;
  14. }
  15. }
  16. }
复制代码

该用户从未签到

发表于 2010-1-6 08:46:11 | 显示全部楼层
setDoPreCompile就可以在DatabasePager的线程中单独执行预编译了;当然也正如您所说的那样,使用CompileContext可以另外建立一个专门的编译线程,进一步提高效率

该用户从未签到

发表于 2010-1-7 19:08:12 | 显示全部楼层
从以上第一段代码中看似乎不是这样呀,预编译需要一个Context,而没有CompileContext的话就不会执行预编译。

该用户从未签到

发表于 2010-1-8 09:35:48 | 显示全部楼层
呵呵,我仔细又看了一遍,indif说得有一定道理:没有CompileContext的话,DatabasePager本身就不会执行预编译。那么这个时候预编译工作实际上是由Renderer代为完成的:
  1. void Renderer::flushAndCompile()
  2. {
  3.     ...
  4.     if (databasePager && databasePager->requiresExternalCompileGLObjects(sceneView->getState()->getContextID()))
  5.     {
  6.         databasePager->compileGLObjects(*(sceneView->getState()), compileTime);
  7.     }
  8. }
复制代码
如果没有定义CompileContext的话,那么此处将自动处理databasePager中等待编译的数据。Windows下的CompileContext一直有问题不能正常使用,因此setDoPreCompile将直接使用Renderer进行预编译工作~~感谢indif,也希望有朋友能给出CompileContext在Windows下的解决方案(在Linux下它是可以使用的)

该用户从未签到

发表于 2010-1-8 16:18:59 | 显示全部楼层
诚如array指出的,在没有CompileContext时,“预编译”是在Renderer::flushAndCompile中执行的,
而Renderer::flushAndCompile是在渲染线程中执行的,这样,所谓“预编译”还是在渲染线程中执行,这与不使用预编译的情况相比,差别在哪里呢?对渲染帧率的平稳似乎没有什么贡献呀?

该用户从未签到

发表于 2010-1-8 16:31:02 | 显示全部楼层
flushAndCompile是在绘制结束时统一进行的,并且有一个availableTime的控制,可以保证每帧只有有限个对象进行编译。相比之下,如果把预编译工作交给各个纹理和几何体自己来完成的话,那么有可能出现同一时刻预编译数目巨大的情形,造成严重的帧延迟

该用户从未签到

发表于 2010-1-8 16:39:53 | 显示全部楼层
是的,我忽略了avaliableTime的作用

该用户从未签到

发表于 2010-1-13 09:19:48 | 显示全部楼层
我想把预编译的功能单独提出来,写到自己的调度程序中(没有使用pagerlod),大家有什么好的建议,谢谢 。

该用户从未签到

发表于 2010-1-13 12:07:16 | 显示全部楼层
简单来说,就是在适当的时候执行对象的compileGLObjects();具体您可以参考一下DatabasePager中的实现

该用户从未签到

发表于 2010-1-13 19:13:25 | 显示全部楼层
这个函数大致看了,核心的部分是分别对Drawable和StateSet调用各自的compileGLObjects函数进行显示列表的创建和纹理绑定等。而在osgUtil中有一个GLObjectsVisitor的节点访问器,该类的功能貌似和这个函数的功能一致,不知道我的理解是否正确?

该用户从未签到

发表于 2010-1-14 08:54:00 | 显示全部楼层
是这样,如果我没想错的话,您可以直接结合GLObjectsVisitor来完成自己的预编译工作

该用户从未签到

发表于 2010-1-14 11:31:10 | 显示全部楼层
谢谢您耐心的指点
osgcatch和osgterrain这两个例子中,有相关的用法
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

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

联系我们

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