Hi Nerissa,
On Tue, 2003-10-28 at 02:44, Nerissa Oberlander wrote:
>
> Has anyone else been able to separate just the displaying of the scene
> graph into a thread? Can anyone tell me what I doing wrong here?
yup. But not using GLUT. ;)
The problem is that GLUT sees itself as the lord of the GL context, and
will activate it before it does any callbacks to the user. Which makes a
lot of sense for GLUT programs. So if you have a separate thread that is
supposed to run OpenGL commands, GLUT will just pull the GL context out
from under it and make it dump core.
The only solution is that all OpenGL calls have to be made from the main
thread. You can do parallel stuff in a different thread, but you have to
do all redraw calls from the main thread.
I attached a little producer-consumer program that shows the idea. It's
not an official tutorial yet, because there are some strange performance
problems with it, but it's a start.
Hope it helps
Dirk
// Synced Threads: use a synchronized producer to create the geometry in
// parallel to the renderer
#include <OpenSG/OSGGLUT.h>
#include <OpenSG/OSGConfig.h>
#include <OpenSG/OSGLog.h>
#include <OpenSG/OSGGLUTWindow.h>
#include <OpenSG/OSGSimpleSceneManager.h>
#include <OpenSG/OSGSimpleGeometry.h>
#include <OpenSG/OSGBaseFunctions.h>
#include <OpenSG/OSGGeoFunctions.h>
#include <OpenSG/OSGTime.h>
#include <OpenSG/OSGLog.h>
#include <OpenSG/OSGThread.h>
#include <OpenSG/OSGThreadManager.h>
#include <OpenSG/OSGChangeList.h>
OSG_USING_NAMESPACE
SimpleSceneManager *mgr;
Thread *producer = NULL;
Barrier *syncBarrier = NULL;
NodePtr scene;
GeometryPtr plane;
volatile bool exiting = false;
#undef FLOG
#if 0
#define FLOG(var) printf var
#else
#define FLOG(var)
#endif
// Standard GLUT callback functions
void display( void )
{
FLOG(( "display::waitProd\n"));
// check for the producer to finish its part
syncBarrier->enter( 2 );
FLOG(( "display::merge\n"));
// merge the changes into our aspect
producer->getChangeList()->applyToCurrent();
producer->getChangeList()->clearAll();
FLOG(( "display::waitSync\n"));
// notify successful synchronisation
syncBarrier->enter( 2 );
FLOG(( "display::synced\n"));
mgr->redraw();
}
void reshape( int w, int h )
{
mgr->resize( w, h );
glutPostRedisplay();
}
void
motion(int x, int y)
{
mgr->mouseMove( x, y );
glutPostRedisplay();
}
void
mouse(int button, int state, int x, int y)
{
if ( state )
mgr->mouseButtonRelease( button, x, y );
else
mgr->mouseButtonPress( button, x, y );
glutPostRedisplay();
}
void
key(unsigned char key, int x, int y)
{
switch (key)
{
case 27: exiting = true;
syncBarrier->enter( 2 );
syncBarrier->enter( 2 );
Thread::join(producer);
exit(0);
break;
}
}
#undef FLOG
#define FLOG(a)
void produce(void *arg)
{
GeoPositions3fPtr pos = GeoPositions3fPtr::dcast(plane->getPositions());
GeoPositions3f::StoredFieldType *posfield = pos->getFieldPtr();
GeoPositions3f::StoredFieldType::iterator last,it;
Time start = getSystemTime();
FLOG(( "produce::start\n"));
while(exiting == false)
{
FLOG(( "produce::produce\n"));
// produce the data for the next frame
Time now = getSystemTime();
Time delta = now - start;
last = posfield->end();
FLOG(( "produce: %d elements, value %f\n",last-posfield->begin(),
osgsin(delta) ));
#if 1
beginEditCP(pos);
for ( it = posfield->begin(); it != last; ++it )
{
Real32 d = osgsqrt( (*it)[0] * (*it)[0] + (*it)[1] * (*it)[1] );
(*it)[2] = osgsin( d + delta );
//FLOG(( "it:%p [0]:%.3f [1]:%.3f [2]:%.3f\n",it,(*it)[0],(*it)[1],(*it)[2]));
}
endEditCP(pos);
calcVertexNormals(plane);
#endif
FLOG(( "produce::waitDraw\n"));
// wait for drawer to finish drawing
syncBarrier->enter( 2 );
FLOG(( "produce::waitSync\n"));
// wait for drawer to finish synchronization
syncBarrier->enter( 2 );
FLOG(( "produce::synced\n"));
}
FLOG(( "produce::stop\n"));
return;
}
// Initialize GLUT & OpenSG and set up the scene
int main (int argc, char **argv)
{
// OSG init
ChangeList::setReadWriteDefault();
osgInit(argc,argv);
// GLUT init
glutInit(&argc, argv);
glutInitDisplayMode( GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
int winid = glutCreateWindow("OpenSG");
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutIdleFunc(display);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutKeyboardFunc(key);
// the connection between GLUT and OpenSG
GLUTWindowPtr gwin= GLUTWindow::create();
beginEditCP(gwin);
gwin->setId(winid);
endEditCP (gwin);
gwin->init();
// create the geometry to manipulate
plane = makePlaneGeo( 6, 6, 32, 32 );
beginEditCP(plane);
plane->setDlistCache(false);
endEditCP(plane);
NodePtr scene = Node::create();
beginEditCP(scene);
scene->setCore(plane);
endEditCP(scene);
// setup the logger to add timestamps
osgLog().addHeaderElem(LOG_TIMESTAMP_HEADER);
// create the barrier
syncBarrier = Barrier::get("Producer");
// create the producer thread
producer = OSG::Thread::get("Producer");
if(producer == NULL)
{
FFATAL(("Could not create thread\n"));
return 1;
}
// Populate the producer's aspect
Thread::getCurrent()->getChangeList()->applyTo(1);
Thread::getCurrent()->getChangeList()->clearAll();
// run it, using a separate aspect
producer->runFunction(produce, 1, (void *) NULL );
// create the SimpleSceneManager helper
mgr = new SimpleSceneManager;
mgr->setWindow( gwin );
mgr->setRoot( scene );
mgr->showAll();
// GLUT main loop
glutMainLoop();
return 0;
}