Hi David, By default osgViewer will look at the number of processing CPU cores avaiable and select an appropriate threading model for the viewer to produce the best performance utilizes the CPU cores. If you have two or more CPU cores then it'll select DrawThreadPerContext, and with this threading model there is dedicated graphics thread running for each graphics context, and these threads take ref_ptr<> to the StateSet and Drawable that they are currently drawing. While these graphics thread run the main thread can continue, so viewer.frame() continues even when the graphics threads are still working and retaining a ref count of the objects that they are still using.
Essentially there is nothing to worry about at all, the OSG will automatically clean up after itself once it's finished using scene graph objects. Robert. On Wed, Oct 26, 2011 at 7:37 PM, David Benoit <[email protected]> wrote: > > 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 > _______________________________________________ osg-users mailing list [email protected] http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

