/* -*-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 namespace osgUtil { // TODO // priority of CompileSets // isCompiled // time estimation // early completion // needs compile given time slot // custom CompileData elements // pruneOldRequestsAndCheckIfEmpty() // Use? : // #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE) // GLint p; // glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_RESIDENT, &p); // #endif static osg::ApplicationUsageProxy ICO_e1(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_MINIMUM_COMPILE_TIME_PER_FRAME ","minimum compile time alloted to compiling OpenGL objects per frame in database pager."); static osg::ApplicationUsageProxy UCO_e2(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_MAXIMUM_OBJECTS_TO_COMPILE_PER_FRAME ","maximum number of OpenGL objects to compile per frame in database pager."); ///////////////////////////////////////////////////////////////// // // CollectStateToCompile // StateToCompile::StateToCompile(GLObjectsVisitor::Mode mode): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _mode(mode) { } void StateToCompile::apply(osg::Node& node) { if (node.getStateSet()) { apply(*(node.getStateSet())); } traverse(node); } void StateToCompile::apply(osg::Geode& node) { if (node.getStateSet()) { apply(*(node.getStateSet())); } for(unsigned int i=0;igetStateSet()) { apply(*(drawable->getStateSet())); } } } } void StateToCompile::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 && (drawable.getUseDisplayList() || drawable.getUseVertexBufferObjects())) { _drawables.insert(&drawable); } } void StateToCompile::apply(osg::StateSet& stateset) { if (_statesetsHandled.count(&stateset)!=0) return; _statesetsHandled.insert(&stateset); if (_mode & GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES) { osg::Program* program = dynamic_cast(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(texItr->second.first.get()); if (texture) { if (_textures.count(texture)==0) { apply(*texture); } } } } } } void StateToCompile::apply(osg::Texture& texture) { _textures.insert(&texture); } ///////////////////////////////////////////////////////////////// // // CompileOps // IncrementalCompileOperation::CompileDrawableOp::CompileDrawableOp(osg::Drawable* drawable): _drawable(drawable) { } double IncrementalCompileOperation::CompileDrawableOp::estimatedTimeForCompile(CompileInfo& compileInfo) const { return 0.0; } bool IncrementalCompileOperation::CompileDrawableOp::compile(CompileInfo& compileInfo) { //OSG_NOTICE<<"CompileDrawableOp::compile(..)"<compileGLObjects(compileInfo); return true; } IncrementalCompileOperation::CompileTextureOp::CompileTextureOp(osg::Texture* texture): _texture(texture) { } double IncrementalCompileOperation::CompileTextureOp::estimatedTimeForCompile(CompileInfo& compileInfo) const { return 0.0; } bool IncrementalCompileOperation::CompileTextureOp::compile(CompileInfo& compileInfo) { //OSG_NOTICE<<"CompileTextureOp::compile(..)"<getForceTextureDownloadGeometry(); if (forceDownloadGeometry) { if (forceDownloadGeometry->getStateSet()) { compileInfo.getState()->apply(forceDownloadGeometry->getStateSet()); } compileInfo.getState()->applyTextureMode(0, _texture->getTextureTarget(), true); compileInfo.getState()->applyTextureAttribute(0, _texture.get()); forceDownloadGeometry->draw(compileInfo); } else { _texture->apply(*compileInfo.getState()); } return true; } IncrementalCompileOperation::CompileProgramOp::CompileProgramOp(osg::Program* program): _program(program) { } double IncrementalCompileOperation::CompileProgramOp::estimatedTimeForCompile(CompileInfo& compileInfo) const { return 0.0; } bool IncrementalCompileOperation::CompileProgramOp::compile(CompileInfo& compileInfo) { //OSG_NOTICE<<"CompileProgramOp::compile(..)"<apply(*compileInfo.getState()); return true; } IncrementalCompileOperation::CompileInfo::CompileInfo(osg::GraphicsContext* context, IncrementalCompileOperation* ico) { setState(context->getState()); incrementalCompileOperation = ico; } ///////////////////////////////////////////////////////////////// // // CompileList // IncrementalCompileOperation::CompileList::CompileList() { } IncrementalCompileOperation::CompileList::~CompileList() { } void IncrementalCompileOperation::CompileList::add(CompileOp* compileOp) { _compileOps.push_back(compileOp); } double IncrementalCompileOperation::CompileList::estimatedTimeForCompile(CompileInfo& compileInfo) const { double estimateTime = 0.0; for(CompileOps::const_iterator itr = _compileOps.begin(); itr != _compileOps.begin(); ++itr) { estimateTime += (*itr)->estimatedTimeForCompile(compileInfo); } return estimateTime; } bool IncrementalCompileOperation::CompileList::compile(CompileInfo& compileInfo) { for(CompileOps::iterator itr = _compileOps.begin(); itr != _compileOps.end() && compileInfo.availableTime()>0.0 && compileInfo.maxNumObjectsToCompile>0; ) { --compileInfo.maxNumObjectsToCompile; CompileOps::iterator saved_itr(itr); ++itr; if ((*saved_itr)->compile(compileInfo)) { _compileOps.erase(saved_itr); } } return empty(); } ///////////////////////////////////////////////////////////////// // // CompileSet // void IncrementalCompileOperation::CompileSet::buildCompileMap(ContextSet& contexts, StateToCompile& stc) { if (contexts.empty() || stc.empty()) return; if (stc.empty()) return; for(ContextSet::iterator itr = contexts.begin(); itr != contexts.end(); ++itr) { // increment the number of compile lists that will need to compile ++_numberCompileListsToCompile; CompileList& cl = _compileMap[*itr]; for(StateToCompile::DrawableSet::iterator ditr = stc._drawables.begin(); ditr != stc._drawables.end(); ++ditr) { cl.add(*ditr); } for(StateToCompile::TextureSet::iterator titr = stc._textures.begin(); titr != stc._textures.end(); ++titr) { cl.add(*titr); } for(StateToCompile::ProgramSet::iterator pitr = stc._programs.begin(); pitr != stc._programs.end(); ++pitr) { cl.add(*pitr); } } } void IncrementalCompileOperation::CompileSet::buildCompileMap(ContextSet& contexts, GLObjectsVisitor::Mode mode) { if (contexts.empty() || !_subgraphToCompile) return; StateToCompile stc(mode); _subgraphToCompile->accept(stc); buildCompileMap(contexts, stc); } bool IncrementalCompileOperation::CompileSet::compile(CompileInfo& compileInfo) { CompileList& compileList = _compileMap[compileInfo.getState()->getGraphicsContext()]; if (!compileList.empty()) { if (compileList.compile(compileInfo)) { --_numberCompileListsToCompile; return _numberCompileListsToCompile==0; } } return _numberCompileListsToCompile==0; } ///////////////////////////////////////////////////////////////// // // IncrementalCompileOperation // 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); } _graphicsCostEstimator = new GraphicsCostEstimator; // assignForceTextureDownloadGeometry(); } IncrementalCompileOperation::~IncrementalCompileOperation() { } void IncrementalCompileOperation::assignForceTextureDownloadGeometry() { osg::Geometry* geometry = new osg::Geometry; osg::Vec3Array* vertices = new osg::Vec3Array; vertices->push_back(osg::Vec3(0.0f,0.0f,0.0f)); geometry->setVertexArray(vertices); osg::Vec4Array* texcoords = new osg::Vec4Array; texcoords->push_back(osg::Vec4(0.0f,0.0f,0.0f,0.0f)); geometry->setTexCoordArray(0, texcoords); geometry->addPrimitiveSet(new osg::DrawArrays(GL_POINTS,0,1)); osg::StateSet* stateset = geometry->getOrCreateStateSet(); stateset->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON); osg::Depth* depth = new osg::Depth; depth->setWriteMask(false); stateset->setAttribute(depth); osg::ColorMask* colorMask = new osg::ColorMask(false,false,false,false); stateset->setAttribute(colorMask); _forceTextureDownloadGeometry = geometry; } 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); } } bool IncrementalCompileOperation::requiresCompile(StateToCompile& stateToCompile) { return isActive() && !stateToCompile.empty(); } void IncrementalCompileOperation::add(osg::Node* subgraphToCompile) { OSG_INFO<<"IncrementalCompileOperation::add("<_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_INFO<<"IncrementalCompileOperation::add(CompileSet = "< lock(_toCompileMutex); _toCompile.push_back(compileSet); } void IncrementalCompileOperation::remove(CompileSet* compileSet) { // OSG_NOTICE<<"IncrementalCompileOperation::remove(CompileSet* compileSet)"< lock(_toCompileMutex); for(CompileSets::iterator itr = _toCompile.begin(); itr != _toCompile.end(); ++itr) { if (*itr == compileSet) { _toCompile.erase(itr); return; } } } // remove CompileSet from _compiled list if it's present. { OpenThreads::ScopedLock lock(_compiledMutex); for(CompileSets::iterator itr = _compiled.begin(); itr != _compiled.end(); ++itr) { if (*itr == compileSet) { _toCompile.erase(itr); return; } } } } void IncrementalCompileOperation::mergeCompiledSubgraphs() { // OSG_INFO<<"IncrementalCompileOperation::mergeCompiledSubgraphs()"< compilded_lock(_compiledMutex); for(CompileSets::iterator itr = _compiled.begin(); itr != _compiled.end(); ++itr) { CompileSet* cs = itr->get(); osg::ref_ptr group; if (cs->_attachmentPoint.lock(group)) { group->addChild(cs->_subgraphToCompile.get()); } } _compiled.clear(); } void IncrementalCompileOperation::operator () (osg::GraphicsContext* context) { 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 = "<_compileCompletedCallback.valid() && cs->_compileCompletedCallback->compileCompleted(cs)) { // callback will handle merging of subgraph so no need to place CompileSet in merge. } else { OpenThreads::ScopedLock compilded_lock(_compiledMutex); _compiled.push_back(cs); } } } //glFush(); //glFinish(); } } // end of namespace osgUtil