Open Inventor Release 2024.2.0
 
Loading...
Searching...
No Matches
Modifying the Scene Graph

The critical issue for worker threads is how to safely modify the scene graph while other threads are either modifying the scene graph or traversing the scene graph. Similar to a database, we distinguish between scene graph readers and scene graph writers. A reader uses information contained in node fields, but does not modify the scene graph. Most action classes, for example SoGLRenderAction, are readers. A writer modifies the scene graph, either by changing field values or by adding and removing nodes. Any number of readers can access the scene graph at the same time, but only one writer can access the scene graph at any time (and no readers are allowed while the writer is modifying the scene graph).

Open Inventor currently implements a cooperative model of reader/writer control, using the SoDB methods readlock() / readunlock() and writelock() / writeunlock(). All action classes automatically request a read lock, by calling SoDB::readlock() , before beginning to traverse the scene graph. Any number of threads can be executing actions and be traversing the scene graph at the same time. Any thread that wants to modify the scene graph should request a write lock, by calling SoDB::writelock(), before making any change. SoDB::writelock() will not return until all readers have released their read locks (by calling SoDB::readunlock() ). Successfully acquiring the write lock ensures that no other threads are currently traversing (or modifying) the scene graph. After making its changes, the thread should release the write lock by calling SoDB::writeunlock() .Request/Release exclusive access to scene graph show an example of use of these exclusions methods.

Example : Request/Release exclusive access to scene graph

C++ :

// Request exclusive access to scene graph
SoDB::writelock();
// Modify text node
pTextNode->string.setValue( str );
// Release exclusive access as soon as possible
SoDB::writeunlock();

A simple example named MT_Modify1 is provided in $OIVHOME/src/Inventor/MultiThread/examples. This example is based on the standard Inventor Mentor example Using the Real-Time Global Field . In that example a digital clock is implemented by connecting the RealTime global field to an SoText3 string field. In the new example a digital clock is implemented by creating a separate thread which wakes up once every second and updates the string field of an SoText3 node. Some important points regarding MT_Modify1.cxx .

  1. Open Inventor must be initialized by calling SoXt::init().
  2. This example uses Open Inventor’s portable SbThread class to create the thread, but this is only for convenience.
  3. If more than one thread may be accessing the scene graph (for example, the viewer in the main thread may be rendering), in order to safely modify the scene graph, the second thread must gain exclusive access by calling SoDB::writelock().

Example : MT_Modify1.cxx

C++ :

#include <Inventor/SoDB.h>
#include <Inventor/Xt/SoXt.h>
#include <Inventor/Xt/viewers/SoXtExaminerViewer.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoText3.h>
// Use Open Inventor's portable thread classes
#include <Inventor/threads/SbThread.h>
// Forward decls
void* threadRout( void* _userData );
void updateText( SoText3* pTextNode );
** ////////////////////////////////////////////////////////////////////////
int
main( int argc, char*\* argv )
{
// Initialize Open Inventor
// *** Be sure to initialize with multithread support! ***
Widget myWindow = SoXt::init( argv[0] );
if ( myWindow == NULL )
{
exit( 1 );
}
// Create scene graph ------------------------------------------------
SoSeparator* root = new SoSeparator;
root->ref();
SoMaterial* myMaterial = new SoMaterial;
myMaterial->diffuseColor.setValue( 1.0, 0.0, 0.0 );
root->addChild( myMaterial );
SoText3* myText = new SoText3;
root->addChild( myText );
myText->string.setValue( SbTime::getTimeOfDay().formatDate() );
// Create viewer -----------------------------------------------------
SoXtExaminerViewer* myViewer = new SoXtExaminerViewer( myWindow );
myViewer->setSceneGraph( root );
myViewer->setTitle( "Date & Time" );
myViewer->show();
// Create thread to modify scene graph
//
// threadRout is the function the thread will execute
// myText is passed to threadRout as userData
SbThread* pModifyThread = SbThread::create( threadRout, ( void\* )myText );
SoXt::show( myWindow );
SoXt::mainLoop();
return 0;
}
** ////////////////////////////////////////////////////////////////////////
// Modify the text string
void
updateText( SoText3* pTextNode )
{
// Get date/time string
SbString str = SbTime::getTimeOfDay().formatDate();
// Request exclusive access to scene graph
SoDB::writelock();
// Modify text node
pTextNode->string.setValue( str );
// Release exclusive access as soon as possible
SoDB::writeunlock();
}
** ////////////////////////////////////////////////////////////////////////
// Thread action function
void*
threadRout( void* userData )
{
// Get pointer to text node
SoText3* pTextNode = ( SoText3* )userData;
// Wakeup interval
const int seconds = 1;
#ifndef WIN32
// On some Unix systems, Open Inventor's automatic redraw on scene
// graph change does not work reliably when the scene graph is
// changed in a non-UI thread (no event loop). This is an issue
// with Xt timers that will be corrected in the next release.
// For now, enabling the RealTimeSensor will ensure redraws happen.
SoDB::enableRealTimeSensor( TRUE );
#endif
// In this simple example the thread just runs continuously.
// It will be cleaned up by the operating system when the program exits.
while ( 1 )
{
#ifdef WIN32
// Note the WIN32 sleep function takes milli-seconds, not seconds.
Sleep( seconds * 1000 );
#else
sleep( seconds );
#endif
updateText( pTextNode );
}
return NULL;
}