wiki:Tutorial/OpenSG1/Memleak

First Considerations

Use professional tools such as Valgrind or anything else that enables you to find memory leaks in your

application. If you have no access to such tools, you might find the following useful.

See this and this FAQ first.

As you might have already read, OpenSG does use Ptr's and/or RefPtr's

instead of ordinary C pointers. Those are powerful constructs that

allows us to use refcounting. In order to create any OpenSG object

instance you use the respective create method. Each time you do so, OpenSG stores

this new instance into a STL vector by just using push_back. So, be aware

that this internal vector will always grow, it never shrinks (mainly

because of performance considerations). So OpenSG is keeping track of all

the allocated (and disallocated!) instances. You do have access to the

list and of course you can print it:

First of all, after you have called osgInit(), OpenSG has created

some internal static variables, to avoid to show them as user

allocated, you should get the offset index right after calling osgInit():

#include <OpenSG/OSGFieldContainerFactory.h>

//...
osgInit( argc, argv );

fcStoreSize = FieldContainerFactory::the()->getFieldContainerStore()->size();
void printOSGInstances()
{
  const std::vector<FieldContainerPtr> &fcs = *FieldContainerFactory::the()->getFieldContainerStore();

  int notNull = 0;

  for(int i=fcStoreSize;i<fcs.size();++i)
  {
    FieldContainerPtr fc = fcs[i];

    if(fc != NullFC)
    {
      notNull++;

      AttachmentContainerPtr ac = AttachmentContainerPtr::dcast(fc);

      if ( ac == NullFC )
      {
        AttachmentPtr a = AttachmentPtr::dcast( fc );

        if ( a != NullFC )
        {
          FieldContainerPtr dad = NullFC;

          if ( a->getParents().size() > 0 )
          {
            dad = a->getParents().getValue(0);
          }

          ac = AttachmentContainerPtr::dcast(dad);
        }
      }

      const osg::Char8 * name = OSG::getName( ac );

      if (  name != NULL )
      {
        printf( "Detected living FC %s (%s) %p refcount %d\n", fc->getTypeName(), name, fc.getCPtr(), fc.getRefCount());
      }
      else
      {
        printf( "Detected living FC %s %p refcount %d\n", fc->getTypeName(), fc.getCPtr(), fc.getRefCount() );
      }
    }

  }

  printf( "%d out of %d FC's have not been destroyed\n", notNull, fcs.size()-fcStoreSize );
}

Before you call printOSGInstances(), make sure to call Thread::getCurrent()->getChangeList()->clearAll(); in order to clear the change list. You can print this list anytime in your application, typically you print it right before exiting in debug mode in order to see if there are any allocated FC's remaining in memory. If you have like 4 (or something in this magnitude) FC's remaining, don't worry, OpenSG sometimes allocates some static variables, e.g. when you create a SimpleGeometry, OpenSG allocates a few static variables which will show up in your FC list. Now, If you're a beginner you'll typically have a huge list of non dealloced FC's, don't panic, there are some things you should know, the most common pitfalls are described in the following sections!

The output might look somewhat anonymous, you might have no clue which FC matches the FC you've created in your code. The only thing you know is that the list is sorted in order of creation time (most recently allocated objects appear at the end of the list). However, some FC (more precisely, FC which derive from "AttachmentContainer") can carry a name attachment which can be set with the convenience method "setName". If you set those, you'll see them in you output of printOSGInstances():

  ImagePtr img = Image::create();
  setName( img, "My Whatever Image Name" );

Unfortunately some frequently used OpenSG objects (e.g. ChunkMaterial, BlendChunk, etc) don't derive from AttachmentContainer and thus you can't associate names to them.

RefPtr vs Ptr

Although most OpenSG tutorials use "ordinary" Ptr's, you should definetly use RefPtr's in your application. The difference is that if you use RefPtr's automatically handle the refcounting, whereas the Ptr's are "passive", you have to call addRefCP and subRefCP on them. A small code sample says more than 1000 words:

{
  NodeRefPtr node;

  node = Node::create(); // node has refcount 1
}

// leaving scope, the RefPtr instance is destroyed, 
// thus the refcounter is decreased, and finally, 
// because the refcount is <= 0, the actual Node is deleted

You can achieve the same effect with Ptr's, but it's more manual work:

#!cpp
{

  NodePtr node = Node::create(); // node has refcount 0 (note the difference!) 
  addRefCP( node );              // node has refcount 1
}

// leaving scope, the NodePtr is destroyed, but the refcount is not decreased,
// Node is still alive -> you have a memory leak! 
// you have to call subRefCP explicitely

{
  NodePtr node = Node::create(); // refcount = 0
  addRefCP( node );              // refcount 1
  subRefCP( node ); // refcount 0 -> Node is destroyed
}

Most of the time you create Ptr's and add them as children to a parent, in those cases you use methods like addChild() which automatically increase the reference counter, so you never need to care about them again because they are freed when one of his ancestors get destroyed. But you typically hold a reference to the root node of your scene, and as you're having the root scene as a member variable of some class, it's good practice to use a RefPtr. You don't have to write extra code in your destructor. Here's the difference:

// use RefPtr
class SceneStuff
{
public:
  SceneStuff(){ m_pRootScene = SceneFileHandler::the().read( "dinopet.3ds" ); };
  ~SceneStuff(){}; // m_pRootScene is destroyed and the scene gets destroyed

private:
  NodeRefPtr m_pRootScene;
};

