Hello Robert, all,
I'm seeing a crash in our simulator after many scenario loads. The stack
trace differs, but it's always similar to this:
a) Thread A is calling osg::Texture2D::getModifiedCount() for a new
contextID, causing the _modifiedCount variable (which is an
osg::buffered_value) to be resized.
b) At the same time, Thread B is calling
osg::Texture::getTextureParameterDirty() for a new contextID (different
from the one in a), causing the _texParametersDirtyList variable (also
an osg::buffered_value) to be resized.
The osg::Texture and osg::Texture2D in threads A and B are the same
object. But the graphics context for which these operations happen is
different. Both threads are running the osgUtil::GLObjectsVisitor
(starting from osgViewer::Renderer::compile()) when this happens.
The stack trace can differ in the sense that instead of
getModifiedCount(), thread A could be calling applyTexImage2D_load() or
some other method on that same Texture. But getTextureParameterDirty()
is always involved in the other thread from what I've seen. The crash
happens in operator delete in thread B, while trying to resize the
_texParametersDirtyList variable. The crash can happen at the second
scenario load, or at the fifth, or at the 15th. And only if there are
two or more GraphicsContexts running at the same time, so using a single
window doesn't reproduce the problem.
I don't see why doing different things in different threads causes a
problem here, but apparently it does. And in general, running the
GLObjectsVisitor from two different threads, one per context, even if
it's on the same scene, should not cause problems, right?
A workaround I have found is to do the following before the viewer
starts rendering, but after the scene data has been loaded and the
viewer has been realized (so all GraphicsContexts are created):
a) stop the viewer's threads
b) for each graphics context
b.1) make the context current
b.2) run the GLObjectsVisitor on the scene
b.3) release the context
c) start the viewer's threads
This basically forces the GLObjectsVisitor to be run for each context
sequentially instead of in parallel, so any resizes that need to happen
will happen single-threaded. After this, the viewer will re-run the
GLObjectsVisitor, but all resizes will already have been done, so there
is no crash. But I shouldn't have to do this. I feel "dirty" even having
this kind of workaround in the code :-)
The code example I've included demonstrates this. You'll notice we use
osg::GraphicsContext::incrementContextIDUsageCount() to ensure OSG
doesn't reuse old graphics contexts. This is actually required to
reproduce this crash. Also you'll notice the example creates 8 windows,
but the problem is also present when you have 2 screens and use
setUpViewAcrossAllScreens() on a single view, since then 2 contexts are
created. This is what happens in our app. I've just made it create 8
windows so the crash happens quicker.
In my case, I just run the example and press "esc" each time the viewer
windows appear, and after a few times (generally less than 5 x 8
windows) I get a crash with the symptoms I gave above. If you uncomment
the ENABLE_WORKAROUND define, and do the same thing, it shouldn't crash
(I've gone to over 30 x 8 windows a few times with no crash).
Note that I have no idea how OS/compiler dependent this is, perhaps it
only happens on Windows when OSG is compiled with Visual Studio, or
something like that. That's actually something I hope others can help me
find out.
Has anyone encountered something similar to this?
Thanks in advance,
J-S
--
______________________________________________________
Jean-Sebastien Guay [email protected]
http://www.cm-labs.com/
http://whitestar02.webhop.org/
#include <osgDB/ReadFile>
#include <osgViewer/CompositeViewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgGA/TrackballManipulator>
#include <iostream>
//#define ENABLE_WORKAROUND
void main_func(int argc, char** argv)
{
// load the data
osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild(osgDB::readNodeFile("cow.osg"));
root->addChild(osgDB::readNodeFile("cessnafire.osg"));
root->addChild(osgDB::readNodeFile("lz.osg"));
root->addChild(osgDB::readNodeFile("skydome.osg"));
osgViewer::CompositeViewer viewer;
for (unsigned int i = 0; i < 8; ++i)
{
osgViewer::View* view = new osgViewer::View;
view->setCameraManipulator( new osgGA::TrackballManipulator );
// add the stats handler
view->addEventHandler(new osgViewer::StatsHandler);
view->setSceneData( root.get() );
view->setUpViewInWindow(50+i*10, 50+i*10, 800, 600);
viewer.addView(view);
}
viewer.realize();
#ifdef ENABLE_WORKAROUND
viewer.stopThreading();
#endif
osgViewer::CompositeViewer::Cameras cameras;
viewer.getCameras(cameras, false);
for (unsigned int i = 0; i < cameras.size(); ++i)
{
osg::GraphicsContext* gc = cameras[i]->getGraphicsContext();
if (gc)
{
osg::GraphicsContext::incrementContextIDUsageCount(gc->getState()->getContextID());
#ifdef ENABLE_WORKAROUND
gc->makeCurrent();
osgUtil::GLObjectsVisitor glov;
glov.setState(gc->getState());
root->accept(glov);
gc->releaseContext();
#endif
}
}
#ifdef ENABLE_WORKAROUND
viewer.startThreading();
#endif
viewer.run();
}
int main(int argc, char** argv)
{
while (true)
main_func(argc, argv);
}
_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org