[osg-users] Removing objects with shared GL state from scene graph

Chris Djali krizdjali at gmail.com
Sun Aug 25 17:22:57 PDT 2019


Hi,

The reason I couldn't add or remove views during the event traversal was that it was invalidating iterators that iterated over the views. Instead, I'm adding an update operation to add or remove the viewer.

I now have a fairly minimal example that replicates my use case and bug:


Code:

#include <osg/AutoTransform>

#include <osgText/Text>

#include <osgGA/TrackballManipulator>

#include <osgViewer/ViewerEventHandlers>

#include <osgViewer/CompositeViewer>

class AddViewOperation : public osg::Operation
{
public:
    AddViewOperation(osg::ref_ptr<osgViewer::View> view)
        : osg::Operation("AddView", false)
        , _view(view)
    {}

    void operator() (osg::Object * compositeViewer) override
    {
        OSG_NOTICE << "AddView operator()" << std::endl;
        osgViewer::CompositeViewer * viewer = dynamic_cast<osgViewer::CompositeViewer *>(compositeViewer);
        viewer->stopThreading();
        viewer->addView(_view);
        viewer->startThreading();
    }

protected:
    osg::ref_ptr<osgViewer::View> _view;
};

class RemoveViewOperation : public osg::Operation
{
public:
    RemoveViewOperation(osg::ref_ptr<osgViewer::View> view)
        : osg::Operation("RemoveView", false)
        , _view(view)
    {}

    void operator() (osg::Object * compositeViewer) override
    {
        OSG_NOTICE << "RemoveView operator()" << std::endl;
        osgViewer::CompositeViewer * viewer = dynamic_cast<osgViewer::CompositeViewer *>(compositeViewer);
        viewer->stopThreading();
        viewer->removeView(_view);
        viewer->startThreading();
    }

protected:
    osg::ref_ptr<osgViewer::View> _view;
};

class ViewAdder : public osgGA::GUIEventHandler
{
public:
    ViewAdder(osgViewer::CompositeViewer * viewer)
        : _viewer(viewer)
        , _view(nullptr)
    {}

    bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
    {
        if (ea.getEventType() == osgGA::GUIEventAdapter::KEYUP && (ea.getKey() == 'v' || ea.getKey() == 'V'))
        {
            if (_view)
            {
                OSG_NOTICE << "Existing view, remove it" << std::endl;
                // parts of the scene get removed before the view gets destroyed.
                // normally this is fine as things get handled by destructors.
                // however, things that are still cached require the cache to be released
                _view->setSceneData(nullptr);
                // We need to remove the view after the event traversal is done to avoid invalidating iterators
                _viewer->addUpdateOperation(new RemoveViewOperation(_view));
                _view = nullptr;
            }
            else
            {
                OSG_NOTICE << "No existing view, create one" << std::endl;
                _view = new osgViewer::View;
                _view->setName("View two");

                _view->setUpViewOnSingleScreen(1);


                osg::ref_ptr<osgText::Text> text2 = new osgText::Text();
                text2->setText("Here's some other text. It appears in the dynamically-added view. It ensures the default font gets used with a context that goes away, and that lives in the cache.");
                osg::ref_ptr<osg::AutoTransform> autoTransform2 = new osg::AutoTransform();
                autoTransform2->setAutoRotateMode(osg::AutoTransform::ROTATE_TO_SCREEN);
                autoTransform2->addChild(text2);
                osg::ref_ptr<osg::Node> scene2 = autoTransform2;

                _view->setSceneData(scene2.get());
                _view->setCameraManipulator(new osgGA::TrackballManipulator);

                _viewer->addUpdateOperation(new AddViewOperation(_view));
            }
            return true;
        }
        return false;
    }

protected:
    // raw pointer because it's stack-allocated
    osgViewer::CompositeViewer * _viewer;
    osg::ref_ptr<osgViewer::View> _view;
};

int main(int argc, char **argv)
{

    // use an ArgumentParser object to manage the program arguments.
    osg::ArgumentParser arguments(&argc, argv);

    osg::ref_ptr<osgText::Text> text = new osgText::Text();
    text->setText("Here's some text. It doesn't have to be text, and in fact we don't need anything in the main view as it just exists to keep the application alive.");
    osg::ref_ptr<osg::AutoTransform> autoTransform = new osg::AutoTransform();
    autoTransform->setAutoRotateMode(osg::AutoTransform::ROTATE_TO_SCREEN);
    autoTransform->addChild(text);
    osg::ref_ptr<osg::Node> scene = autoTransform;

    if (!scene)
    {
        OSG_NOTICE << argv[0] << ": requires filename argument." << std::endl;
        return 1;
    }

    // construct the viewer.
    osgViewer::CompositeViewer viewer(arguments);

    // single-threaded to prove a threading bug isn't responsible for the symptoms
    viewer.setThreadingModel(osgViewer::CompositeViewer::SingleThreaded);

    // view one
    {
        osg::ref_ptr<osgViewer::View> view = new osgViewer::View;
        view->setName("View one");
        viewer.addView(view);

        view->setUpViewOnSingleScreen(0);
        view->setSceneData(scene.get());
        view->setCameraManipulator(new osgGA::TrackballManipulator);

        view->addEventHandler(new ViewAdder(&viewer));
    }

    // run the viewer's main frame loop
    return viewer.run();
}





This started life as the Composite Viewer example, and I deleted chunks of it and then added the new behaviour I needed, so I don't think I've done anything invalid.

Upon starting the application, you'll see a view with some text on one monitor. This isn't important, but I believe I need a living viewer to be able to have an event handler.

When you press the V key, a second view with some different text will appear on the other monitor. Pressing V again will remove it, and then again will add it again, and so on and so forth.

The second appearance of the second view will have a white box instead of the text, though, as the object cache doesn't have its GL objects released when the view was destroyed, so OSG tries using the same object names, which are no longer valid with the new context. This happens because the text isn't attached to the scene graph any more when the context is destroyed, so the default font is only referenced by the object cache, and the camera and its renderer never have releaseGLObjects called.

The osg::GraphicsContext::removeCamera diff I posted earlier solves the issue.

Cheers,
Chris

------------------
Read this topic online here:
http://forum.openscenegraph.org/viewtopic.php?p=76611#76611







More information about the osg-users mailing list