OpenSceneGraph Forum Forum Index OpenSceneGraph Forum
Official forum which mirrors the existent OSG mailing lists. Messages posted here are forwarded to the mailing list and vice versa.
 
   FAQFAQ    SearchSearch    MemberlistMemberlist    RulesRules    UsergroupsUsergroups    RegisterRegister 
 Mail2Forum SettingsMail2Forum Settings  ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 
   AlbumAlbum  OpenSceneGraph IRC ChatOpenSceneGraph IRC Chat   SmartFeedSmartFeed 

Removing objects with shared GL state from scene graph

Goto page 1, 2  Next
 
Post new topic   Reply to topic    OpenSceneGraph Forum Forum Index -> General
View previous topic :: View next topic  
Author Message
AnyOldName3 (Chris Djali)
User


Joined: 08 Sep 2017
Posts: 34

PostPosted: Wed Mar 20, 2019 5:05 pm    Post subject:
Removing objects with shared GL state from scene graph
Reply with quote

Hi,

I know that when an OpenGL context is destroyed, the associated viewer can only call releaseGLObjects(osg::State *) for nodes still attached to the scene graph, so if you're removing things before that, you're manually supposed to call releaseGLObjects on the subgraph.

However, as I see it, there's a potential problem here. If the subgraph contains nodes that share GL objects with things still attached to the main scenegraph, you'll end up releasing those, too. This can mostly be worked around by not sharing GL objects between nodes with different lifetimes, but sometimes that's not possible.

For example, osgText::Text seems to have a global osg::Program used for all instances (or, at least, all instances with the same settings). If I always call releaseGLObjects when removing subgraphs containing an osgText::Text, it's going to kill the per-context programs and they'll need rebuilding for any other text nodes, which isn't desirable. If I never call releaseGLObjects, then that's even worse (for obvious reasons). It seems like the only good ways of handling this would be to either keep track of when the last text node was removed so the objects could be released when their last user was removed, or move 'removed' objects to another part of the scene graph (e.g. as a disabled osg::Switch child) so they're actually kept around without being drawn, but still get their GL objects released when the context is destroyed. There's also the hacky option of attaching another text node somewhere else in the scenegraph so it always gets released, which works in the specific case of osgText::Text, but won't work in the general case, and doesn't seem desirable.

Is there a better way of handling this than I've suggested here? If there's something intended for this built into OSG that I just don't know about, that would be great, but I'm not going to shy away from writing something custom if that's what's necessary.

Cheers,
Chris
Back to top
View user's profile Send private message
mp3butcher (Julien Valentin)
Appreciator


Joined: 17 Feb 2010
Posts: 519
Location: France

PostPosted: Thu Mar 28, 2019 12:44 pm    Post subject:
Re: Removing objects with shared GL state from scene graph
Reply with quote

Hi Chris,
I haven't dive into the problem, but would making the osgText::Text::program static (and share it with all instances) could be a proper way to handle this issue?
Cheers

AnyOldName3 wrote:
Hi,

I know that when an OpenGL context is destroyed, the associated viewer can only call releaseGLObjects(osg::State *) for nodes still attached to the scene graph, so if you're removing things before that, you're manually supposed to call releaseGLObjects on the subgraph.

However, as I see it, there's a potential problem here. If the subgraph contains nodes that share GL objects with things still attached to the main scenegraph, you'll end up releasing those, too. This can mostly be worked around by not sharing GL objects between nodes with different lifetimes, but sometimes that's not possible.

For example, osgText::Text seems to have a global osg::Program used for all instances (or, at least, all instances with the same settings). If I always call releaseGLObjects when removing subgraphs containing an osgText::Text, it's going to kill the per-context programs and they'll need rebuilding for any other text nodes, which isn't desirable. If I never call releaseGLObjects, then that's even worse (for obvious reasons). It seems like the only good ways of handling this would be to either keep track of when the last text node was removed so the objects could be released when their last user was removed, or move 'removed' objects to another part of the scene graph (e.g. as a disabled osg::Switch child) so they're actually kept around without being drawn, but still get their GL objects released when the context is destroyed. There's also the hacky option of attaching another text node somewhere else in the scenegraph so it always gets released, which works in the specific case of osgText::Text, but won't work in the general case, and doesn't seem desirable.

