场景为加载窗口,经常在->block()缓解无响应,推测是路径问题或CPU占用过大。
1 LoadScene();->
2 {
3 osg::ref_ptr<UserOperation> pUserOperation =
4 new UserOperation(UserOperation::Operation_AddView, m_pView);
5 m_pParent->GetSimCompositeViewer()->addUpdateOperation(pUserOperation);
6 pUserOperation->block();//经常无响应
7 }
由于该步骤并没有任何路径调用,因此应该为CPU占用过高导致的问题。
解决方法:在5-6行之间添加如下代码。其中ViewerFrameThread是用于打开多个qt窗口的类。貌似可以节省CPU占用。
m_pParent->GetViewerFrameThread()->SetRun(true);
具体用法参考两个资料:
防丢copy:
The problems now begin when we want to create and show multiple widgets. Since each widget comes with its own timer, performance rapidly decreases with the number of open widgets. Not only is the interaction with the OSG widgets very slow, also the interaction with other Qt widgets noticeably lags. Even a halfway recent quad-core system is almost overwhelmed when approximately 5 windows are open. This issue is definitely not related to our graphics hardware. Other applications may render much larger scenes (Blender, Meshlab etc.) without any negative performance impact.
So, to summarize: What would be the best way of creating multiple Qt widgets showing different OpenSceneGraph scenes without a performance impact?
What we already tried:
- We already considered using a single
osgViewer::CompositeViewer
for rendering all scene objects. However, we discarded this idea for now because it will probably make interactions with a single widget very complicated. - We tried putting the rendering portion of each
osgViewer::CompositeViewer
in a separate thread as detailed by the osgQtWidgets example.
Our second try (using threads) looked roughly like this:
class ViewerFrameThread : public OpenThreads::Thread
{
public:
ViewerFrameThread(osgViewer::ViewerBase* viewerBase):
_viewerBase(viewerBase) {}
~ViewerFrameThread()
{
cancel();
while(isRunning())
{
OpenThreads::Thread::YieldCurrentThread();
}
}
int cancel()
{
_viewerBase->setDone(true);
return 0;
}
void run()
{
int result = _viewerBase->run();
}
osg::ref_ptr<osgViewer::ViewerBase> _viewerBase;
};
However, this also resulted in a remarkable performance decrease. Each thread still requires much CPU time (which is not surprising as the basic interaction is still handled with a timer). The only advantage of this approach is that at least interaction with other Qt widgets remain possible.
The ideal solution for us would be a widget that only fires redraw requests whenever the user interacts with it, for example by clicking, double-clicking, scrolling etc. More precisely, this widget should remain idle until there is a need for an update. Is something akin to this possible at all? We would welcome any suggestions.
Having tried out several models for this problem, I am happy to report that I found one that is working perfectly. I am using a QThread
(similar to the thread described above) that essentially wraps an osgViewer::ViewerBase
object and simply calls viewer->run()
.
The trick to keep CPU usage low is to force OpenSceneGraph to render on demand only. Having tried out the various options, I found the following two settings to work best:
viewer->setRunFrameScheme( osgViewer::ViewerBase::ON_DEMAND );
viewer->setThreadingModel( osgViewer::ViewerBase::CullDrawThreadPerContext );
A viewer that is modified like this will not use spurious CPU cycles for continuous updates while still using multiple threads for culling and drawing. Other threading models might of course perform better in some cases, but for me, this was sufficient.
If any one else attempts a similar solution, be warned that some operations now require explicit redraw requests. For example, when handling interactions with OSG objects or when you are writing your own CameraManipulator
class, it doesn't hurt to call viewer->requestRedraw()
after changing viewer settings. Else, the viewer will only refresh when the widget requires a repaint.