Ulrich Hertlein wrote:
> 
> There is no 'OSG garbage collector' for objects in the scene graph.
> 


But it is the case that the referenceCount for an object will not necessarily 
be 0 after it is removed as a child from a scene.  The viewer can make 
references to objects in a scene and it does not give them up immediately when 
removeChild is called.  I have a test program (below) in which you must wait 
until the second viewer.frame() after a removeChildren() before the 
referenceCounts return to the value you'd otherwise expect.

This was the source of my confusion and memory problems.

Is there a way to explicitly force the viewer to finish doing whatever it needs 
to do so that I can get my object/memory back?  The work-around with redrawing 
and viewer.frame() calls don't seem to be guaranteed, even though in practice 
it works for me.

The code is below.  Compile it as testViewer and run the program like this to 
see the issue with refCounts.  (Add the angle brackets for the #includes; the 
editor here (The code demonstrates other issues, which I will post about 
separately.)  

./testViewer 10000 share 1 1 true
Execution times for viewer.frame(): 0.0192079, 0.634751, 0.008714, ...

First, the execution times show that after adding the 10000 spheres, 
viewer.frame() does a significant amount of work on the second call.

Then, later, after calling removeChildren() the code calls viewer.frame() many 
times before checking the referenceCounts of the osg::ShapeDrawable.  In this 
case the referenceCounts are what I would expect them to be.

$ ./testViewer 10000 share 1 1 false
Execution times for viewer.frame(): 0.0184321, 0.639288, 0.00910108, ...
Shape refCounts wrong.

Here the code doesn't call viewer.frame() before checking the referenceCounts 
of the ShapeDrawable.  You get "Shape refCounts wrong." which means that the 
referenceCounts are not what I would expect.

Thanks!


Code:

// Remove the spaces in the #includes. BBCode didn't display this 
// correctly without them.
#include < iostream >
#include < iomanip >
#include < sys/time.h >
#include < string.h >
#include < stdlib.h >

#include < osgViewer/Viewer >
#include < osgViewer/CompositeViewer >
#include < osgGA/TrackballManipulator >
#include < osg/PositionAttitudeTransform >
#include < osg/ShapeDrawable >

#define UNIFORM(a,b) ((((double)rand() / (double)RAND_MAX) * ((b)-(a))) + (a))

double getCurrentTime() {
  struct timeval time;
  gettimeofday(&time, 0);
  return time.tv_sec + (time.tv_usec * 0.000001);
}

int main(int argc, char* argv[])
{
  
  srand((unsigned int) getCurrentTime());

  size_t numSpheres = 1;
  bool shareGeode = false;
  size_t maxIterations = 1;
  double waitTime = 1.0;
  bool pause = true;

  // Command line args.
  if ((argc < 2) || (argc > 6)) return 1;
  if (argc >= 2) numSpheres = atoi(argv[1]);
  if (argc >= 3) shareGeode = (strcmp("share", argv[2]) == 0);
  if (argc >= 4) maxIterations = atoi(argv[3]);
  if (argc >= 5) waitTime = (double)atoi(argv[4]);
  if (argc >= 6) pause = (strcmp("true", argv[5]) == 0);

  // The scene goes here.
  osg::ref_ptr<osg::Group> sceneRoot = new osg::Group();

  // We keep our own references to all the objects so that we 
  // can check their reference counts later.
  std::vector<osg::ref_ptr<osg::PositionAttitudeTransform> > pats;
  std::vector<osg::ref_ptr<osg::Geode> > geodes;
  std::vector<osg::ref_ptr<osg::ShapeDrawable> > shapes;

  // If shareGeode is true, construct the scene by sharing a single sphere
  // geode among all PATs for each sphere that is drawn.
  if (shareGeode) {
    osg::ref_ptr<osg::Geode> sphereGeode = new osg::Geode();
    osg::ref_ptr<osg::Sphere> sphere = new osg::Sphere(osg::Vec3(0,0,0),0.2);
    osg::ref_ptr<osg::ShapeDrawable> sphereDrawable = 
      new osg::ShapeDrawable(sphere);

    sphereGeode->addDrawable(sphereDrawable);

    shapes.push_back(sphereDrawable);
    geodes.push_back(sphereGeode);
  }

  osgViewer::Viewer viewer;
  viewer.setUpViewInWindow( 0, 0, 1200, 900 );
  viewer.setSceneData(sceneRoot);
  viewer.setCameraManipulator(new osgGA::TrackballManipulator());
  viewer.realize();

  // This whole state-machine cruft is just to give the system monitor
  // time to reflect changes in the memory usage so we can see them.
  typedef enum { ADD, REMOVE, QUIT } Action;
  Action action = ADD;
  double lastActionTime = 0.0;
  bool quit = false;
  size_t numIterations = 0;
  size_t geodeIndex;
  while (!quit && !viewer.done()) {

    if ((getCurrentTime() - lastActionTime) > waitTime) {
      lastActionTime = getCurrentTime();
      switch (action) {
      case ADD:
        {
          for (size_t ii=0; ii<numSpheres; ++ii) {
            // Create a new geode or reuse the one already created.
            if (shareGeode) {
              geodeIndex = 0;
            } else {
              osg::ref_ptr<osg::Geode> sphereGeode = new osg::Geode(); 
              osg::ref_ptr<osg::Sphere> sphere = 
                new osg::Sphere(osg::Vec3(0,0,0),0.2);
              osg::ref_ptr<osg::ShapeDrawable> sphereDrawable = 
                new osg::ShapeDrawable(sphere);

              sphereGeode->addDrawable(sphereDrawable);
          
              geodeIndex = geodes.size();
              shapes.push_back(sphereDrawable);
              geodes.push_back(sphereGeode);
            }

            // Move the sphere to a random location.
            double x = UNIFORM(-1,1);
            double y = UNIFORM(-1,1);
            double z = UNIFORM(-1,1);
            osg::ref_ptr<osg::PositionAttitudeTransform> sphereXform = 
              new osg::PositionAttitudeTransform();
            osg::Vec3 position(x,y,z);
            sphereXform->setPosition(position); 
            pats.push_back(sphereXform);

            // Add it to the scene.
            sphereXform->addChild(geodes[geodeIndex]);
            sceneRoot->addChild(sphereXform);
          }

          std::cout << "Execution times for viewer.frame(): ";
          for (size_t ff=0; ff<5; ff++) {
            double start, stop;
            start = getCurrentTime();
            viewer.frame();
            stop = getCurrentTime();
            std::cout << (stop - start) << ", "; std::cout.flush();
          }
          std::cout << std::endl;


          action = REMOVE;
        }
        break;
      case REMOVE:
        {
          sceneRoot->removeChildren(0,sceneRoot->getNumChildren());

          // Do this to give viewer a chance to release everything.
            
          // The refCount for the PATs should all be 1 now.  They no longer
          // exist as children of sceneRoot, but they are referenced in the
          // vector 'pats'.
          bool patsOK = true;
          for (size_t pp=0; pp<pats.size(); ++pp) {
            if (pats[pp]->referenceCount() != 1) patsOK = false;
          }
          if (!patsOK) std::cout << "PAT refCounts wrong." << std::endl;

          // Release all the pats.
          pats = std::vector<osg::ref_ptr<osg::PositionAttitudeTransform> >();
          // Do this to give viewer a chance to release everything.
          if (pause) for (size_t ff=0; ff<10; ff++) { viewer.frame(); }

          // The refCount for the geodes should be 1 because they no longer
          // exist as a child of any PAT, but they are referenced in the
          // geodes vector.
          bool geodesOK = true;
          for (size_t gg=0; gg<geodes.size(); ++gg) {
            if (geodes[gg]->referenceCount() != 1) geodesOK = false;
          }
          if (!geodesOK) std::cout << "Geode refCounts wrong." << std::endl;

          // The refCount for the shapeDrawables should be 2 because they
          // are each a child of a geode and also referenced in the
          // shapes vector.
          bool shapesOK = true;
          for (size_t ss=0; ss<shapes.size(); ++ss) {
            if (shapes[ss]->referenceCount() != 2) shapesOK = false;
          }
          if (!shapesOK) std::cout << "Shape refCounts wrong." << std::endl;

          if (!shareGeode) {
            // If we're not sharing the geode, we clear these vectors
            // because we'll refill them on the next iteration.
            geodes = std::vector<osg::ref_ptr<osg::Geode> >();
            shapes = std::vector<osg::ref_ptr<osg::ShapeDrawable> >();

            // Do this to give viewer a chance to release everything.
            if (pause) for (size_t ff=0; ff<10; ff++) { viewer.frame(); }
          }
          

          action = ADD;
          numIterations++;
          if (numIterations >= maxIterations) {
            action = QUIT;
          }
        }
        break;

      case QUIT: quit = true; break;

      }
    }

    viewer.frame();
  }
  return 0;
}





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





_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

Reply via email to