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

Reply via email to