Is there a better way of handling this than I've suggested here? If there's something intended for this built into OSG that I just don't know about, that would be great, but I'm not going to shy away from writing something custom if that's what's necessary.

Cheers,
Chris

_________________
Twirling twirling twirling toward freedom
Back to top
View user's profile Send private message Visit poster's website
AnyOldName3 (Chris Djali)
User


Joined: 08 Sep 2017
Posts: 34

PostPosted: Thu Mar 28, 2019 11:14 pm    Post subject:
Reply with quote

The problem is specifically because it's static. Unless I'm manually keeping track of when the last text object is removed from the scene, I can't release the program without it potentially still being in use. If I never release it, references to it can persist beyond the lifetime of the context it was created for.


Cheers,
Chris
Back to top
View user's profile Send private message
mp3butcher (Julien Valentin)
Appreciator


Joined: 17 Feb 2010
Posts: 519
Location: France

PostPosted: Fri Mar 29, 2019 2:36 am    Post subject:
Reply with quote

I don't understand:
I can't find any static osg::ref_ptr<osg::Program> in osgText/Text...
please give a simple code illustrating your problem

AnyOldName3 wrote:
The problem is specifically because it's static. Unless I'm manually keeping track of when the last text object is removed from the scene, I can't release the program without it potentially still being in use. If I never release it, references to it can persist beyond the lifetime of the context it was created for.


Cheers,
Chris

_________________
Twirling twirling twirling toward freedom
Back to top
View user's profile Send private message Visit poster's website
robertosfield
OSG Project Lead


Joined: 18 Mar 2009
Posts: 12279

PostPosted: Fri Mar 29, 2019 7:41 am    Post subject:
Removing objects with shared GL state from scene graph
Reply with quote

Hi Chris,

Which version of the OSG are you working with?

Robert.


------------------
Post generated by Mail2Forum
Back to top
View user's profile Send private message
AnyOldName3 (Chris Djali)
User


Joined: 08 Sep 2017
Posts: 34

PostPosted: Fri Mar 29, 2019 7:00 pm    Post subject:
Reply with quote

All out of the 3.6 branch, 3.4.1 and OpenMW's 3.4.1 fork (which doesn't make any changes to osgText, and we're trying to abandon, but it's much faster than the official 3.4.1 release). If everything's working as we think it does, OpenMW should work with any OSG release after 3.4.

The issue of the text program not being released when the context is torn down happens with all the versions listed above. Also, an answer to the question in the general case would probably be helpful for all of the versions.
Back to top
View user's profile Send private message
AnyOldName3 (Chris Djali)
User


Joined: 08 Sep 2017
Posts: 34

PostPosted: Fri Apr 12, 2019 3:53 pm    Post subject:
Reply with quote

Hi,

It's been a couple of weeks since the last reply to this. I'm guessing this has fallen off the radar, but I still need to fix the issue, so would appreciate if another look could be taken at this.

Thank you!

Cheers,
Chris
Back to top
View user's profile Send private message
robertosfield
OSG Project Lead


Joined: 18 Mar 2009
Posts: 12279

PostPosted: Sun Apr 14, 2019 6:42 am    Post subject:
Removing objects with shared GL state from scene graph
Reply with quote

Hi Chris,

On Fri, 12 Apr 2019 at 21:07, Chris Djali <> wrote:
Quote:
It's been a couple of weeks since the last reply to this. I'm guessing this has fallen off the radar, but I still need to fix the issue, so would appreciate if another look could be taken at this.

There is really enough to go on to be able to diagnose what is going
on, there is a limit to how much we can advise on usage combinations
that we haven't seen locally.

Could you create a small OSG example that illustrates the problem
across the different versions of the OSG.

Robert.


