/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include static osg::ApplicationUsageProxy ViewerBase_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_CONFIG_FILE ","Specify a viewer configuration file to load by default."); static osg::ApplicationUsageProxy ViewerBase_e1(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_THREADING ","Set the threading model using by Viewer, can be SingleThreaded, CullDrawThreadPerContext, DrawThreadPerContext or CullThreadPerCameraDrawThreadPerContext."); static osg::ApplicationUsageProxy ViewerBase_e2(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_SCREEN ","Set the default screen that windows should open up on."); static osg::ApplicationUsageProxy ViewerBase_e3(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_WINDOW x y width height","Set the default window dimensions that windows should open up on."); static osg::ApplicationUsageProxy ViewerBase_e4(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_RUN_FRAME_SCHEME","Frame rate manage scheme that viewer run should use, ON_DEMAND or CONTINUOUS (default)."); static osg::ApplicationUsageProxy ViewerBase_e5(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_RUN_MAX_FRAME_RATE","Set the maximum number of frame as second that viewer run. 0.0 is default and disables an frame rate capping."); using namespace osgViewer; ViewerBase::ViewerBase(): osg::Object(true) { viewerBaseInit(); } ViewerBase::ViewerBase(const ViewerBase& base): osg::Object(true) { viewerBaseInit(); } void ViewerBase::viewerBaseInit() { _firstFrame = true; _done = false; _keyEventSetsDone = osgGA::GUIEventAdapter::KEY_Escape; _quitEventSetsDone = true; _releaseContextAtEndOfFrameHint = true; _threadingModel = AutomaticSelection; _threadsRunning = false; _endBarrierPosition = AfterSwapBuffers; _requestRedraw = true; _requestContinousUpdate = false; _runFrameScheme = CONTINUOUS; _runMaxFrameRate = 0.0f; const char* str = getenv("OSG_RUN_FRAME_SCHEME"); if (str) { if (strcmp(str, "ON_DEMAND")==0) _runFrameScheme = ON_DEMAND; else if (strcmp(str, "CONTINUOUS")==0) _runFrameScheme = CONTINUOUS; } str = getenv("OSG_RUN_MAX_FRAME_RATE"); if (str) { _runMaxFrameRate = osg::asciiToDouble(str); } } void ViewerBase::setThreadingModel(ThreadingModel threadingModel) { if (_threadingModel == threadingModel) return; if (_threadsRunning) stopThreading(); _threadingModel = threadingModel; if (isRealized() && _threadingModel!=SingleThreaded) startThreading(); } ViewerBase::ThreadingModel ViewerBase::suggestBestThreadingModel() { const char* str = getenv("OSG_THREADING"); if (str) { if (strcmp(str,"SingleThreaded")==0) return SingleThreaded; else if (strcmp(str,"CullDrawThreadPerContext")==0) return CullDrawThreadPerContext; else if (strcmp(str,"DrawThreadPerContext")==0) return DrawThreadPerContext; else if (strcmp(str,"CullThreadPerCameraDrawThreadPerContext")==0) return CullThreadPerCameraDrawThreadPerContext; } Contexts contexts; getContexts(contexts); if (contexts.empty()) return SingleThreaded; #if 0 // temporary hack to disable multi-threading under Windows till we find good solutions for // crashes that users are seeing. return SingleThreaded; #endif Cameras cameras; getCameras(cameras); if (cameras.empty()) return SingleThreaded; int numProcessors = OpenThreads::GetNumberOfProcessors(); if (contexts.size()==1) { if (numProcessors==1) return SingleThreaded; else return DrawThreadPerContext; } #if 1 if (numProcessors >= static_cast(cameras.size()+contexts.size())) { return CullThreadPerCameraDrawThreadPerContext; } #endif return DrawThreadPerContext; } void ViewerBase::setUpThreading() { Contexts contexts; getContexts(contexts); if (_threadingModel==SingleThreaded) { if (_threadsRunning) stopThreading(); else { // we'll set processor affinity here to help single threaded apps // with multiple processor cores, and using the database pager. int numProcessors = OpenThreads::GetNumberOfProcessors(); bool affinity = numProcessors>1; if (affinity) { OpenThreads::SetProcessorAffinityOfCurrentThread(0); Scenes scenes; getScenes(scenes); } } } else { if (!_threadsRunning) startThreading(); } } void ViewerBase::setEndBarrierPosition(BarrierPosition bp) { if (_endBarrierPosition == bp) return; if (_threadsRunning) stopThreading(); _endBarrierPosition = bp; if (_threadingModel!=SingleThreaded) startThreading(); } void ViewerBase::stopThreading() { if (!_threadsRunning) return; OSG_INFO<<"ViewerBase::stopThreading() - stopping threading"<(camera->getRenderer()); if (renderer) renderer->release(); } // delete all the graphics threads. for(gcitr = contexts.begin(); gcitr != contexts.end(); ++gcitr) { (*gcitr)->setGraphicsThread(0); } // delete all the camera threads. for(citr = cameras.begin(); citr != cameras.end(); ++citr) { (*citr)->setCameraThread(0); } for(Cameras::iterator camItr = cameras.begin(); camItr != cameras.end(); ++camItr) { osg::Camera* camera = *camItr; Renderer* renderer = dynamic_cast(camera->getRenderer()); if (renderer) { renderer->setGraphicsThreadDoesCull( true ); renderer->setDone(false); } } _threadsRunning = false; _startRenderingBarrier = 0; _endRenderingDispatchBarrier = 0; _endDynamicDrawBlock = 0; OSG_INFO<<"Viewer::stopThreading() - stopped threading."<getSceneData()) { OSG_INFO<<"Making scene thread safe"<getSceneData()->setThreadSafeRefUnref(true); // update the scene graph so that it has enough GL object buffer memory for the graphics contexts that will be using it. (*scitr)->getSceneData()->resizeGLObjectBuffers(osg::DisplaySettings::instance()->getMaxNumberOfGraphicsContexts()); } } int numProcessors = OpenThreads::GetNumberOfProcessors(); bool affinity = numProcessors>1; Contexts::iterator citr; unsigned int numViewerDoubleBufferedRenderingOperation = 0; bool graphicsThreadsDoesCull = _threadingModel == CullDrawThreadPerContext || _threadingModel==SingleThreaded; for(Cameras::iterator camItr = cameras.begin(); camItr != cameras.end(); ++camItr) { osg::Camera* camera = *camItr; Renderer* renderer = dynamic_cast(camera->getRenderer()); if (renderer) { renderer->setGraphicsThreadDoesCull(graphicsThreadsDoesCull); renderer->setDone(false); ++numViewerDoubleBufferedRenderingOperation; } } if (_threadingModel==CullDrawThreadPerContext) { _startRenderingBarrier = 0; _endRenderingDispatchBarrier = 0; _endDynamicDrawBlock = 0; } else if (_threadingModel==DrawThreadPerContext || _threadingModel==CullThreadPerCameraDrawThreadPerContext) { _startRenderingBarrier = 0; _endRenderingDispatchBarrier = 0; _endDynamicDrawBlock = new osg::EndOfDynamicDrawBlock(numViewerDoubleBufferedRenderingOperation); #ifndef OSGUTIL_RENDERBACKEND_USE_REF_PTR if (!osg::Referenced::getDeleteHandler()) osg::Referenced::setDeleteHandler(new osg::DeleteHandler(2)); else osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(2); #endif } if (numThreadsOnStartBarrier>1) { _startRenderingBarrier = new osg::BarrierOperation(numThreadsOnStartBarrier, osg::BarrierOperation::NO_OPERATION); } if (numThreadsOnEndBarrier>1) { _endRenderingDispatchBarrier = new osg::BarrierOperation(numThreadsOnEndBarrier, osg::BarrierOperation::NO_OPERATION); } osg::ref_ptr swapReadyBarrier = contexts.empty() ? 0 : new osg::BarrierOperation(contexts.size(), osg::BarrierOperation::NO_OPERATION); osg::ref_ptr swapOp = new osg::SwapBuffersOperation(); typedef std::map ThreadAffinityMap; ThreadAffinityMap threadAffinityMap; unsigned int processNum = 1; for(citr = contexts.begin(); citr != contexts.end(); ++citr, ++processNum) { osg::GraphicsContext* gc = (*citr); if (!gc->isRealized()) { OSG_INFO<<"ViewerBase::startThreading() : Realizng window "<realize(); } gc->getState()->setDynamicObjectRenderingCompletedCallback(_endDynamicDrawBlock.get()); // create the a graphics thread for this context gc->createGraphicsThread(); if (affinity) gc->getGraphicsThread()->setProcessorAffinity(processNum % numProcessors); threadAffinityMap[gc->getGraphicsThread()] = processNum % numProcessors; // add the startRenderingBarrier if (_threadingModel==CullDrawThreadPerContext && _startRenderingBarrier.valid()) gc->getGraphicsThread()->add(_startRenderingBarrier.get()); // add the rendering operation itself. gc->getGraphicsThread()->add(new osg::RunOperations()); if (_threadingModel==CullDrawThreadPerContext && _endBarrierPosition==BeforeSwapBuffers && _endRenderingDispatchBarrier.valid()) { // add the endRenderingDispatchBarrier gc->getGraphicsThread()->add(_endRenderingDispatchBarrier.get()); } if (swapReadyBarrier.valid()) gc->getGraphicsThread()->add(swapReadyBarrier.get()); // add the swap buffers gc->getGraphicsThread()->add(swapOp.get()); if (_threadingModel==CullDrawThreadPerContext && _endBarrierPosition==AfterSwapBuffers && _endRenderingDispatchBarrier.valid()) { // add the endRenderingDispatchBarrier gc->getGraphicsThread()->add(_endRenderingDispatchBarrier.get()); } } if (_threadingModel==CullThreadPerCameraDrawThreadPerContext && numThreadsOnStartBarrier>1) { Cameras::iterator camItr; for(camItr = cameras.begin(); camItr != cameras.end(); ++camItr, ++processNum) { osg::Camera* camera = *camItr; camera->createCameraThread(); if (affinity) camera->getCameraThread()->setProcessorAffinity(processNum % numProcessors); threadAffinityMap[camera->getCameraThread()] = processNum % numProcessors; osg::GraphicsContext* gc = camera->getGraphicsContext(); // add the startRenderingBarrier if (_startRenderingBarrier.valid()) camera->getCameraThread()->add(_startRenderingBarrier.get()); Renderer* renderer = dynamic_cast(camera->getRenderer()); renderer->setGraphicsThreadDoesCull(false); camera->getCameraThread()->add(renderer); if (_endRenderingDispatchBarrier.valid()) { // add the endRenderingDispatchBarrier gc->getGraphicsThread()->add(_endRenderingDispatchBarrier.get()); } } for(camItr = cameras.begin(); camItr != cameras.end(); ++camItr) { osg::Camera* camera = *camItr; if (camera->getCameraThread() && !camera->getCameraThread()->isRunning()) { OSG_INFO<<" camera->getCameraThread()-> "<getCameraThread()<getCameraThread()->startThread(); } } } #if 0 if (affinity) { OpenThreads::SetProcessorAffinityOfCurrentThread(0); if (_scene.valid() && _scene->getDatabasePager()) { #if 0 _scene->getDatabasePager()->setProcessorAffinity(1); #else _scene->getDatabasePager()->setProcessorAffinity(0); #endif } } #endif #if 0 if (affinity) { for(ThreadAffinityMap::iterator titr = threadAffinityMap.begin(); titr != threadAffinityMap.end(); ++titr) { titr->first->setProcessorAffinity(titr->second); } } #endif for(citr = contexts.begin(); citr != contexts.end(); ++citr) { osg::GraphicsContext* gc = (*citr); if (gc->getGraphicsThread() && !gc->getGraphicsThread()->isRunning()) { OSG_INFO<<" gc->getGraphicsThread()->startThread() "<getGraphicsThread()<getGraphicsThread()->startThread(); // OpenThreads::Thread::YieldCurrentThread(); } } _threadsRunning = true; OSG_INFO<<"Set up threading"<(*itr); if (gw) windows.push_back(gw); } } void ViewerBase::checkWindowStatus() { Contexts contexts; getContexts(contexts); // OSG_NOTICE<<"Viewer::checkWindowStatus() - "<add(operation); } void ViewerBase::removeUpdateOperation(osg::Operation* operation) { if (!operation) return; if (_updateOperations.valid()) { _updateOperations->remove(operation); } } void ViewerBase::setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation* ico) { if (_incrementalCompileOperation == ico) return; Contexts contexts; getContexts(contexts, false); if (_incrementalCompileOperation.valid()) _incrementalCompileOperation->removeContexts(contexts); // assign new operation _incrementalCompileOperation = ico; if (_incrementalCompileOperation) _incrementalCompileOperation->assignContexts(contexts); } int ViewerBase::run() { if (!isRealized()) { realize(); } const char* str = getenv("OSG_RUN_FRAME_COUNT"); int runTillFrameNumber = str==0 ? -1 : atoi(str); while(!done() && (runTillFrameNumber<0 || getViewerFrameStamp()->getFrameNumber()0.0 ? 1.0/_runMaxFrameRate : 0.0; osg::Timer_t startFrameTick = osg::Timer::instance()->tick(); if (_runFrameScheme==ON_DEMAND) { if (checkNeedToDoFrame()) { frame(); } else { // we don't need to render a frame but we don't want to spin the run loop so make sure the minimum // loop time is 1/100th of second, if not otherwise set, so enabling the frame microSleep below to // avoid consume excessive CPU resources. if (minFrameTime==0.0) minFrameTime=0.01; } } else { frame(); } // work out if we need to force a sleep to hold back the frame rate osg::Timer_t endFrameTick = osg::Timer::instance()->tick(); double frameTime = osg::Timer::instance()->delta_s(startFrameTick, endFrameTick); if (frameTime < minFrameTime) OpenThreads::Thread::microSleep(static_cast(1000000.0*(minFrameTime-frameTime))); } return 0; } void ViewerBase::frame(double simulationTime) { if (_done) return; // OSG_NOTICE<getCamera()->getInverseViewMatrix(); OSG_NOTICE<<"View "<collectStats("scene")) { int frameNumber = frameStamp ? frameStamp->getFrameNumber() : 0; Views views; getViews(views); for(Views::iterator vitr = views.begin(); vitr != views.end(); ++vitr) { View* view = *vitr; osg::Stats* stats = view->getStats(); osg::Node* sceneRoot = view->getSceneData(); if (sceneRoot && stats) { osgUtil::StatsVisitor statsVisitor; sceneRoot->accept(statsVisitor); statsVisitor.totalUpStats(); unsigned int unique_primitives = 0; osgUtil::Statistics::PrimitiveCountMap::iterator pcmitr; for(pcmitr = statsVisitor._uniqueStats.GetPrimitivesBegin(); pcmitr != statsVisitor._uniqueStats.GetPrimitivesEnd(); ++pcmitr) { unique_primitives += pcmitr->second; } stats->setAttribute(frameNumber, "Number of unique StateSet", static_cast(statsVisitor._statesetSet.size())); stats->setAttribute(frameNumber, "Number of unique Group", static_cast(statsVisitor._groupSet.size())); stats->setAttribute(frameNumber, "Number of unique Transform", static_cast(statsVisitor._transformSet.size())); stats->setAttribute(frameNumber, "Number of unique LOD", static_cast(statsVisitor._lodSet.size())); stats->setAttribute(frameNumber, "Number of unique Switch", static_cast(statsVisitor._switchSet.size())); stats->setAttribute(frameNumber, "Number of unique Geode", static_cast(statsVisitor._geodeSet.size())); stats->setAttribute(frameNumber, "Number of unique Drawable", static_cast(statsVisitor._drawableSet.size())); stats->setAttribute(frameNumber, "Number of unique Geometry", static_cast(statsVisitor._geometrySet.size())); stats->setAttribute(frameNumber, "Number of unique Vertices", static_cast(statsVisitor._uniqueStats._vertexCount)); stats->setAttribute(frameNumber, "Number of unique Primitives", static_cast(unique_primitives)); unsigned int instanced_primitives = 0; for(pcmitr = statsVisitor._instancedStats.GetPrimitivesBegin(); pcmitr != statsVisitor._instancedStats.GetPrimitivesEnd(); ++pcmitr) { instanced_primitives += pcmitr->second; } stats->setAttribute(frameNumber, "Number of instanced Stateset", static_cast(statsVisitor._numInstancedStateSet)); stats->setAttribute(frameNumber, "Number of instanced Group", static_cast(statsVisitor._numInstancedGroup)); stats->setAttribute(frameNumber, "Number of instanced Transform", static_cast(statsVisitor._numInstancedTransform)); stats->setAttribute(frameNumber, "Number of instanced LOD", static_cast(statsVisitor._numInstancedLOD)); stats->setAttribute(frameNumber, "Number of instanced Switch", static_cast(statsVisitor._numInstancedSwitch)); stats->setAttribute(frameNumber, "Number of instanced Geode", static_cast(statsVisitor._numInstancedGeode)); stats->setAttribute(frameNumber, "Number of instanced Drawable", static_cast(statsVisitor._numInstancedDrawable)); stats->setAttribute(frameNumber, "Number of instanced Geometry", static_cast(statsVisitor._numInstancedGeometry)); stats->setAttribute(frameNumber, "Number of instanced Vertices", static_cast(statsVisitor._instancedStats._vertexCount)); stats->setAttribute(frameNumber, "Number of instanced Primitives", static_cast(instanced_primitives)); } } } Scenes scenes; getScenes(scenes); for(Scenes::iterator sitr = scenes.begin(); sitr != scenes.end(); ++sitr) { Scene* scene = *sitr; osgDB::DatabasePager* dp = scene ? scene->getDatabasePager() : 0; if (dp) { dp->signalBeginFrame(frameStamp); } if (scene->getSceneData()) { // fire off a build of the bounding volumes while we // are still running single threaded. scene->getSceneData()->getBound(); } } // OSG_NOTICE<reset(); } // dispatch the rendering threads if (_startRenderingBarrier.valid()) _startRenderingBarrier->block(); // reset any double buffer graphics objects for(Cameras::iterator camItr = cameras.begin(); camItr != cameras.end(); ++camItr) { osg::Camera* camera = *camItr; Renderer* renderer = dynamic_cast(camera->getRenderer()); if (renderer) { if (!renderer->getGraphicsThreadDoesCull() && !(camera->getCameraThread())) { renderer->cull(); } } } for(itr = contexts.begin(); itr != contexts.end(); ++itr) { if (_done) return; if (!((*itr)->getGraphicsThread()) && (*itr)->valid()) { doneMakeCurrentInThisThread = true; makeCurrent(*itr); (*itr)->runOperations(); } } // OSG_NOTICE<<"Joing _endRenderingDispatchBarrier block "<<_endRenderingDispatchBarrier.get()<block(); for(itr = contexts.begin(); itr != contexts.end(); ++itr) { if (_done) return; if (!((*itr)->getGraphicsThread()) && (*itr)->valid()) { doneMakeCurrentInThisThread = true; makeCurrent(*itr); (*itr)->swapBuffers(); } } for(Scenes::iterator sitr = scenes.begin(); sitr != scenes.end(); ++sitr) { Scene* scene = *sitr; osgDB::DatabasePager* dp = scene ? scene->getDatabasePager() : 0; if (dp) { dp->signalEndFrame(); } } // wait till the dynamic draw is complete. if (_endDynamicDrawBlock.valid()) { // osg::Timer_t startTick = osg::Timer::instance()->tick(); _endDynamicDrawBlock->block(); // OSG_NOTICE<<"Time waiting "<delta_m(startTick, osg::Timer::instance()->tick())<collectStats("update")) { double endRenderingTraversals = elapsedTime(); // update current frames stats getViewerStats()->setAttribute(frameStamp->getFrameNumber(), "Rendering traversals begin time ", beginRenderingTraversals); getViewerStats()->setAttribute(frameStamp->getFrameNumber(), "Rendering traversals end time ", endRenderingTraversals); getViewerStats()->setAttribute(frameStamp->getFrameNumber(), "Rendering traversals time taken", endRenderingTraversals-beginRenderingTraversals); } _requestRedraw = false; }