From a50f0ccfaf520cb6dfb8bfa472d3933812cff925 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Fri, 24 Apr 2009 16:20:50 +0000 Subject: [PATCH] Introduce new run frame rate management support to allow control of maximum frame rate and to support on demand rendering of frames --- include/osgViewer/CompositeViewer | 13 +++-- include/osgViewer/View | 1 + include/osgViewer/Viewer | 14 +++--- include/osgViewer/ViewerBase | 42 ++++++++++++---- src/osgViewer/CompositeViewer.cpp | 48 +++++++++++++++++- src/osgViewer/StatsHandler.cpp | 2 +- src/osgViewer/View.cpp | 4 +- src/osgViewer/Viewer.cpp | 43 +++++++++++++--- src/osgViewer/ViewerBase.cpp | 71 +++++++++++++++++---------- src/osgViewer/ViewerEventHandlers.cpp | 17 ++++++- 10 files changed, 198 insertions(+), 57 deletions(-) diff --git a/include/osgViewer/CompositeViewer b/include/osgViewer/CompositeViewer index f9d9425f8..240fe30a2 100644 --- a/include/osgViewer/CompositeViewer +++ b/include/osgViewer/CompositeViewer @@ -73,9 +73,9 @@ class OSGVIEWER_EXPORT CompositeViewer : public ViewerBase, public virtual osg:: const osg::FrameStamp* getFrameStamp() const { return _frameStamp.get(); } virtual double elapsedTime(); - + virtual osg::FrameStamp* getViewerFrameStamp() { return getFrameStamp(); } - + /** Execute a main frame loop. * Equivalent to while (!viewer.done()) viewer.frame(); @@ -84,17 +84,20 @@ class OSGVIEWER_EXPORT CompositeViewer : public ViewerBase, public virtual osg:: */ virtual int run(); + /** check to see if the new frame is required, called by run(..) when FrameScheme is set to ON_DEMAND.*/ + virtual bool checkNeedToDoFrame(); + virtual void advance(double simulationTime=USE_REFERENCE_TIME); virtual void eventTraversal(); virtual void updateTraversal(); - - + + void setCameraWithFocus(osg::Camera* camera); osg::Camera* getCameraWithFocus() { return _cameraWithFocus.get(); } const osg::Camera* getCameraWithFocus() const { return _cameraWithFocus.get(); } - + osgViewer::View* getViewWithFocus() { return _viewWithFocus.get(); } const osgViewer::View* getViewWithFocus() const { return _viewWithFocus.get(); } diff --git a/include/osgViewer/View b/include/osgViewer/View index 8a0d1a152..a8669fb21 100644 --- a/include/osgViewer/View +++ b/include/osgViewer/View @@ -233,6 +233,7 @@ class OSGVIEWER_EXPORT View : public osg::View, public osgGA::GUIActionAdapter osg::ref_ptr _displaySettings; osgUtil::SceneView::FusionDistanceMode _fusionDistanceMode; float _fusionDistanceValue; + }; } diff --git a/include/osgViewer/Viewer b/include/osgViewer/Viewer index 6d820c940..d8b612eab 100644 --- a/include/osgViewer/Viewer +++ b/include/osgViewer/Viewer @@ -35,7 +35,7 @@ class OSGVIEWER_EXPORT Viewer : public ViewerBase, public osgViewer::View Viewer(const osgViewer::Viewer& viewer, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY); virtual ~Viewer(); - + META_Object(osgViewer,Viewer); /** Take all the settings, Camera and Slaves from the passed in view(er), leaving it empty. */ @@ -74,9 +74,8 @@ class OSGVIEWER_EXPORT Viewer : public ViewerBase, public osgViewer::View virtual double elapsedTime(); - - virtual osg::FrameStamp* getViewerFrameStamp() { return getFrameStamp(); } + virtual osg::FrameStamp* getViewerFrameStamp() { return getFrameStamp(); } /** Execute a main frame loop. * Equivalent to while (!viewer.done()) viewer.frame(); @@ -85,18 +84,21 @@ class OSGVIEWER_EXPORT Viewer : public ViewerBase, public osgViewer::View */ virtual int run(); + /** check to see if the new frame is required, called by run(..) when FrameScheme is set to ON_DEMAND.*/ + virtual bool checkNeedToDoFrame(); + virtual void advance(double simulationTime=USE_REFERENCE_TIME); virtual void eventTraversal(); virtual void updateTraversal(); - + void setCameraWithFocus(osg::Camera* camera) { _cameraWithFocus = camera; } osg::Camera* getCameraWithFocus() { return _cameraWithFocus.get(); } const osg::Camera* getCameraWithFocus() const { return _cameraWithFocus.get(); } - + virtual void getCameras(Cameras& cameras, bool onlyActive=true); - + virtual void getContexts(Contexts& contexts, bool onlyValid=true); virtual void getAllThreads(Threads& threads, bool onlyActive=true); diff --git a/include/osgViewer/ViewerBase b/include/osgViewer/ViewerBase index 86cb2ad5e..eba08acb0 100644 --- a/include/osgViewer/ViewerBase +++ b/include/osgViewer/ViewerBase @@ -199,14 +199,27 @@ class OSGVIEWER_EXPORT ViewerBase : public virtual osg::Object /** Check to see if windows are still open, if not set viewer done to true. */ void checkWindowStatus(); + enum FrameScheme + { + ON_DEMAND, + CONTINUOUS + }; + void setRunFrameScheme(FrameScheme fs) { _runFrameScheme = fs; } + FrameScheme getRunFrameScheme() const { return _runFrameScheme; } + + void setRunMaxFrameRate(double frameRate) { _runMaxFrameRate = frameRate; } + double getRunMaxFrameRate() const { return _runMaxFrameRate; } /** Execute a main frame loop. * Equivalent to while (!viewer.done()) viewer.frame(); * Also calls realize() if the viewer is not already realized, * and installs trackball manipulator if one is not already assigned. */ - virtual int run() = 0; + virtual int run(); + + /** check to see if the new frame is required, called by run(..) when FrameScheme is set to ON_DEMAND.*/ + virtual bool checkNeedToDoFrame() = 0; /** Render a complete new frame. * Calls advance(), eventTraversal(), updateTraversal(), renderingTraversals(). */ @@ -249,16 +262,20 @@ class OSGVIEWER_EXPORT ViewerBase : public virtual osg::Object virtual void getUsage(osg::ApplicationUsage& usage) const = 0; protected: - + + void viewerBaseInit(); + + friend class osgViewer::View; + inline void makeCurrent(osg::GraphicsContext* gc) { if (_currentContext==gc) return; - + releaseContext(); - + if (gc && gc->valid() && gc->makeCurrent()) _currentContext = gc; } - + inline void releaseContext() { if (_currentContext.valid() && _currentContext->valid()) @@ -275,21 +292,28 @@ class OSGVIEWER_EXPORT ViewerBase : public virtual osg::Object int _keyEventSetsDone; bool _quitEventSetsDone; bool _releaseContextAtEndOfFrameHint; - + ThreadingModel _threadingModel; bool _threadsRunning; + bool _requestRedraw; + bool _requestContinousUpdate; + + FrameScheme _runFrameScheme; + double _runMaxFrameRate; + + BarrierPosition _endBarrierPosition; osg::ref_ptr _startRenderingBarrier; osg::ref_ptr _endRenderingDispatchBarrier; osg::ref_ptr _endDynamicDrawBlock; - + osg::ref_ptr _eventVisitor; - + osg::ref_ptr _updateOperations; osg::ref_ptr _updateVisitor; - + osg::ref_ptr _realizeOperation; osg::ref_ptr _incrementalCompileOperation; diff --git a/src/osgViewer/CompositeViewer.cpp b/src/osgViewer/CompositeViewer.cpp index 8aed17d5d..8b560143b 100644 --- a/src/osgViewer/CompositeViewer.cpp +++ b/src/osgViewer/CompositeViewer.cpp @@ -37,7 +37,17 @@ CompositeViewer::CompositeViewer(const CompositeViewer& cv,const osg::CopyOp& co CompositeViewer::CompositeViewer(osg::ArgumentParser& arguments) { constructorInit(); - + + arguments.getApplicationUsage()->addCommandLineOption("--SingleThreaded","Select SingleThreaded threading model for viewer."); + arguments.getApplicationUsage()->addCommandLineOption("--CullDrawThreadPerContext","Select CullDrawThreadPerContext threading model for viewer."); + arguments.getApplicationUsage()->addCommandLineOption("--DrawThreadPerContext","Select DrawThreadPerContext threading model for viewer."); + arguments.getApplicationUsage()->addCommandLineOption("--CullThreadPerCameraDrawThreadPerContext","Select CullThreadPerCameraDrawThreadPerContext threading model for viewer."); + + arguments.getApplicationUsage()->addCommandLineOption("--run-on-demand","Set the run methods frame rate management to only rendering frames when required."); + arguments.getApplicationUsage()->addCommandLineOption("--run-continuous","Set the run methods frame rate management to rendering frames continuously."); + arguments.getApplicationUsage()->addCommandLineOption("--run-max-frame-rate","Set the run methods maximum permissable frame rate, 0.0 is default and switching off frame rate capping."); + + std::string filename; bool readConfig = false; while (arguments.read("-c",filename)) @@ -50,6 +60,14 @@ CompositeViewer::CompositeViewer(osg::ArgumentParser& arguments) while (arguments.read("--DrawThreadPerContext")) setThreadingModel(DrawThreadPerContext); while (arguments.read("--CullThreadPerCameraDrawThreadPerContext")) setThreadingModel(CullThreadPerCameraDrawThreadPerContext); + + while(arguments.read("--run-on-demand")) { setRunFrameScheme(ON_DEMAND); } + while(arguments.read("--run-continuous")) { setRunFrameScheme(CONTINUOUS); } + + double runMaxFrameRate; + while(arguments.read("--run-max-frame-rate", runMaxFrameRate)) { setRunMaxFrameRate(runMaxFrameRate); } + + osg::DisplaySettings::instance()->readCommandLine(arguments); osgDB::readCommandLine(arguments); } @@ -222,6 +240,34 @@ bool CompositeViewer::isRealized() const return numRealizedWindows > 0; } +bool CompositeViewer::checkNeedToDoFrame() +{ + if (_requestRedraw) return true; + if (_requestContinousUpdate) return true; + + for(RefViews::iterator itr = _views.begin(); + itr != _views.end(); + ++itr) + { + osgViewer::View* view = itr->get(); + if (view) + { + // If the database pager is going to update the scene the render flag is + // set so that the updates show up + if (view->getDatabasePager()->requiresUpdateSceneGraph() || + view->getDatabasePager()->getRequestsInProgress()) return true; + } + } + + // now do a eventTraversal to see if any events might require a new frame. + eventTraversal(); + + if (_requestRedraw) return true; + if (_requestContinousUpdate) return true; + + return false; +} + int CompositeViewer::run() { for(RefViews::iterator itr = _views.begin(); diff --git a/src/osgViewer/StatsHandler.cpp b/src/osgViewer/StatsHandler.cpp index 549d1fe1c..7e9e7eaa3 100644 --- a/src/osgViewer/StatsHandler.cpp +++ b/src/osgViewer/StatsHandler.cpp @@ -183,7 +183,7 @@ bool StatsHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdap break; } - + aa.requestRedraw(); } return true; } diff --git a/src/osgViewer/View.cpp b/src/osgViewer/View.cpp index 382e12d71..cc192fe68 100644 --- a/src/osgViewer/View.cpp +++ b/src/osgViewer/View.cpp @@ -1696,10 +1696,12 @@ void View::assignSceneDataToCameras() void View::requestRedraw() { + getViewerBase()->_requestRedraw = true; } -void View::requestContinuousUpdate(bool) +void View::requestContinuousUpdate(bool flag) { + getViewerBase()->_requestContinousUpdate = flag; } void View::requestWarpPointer(float x,float y) diff --git a/src/osgViewer/Viewer.cpp b/src/osgViewer/Viewer.cpp index 86b1b6666..de3068541 100644 --- a/src/osgViewer/Viewer.cpp +++ b/src/osgViewer/Viewer.cpp @@ -53,6 +53,11 @@ Viewer::Viewer(osg::ArgumentParser& arguments) arguments.getApplicationUsage()->addCommandLineOption("--clear-color ","Set the background color of the viewer in the form \"r,g,b[,a]\"."); arguments.getApplicationUsage()->addCommandLineOption("--screen ","Set the screen to use when multiple screens are present."); arguments.getApplicationUsage()->addCommandLineOption("--window ","Set the position (x,y) and size (w,h) of the viewer window."); + + arguments.getApplicationUsage()->addCommandLineOption("--run-on-demand","Set the run methods frame rate management to only rendering frames when required."); + arguments.getApplicationUsage()->addCommandLineOption("--run-continuous","Set the run methods frame rate management to rendering frames continuously."); + arguments.getApplicationUsage()->addCommandLineOption("--run-max-frame-rate","Set the run methods maximum permissable frame rate, 0.0 is default and switching off frame rate capping."); + // FIXME: Uncomment these lines when the options have been documented properly //arguments.getApplicationUsage()->addCommandLineOption("--3d-sd",""); //arguments.getApplicationUsage()->addCommandLineOption("--panoramic-sd",""); @@ -84,13 +89,21 @@ Viewer::Viewer(osg::ArgumentParser& arguments) if( cnt==3 || cnt==4 ) getCamera()->setClearColor( osg::Vec4(r,g,b,a) ); else osg::notify(osg::WARN)<<"Invalid clear color \""< 0; } +bool Viewer::checkNeedToDoFrame() +{ + if (_requestRedraw) return true; + if (_requestContinousUpdate) return true; + + // If the database pager is going to update the scene the render flag is + // set so that the updates show up + if(getDatabasePager()->requiresUpdateSceneGraph() || getDatabasePager()->getRequestsInProgress()) return true; + + // now do a eventTraversal to see if any events might require a new frame. + eventTraversal(); + + if (_requestRedraw) return true; + if (_requestContinousUpdate) return true; + + return false; +} + int Viewer::run() { if (!getCameraManipulator() && getCamera()->getAllowEventFocus()) { setCameraManipulator(new osgGA::TrackballManipulator()); } - + setReleaseContextAtEndOfFrameHint(false); - + return ViewerBase::run(); } void Viewer::setStartTick(osg::Timer_t tick) { View::setStartTick(tick); - + Contexts contexts; getContexts(contexts,false); diff --git a/src/osgViewer/ViewerBase.cpp b/src/osgViewer/ViewerBase.cpp index 2e1d4a5c2..7a2ad3437 100644 --- a/src/osgViewer/ViewerBase.cpp +++ b/src/osgViewer/ViewerBase.cpp @@ -33,25 +33,24 @@ static osg::ApplicationUsageProxy ViewerBase_e0(osg::ApplicationUsage::ENVIRONME 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) { - _firstFrame = true; - _done = false; - _keyEventSetsDone = osgGA::GUIEventAdapter::KEY_Escape; - _quitEventSetsDone = true; - _releaseContextAtEndOfFrameHint = true; - _threadingModel = AutomaticSelection; - _threadsRunning = false; - _endBarrierPosition = AfterSwapBuffers; + viewerBaseInit(); } ViewerBase::ViewerBase(const ViewerBase& base): osg::Object(true) +{ + viewerBaseInit(); +} + +void ViewerBase::viewerBaseInit() { _firstFrame = true; _done = false; @@ -61,6 +60,24 @@ ViewerBase::ViewerBase(const ViewerBase& base): _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 = atof(str); + } } void ViewerBase::setThreadingModel(ThreadingModel threadingModel) @@ -573,30 +590,28 @@ int ViewerBase::run() realize(); } -#if 0 - while (!done()) - { - frame(); - } -#else - const char* str = getenv("OSG_RUN_FRAME_COUNT"); - if (str) + int runTillFrameNumber = str==0 ? -1 : atoi(str); + + while(!done() || (runTillFrameNumber>=0 && getViewerFrameStamp()->getFrameNumber()>runTillFrameNumber)) { - int runTillFrameNumber = atoi(str); - while (!done() && 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 { 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(1000000.0*(minFrameTime-frameTime)); } - else - { - while (!done()) - { - frame(); - } - } -#endif + return 0; } @@ -842,5 +857,7 @@ void ViewerBase::renderingTraversals() getViewerStats()->setAttribute(frameStamp->getFrameNumber(), "Rendering traversals end time ", endRenderingTraversals); getViewerStats()->setAttribute(frameStamp->getFrameNumber(), "Rendering traversals time taken", endRenderingTraversals-beginRenderingTraversals); } + + _requestRedraw = false; } diff --git a/src/osgViewer/ViewerEventHandlers.cpp b/src/osgViewer/ViewerEventHandlers.cpp index 223b3e137..ed727c527 100644 --- a/src/osgViewer/ViewerEventHandlers.cpp +++ b/src/osgViewer/ViewerEventHandlers.cpp @@ -100,6 +100,8 @@ bool WindowSizeHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActio { toggleFullscreen(*itr); } + + aa.requestRedraw(); return true; } else if (_changeWindowedResolution == true && ea.getKey() == _keyEventWindowedResolutionUp) @@ -114,6 +116,8 @@ bool WindowSizeHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActio { changeWindowedResolution(*itr, true); } + + aa.requestRedraw(); return true; } else if (_changeWindowedResolution == true && ea.getKey() == _keyEventWindowedResolutionDown) @@ -128,6 +132,8 @@ bool WindowSizeHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActio { changeWindowedResolution(*itr, false); } + + aa.requestRedraw(); return true; } break; @@ -317,6 +323,7 @@ bool ThreadingHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIAction if (_changeThreadingModel == true && ea.getKey() == _keyEventChangeThreadingModel && delta > 1.0) { + _tickOrLastKeyPress = osg::Timer::instance()->tick(); switch(viewerBase->getThreadingModel()) @@ -348,6 +355,8 @@ bool ThreadingHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIAction #endif break; } + + aa.requestRedraw(); return true; } if (viewer && _changeEndBarrierPosition == true && ea.getKey() == _keyEventChangeEndBarrierPosition) @@ -363,6 +372,8 @@ bool ThreadingHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIAction osg::notify(osg::NOTICE)<<"Threading model 'BeforeSwapBuffers' selected."<setLODScale(camera->getLODScale()*1.1); osg::notify(osg::NOTICE)<<"LODScale = "<getLODScale()<setLODScale(camera->getLODScale()/1.1); osg::notify(osg::NOTICE)<<"LODScale = "<getLODScale()<