// use boring Ptr
class SceneStuff
{
public:
  SceneStuff(){ 
    m_pRootScene = SceneFileHandler::the().read( "dinopet.3ds" ); 
    addRefCP( m_pRootScene );
  };

  ~SceneStuff(){ subRefCP( m_pRootScene ); };

private:
  NodePtr m_pRootScene;
};

Viewports, Windows, Foregrounds and Backgrounds

Usually when you create Nodes, Materials etc, you add them to parents, set them as cores, etc. you don't care of those objects anymore becuase the containing objects will take care of their destruction. This does not apply to Viewports or Windows! E.g. (senseless) loops like the following will cause memory leaks:

ViewportPtr view = Viewport::create();

for( int i = 0; i < 100; ++i )
  view->setBackground( SolidBackground::create() ); // previous SolidBackground won't get subreferenced!

for( int i = 0; i < 100; ++i )
  view->setRoot( Node::create() ); // previous node won't get subreferenced!

for( int i = 0; i < 100; ++i )
  view->setCamera( PerspectiveCamera::create() ); // previous camera won't get subreferenced!

subRefCP( view ); // Viewport is destroyed, but the previousely set root, camera and nackground are not!

As a rule of thumb, all OpenSG classes concerning Viewports, Windows, Foregrounds and Backgrounds don't use reference counting at all. You'll have to take care of object destruction of the objects you pass to those classes.

ChunkMaterial

Consider the case in which you have created a ChunkMaterial with a set of Chunk attached to it. You might want to change the chunk arragement within the ChunkMaterial? at run-time like this:

  matCk = ChunkMaterial::create();
  beginEditCP(matCk);
    matCk->addChunk(shaderCk);
    matCk->addChunk(blendCk);
    matCk->addChunk(depthCk);
  endEditCP(matCk);

// ok, use matCk somehow (e.g. render ...)

// reconfigure matCk
// HOW to NOT rearrange matCk:

  beginEditCP(matCk);
    matCk->getChunks().clear(); // clear chunk list -> mem-leak
    matCk->addChunk(otherShaderCk);
    matCk->addChunk(otherBlendCk);
  endEditCP(matCk);

// How to rearrange matCk without mem-leak:

  beginEditCP(matCk);
    // clear the chunk list using subChunk!
    while( matCk->getChunks().size() )
      matCk->subChunk(matCk->getChunks()[0]);

    matCk->addChunk(otherShaderCk);
    matCk->addChunk(otherBlendCk);
  endEditCP(matCk);

So what happened? Keep in mind that the addRef method increases the ref counter of the chunks. The subRef method on the other hand does decrease the refcount. OpenSG offers a convenience method to access directly the STL list holding the chunks, in this case "getChunks()". When you call clear on this list, it actually just does clear the list, without modifying any ref counter of the objects stored in that list! You should be aware of this before using any of the operators on the STL list (same applies of course to the "erase" function).

Beacons

When you create a PerspectiveCamera you usueally set a beacon node (your virtual "tripod") using the method "setBeacon" like in this sample:

OSG::PerspectiveCameraPtr createCamera()
{
  OSG::PerspectiveCameraPtr cam = OSG::PerspectiveCamera::create();

  beginEditCP(cam);
    cam->setFov( OSG::deg2rad(60) );
    cam->setNear( 1 );
    cam->setFar( 100 );

    OSG::NodePtr node = OSG::Node::create();
    setName( node, "camera beacon" );

    OSG::TransformPtr transformCore = OSG::Transform::create();
    Matrix m;
    m.setIdentity();

    beginEditCP(transformCore, OSG::Transform::MatrixFieldMask);
      transformCore->setMatrix(m);
    endEditCP(transformCore, OSG::Transform::MatrixFieldMask);

    beginEditCP( node, OSG::Node::CoreFieldMask );
      node->setCore( transformCore );
    endEditCP( node, OSG::Node::CoreFieldMask );

    cam->setBeacon( node );
  endEditCP(cam);

  return cam;
}

void main()
{
  // ... init etc ...

  OSG::PerspectiveCamera cam = createCamera();

  // do some more, setup scene etc.
  // render scene 

  subRefCP( cam->getBeacon() ); // destroy beacon
  subRefCP( cam ); // destroy camera
}

The PerspectiveCamera does no refcounting on his beacon. You have to take care of it. Usually your beacon is a sub-child of some node, such that the parent takes care of destruction, but in case you create a orphan node as a beacon, you have to subRef it by yourself.

Textures

When you create a texture in OpenSG, you first create an image, configure the image size and content, after that OpenSG takes care of uploading the image data into the Texture as soon as it gets rendered the first time.

  ImagePtr image = Image::create();
  setName( image, "some texture" );

  image->set( Image::OSG_RGBA_PF, 512, 512 );

  TextureChunkPtr texture = TextureChunk::create();

  beginEditCP( texture );
    texture->setMinFilter( GL_LINEAR );
    texture->setMagFilter( GL_LINEAR );
    texture->setImage(image); // link the image to the texture
  endEditCP( texture );

Keep in mind that OpenSG never destroys the image although it might have been uploaded to graphics memory at some point. OpenSG relies on this fact for clustering and for correct saving of the scene to disk. When you call subRefCP on the image, be sure that you also subref the texture, else the behaviour is undefined. In practice you just create the image, attach it to the texture, then the image will be subrefed by the destructor of the texture. Just consider that whenever you create a texture in OpenSG, the memory usage of an image in you application get's easily tripled (copy in memory + copy in graphics memory + copy made by graphics driver in main memory (for swapping!)).

Last modified 6 years ago Last modified on 09/14/11 03:26:12