/* -*-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. */ #ifndef OSGUTIL_INCREMENTALCOMPILEOPERATOR #define OSGUTIL_INCREMENTALCOMPILEOPERATOR #include #include namespace osgUtil { class OSGUTIL_EXPORT StateToCompile : public osg::NodeVisitor { public: StateToCompile(GLObjectsVisitor::Mode mode); typedef std::set DrawableSet; typedef std::set StateSetSet; typedef std::set TextureSet; typedef std::set ProgramSet; DrawableSet _drawablesHandled; StateSetSet _statesetsHandled; GLObjectsVisitor::Mode _mode; DrawableSet _drawables; TextureSet _textures; ProgramSet _programs; bool _assignPBOToImages; osg::ref_ptr _pbo; bool empty() const { return _textures.empty() && _programs.empty() && _drawables.empty(); } virtual void apply(osg::Node& node); virtual void apply(osg::Geode& node); virtual void apply(osg::Drawable& drawable); virtual void apply(osg::StateSet& stateset); virtual void apply(osg::Texture& texture); }; class OSGUTIL_EXPORT IncrementalCompileOperation : public osg::GraphicsOperation { public: IncrementalCompileOperation(); /** Return true if the IncrementCompileOperation is active.*/ bool isActive() const { return !_contexts.empty(); } bool requiresCompile(StateToCompile& stateToCompile); /** Set the target frame rate that the IncrementalCompileOperation should assume. * Typically one would set this to the value refresh rate of your display system i.e. 60Hz. * Default value is 100. * Usage notes. The TargetFrameRate and the MinimumTimeAvailableForGLCompileAndDeletePerFrame * parameters are not directly used by IncrementalCompileOperation, but are should be used as a guide for how * long to set aside per frame for compiling and deleting OpenGL objects. The longer amount of * time to set aside the faster databases will be paged in but with increased chance of frame drops, * the lower the amount of time the set aside the slower databases will paged it but with better * chance of avoid any frame drops. The default values are chosen to achieve the later when running * on a modern mid to high end PC. * The way to compute the amount of available time use a scheme such as : * availableTime = maximum(1.0/targetFrameRate - timeTakenDuringUpdateCullAndDraw, minimumTimeAvailableForGLCompileAndDeletePerFrame). */ void setTargetFrameRate(double tfr) { _targetFrameRate = tfr; } /** Get the target frame rate that the IncrementalCompileOperation should assume.*/ double getTargetFrameRate() const { return _targetFrameRate; } /** Set the minimum amount of time (in seconds) that should be made available for compiling and delete OpenGL objects per frame. * Default value is 0.001 (1 millisecond). * For usage see notes in setTargetFrameRate.*/ void setMinimumTimeAvailableForGLCompileAndDeletePerFrame(double ta) { _minimumTimeAvailableForGLCompileAndDeletePerFrame = ta; } /** Get the minimum amount of time that should be made available for compiling and delete OpenGL objects per frame. * For usage see notes in setTargetFrameRate.*/ double getMinimumTimeAvailableForGLCompileAndDeletePerFrame() const { return _minimumTimeAvailableForGLCompileAndDeletePerFrame; } /** Set the maximum number of OpenGL objects that the page should attempt to compile per frame. * Note, Lower values reduces chances of a frame drop but lower the rate that database will be paged in at. * Default value is 8. */ void setMaximumNumOfObjectsToCompilePerFrame(unsigned int num) { _maximumNumOfObjectsToCompilePerFrame = num; } /** Get the maximum number of OpenGL objects that the page should attempt to compile per frame.*/ unsigned int getMaximumNumOfObjectsToCompilePerFrame() const { return _maximumNumOfObjectsToCompilePerFrame; } /** FlushTimeRatio governs how much of the spare time in each frame is used for flushing deleted OpenGL objects. * Default value is 0.5, valid range is 0.1 to 0.9.*/ void setFlushTimeRatio(double ratio) { _flushTimeRatio = ratio; } double getFlushTimeRatio() const { return _flushTimeRatio; } /** ConservativeTimeRatio governs how much of the measured spare time in each frame is used for flushing deleted and compile new OpenGL objects. * Default value is 0.5, valid range is 0.1 to 1.0. * A ratio near 1.0 will lead to paged databases being compiled and merged quicker but increase the chances of frame drop. * A ratio near 0.1 will lead to paged databases being compiled and merged closer but reduse the chances of frame drop.*/ void setConservativeTimeRatio(double ratio) { _conservativeTimeRatio = ratio; } double getConservativeTimeRatio() const { return _conservativeTimeRatio; } /** Assign a geometry and associated StateSet than is applied after each texture compile to atttempt to force the OpenGL * drive to download the texture object to OpenGL graphics card.*/ void assignForceTextureDownloadGeometry(); /** Set the osg::Geometry to apply after each texture compile to atttempt to force the OpenGL * drive to download the texture object to OpenGL graphics card.*/ void setForceTextureDownloadGeometry(osg::Geometry* geom) { _forceTextureDownloadGeometry = geom; } osg::Geometry* getForceTextureDownloadGeometry() { return _forceTextureDownloadGeometry.get(); } const osg::Geometry* getForceTextureDownloadGeometry() const { return _forceTextureDownloadGeometry.get(); } typedef std::vector Contexts; void assignContexts(Contexts& contexts); void removeContexts(Contexts& contexts); void addGraphicsContext(osg::GraphicsContext* gc); void removeGraphicsContext(osg::GraphicsContext* gc); typedef std::set ContextSet; ContextSet& getContextSet() { return _contexts; } const ContextSet& getContextSet() const { return _contexts; } /** Merge subgraphs that have been compiled.*/ void mergeCompiledSubgraphs(const osg::FrameStamp* frameStamp); /** Set the current frame number that the IncrementalCompileOperation should use as a reference * value for calculations based on current frame number. * Note, this value is set by the mergeCompiledSubgraphs(..) method so one won't normally need to call * set the CurrentFrameNumber manually.*/ void setCurrentFrameNumber(unsigned int fn) { _currentFrameNumber = fn; } unsigned int getCurrentFrameNumber() const { return _currentFrameNumber; } /** tell the IncrementalCompileOperation to compile all pending objects during next draw traversal, * for specified number of frames.*/ void compileAllForNextFrame(unsigned int numFramesToDoCompileAll=1); /** tell the IncrementalCompileOperation to compile all pending objects during next draw traversal, * till specified frame number.*/ void setCompileAllTillFrameNumber(unsigned int fn) { _compileAllTillFrameNumber = fn; } unsigned int getCompileAllTillFrameNumber() const { return _compileAllTillFrameNumber; } virtual void operator () (osg::GraphicsContext* context); struct OSGUTIL_EXPORT CompileInfo : public osg::RenderInfo { CompileInfo(osg::GraphicsContext* context, IncrementalCompileOperation* ico); bool okToCompile(double estimatedTimeForCompile=0.0) const { if (compileAll) return true; if (maxNumObjectsToCompile==0) return false; return (allocatedTime - timer.elapsedTime()) >= estimatedTimeForCompile; } IncrementalCompileOperation* incrementalCompileOperation; bool compileAll; unsigned int maxNumObjectsToCompile; double allocatedTime; osg::ElapsedTime timer; }; struct CompileOp : public osg::Referenced { /** return an estimate for how many seconds the compile will take.*/ virtual double estimatedTimeForCompile(CompileInfo& compileInfo) const = 0; /** compile associated objects, return true if object as been fully compiled and this CompileOp can be removed from the to compile list.*/ virtual bool compile(CompileInfo& compileInfo) = 0; }; struct OSGUTIL_EXPORT CompileDrawableOp : public CompileOp { CompileDrawableOp(osg::Drawable* drawable); double estimatedTimeForCompile(CompileInfo& compileInfo) const; bool compile(CompileInfo& compileInfo); osg::ref_ptr _drawable; }; struct OSGUTIL_EXPORT CompileTextureOp : public CompileOp { CompileTextureOp(osg::Texture* texture); double estimatedTimeForCompile(CompileInfo& compileInfo) const; bool compile(CompileInfo& compileInfo); osg::ref_ptr _texture; }; struct OSGUTIL_EXPORT CompileProgramOp : public CompileOp { CompileProgramOp(osg::Program* program); double estimatedTimeForCompile(CompileInfo& compileInfo) const; bool compile(CompileInfo& compileInfo); osg::ref_ptr _program; }; class OSGUTIL_EXPORT CompileList { public: CompileList(); ~CompileList(); bool empty() const { return _compileOps.empty(); } void add(CompileOp* compileOp); void add(osg::Drawable* drawable) { add(new CompileDrawableOp(drawable)); } void add(osg::Texture* texture) { add(new CompileTextureOp(texture)); } void add(osg::Program* program) { add(new CompileProgramOp(program)); } double estimatedTimeForCompile(CompileInfo& compileInfo) const; bool compile(CompileInfo& compileInfo); typedef std::list< osg::ref_ptr > CompileOps; CompileOps _compileOps; }; class CompileSet; struct CompileCompletedCallback : public virtual osg::Referenced { /// return true if the callback assumes responsibility for merging any associated subgraphs with the main scene graph /// return false if callback doesn't handle the merge, and instead requires the IncrementalCompileOperation to handle this for us virtual bool compileCompleted(CompileSet* compileSet) = 0; }; class OSGUTIL_EXPORT CompileSet : public osg::Referenced { public: CompileSet() {} CompileSet(osg::Node*subgraphToCompile): _subgraphToCompile(subgraphToCompile) {} CompileSet(osg::Group* attachmentPoint, osg::Node* subgraphToCompile): _attachmentPoint(attachmentPoint), _subgraphToCompile(subgraphToCompile) {} void buildCompileMap(ContextSet& contexts, StateToCompile& stateToCompile); void buildCompileMap(ContextSet& contexts, GLObjectsVisitor::Mode mode=GLObjectsVisitor::COMPILE_DISPLAY_LISTS|GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES); bool compile(CompileInfo& compileInfo); bool compiled() const { return _numberCompileListsToCompile==0; } OpenThreads::Atomic _numberCompileListsToCompile; osg::observer_ptr _attachmentPoint; osg::ref_ptr _subgraphToCompile; osg::ref_ptr _compileCompletedCallback; typedef std::map CompileMap; CompileMap _compileMap; protected: virtual ~CompileSet() {} }; typedef std::list< osg::ref_ptr > CompileSets; /** Add a subgraph to be compiled.*/ void add(osg::Node* subgraphToCompile); /** Add a subgraph to be compiled and add automatically to attachPoint on call to mergeCompiledSubgraphs.*/ void add(osg::Group* attachmentPoint, osg::Node* subgraphToCompile); /** Add a CompileSet to be compiled.*/ void add(CompileSet* compileSet, bool callBuildCompileMap=true); /** Remove CompileSet from list.*/ void remove(CompileSet* compileSet); OpenThreads::Mutex* getToCompiledMutex() { return &_toCompileMutex; } CompileSets& getToCompile() { return _toCompile; } OpenThreads::Mutex* getCompiledMutex() { return &_compiledMutex; } CompileSets& getCompiled() { return _compiled; } protected: virtual ~IncrementalCompileOperation(); void compileSets(CompileSets& toCompile, CompileInfo compileInfo); double _targetFrameRate; double _minimumTimeAvailableForGLCompileAndDeletePerFrame; unsigned int _maximumNumOfObjectsToCompilePerFrame; double _flushTimeRatio; double _conservativeTimeRatio; unsigned int _currentFrameNumber; unsigned int _compileAllTillFrameNumber; osg::ref_ptr _forceTextureDownloadGeometry; OpenThreads::Mutex _toCompileMutex; CompileSets _toCompile; OpenThreads::Mutex _compiledMutex; CompileSets _compiled; ContextSet _contexts; }; } #endif