Files
OpenSceneGraph/src/osgUtil/IncrementalCompileOperation.cpp
Robert Osfield 06abd75198 From Mathias Froehlich, "We are currently getting issues with locale settings and some osg plugins.
Therefore I have changed all the occurances of atof by asciiToFloat or
asciiToDouble.

I believe that it is safe to do so at least for all the plugins.
Included here are also asciiToFloat conversion of environment variables. One
might argue that these should be locale dependent. But IMO these should be
set and interpreted by osg independent of the current locale.
"
2009-06-25 16:07:49 +00:00

429 lines
14 KiB
C++

/* -*-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 <osgUtil/IncrementalCompileOperation>
#include <osg/Drawable>
#include <osg/Notify>
#include <osg/Timer>
#include <osg/GLObjects>
#include <OpenThreads/ScopedLock>
#include <algorithm>
namespace osgUtil
{
IncrementalCompileOperation::IncrementalCompileOperation():
osg::GraphicsOperation("IncrementalCompileOperation",true),
_flushTimeRatio(0.5),
_conservativeTimeRatio(0.5)
{
_targetFrameRate = 100.0;
_minimumTimeAvailableForGLCompileAndDeletePerFrame = 0.001; // 1ms.
_maximumNumOfObjectsToCompilePerFrame = 20;
const char* ptr = 0;
if( (ptr = getenv("OSG_MINIMUM_COMPILE_TIME_PER_FRAME")) != 0)
{
_minimumTimeAvailableForGLCompileAndDeletePerFrame = osg::asciiToDouble(ptr);
}
if( (ptr = getenv("OSG_MAXIMUM_OBJECTS_TO_COMPILE_PER_FRAME")) != 0)
{
_maximumNumOfObjectsToCompilePerFrame = atoi(ptr);
}
}
IncrementalCompileOperation::~IncrementalCompileOperation()
{
}
void IncrementalCompileOperation::assignContexts(Contexts& contexts)
{
for(Contexts::iterator itr = contexts.begin();
itr != contexts.end();
++itr)
{
osg::GraphicsContext* gc = *itr;
addGraphicsContext(gc);
}
}
void IncrementalCompileOperation::removeContexts(Contexts& contexts)
{
for(Contexts::iterator itr = contexts.begin();
itr != contexts.end();
++itr)
{
osg::GraphicsContext* gc = *itr;
removeGraphicsContext(gc);
}
}
void IncrementalCompileOperation::addGraphicsContext(osg::GraphicsContext* gc)
{
if (_contexts.count(gc)==0)
{
gc->add(this);
_contexts.insert(gc);
}
}
void IncrementalCompileOperation::removeGraphicsContext(osg::GraphicsContext* gc)
{
if (_contexts.count(gc)!=0)
{
gc->remove(this);
_contexts.erase(gc);
}
}
void IncrementalCompileOperation::add(osg::Node* subgraphToCompile)
{
osg::notify(osg::INFO)<<"IncrementalCompileOperation::add("<<subgraphToCompile<<")"<<std::endl;
add(new CompileSet(subgraphToCompile));
}
void IncrementalCompileOperation::add(osg::Group* attachmentPoint, osg::Node* subgraphToCompile)
{
osg::notify(osg::INFO)<<"IncrementalCompileOperation::add("<<attachmentPoint<<", "<<subgraphToCompile<<")"<<std::endl;
add(new CompileSet(attachmentPoint, subgraphToCompile));
}
void IncrementalCompileOperation::add(CompileSet* compileSet, bool callBuildCompileMap)
{
if (!compileSet) return;
if (compileSet->_subgraphToCompile.valid())
{
// force a compute of the bound of the subgraph to avoid the update traversal from having to do this work
// and reducing the change of frame drop.
compileSet->_subgraphToCompile->getBound();
}
if (callBuildCompileMap) compileSet->buildCompileMap(_contexts);
osg::notify(osg::INFO)<<"IncrementalCompileOperation::add(CompileSet = "<<compileSet<<", "<<", "<<callBuildCompileMap<<")"<<std::endl;
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_toCompileMutex);
_toCompile.push_back(compileSet);
}
void IncrementalCompileOperation::mergeCompiledSubgraphs()
{
// osg::notify(osg::INFO)<<"IncrementalCompileOperation::mergeCompiledSubgraphs()"<<std::endl;
OpenThreads::ScopedLock<OpenThreads::Mutex> compilded_lock(_compiledMutex);
for(CompileSets::iterator itr = _compiled.begin();
itr != _compiled.end();
++itr)
{
CompileSet* cs = itr->get();
if (cs->_attachmentPoint.valid())
{
cs->_attachmentPoint->addChild(cs->_subgraphToCompile.get());
}
}
_compiled.clear();
}
class IncrementalCompileOperation::CollectStateToCompile : public osg::NodeVisitor
{
public:
CollectStateToCompile(GLObjectsVisitor::Mode mode):
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_mode(mode) {}
GLObjectsVisitor::Mode _mode;
typedef std::set<osg::Drawable*> DrawableSet;
typedef std::set<osg::StateSet*> StateSetSet;
typedef std::set<osg::Texture*> TextureSet;
typedef std::set<osg::Program*> ProgramSet;
DrawableSet _drawablesHandled;
StateSetSet _statesetsHandled;
DrawableSet _drawables;
TextureSet _textures;
ProgramSet _programs;
void apply(osg::Node& node)
{
if (node.getStateSet())
{
apply(*(node.getStateSet()));
}
traverse(node);
}
void apply(osg::Geode& node)
{
if (node.getStateSet())
{
apply(*(node.getStateSet()));
}
for(unsigned int i=0;i<node.getNumDrawables();++i)
{
osg::Drawable* drawable = node.getDrawable(i);
if (drawable)
{
apply(*drawable);
if (drawable->getStateSet())
{
apply(*(drawable->getStateSet()));
}
}
}
}
void apply(osg::Drawable& drawable)
{
if (_drawablesHandled.count(&drawable)!=0) return;
_drawablesHandled.insert(&drawable);
if (_mode&GLObjectsVisitor::SWITCH_OFF_DISPLAY_LISTS)
{
drawable.setUseDisplayList(false);
}
if (_mode&GLObjectsVisitor::SWITCH_ON_DISPLAY_LISTS)
{
drawable.setUseDisplayList(true);
}
if (_mode&GLObjectsVisitor::SWITCH_ON_VERTEX_BUFFER_OBJECTS)
{
drawable.setUseVertexBufferObjects(true);
}
if (_mode&GLObjectsVisitor::SWITCH_OFF_VERTEX_BUFFER_OBJECTS)
{
drawable.setUseVertexBufferObjects(false);
}
if (_mode&GLObjectsVisitor::COMPILE_DISPLAY_LISTS)
{
_drawables.insert(&drawable);
}
}
void apply(osg::StateSet& stateset)
{
if (_statesetsHandled.count(&stateset)!=0) return;
_statesetsHandled.insert(&stateset);
if (_mode & GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES)
{
osg::Program* program = dynamic_cast<osg::Program*>(stateset.getAttribute(osg::StateAttribute::PROGRAM));
if (program)
{
_programs.insert(program);
}
osg::StateSet::TextureAttributeList& tal = stateset.getTextureAttributeList();
for(osg::StateSet::TextureAttributeList::iterator itr = tal.begin();
itr != tal.end();
++itr)
{
osg::StateSet::AttributeList& al = *itr;
osg::StateAttribute::TypeMemberPair tmp(osg::StateAttribute::TEXTURE,0);
osg::StateSet::AttributeList::iterator texItr = al.find(tmp);
if (texItr != al.end())
{
osg::Texture* texture = dynamic_cast<osg::Texture*>(texItr->second.first.get());
if (texture) _textures.insert(texture);
}
}
}
}
};
void IncrementalCompileOperation::CompileSet::buildCompileMap(ContextSet& contexts, GLObjectsVisitor::Mode mode)
{
if (contexts.empty() || !_subgraphToCompile) return;
CollectStateToCompile cstc(mode);
_subgraphToCompile->accept(cstc);
if (cstc._textures.empty() && cstc._programs.empty() && cstc._drawables.empty()) return;
for(ContextSet::iterator itr = contexts.begin();
itr != contexts.end();
++itr)
{
CompileData& cd = _compileMap[*itr];
std::copy(cstc._textures.begin(), cstc._textures.end(), std::back_inserter<CompileData::Textures>(cd._textures));
std::copy(cstc._programs.begin(), cstc._programs.end(), std::back_inserter<CompileData::Programs>(cd._programs));
std::copy(cstc._drawables.begin(), cstc._drawables.end(), std::back_inserter<CompileData::Drawables>(cd._drawables));
}
}
void IncrementalCompileOperation::operator () (osg::GraphicsContext* context)
{
// osg::notify(osg::NOTICE)<<"IncrementalCompileOperation::operator () ("<<context<<")"<<std::endl;
osg::NotifySeverity level = osg::INFO;
double targetFrameRate = _targetFrameRate;
double minimumTimeAvailableForGLCompileAndDeletePerFrame = _minimumTimeAvailableForGLCompileAndDeletePerFrame;
double targetFrameTime = 1.0/targetFrameRate;
const osg::FrameStamp* fs = context->getState()->getFrameStamp();
double currentTime = fs ? fs->getReferenceTime() : 0.0;
double currentElapsedFrameTime = context->getTimeSinceLastClear();
osg::notify(level)<<"currentTime = "<<currentTime<<std::endl;
osg::notify(level)<<"currentElapsedFrameTime = "<<currentElapsedFrameTime<<std::endl;
double _flushTimeRatio(0.5);
double _conservativeTimeRatio(0.5);
double availableTime = std::max((targetFrameTime - currentElapsedFrameTime)*_conservativeTimeRatio,
minimumTimeAvailableForGLCompileAndDeletePerFrame);
double flushTime = availableTime * _flushTimeRatio;
double compileTime = availableTime - flushTime;
#if 1
osg::notify(level)<<"total availableTime = "<<availableTime*1000.0<<std::endl;
osg::notify(level)<<" flushTime = "<<flushTime*1000.0<<std::endl;
osg::notify(level)<<" compileTime = "<<compileTime*1000.0<<std::endl;
#endif
osg::flushDeletedGLObjects(context->getState()->getContextID(), currentTime, flushTime);
// if any time left over from flush add this to compile time.
if (flushTime>0.0) compileTime += flushTime;
#if 1
osg::notify(level)<<" revised compileTime = "<<compileTime*1000.0<<std::endl;
#endif
osg::RenderInfo renderInfo;
renderInfo.setState(context->getState());
CompileSets toCompileCopy;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> toCompile_lock(_toCompileMutex);
std::copy(_toCompile.begin(),_toCompile.end(),std::back_inserter<CompileSets>(toCompileCopy));
}
osg::Timer_t startTick = osg::Timer::instance()->tick();
for(CompileSets::iterator itr = toCompileCopy.begin();
itr != toCompileCopy.end();
++itr)
{
CompileSet* cs = itr->get();
CompileMap& cm = cs->_compileMap;
CompileData& cd = cm[context];
if (!cd.empty())
{
osg::notify(level)<<"cd._drawables.size()="<<cd._drawables.size()<<std::endl;
osg::notify(level)<<"cd._textures.size()="<<cd._textures.size()<<std::endl;
osg::notify(level)<<"cd._programs.size()="<<cd._programs.size()<<std::endl;
while(!cd._drawables.empty() &&
osg::Timer::instance()->delta_s(startTick, osg::Timer::instance()->tick()) < compileTime)
{
cd._drawables.back()->compileGLObjects(renderInfo);
cd._drawables.pop_back();
}
while(!cd._textures.empty() &&
osg::Timer::instance()->delta_s(startTick, osg::Timer::instance()->tick()) < compileTime)
{
cd._textures.back()->apply(*renderInfo.getState());
cd._textures.pop_back();
}
if (!cd._programs.empty())
{
// compile programs
while(!cd._programs.empty() &&
osg::Timer::instance()->delta_s(startTick, osg::Timer::instance()->tick()) < compileTime)
{
cd._programs.back()->apply(*renderInfo.getState());
cd._programs.pop_back();
}
// switch off Program,
osg::GL2Extensions* extensions = osg::GL2Extensions::Get(renderInfo.getState()->getContextID(), true);
extensions->glUseProgram(0);
renderInfo.getState()->setLastAppliedProgramObject(0);
}
}
if (cd.empty())
{
bool csCompleted = false;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> toCompile_lock(_toCompileMutex);
if (cs->compileCompleted())
{
CompileSets::iterator cs_itr = std::find(_toCompile.begin(), _toCompile.end(), *itr);
if (cs_itr != _toCompile.end())
{
osg::notify(level)<<"Erasing from list"<<std::endl;
// remove from the _toCompile list, note cs won't be deleted here as the tempoary
// toCompile_Copy list will retain a reference.
_toCompile.erase(cs_itr);
// signal that we need to do clean up operations/pass cs on to _compile list.
csCompleted = true;
}
}
}
if (csCompleted && cs->_compileCompletedCallback.valid())
{
if (cs->_compileCompletedCallback->compileCompleted(cs))
{
// callback will handle merging of subgraph so no need to place CompileSet in merge.
}
else
{
OpenThreads::ScopedLock<OpenThreads::Mutex> compilded_lock(_compiledMutex);
_compiled.push_back(cs);
}
}
}
}
}
} // end of namespace osgUtil