#include #include #include #include #include #include #include #ifdef WIN32 #include #else #include #endif using namespace osgDB; DatabasePager::DatabasePager() { //osg::notify(osg::INFO)<<"Constructing DatabasePager()"<getOrCreateSharedStateManager(); //if (osgDB::Registry::instance()->getSharedStateManager()) //osgDB::Registry::instance()->setUseObjectCacheHint(true); } DatabasePager::~DatabasePager() { //std::cout<<"DatabasePager::~DatabasePager()"<release(); _fileRequestListEmptyBlock->release(); // then wait for the the thread to stop running. while(isRunning()) { osg::notify(osg::DEBUG_INFO)<<"Waiting for DatabasePager to cancel"<getReferenceTime():0.0; int frameNumber = framestamp?framestamp->getFrameNumber():_frameNumber; // search to see if filename already exist in the file loaded list. bool foundEntry = false; _dataToCompileListMutex.lock(); for(DatabaseRequestList::iterator litr = _dataToCompileList.begin(); litr != _dataToCompileList.end() && !foundEntry; ++litr) { if ((*litr)->_fileName==fileName) { foundEntry = true; (*litr)->_frameNumberLastRequest = frameNumber; (*litr)->_timestampLastRequest = timestamp; (*litr)->_priorityLastRequest = priority; ++((*litr)->_numOfRequests); } } _dataToCompileListMutex.unlock(); if (!foundEntry) { _dataToMergeListMutex.lock(); for(DatabaseRequestList::iterator litr = _dataToMergeList.begin(); litr != _dataToMergeList.end() && !foundEntry; ++litr) { if ((*litr)->_fileName==fileName) { foundEntry = true; (*litr)->_frameNumberLastRequest = frameNumber; (*litr)->_timestampLastRequest = timestamp; (*litr)->_priorityLastRequest = priority; ++((*litr)->_numOfRequests); } } _dataToMergeListMutex.unlock(); } if (!foundEntry) { _fileRequestListMutex.lock(); // search to see if entry already in file request list. bool foundEntry = false; for(DatabaseRequestList::iterator ritr = _fileRequestList.begin(); ritr != _fileRequestList.end() && !foundEntry; ++ritr) { if ((*ritr)->_fileName==fileName) { foundEntry = true; (*ritr)->_timestampLastRequest = timestamp; (*ritr)->_priorityLastRequest = priority; (*ritr)->_frameNumberLastRequest = frameNumber; ++((*ritr)->_numOfRequests); } } if (!foundEntry) { osg::notify(osg::INFO)<<"In DatabasePager::fileRquest("< databaseRequest = new DatabaseRequest; databaseRequest->_fileName = fileName; databaseRequest->_frameNumberFirstRequest = frameNumber; databaseRequest->_timestampFirstRequest = timestamp; databaseRequest->_priorityFirstRequest = priority; databaseRequest->_frameNumberLastRequest = frameNumber; databaseRequest->_timestampLastRequest = timestamp; databaseRequest->_priorityLastRequest = priority; databaseRequest->_groupForAddingLoadedSubgraph = group; if (_fileRequestList.empty()) _fileRequestListEmptyBlock->release(); _fileRequestList.push_back(databaseRequest); } _fileRequestListMutex.unlock(); } if (!isRunning()) { static OpenThreads::Mutex s_mutex; OpenThreads::ScopedLock lock(s_mutex); static bool s_startThreadCalled = false; if (!s_startThreadCalled) { s_startThreadCalled = true; osg::notify(osg::DEBUG_INFO)<<"DatabasePager::startThread()"<getFrameNumber()<<">>>>>>>>>>>>>>>>"<getFrameNumber(); } //else osg::notify(osg::INFO) << "signalBeginFrame >>>>>>>>>>>>>>>>"<reset(); if (_threadPriorityDuringFrame!=getSchedulePriority()) setSchedulePriority(_threadPriorityDuringFrame); } void DatabasePager::signalEndFrame() { //osg::notify(osg::INFO) << "signalEndFrame <<<<<<<<<<<<<<<<<<<< "<release(); if (_threadPriorityOutwithFrame!=getSchedulePriority()) setSchedulePriority(_threadPriorityOutwithFrame); } class FindCompileableGLObjectsVisitor : public osg::NodeVisitor { public: FindCompileableGLObjectsVisitor(DatabasePager::DataToCompile& dataToCompile, bool changeAutoUnRef, bool valueAutoUnRef, bool changeAnisotropy, float valueAnisotropy): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _dataToCompile(dataToCompile), _changeAutoUnRef(changeAutoUnRef), _valueAutoUnRef(valueAutoUnRef), _changeAnisotropy(changeAnisotropy), _valueAnisotropy(valueAnisotropy) { } virtual void apply(osg::Node& node) { apply(node.getStateSet()); traverse(node); } virtual void apply(osg::Geode& geode) { apply(geode.getStateSet()); for(unsigned int i=0;igetTextureAttributeList().size();++i) { osg::Texture* texture = dynamic_cast(stateset->getTextureAttribute(i,osg::StateAttribute::TEXTURE)); if (texture) { if (_changeAutoUnRef) texture->setUnRefImageDataAfterApply(_valueAutoUnRef); if (_changeAnisotropy) texture->setMaxAnisotropy(_valueAnisotropy); foundTextureState = true; } } // if texture object attributes exist add the state to the list for later compilation. if (foundTextureState) { //osg::notify(osg::DEBUG_INFO)<<"Found compilable texture state"<getStateSet()); if (drawable->getUseDisplayList() || drawable->getUseVertexBufferObjects()) { //osg::notify(osg::INFO)<<"Found compilable drawable"<& lhs,const osg::ref_ptr& rhs) const { if (lhs->_timestampLastRequest>rhs->_timestampLastRequest) return true; else if (lhs->_timestampLastRequest_timestampLastRequest) return false; else return (lhs->_priorityLastRequest>rhs->_priorityLastRequest); } }; void DatabasePager::run() { osg::notify(osg::INFO)<<"DatabasePager::run()"<setExpiryDelay(10.0f); bool firstTime = true; do { _fileRequestListEmptyBlock->block(); if (_useFrameBlock) { _frameBlock->block(); } // // delete any children if required. // if (_deleteRemovedSubgraphsInDatabaseThread) { _childrenToDeleteListMutex.lock(); if (!_childrenToDeleteList.empty()) { //std::cout<<"In DatabasePager thread deleting "<<_childrenToDeleteList.size()<<" objects"< databaseRequest; // get the front of the file request list. _fileRequestListMutex.lock(); if (!_fileRequestList.empty()) { std::sort(_fileRequestList.begin(),_fileRequestList.end(),SortFileRequestFunctor()); databaseRequest = _fileRequestList.front(); } _fileRequestListMutex.unlock(); if (databaseRequest.valid()) { // check if databaseRequest is still relevant if (_frameNumber-databaseRequest->_frameNumberLastRequest<=1) { // load the data, note safe to write to the databaseRequest since once // it is created this thread is the only one to write to the _loadedModel pointer. osg::notify(osg::INFO)<<"In DatabasePager thread readNodeFile("<_fileName<<")"<tick(); databaseRequest->_loadedModel = osgDB::readNodeFile(databaseRequest->_fileName); osg::notify(osg::INFO)<<" node read in "<delta_m(before,osg::Timer::instance()->tick())<<" ms"<_loadedModel.valid() && !_activeGraphicsContexts.empty()) { // force a compute of the loaded model's bounding volume, so that when the subgraph // merged with the main scene graph and large computeBound() isn't incurred. databaseRequest->_loadedModel->getBound(); ActiveGraphicsContexts::iterator itr = _activeGraphicsContexts.begin(); DataToCompile& dtc = databaseRequest->_dataToCompileMap[*itr]; ++itr; // find all the compileable rendering objects FindCompileableGLObjectsVisitor frov(dtc, _changeAutoUnRef, _valueAutoUnRef, _changeAnisotropy, _valueAnisotropy); databaseRequest->_loadedModel->accept(frov); if (!dtc.first.empty() || !dtc.second.empty()) { loadedObjectsNeedToBeCompiled = true; // copy the objects to compile list to the other graphics context list. for(; itr != _activeGraphicsContexts.end(); ++itr) { databaseRequest->_dataToCompileMap[*itr] = dtc; } } } // move the databaseRequest from the front of the fileRequest to the end of // dataLoad list. _fileRequestListMutex.lock(); if (databaseRequest->_loadedModel.valid()) { if (loadedObjectsNeedToBeCompiled) { _dataToCompileListMutex.lock(); _dataToCompileList.push_back(databaseRequest); _dataToCompileListMutex.unlock(); } else { _dataToMergeListMutex.lock(); _dataToMergeList.push_back(databaseRequest); _dataToMergeListMutex.unlock(); } } _fileRequestList.erase(_fileRequestList.begin()); if (_fileRequestList.empty()) _fileRequestListEmptyBlock->reset(); _fileRequestListMutex.unlock(); } else { //std::cout<<"frame number delta for "<_fileName<<" "<<_frameNumber-databaseRequest->_frameNumberLastRequest<reset(); _fileRequestListMutex.unlock(); } } // go to sleep till our the next time our thread gets scheduled. if (firstTime) { // do a yield to get round a peculiar thread hang when testCancel() is called // in certain cirumstances - of which there is no particular pattern. YieldCurrentThread(); firstTime = false; } } while (!testCancel()); } void DatabasePager::addLoadedDataToSceneGraph(double timeStamp) { DatabaseRequestList localFileLoadedList; // get the dat for the _dataToCompileList, leaving it empty via a std::vector<>.swap. _dataToMergeListMutex.lock(); localFileLoadedList.swap(_dataToMergeList); _dataToMergeListMutex.unlock(); // add the loaded data into the scene graph. for(DatabaseRequestList::iterator itr=localFileLoadedList.begin(); itr!=localFileLoadedList.end(); ++itr) { DatabaseRequest* databaseRequest = itr->get(); if (osgDB::Registry::instance()->getSharedStateManager()) osgDB::Registry::instance()->getSharedStateManager()->share(databaseRequest->_loadedModel.get()); registerPagedLODs(databaseRequest->_loadedModel.get()); osg::Group* group = databaseRequest->_groupForAddingLoadedSubgraph.get(); osg::PagedLOD* plod = dynamic_cast(group); if (plod) { plod->setTimeStamp(plod->getNumChildren(),timeStamp); } group->addChild(databaseRequest->_loadedModel.get()); osg::notify(osg::INFO)<<"merged subgraph"<_fileName<<" after "<_numOfRequests<<" requests."<referenceCount()==1) { osg::Texture* texture = const_cast(titr->get()); texture->releaseGLObjects(); objectsToDelete.push_back(texture); } } for(DrawableSet::iterator ditr=_drawableSet.begin(); ditr!=_drawableSet.end(); ++ditr) { if ((*ditr)->referenceCount()==1) { osg::Drawable* drawable = const_cast(ditr->get()); drawable->releaseGLObjects(); objectsToDelete.push_back(drawable); } } } inline void apply(osg::StateSet* stateset) { if (stateset) { // search for the existance of any texture object attributes bool foundTextureState = false; osg::StateSet::TextureAttributeList& tal = stateset->getTextureAttributeList(); for(osg::StateSet::TextureAttributeList::iterator itr=tal.begin(); itr!=tal.end() && !foundTextureState; ++itr) { osg::StateSet::AttributeList& al = *itr; osg::StateSet::AttributeList::iterator alitr = al.find(osg::StateAttribute::TEXTURE); if (alitr!=al.end()) { // found texture, so place it in the texture list. osg::Texture* texture = static_cast(alitr->second.first.get()); _textureSet.insert(texture); } } } } inline void apply(osg::Drawable* drawable) { apply(drawable->getStateSet()); _drawableSet.insert(drawable); } virtual void apply(osg::Node& node) { apply(node.getStateSet()); traverse(node); } virtual void apply(osg::Geode& geode) { apply(geode.getStateSet()); for(unsigned int i=0;i > DrawableSet; typedef std::set< osg::ref_ptr > TextureSet; TextureSet _textureSet; DrawableSet _drawableSet; }; void DatabasePager::removeExpiredSubgraphs(double currentFrameTime) { double expiryTime = currentFrameTime - _expiryDelay; osg::NodeList childrenRemoved; //osg::notify(osg::INFO)<<"DatabasePager::removeExpiredSubgraphs("<get(); plod->removeExpiredChildren(expiryTime,childrenRemoved); } if (osgDB::Registry::instance()->getSharedStateManager()) osgDB::Registry::instance()->getSharedStateManager()->prune(); if (_deleteRemovedSubgraphsInDatabaseThread) { // for all the subgraphs to remove find all the textures and drawables and // strip them from the display lists. ReleaseTexturesAndDrawablesVisitor rtadv; for(osg::NodeList::iterator nitr=childrenRemoved.begin(); nitr!=childrenRemoved.end(); ++nitr) { (*nitr)->accept(rtadv); } // unref' the children we need to remove, keeping behind the Texture's and Drawables for later deletion // inside the database thread. childrenRemoved.clear(); // transfer the removed children over to the to delete list so the database thread can delete them. _childrenToDeleteListMutex.lock(); rtadv.releaseGLObjects(_childrenToDeleteList); _childrenToDeleteListMutex.unlock(); } // flush all the references from the child removed list. If _deleteRemovedSubgraphsInDatabaseThread // is false then this will typically resulting in a delete, otherwise this will be left to the // clean up of the _childrenToDeleteList from within the database paging thread. childrenRemoved.clear(); for(unsigned int i=_pagedLODList.size(); i>0; ) { --i; osg::PagedLOD* plod = _pagedLODList[i].get(); if (plod->referenceCount()==1) { _pagedLODList.erase(_pagedLODList.begin()+i); } else { //osg::notify(osg::INFO)<<" PagedLOD "<referenceCount()<updateTimeStampOfObjectsInCacheWithExtenalReferences(currentFrameTime); osgDB::Registry::instance()->removeExpiredObjectsInCache(expiryTime); } class FindPagedLODsVisitor : public osg::NodeVisitor { public: FindPagedLODsVisitor(DatabasePager::PagedLODList& pagedLODList): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _pagedLODList(pagedLODList) { } virtual void apply(osg::PagedLOD& plod) { _pagedLODList.push_back(&plod); traverse(plod); } DatabasePager::PagedLODList& _pagedLODList; }; void DatabasePager::registerPagedLODs(osg::Node* subgraph) { FindPagedLODsVisitor fplv(_pagedLODList); if (subgraph) subgraph->accept(fplv); } void DatabasePager::setCompileGLObjectsForContexID(unsigned int contextID, bool on) { if (on) { _activeGraphicsContexts.insert(contextID); } else { _activeGraphicsContexts.erase(contextID); } } bool DatabasePager::getCompileGLObjectsForContexID(unsigned int contextID) { return _activeGraphicsContexts.count(contextID)!=0; } void DatabasePager::compileGLObjects(osg::State& state, double& availableTime) { const osg::Timer& timer = *osg::Timer::instance(); osg::Timer_t start_tick = timer.tick(); double elapsedTime = 0.0; osg::ref_ptr databaseRequest; // get the first compileable entry. _dataToCompileListMutex.lock(); if (!_dataToCompileList.empty()) { std::sort(_dataToCompileList.begin(),_dataToCompileList.end(),SortFileRequestFunctor()); databaseRequest = _dataToCompileList.front(); } _dataToCompileListMutex.unlock(); // while there are valid databaseRequest's in the to compile list and there is // sufficient time left compile each databaseRequest's stateset and drawables. while (databaseRequest.valid() && elapsedTime_dataToCompileMap; DataToCompile& dtc = dcm[state.getContextID()]; if (!dtc.first.empty() && elapsedTimecompileGLObjects(state); elapsedTime = timer.delta_s(start_tick,timer.tick()); } // remove the compiled stateset from the list. sslist.erase(sslist.begin(),itr); } if (!dtc.second.empty() && elapsedTimecompileGLObjects(state); elapsedTime = timer.delta_s(start_tick,timer.tick()); } // remove the compiled drawables from the list. dwlist.erase(dwlist.begin(),itr); } //osg::notify(osg::INFO)<<"Checking if compiled"<second.first.empty())) allCompiled=false; if (!(itr->second.second.empty())) allCompiled=false; } if (allCompiled) { osg::notify(osg::INFO)<<"All compiled"<