#include #include #include #include #include #include #include #ifdef WIN32 #include #else #include #endif using namespace osgDB; using namespace OpenThreads; DatabasePager::DatabasePager() { //osg::notify(osg::INFO)<<"Constructing DatabasePager()"<getOrCreateSharedStateManager(); //if (osgDB::Registry::instance()->getSharedStateManager()) //osgDB::Registry::instance()->setUseObjectCacheHint(true); } DatabasePager::~DatabasePager() { cancel(); } int DatabasePager::cancel() { int result = 0; if( isRunning() ) { _done = true; // cancel the thread.. result = Thread::cancel(); //join(); // release the frameBlock and _databasePagerThreadBlock incase its holding up thread cancelation. _frameBlock->release(); _databasePagerThreadBlock->release(); // then wait for the the thread to stop running. while(isRunning()) { osg::notify(osg::DEBUG_INFO)<<"Waiting for DatabasePager to cancel"< lock(_fileRequestListMutex); _fileRequestList.clear(); } { OpenThreads::ScopedLock lock(_dataToCompileListMutex); _dataToCompileList.clear(); } { OpenThreads::ScopedLock lock(_childrenToDeleteListMutex); _childrenToDeleteList.clear(); } { OpenThreads::ScopedLock lock(_dataToMergeListMutex); _dataToMergeList.clear(); } // no mutex?? _activePagedLODList.clear(); _inactivePagedLODList.clear(); // ?? // _activeGraphicsContexts } void DatabasePager::requestNodeFile(const std::string& fileName,osg::Group* group, float priority, const osg::FrameStamp* framestamp) { if (!_acceptNewRequests) return; double timestamp = framestamp?framestamp->getReferenceTime():0.0; int frameNumber = framestamp?framestamp->getFrameNumber():_frameNumber; // search to see if filename already exist in the file loaded list. bool foundEntry = false; { OpenThreads::ScopedLock lock(_dataToCompileListMutex); 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); } } } if (!foundEntry) { OpenThreads::ScopedLock lock(_dataToMergeListMutex); 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); } } } if (!foundEntry) { OpenThreads::ScopedLock lock(_fileRequestListMutex); // 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; _fileRequestList.push_back(databaseRequest); updateDatabasePagerThreadBlock(); } } if (!isRunning()) { OpenThreads::ScopedLock lock(_run_mutex); if (!_startThreadCalled) { _startThreadCalled = true; _done = false; osg::notify(osg::DEBUG_INFO)<<"DatabasePager::startThread()"<getFrameNumber()<<">>>>>>>>>>>>>>>>"<getFrameNumber(); } //else osg::notify(osg::INFO) << "signalBeginFrame >>>>>>>>>>>>>>>>"<0 && _threadPriorityDuringFrame!=getSchedulePriority()) setSchedulePriority(_threadPriorityDuringFrame); } void DatabasePager::signalEndFrame() { //osg::notify(osg::INFO) << "signalEndFrame <<<<<<<<<<<<<<<<<<<< "<getTextureAttributeList().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()); // drawable->setUseDisplayList(false); // drawable->setUseVertexBufferObjects(true); 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::setDatabasePagerThreadPause(bool pause) { _databasePagerThreadPaused = pause; updateDatabasePagerThreadBlock(); } void DatabasePager::run() { osg::notify(osg::INFO)<<"DatabasePager::run()"<block(); if (_useFrameBlock) { _frameBlock->block(); } // // delete any children if required. // if (_deleteRemovedSubgraphsInDatabaseThread) { osg::ref_ptr obj = 0; { OpenThreads::ScopedLock lock(_childrenToDeleteListMutex); if (!_childrenToDeleteList.empty()) { //osg::notify(osg::NOTICE)<<"In DatabasePager thread deleting "<<_childrenToDeleteList.size()<<" objects"<tick(); obj = _childrenToDeleteList.back(); _childrenToDeleteList.pop_back(); //osg::notify(osg::NOTICE)<<"Done DatabasePager thread deleted in "<delta_m(before,osg::Timer::instance()->tick())<<" ms"<<" objects"< databaseRequest; // get the front of the file request list. { OpenThreads::ScopedLock lock(_fileRequestListMutex); if (!_fileRequestList.empty()) { std::sort(_fileRequestList.begin(),_fileRequestList.end(),SortFileRequestFunctor()); databaseRequest = _fileRequestList.front(); } } 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::NOTICE)<<"In DatabasePager thread readNodeFile("<_fileName<<")"<tick(); bool serialize_readNodeFile = true; if (serialize_readNodeFile) { // do *not* assume that we only have one DatabasePager, or that reaNodeFile is thread safe... static OpenThreads::Mutex s_serialize_readNodeFile_mutex; OpenThreads::ScopedLock lock(s_serialize_readNodeFile_mutex); databaseRequest->_loadedModel = osgDB::readNodeFile(databaseRequest->_fileName); } else { // assume that we only have one DatabasePager, or that reaNodeFile is thread safe... databaseRequest->_loadedModel = osgDB::readNodeFile(databaseRequest->_fileName); } //osg::notify(osg::NOTICE)<<" 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 from the 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. OpenThreads::ScopedLock lock(_fileRequestListMutex); if (databaseRequest->_loadedModel.valid()) { if (loadedObjectsNeedToBeCompiled) { OpenThreads::ScopedLock lock(_dataToCompileListMutex); _dataToCompileList.push_back(databaseRequest); } else { OpenThreads::ScopedLock lock(_dataToMergeListMutex); _dataToMergeList.push_back(databaseRequest); } } if (!_fileRequestList.empty()) _fileRequestList.erase(_fileRequestList.begin()); updateDatabasePagerThreadBlock(); } else { //std::cout<<"frame number delta for "<_fileName<<" "<<_frameNumber-databaseRequest->_frameNumberLastRequest< lock(_fileRequestListMutex); if (!_fileRequestList.empty()) _fileRequestList.erase(_fileRequestList.begin()); updateDatabasePagerThreadBlock(); } } // 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() && !_done); } bool DatabasePager::requiresUpdateSceneGraph() const { { OpenThreads::ScopedLock lock(_dataToMergeListMutex); if (!_dataToMergeList.empty()) return true; } return false; } void DatabasePager::addLoadedDataToSceneGraph(double timeStamp) { // osg::Timer_t before = osg::Timer::instance()->tick(); DatabaseRequestList localFileLoadedList; // get the dat for the _dataToCompileList, leaving it empty via a std::vector<>.swap. { OpenThreads::ScopedLock lock(_dataToMergeListMutex); localFileLoadedList.swap(_dataToMergeList); } // add the loaded data into the scene graph. for(DatabaseRequestList::iterator itr=localFileLoadedList.begin(); itr!=localFileLoadedList.end(); ++itr) { DatabaseRequest* databaseRequest = itr->get(); // osg::notify(osg::NOTICE)<<"Merging "<<_frameNumber-(*itr)->_frameNumberLastRequest<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."<delta_m(before,osg::Timer::instance()->tick())<<" ms objects"<tick(); double expiryTime = currentFrameTime - _expiryDelay; osg::NodeList childrenRemoved; for(PagedLODList::iterator active_itr = _activePagedLODList.begin(); active_itr!=_activePagedLODList.end(); ) { const osg::PagedLOD* plod = active_itr->get(); bool remove_plod = false; if (plod->referenceCount()<=1) { // prune PageLOD's that are no longer externally referenced childrenRemoved.push_back(const_cast(plod)); //osg::notify(osg::NOTICE)<<"_activePagedLODList : pruning no longer externally referenced"<getFrameNumberOfLastTraversal()<_frameNumber) { // osg::notify(osg::NOTICE)<<"_activePagedLODList : moving PageLOD to inactive list"<targetNumOfInActivePagedLODs) targetNumOfRemovedChildPagedLODs = _inactivePagedLODList.size()-targetNumOfInActivePagedLODs; if (targetNumOfRemovedChildPagedLODs>1) targetNumOfRemovedChildPagedLODs=1; // filter out singly referenced PagedLOD and move reactivated PagedLOD into the active list for(PagedLODList::iterator inactive_itr = _inactivePagedLODList.begin(); inactive_itr!=_inactivePagedLODList.end(); ) { const osg::PagedLOD* plod = inactive_itr->get(); bool remove_plod = false; if (plod->referenceCount()<=1) { // prune PageLOD's that are no longer externally referenced childrenRemoved.push_back(const_cast(plod)); //osg::notify(osg::NOTICE)<<"_activePagedLODList : pruning no longer externally referenced"<getFrameNumberOfLastTraversal()>=_frameNumber) { // osg::notify(osg::NOTICE)<<"_inactivePagedLODList : moving PageLOD to active list"<(plod)->removeExpiredChildren(expiryTime,childrenRemoved)) { //osg::notify(osg::NOTICE)<<"Some children removed from PLod"<delta_m(before,osg::Timer::instance()->tick()); //osg::notify(osg::NOTICE)<<" time 1 "< lock(_childrenToDeleteListMutex); for (osg::NodeList::iterator critr = childrenRemoved.begin(); critr!=childrenRemoved.end(); ++critr) { _childrenToDeleteList.push_back(critr->get()); } updateDatabasePagerThreadBlock(); } childrenRemoved.clear(); } // osg::notify(osg::NOTICE)<<" time 2 "<delta_m(before,osg::Timer::instance()->tick())<<" ms "<getSharedStateManager()) osgDB::Registry::instance()->getSharedStateManager()->prune(); // update the Registry object cache. osgDB::Registry::instance()->updateTimeStampOfObjectsInCacheWithExtenalReferences(currentFrameTime); osgDB::Registry::instance()->removeExpiredObjectsInCache(expiryTime); // osg::notify(osg::NOTICE)<<"Done DatabasePager::removeExpiredSubgraphs() "<delta_m(before,osg::Timer::instance()->tick())<<" ms "<accept(fplv); } bool DatabasePager::requiresCompileGLObjects() const { OpenThreads::ScopedLock lock(_dataToCompileListMutex); return !_dataToCompileList.empty(); } void DatabasePager::setCompileGLObjectsForContextID(unsigned int contextID, bool on) { if (on) { _activeGraphicsContexts.insert(contextID); } else { _activeGraphicsContexts.erase(contextID); } } bool DatabasePager::getCompileGLObjectsForContextID(unsigned int contextID) { return _activeGraphicsContexts.count(contextID)!=0; } void DatabasePager::compileGLObjects(osg::State& state, double& availableTime) { // osg::notify(osg::NOTICE)<<"DatabasePager::compileGLObjects "<<_frameNumber<0.0) { const osg::Timer& timer = *osg::Timer::instance(); osg::Timer_t start_tick = timer.tick(); double elapsedTime = 0.0; double estimatedTextureDuration = 0.0001; double estimatedDrawableDuration = 0.0001; osg::ref_ptr databaseRequest; // get the first compileable entry. { OpenThreads::ScopedLock lock(_dataToCompileListMutex); if (!_dataToCompileList.empty()) { std::sort(_dataToCompileList.begin(),_dataToCompileList.end(),SortFileRequestFunctor()); DatabaseRequestList::iterator litr; int i=0; for(litr = _dataToCompileList.begin(); (litr != _dataToCompileList.end()) && (_frameNumber-(*litr)->_frameNumberLastRequest)<=1; ++litr,i++) { //osg::notify(osg::NOTICE)<<"Compile "<<_frameNumber-(*litr)->_frameNumberLastRequest< lock(_childrenToDeleteListMutex); for(DatabaseRequestList::iterator ditr=litr; ditr!=_dataToCompileList.end(); ++ditr) { _childrenToDeleteList.push_back((*ditr)->_loadedModel.get()); } } //osg::notify(osg::NOTICE)<<"Pruning "<<_dataToCompileList.size()-i<_dataToCompileMap; DataToCompile& dtc = dcm[state.getContextID()]; if (!dtc.first.empty() && (elapsedTime+estimatedTextureDuration)compileGLObjects(state); GLint p; glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_RESIDENT, &p); elapsedTime = timer.delta_s(start_tick,timer.tick()); // estimate the duration of the compile based on current compile duration. estimatedTextureDuration = (elapsedTime-startCompileTime); ++numObjectsCompiled; } // remove the compiled stateset from the list. sslist.erase(sslist.begin(),itr); } if (!dtc.second.empty() && (elapsedTime+estimatedDrawableDuration)compileGLObjects(state); elapsedTime = timer.delta_s(start_tick,timer.tick()); // estimate the duration of the compile based on current compile duration. estimatedDrawableDuration = (elapsedTime-startCompileTime); ++numObjectsCompiled; } // 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) { // we've compile all of the current databaseRequest so we can now pop it off the // to compile list and place it on the merge list. OpenThreads::ScopedLock lock(_dataToCompileListMutex); { OpenThreads::ScopedLock lock(_dataToMergeListMutex); _dataToMergeList.push_back(databaseRequest); } if (!_dataToCompileList.empty()) _dataToCompileList.erase(_dataToCompileList.begin()); if (!_dataToCompileList.empty()) { std::sort(_dataToCompileList.begin(),_dataToCompileList.end(),SortFileRequestFunctor()); databaseRequest = _dataToCompileList.front(); } else databaseRequest = 0; } else { //osg::notify(osg::INFO)<<"Not all compiled"<