------------------
Post generated by Mail2Forum
Back to top
View user's profile Send private message
AnyOldName3 (Chris Djali)
User


Joined: 08 Sep 2017
Posts: 34

PostPosted: Tue Apr 16, 2019 3:58 pm    Post subject:
Reply with quote

Hi,

The following basically simulates the use case that's causing problems.

Code:
#include <random>

#include <osg/AutoTransform>
#include <osg/Group>
#include <osg/PositionAttitudeTransform>
#include <osg/ShapeDrawable>

#include <osgText/Text>

#include <osgViewer/Viewer>

/** A representation of something that gets edited.
 *  Pretend it's actually more complicated than this so that reference counting the number of attached text nodes is a nuisance. */
class World
{
public:
    World() : mScene(new osg::Group)
    {
        // add things so the viewer doesn't automatically zoom too far in to see the 'objects'
       
        auto worldCorners = { osg::Vec3(-11, -11, -11), osg::Vec3(-11, -11, 11),
                              osg::Vec3(-11, 11, -11), osg::Vec3(-11, 11, 11),
                              osg::Vec3(11, -11, -11), osg::Vec3(11, -11, 11),
                              osg::Vec3(11, 11, -11), osg::Vec3(11, 11, 11) };

        for (auto corner : worldCorners)
        {
            osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform();
            pat->setPosition(corner);
            pat->addChild(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0, 0, 0), 0.1)));
            mScene->addChild(pat);
        }
    }

    osg::ref_ptr<osg::Group> getScene() { return mScene; }

    /** Adds an object with a label to the scene based on something the user has done. */
    void addObject(std::string name)
    {
        osg::ref_ptr<osg::PositionAttitudeTransform> object = new osg::PositionAttitudeTransform();
        object->setName(name);

        static std::random_device r;
        static std::default_random_engine randEngine(r());
        static std::uniform_real_distribution<> dist(-10, 10);

        object->setPosition(osg::Vec3(dist(randEngine), dist(randEngine), dist(randEngine)));

        osg::ref_ptr<osgText::Text> objectLabel = new osgText::Text();
        osg::ref_ptr<osg::AutoTransform> autoTransform = new osg::AutoTransform();
        autoTransform->addChild(objectLabel);
        autoTransform->setAutoRotateMode(osg::AutoTransform::ROTATE_TO_SCREEN);
        object->addChild(autoTransform);
        autoTransform->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
        objectLabel->setText(name);
        objectLabel->setCharacterSize(1);

        osg::ref_ptr<osg::Shape> shape = new osg::Sphere(osg::Vec3(0, 0, 0), 1);
        object->addChild(new osg::ShapeDrawable(shape));

        mScene->addChild(object);
    }

    /** Removes an object from the scene based on something the user has done. */
    void removeObject(std::string name)
    {
        osg::ref_ptr<osg::Node> child;
        for (unsigned int i = 0; i < mScene->getNumChildren(); ++i)
        {
            if (mScene->getChild(i)->getName() == name)
            {
                child = mScene->getChild(i);
                mScene->removeChild(i);
                break;
            }
        }

        // If we call child->releaseGLObjects() here, the text program will be released, too, even though it could still be being used by other labels.
        // If we don't, we may be detaching the last text node from the scene graph, and so the text program may never get released.
    }

private:
    osg::ref_ptr<osg::Group> mScene;
};


World world;
bool useNewViewer = true;

class EventHandler : public osgGA::GUIEventHandler
{
public:
    bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
    {
        if (ea.getEventType() != osgGA::GUIEventAdapter::KEYDOWN)
            return false;

        // The user may wish to close the 3D view to work on something else and then reopen it later.
        // That would be a pain to implement directly, so instead, we simulate it by reopening the 3D view when it's closed if Return was pressed while it was open.
        if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Return)
        {
            useNewViewer = true;
            return true;
        }
        // Press a letter key without shift to add an object.
        else if (ea.getKey() >= 'a' && ea.getKey() <= 'z')
        {
            std::string name(1, ea.getKey());
            world.addObject(name);
            return true;
        }
        // Press a letter key with shift to remove an object.
        else if (ea.getKey() >= 'A' && ea.getKey() <= 'Z')
        {
            std::string name(1, ea.getKey() - ('Z' - 'z'));
            world.removeObject(name);
            return true;
        }

        return false;
    }
};

int main()
{
    osg::ref_ptr<osgViewer::Viewer> currentViewer;

    int returnCode = 0;

    while (useNewViewer)
    {
        currentViewer = new osgViewer::Viewer;
        currentViewer->setSceneData(world.getScene());
        currentViewer->addEventHandler(new EventHandler);

        useNewViewer = false;
       
        returnCode = currentViewer->run();
    }

    return returnCode;
}


Imagine this is part of a world editor for a game or CAD software or one of any number of types of software packages where you edit something and might want a 3D view of it, but wouldn't necessarily always want that 3D view taking up space in the window, so you'd possibly close it and then reopen it later. So I didn't have to make a load of extra stuff, this is simulated by opening a new viewer after the previous one closed if enter was pressed.

Pressing a letter key will add an object, and pressing the same letter with the shift key will remove it. Pretend that there are actually loads of different object types and not all of them have text labels so that keeping track of when the last text label is removed via reference counting or similar becomes a pain.

If you add at least one object, then remove all of them, then open a new view, you'll see OpenGL errors and text won't render correctly. This is because there were no osgText::Text nodes attached to the scene graph when the view was reset, so the program never got released, and the new text nodes try and reuse it with the new context, where it's not valid.

The obvious solution is to releaseGLObjects in the removeObject method, but if there are other objects still using text nodes, that's problematic as they'll still be using the program, and it would need recreating immediately.

Alternatively, removed objects could be kept around forever, attached to a hidden part of the scene graph so that anything that was ever used gets released when the context gets torn down. That means a bunch of junk needs keeping around forever, though.

Another option would just be to always have a hidden text node attached to the scene graph even when there are no objects. This solves the problem without much overhead but doesn't really seem like a tidy solution.

Thank you!

Cheers,
Chris
Back to top
View user's profile Send private message
AnyOldName3 (Chris Djali)
User


Joined: 08 Sep 2017
Posts: 34

PostPosted: Wed May 15, 2019 10:51 pm    Post subject:
Reply with quote

Hi,

It looks like this has fallen off the radar again as it's been a month. I'd still rather fix this in a robust way rather than making a guess as to the most sensible approach and creating maintenance problems down the line.

Thank you!

Cheers,
Chris
Back to top
View user's profile Send private message
robertosfield
OSG Project Lead


Joined: 18 Mar 2009
Posts: 12279

PostPosted: Thu May 16, 2019 8:01 am    Post subject:
Removing objects with shared GL state from scene graph
Reply with quote

On Thu, 16 May 2019 at 00:07, Chris Djali <> wrote:
Quote:
It looks like this has fallen off the radar again as it's been a month. I'd still rather fix this in a robust way rather than making a guess as to the most sensible approach and creating maintenance problems down the line.

Sorry, I've been submerged in VSG work.

Just now I had a quick scan of your test program but haven't compiled
and run it. My first though is the global World object holds a
ref_ptr<> to the scene graph and there is no mechanism for deleting
this global prior to the viewer cleans up so in essence it's prevent
that scene graph from being deleted and cleaned up. After the exit of
the main frame loop you could explicitly delete this object. Another
approach would be to make this World object a custom Group node in the
scene graph that adds the high level behaviors you want and with it
ensure that you can override any releaseGLObjects() etc on any locally
cached objects that would otherwise be hidden from the OSG's attempts
at cleaning things up.

Robert.


------------------
Post generated by Mail2Forum
Back to top
View user's profile Send private message
AnyOldName3 (Chris Djali)
User


Joined: 08 Sep 2017
Posts: 34

PostPosted: Sat Jun 29, 2019 11:43 pm    Post subject:
Reply with quote

Hi,

I've been putting off responding to the last post as I wanted to be sure I wasn't misunderstanding you, but after several rereadings, I still think you've missed my point. I'll try explaining the issue again, but I'm not sure which part isn't clear, so I don't know how much it'll help.

In the application I'm working with, many viewers (and their OpenGL contexts) can come and go over the lifetime of the program, but the scenegraph doesn't get replaced, so can be used with multiple viewers, potentially at the same time.

OSG has mechanisms that make this work, such as how osg::Program has PerContextProgram. Most of the time, this works just fine - provided you release a node's GL objects when you remove it from the scene, and let a viewer release the GL objects it has in the scene when you destroy it, everything is managed correctly.

However, osgText::Text doesn't work well with this approach. It uses one osg::Program for all instances in the scene that have the same font, so it's not safe to release a text node's GL objects when you remove it, as they may still be being used by another text node. Unless you've got a hacky workaround in place (a few of which I've outlined above), the shared program will leak when the viewer is destroyed (which is bad) and then, if another viewer is assigned the same context ID (which it will be), it's mistaken for the old one, and the invalid PerContextProgram (which refers to the leaked GL program) gets used, causing OpenGL errors.

This might have wider-reaching consequences than just the issue I'm having in the lots-of-viewers case - if two text nodes with the same font are used and one gets deleted, regardless of whether more contexts are created later:
  • calling osgText::Text::releaseGLObjects calls osgText::TextBase::releaseGLObjects, which calls osgText::Font::releaseGLObjects, releasing GL objects still in use by the other text node.
  • Not calling osgText::Text::releaseGLObjects means that the other GL objects the node owns, such as its vertex buffers, get leaked, even though the shared font might get cleaned up later with the other text node.


Maybe the right thing to do is to only release the font's GL objects when all its users request it, rather than just one. I've not thought of a nice way of implementing this yet.


I'll address specific parts of the previous post now:

Quote:
there is no mechanism for deleting
this global prior to the viewer cleans up so in essence it's prevent
that scene graph from being deleted and cleaned up. After the exit of
the main frame loop you could explicitly delete this object.


There's not supposed to be any mechanism for deleting it prior to the viewer cleaning up as it's going to be used by another viewer later. Pretend it's not just a few ShapeDrawables with labels, but instead something really complicated that takes a long time to regenerate.

Quote:
Another
approach would be to make this World object a custom Group node in the
scene graph that adds the high level behaviors you want and with it
ensure that you can override any releaseGLObjects() etc on any locally
cached objects that would otherwise be hidden from the OSG's attempts
at cleaning things up.


The high-level behaviour is all fine as-is. The specific problem is that it's not safe to call osgText::Text::releaseGLObjects when removing a text object as that releases GL objects for the font, too, and that can still be in use by other text nodes.

Cheers,
Chris
Back to top
View user's profile Send private message
robertosfield
OSG Project Lead


Joined: 18 Mar 2009
Posts: 12279

PostPosted: Sun Jun 30, 2019 1:19 pm    Post subject:
Removing objects with shared GL state from scene graph
Reply with quote

Hi Chris,


On Sun, 30 Jun 2019 at 00:52, Chris Djali < (
Only registered users can see emails on this board!
Get registred or enter the forums!
)> wrote:

Quote:
The high-level behaviour is all fine as-is. The specific problem is that it's not safe to call osgText::Text::releaseGLObjects when removing a text object as that releases GL objects for the font, too, and that can still be in use by other text nodes.


It should be safe to call Text/Font::releaseGLObjects(state) with the deletion of GraphicsContext that the state associated with that GraphiscContext, that's the intention, if that isn't working and the high level functionality is working correctly then this is a bug.


I've just done a quick review of the code and master and 3.6 branch do mostly seem to be doing what they should in the Text/Font::releaseGLObjects().  There is one area that does look like it might be missing some releaseGLObjects() calls is in the handling of Glyph3D objects.


However, in your example where you were playing games with globals to prevent destruction, this remains the wrong way to implement viewers and is not supported by the relaseGLObjects() scheme, for reasons I've outlined in my replies above.



Robert.

 


 

------------------
Post generated by Mail2Forum
Back to top
View user's profile Send private message
AnyOldName3 (Chris Djali)
User


Joined: 08 Sep 2017
Posts: 34

PostPosted: Sun Jun 30, 2019 4:00 pm    Post subject:
Reply with quote

Hi,

I still think you're completely missing my point because of a criticism of my example code. For now, ignore the symptoms that brought me here. Just consider your bog-standard single-viewer situation.

If a text node is added to the scene graph and stays attached for the whole lifetime of the viewer, everything is fine.

If two text nodes are added to the scene graph and stay attached for the whole lifetime of the viewer, everything is fine.

If a text node is added to the scene graph and is removed before the viewer is destroyed, you can either not release GL objects, and have a bunch of things leak, or release them, and have everything be fine.

If you have two text nodes in the scene graph, and one is removed before the viewer is destroyed, you can either not release GL objects, and have some things leak (and some get cleaned up), or release them, and break the still-attached node.


Are you seriously telling me that OpenSceneGraph provides no mechanism to safely remove nodes that at one point were attached to the scene graph and this is intentional? If so, it's misleading to even have functions like osg::Group::removeChild as they're providing unsupported behaviour. I'd be very surprised if this is actually what you're saying.

Cheers,
Chris
Back to top
View user's profile Send private message
robertosfield
OSG Project Lead


Joined: 18 Mar 2009
Posts: 12279

PostPosted: Sun Jun 30, 2019 7:53 pm    Post subject:
Removing objects with shared GL state from scene graph
Reply with quote

HI Chris,


On Sun, 30 Jun 2019 at 19:28, Chris Djali < (
Only registered users can see emails on this board!
Get registred or enter the forums!
)> wrote:

Quote:

Are you seriously telling me that OpenSceneGraph provides no mechanism to safely remove nodes that at one point were attached to the scene graph and this is intentional? If so, it's misleading to even have functions like osg::Group::removeChild as they're providing unsupported behaviour. I'd be very surprised if this is actually what you're saying.


I'm not saying that at all.


The limitation with the current design+implementation is you hold a global reference to a scene graph object to prevent it from getting deleted normally as it's hidden from the viewers that manage the graphics contexts. For those objects you have to explictly call releaseGLObjects() as it won't happen for you.


Now, if this mechanism isn't working for a particular non standard usage case then it could be that the above extra house keeping isn't being done correctly, or there's an underlying OSG bug that needs to be addressed.


At this point I think it's pointless trying to explain yet again as that "I don't really understand what you mean", I've read what you've written multiple times tried my best and given you my best answer.  The best way to sort out this type of issue is by creating an example that illustrates the problem usage case.  The one you've posted has problems that I raised, these would need fixing before taking the next step and looking to whether there is an OSG bug. 



It's also important to test against OSG-3.6 branch/master as well as whatever other versions you are building against.  There was a bug in handle osgText in 3.4.x that was addressed in 3.6.x.



Cheers.

Robert.

------------------
Post generated by Mail2Forum
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    OpenSceneGraph Forum Forum Index -> General All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You cannot download files in this forum

Similar Topics
Topic Author Forum Replies Posted
No new posts My intro and need help for making graph ElizaMarshal General [forum] 0 Thu Jul 04, 2019 8:09 am View latest post
No new posts Different colors for a node shared sc... icf80 General 1 Thu Jun 13, 2019 12:49 pm View latest post
No new posts Different views hide/show different n... icf80 General 13 Wed Apr 17, 2019 9:56 am View latest post
No new posts VertexAttribDivisor -- should it be p... gwaldron General 1 Tue Mar 19, 2019 4:51 pm View latest post
No new posts Deep cloning an active root scene node Robert Lockyer General 7 Thu Feb 28, 2019 5:52 pm View latest post


Board Security Anti Bot Question MOD - phpBB MOD against Spam Bots
Powered by phpBB © 2001, 2005 phpBB Group
Protected by Anti-Spam ACP