/* -*-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 #include #include #include #include #include #include #ifdef WIN32 #include #else #include #endif using namespace osgDB; using namespace OpenThreads; static osg::ApplicationUsageProxy DatabasePager_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_DO_PRE_COMPILE ","Switch on or off the pre compile of OpenGL object database pager."); static osg::ApplicationUsageProxy DatabasePager_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 DatabasePager_e2(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_MAXIMUM_OBJECTS_TO_COMPILE_PER_FRAME ","maximum number of OpenGL objects to compile per frame in database pager."); static osg::ApplicationUsageProxy DatabasePager_e3(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_DATABASE_PAGER_DRAWABLE ","Set the drawable policy for setting of loaded drawable to specified type. mode can be one of DoNotModify, DisplayList, VBO or VertexArrays>."); static osg::ApplicationUsageProxy DatabasePager_e4(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_DATABASE_PAGER_PRIORITY ", "Set the thread priority to DEFAULT, MIN, LOW, NOMINAL, HIGH or MAX."); static osg::ApplicationUsageProxy DatabasePager_e7(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_EXPIRY_DELAY ","Set the length of time a PagedLOD child is kept in memory, without being used, before its tagged as expired, and ear marked to deletion."); static osg::ApplicationUsageProxy DatabasePager_e8(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_EXPIRY_FRAMES ","Set number of frames a PagedLOD child is kept in memory, without being used, before its tagged as expired, and ear marked to deletion."); static osg::ApplicationUsageProxy DatabasePager_e9(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_RELEASE_DELAY ","Set the length of time a PagedLOD child's OpenGL objects are kept in memory, without being used, before be released (setting to OFF disables this feature.)"); static osg::ApplicationUsageProxy DatabasePager_e10(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_RELEASE_FRAMES ","Set number of frames a PagedLOD child's OpenGL objects are kept in memory, without being used, before be released."); static osg::ApplicationUsageProxy DatabasePager_e11(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_MAX_PAGEDLOD ","Set the target maximum number of PagedLOD to maintain."); // Convert function objects that take pointer args into functions that a // reference to an osg::ref_ptr. This is quite useful for doing STL // operations on lists of ref_ptr. This code assumes that a function // with an argument const Foo* should be composed into a function of // argument type ref_ptr&, not ref_ptr&. Some support // for that should be added to make this more general. namespace { template struct PointerTraits { typedef class NullType {} PointeeType; }; template struct PointerTraits { typedef U PointeeType; }; template struct PointerTraits { typedef U PointeeType; }; template class RefPtrAdapter : public std::unary_function::PointeeType>, typename FuncObj::result_type> { public: typedef typename PointerTraits::PointeeType PointeeType; typedef osg::ref_ptr RefPtrType; explicit RefPtrAdapter(const FuncObj& funcObj) : _func(funcObj) {} typename FuncObj::result_type operator()(const RefPtrType& refPtr) const { return _func(refPtr.get()); } protected: FuncObj _func; }; template RefPtrAdapter refPtrAdapt(const FuncObj& func) { return RefPtrAdapter(func); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // CountPagedLODList // struct DatabasePager::DatabasePagerCompileCompletedCallback : public osgUtil::IncrementalCompileOperation::CompileCompletedCallback { DatabasePagerCompileCompletedCallback(osgDB::DatabasePager* pager, osgDB::DatabasePager::DatabaseRequest* databaseRequest): _pager(pager), _databaseRequest(databaseRequest) {} virtual bool compileCompleted(osgUtil::IncrementalCompileOperation::CompileSet* compileSet) { _pager->compileCompleted(_databaseRequest.get()); return true; } osgDB::DatabasePager* _pager; osg::ref_ptr _databaseRequest; }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // CountPagedLODList // class DatabasePager::CountPagedLODsVisitor : public osg::NodeVisitor { public: CountPagedLODsVisitor(): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _numPagedLODs(0) { } META_NodeVisitor("osgDB","CountPagedLODsVisitor") virtual void apply(osg::PagedLOD& plod) { ++_numPagedLODs; _pagedLODs.insert(&plod); traverse(plod); } bool removeExpiredChildrenAndCountPagedLODs(osg::PagedLOD* plod, double expiryTime, int expiryFrame, osg::NodeList& removedChildren) { size_t sizeBefore = removedChildren.size(); plod->removeExpiredChildren(expiryTime, expiryFrame, removedChildren); for(size_t i = sizeBefore; iaccept(*this); } for(PagedLODset::iterator itr = _pagedLODs.begin(); itr != _pagedLODs.end(); ++itr) { removedChildren.push_back(*itr); } return sizeBefore!=removedChildren.size(); } typedef std::set PagedLODset; PagedLODset _pagedLODs; int _numPagedLODs; }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SetBasedPagedLODList // class SetBasedPagedLODList : public DatabasePager::PagedLODList { public: typedef std::set< osg::observer_ptr > PagedLODs; PagedLODs _pagedLODs; virtual PagedLODList* clone() { return new SetBasedPagedLODList(); } virtual void clear() { _pagedLODs.clear(); } virtual unsigned int size() { return _pagedLODs.size(); } virtual void moveInactivePagedLODTo(PagedLODList& inactivePagedLODList, const osg::FrameStamp& frameStamp) { for(PagedLODs::iterator itr = _pagedLODs.begin(); itr != _pagedLODs.end(); ) { osg::ref_ptr plod; if (itr->lock(plod)) { int delta = frameStamp.getFrameNumber() - plod->getFrameNumberOfLastTraversal(); if (delta>1) { #if 0 if (_releaseDelay!=DBL_MAX) { plod->releaseGLObjects(); } #endif inactivePagedLODList.insertPagedLOD(*itr); PagedLODs::iterator pitr = itr; ++itr; _pagedLODs.erase(pitr); } else { ++itr; } } else { OSG_INFO<<"DatabasePager::removeExpiredSubgraphs(), removing PagedLOD from _activePagedLODLists"< plod; if (itr->lock(plod)) { int delta = frameStamp.getFrameNumber() - plod->getFrameNumberOfLastTraversal(); if (delta>1) { ++itr; } else { activePagedLODList.insertPagedLOD(*itr); PagedLODs::iterator pitr = itr; ++itr; _pagedLODs.erase(pitr); } } else { OSG_INFO<<"DatabasePager::removeExpiredSubgraphs(), removing PagedLOD from _inactivePagedLODLists"< countPagedLODsVisitor._numPagedLODs; ) { osg::ref_ptr plod; if (itr->lock(plod) && countPagedLODsVisitor._pagedLODs.count(plod.get())==0) { countPagedLODsVisitor.removeExpiredChildrenAndCountPagedLODs(plod.get(), expiryTime, expiryFrame, childrenRemoved); // advance the iterator to the next element ++itr; } else { PagedLODs::iterator pitr = itr; ++itr; _pagedLODs.erase(pitr); OSG_INFO<<"DatabasePager::removeExpiredSubgraphs() _inactivePagedLOD has been invalidated, but ignored"<(itr->get()); osg::observer_ptr obs_ptr(plod); PagedLODs::iterator plod_itr = _pagedLODs.find(obs_ptr); if (plod_itr != _pagedLODs.end()) { OSG_INFO<<"Removing node from PagedLOD list"<& plod) { if (_pagedLODs.count(plod)!=0) { OSG_NOTICE<<"Warning: SetBasedPagedLODList::insertPagedLOD("<& plod) const { return (_pagedLODs.count(plod)!=0); } }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // FindCompileableGLObjectsVisitor // class DatabasePager::FindCompileableGLObjectsVisitor : public osg::NodeVisitor { public: FindCompileableGLObjectsVisitor(DatabasePager::DataToCompile* dataToCompile, bool changeAutoUnRef, bool valueAutoUnRef, bool changeAnisotropy, float valueAnisotropy, DatabasePager::DrawablePolicy drawablePolicy, const DatabasePager* pager): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _dataToCompile(dataToCompile), _changeAutoUnRef(changeAutoUnRef), _valueAutoUnRef(valueAutoUnRef), _changeAnisotropy(changeAnisotropy), _valueAnisotropy(valueAnisotropy), _drawablePolicy(drawablePolicy), _pager(pager) { if (osgDB::Registry::instance()->getBuildKdTreesHint()==osgDB::Options::BUILD_KDTREES && osgDB::Registry::instance()->getKdTreeBuilder()) { _kdTreeBuilder = osgDB::Registry::instance()->getKdTreeBuilder()->clone(); } } META_NodeVisitor("osgDB","FindCompileableGLObjectsVisitor") 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)); // Has this texture already been encountered? if (texture && !_textureSet.count(texture)) { _textureSet.insert(texture); if (_changeAutoUnRef) texture->setUnRefImageDataAfterApply(_valueAutoUnRef); if ((_changeAnisotropy && texture->getMaxAnisotropy() != _valueAnisotropy)) { if (_changeAnisotropy) texture->setMaxAnisotropy(_valueAnisotropy); } if (!_pager->isCompiled(texture)) { _dataToCompile->_textures.insert(texture); if (osg::getNotifyLevel() >= osg::DEBUG_INFO) { OSG_INFO <<"Found compilable texture " << texture << " "; osg::Image* image = texture->getImage(0); if (image) OSG_INFO << image->getFileName(); OSG_INFO << std:: endl; } break; } } } } } inline void apply(osg::Drawable* drawable) { if (_drawableSet.count(drawable)) return; _drawableSet.insert(drawable); apply(drawable->getStateSet()); switch(_drawablePolicy) { case DatabasePager::DO_NOT_MODIFY_DRAWABLE_SETTINGS: // do nothing, leave settings as they came in from loaded database. // OSG_NOTICE<<"DO_NOT_MODIFY_DRAWABLE_SETTINGS"<setUseDisplayList(true); drawable->setUseVertexBufferObjects(false); break; case DatabasePager::USE_VERTEX_BUFFER_OBJECTS: drawable->setUseDisplayList(true); drawable->setUseVertexBufferObjects(true); // OSG_NOTICE<<"USE_VERTEX_BUFFER_OBJECTS"<setUseDisplayList(false); drawable->setUseVertexBufferObjects(false); // OSG_NOTICE<<"USE_VERTEX_ARRAYS"<getUseVertexBufferObjects() || drawable->getUseDisplayList()) && !_pager->isCompiled(drawable)) { _dataToCompile->_drawables.insert(drawable); } } DatabasePager::DataToCompile* _dataToCompile; bool _changeAutoUnRef; bool _valueAutoUnRef; bool _changeAnisotropy; float _valueAnisotropy; DatabasePager::DrawablePolicy _drawablePolicy; const DatabasePager* _pager; std::set > _textureSet; std::set > _drawableSet; osg::ref_ptr _kdTreeBuilder; protected: FindCompileableGLObjectsVisitor& operator = (const FindCompileableGLObjectsVisitor&) { return *this; } }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SortFileRequestFunctor // struct DatabasePager::SortFileRequestFunctor { bool operator() (const osg::ref_ptr& 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); } }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // DatabaseRequest // void DatabasePager::DatabaseRequest::invalidate() { OSG_INFO<<" DatabasePager::DatabaseRequest::invalidate()."<invalidate(); } } bool DatabasePager::RequestQueue::pruneOldRequestsAndCheckIfEmpty() { OpenThreads::ScopedLock lock(_requestMutex); if (_frameNumberLastPruned != _pager->_frameNumber) { for(RequestQueue::RequestList::iterator citr = _requestList.begin(); citr != _requestList.end(); ) { if ((*citr)->isRequestCurrent(_pager->_frameNumber)) { ++citr; } else { (*citr)->invalidate(); OSG_INFO<<"DatabasePager::RequestQueue::pruneOldRequestsAndCheckIfEmpty(): Pruning "<<(*citr)<_frameNumber; if (selected_itr != _requestList.end()) { databaseRequest = *selected_itr; databaseRequest->_requestQueue = 0; _requestList.erase(selected_itr); --_size; OSG_INFO<<" DatabasePager::RequestQueue::takeFirst() Found DatabaseRequest size()="<<_requestList.size()<delta_s(_tickSinceStartOfIteration, osg::Timer::instance()->tick()); } void DatabasePager::DatabaseThread::run() { OSG_INFO<<_name<<": DatabasePager::DatabaseThread::run"< read_queue; osg::ref_ptr out_queue; switch(_mode) { case(HANDLE_ALL_REQUESTS): read_queue = _pager->_fileRequestQueue; break; case(HANDLE_NON_HTTP): read_queue = _pager->_fileRequestQueue; out_queue = _pager->_httpRequestQueue; break; case(HANDLE_ONLY_HTTP): read_queue = _pager->_httpRequestQueue; break; } do { _active = false; _tickSinceStartOfIteration = osg::Timer::instance()->tick(); read_queue->block(); _tickSinceStartOfIteration = osg::Timer::instance()->tick(); _active = true; OSG_INFO<<_name<<": _pager->size()= "<size()<<" to delete = "<_childrenToDeleteList.size()<_deleteRemovedSubgraphsInDatabaseThread/* && !(read_queue->_childrenToDeleteList.empty())*/) { ObjectList deleteList; { OpenThreads::ScopedLock lock(read_queue->_childrenToDeleteListMutex); if (!read_queue->_childrenToDeleteList.empty()) { deleteList.swap(read_queue->_childrenToDeleteList); read_queue->updateBlock(); } } } // // load any subgraphs that are required. // osg::ref_ptr databaseRequest; read_queue->takeFirst(databaseRequest); bool readFromFileCache = false; osg::ref_ptr fileCache = osgDB::Registry::instance()->getFileCache(); osg::ref_ptr fileLocationCallback = osgDB::Registry::instance()->getFileLocationCallback(); if (databaseRequest.valid()) { if (databaseRequest->_loadOptions.valid()) { if (databaseRequest->_loadOptions->getFileCache()) fileCache = databaseRequest->_loadOptions->getFileCache(); if (databaseRequest->_loadOptions->getFileLocationCallback()) fileLocationCallback = databaseRequest->_loadOptions->getFileLocationCallback(); } // disable the FileCache if the fileLocationCallback tells us that it isn't required for this request. if (fileLocationCallback.valid() && !fileLocationCallback->useFileCache()) fileCache = 0; // check if databaseRequest is still relevant if ((_pager->_frameNumber-databaseRequest->_frameNumberLastRequest)<=1) { // now check to see if this request is appropriate for this thread switch(_mode) { case(HANDLE_ALL_REQUESTS): { // do nothing as this thread can handle the load if (fileCache.valid() && fileCache->isFileAppropriateForFileCache(databaseRequest->_fileName)) { if (fileCache->existsInCache(databaseRequest->_fileName)) { readFromFileCache = true; } } break; } case(HANDLE_NON_HTTP): { // check the cache first bool isHighLatencyFileRequest = false; if (fileLocationCallback.valid()) { isHighLatencyFileRequest = fileLocationCallback->fileLocation(databaseRequest->_fileName, databaseRequest->_loadOptions.get()) == FileLocationCallback::REMOTE_FILE; } else if (fileCache.valid() && fileCache->isFileAppropriateForFileCache(databaseRequest->_fileName)) { isHighLatencyFileRequest = true; } if (isHighLatencyFileRequest) { if (fileCache.valid() && fileCache->existsInCache(databaseRequest->_fileName)) { readFromFileCache = true; } else { OSG_INFO<<_name<<": Passing http requests over "<_fileName<add(databaseRequest.get()); databaseRequest = 0; } } break; } case(HANDLE_ONLY_HTTP): { // accept all requests, as we'll assume only high latency requests will have got here. break; } } } else { databaseRequest = 0; } } if (databaseRequest.valid()) { // 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_NOTICE<<"In DatabasePager thread readNodeFile("<_fileName<<")"<tick(); // assume that readNode is thread safe... ReaderWriter::ReadResult rr = readFromFileCache ? fileCache->readNode(databaseRequest->_fileName, databaseRequest->_loadOptions.get(), false) : Registry::instance()->readNode(databaseRequest->_fileName, databaseRequest->_loadOptions.get(), false); if (rr.validNode()) databaseRequest->_loadedModel = rr.getNode(); if (rr.error()) OSG_WARN<<"Error in reading file "<_fileName<<" : "<_fileName << std::endl; if (databaseRequest->_loadedModel.valid() && fileCache.valid() && fileCache->isFileAppropriateForFileCache(databaseRequest->_fileName) && !readFromFileCache) { fileCache->writeNode(*(databaseRequest->_loadedModel), databaseRequest->_fileName, databaseRequest->_loadOptions.get()); } if ((_pager->_frameNumber-databaseRequest->_frameNumberLastRequest)>1) { OSG_INFO<<_name<<": Warning DatabaseRquest no longer required."<_loadedModel = 0; } // take a refNodePath to ensure that none of the nodes go out of scope while we are using them. osg::RefNodePath refNodePath; if (!databaseRequest->_observerNodePath.getRefNodePath(refNodePath)) { OSG_INFO<<_name<<": Warning node in parental chain has been deleted, discarding load."<_loadedModel = 0; } //OSG_NOTICE<<" node read in "<delta_m(before,osg::Timer::instance()->tick())<<" ms"<_loadedModel.valid()) { databaseRequest->_loadedModel->getBound(); osg::NodePath nodePath; for(osg::RefNodePath::iterator rnp_itr = refNodePath.begin(); rnp_itr != refNodePath.end(); ++rnp_itr) { nodePath.push_back(rnp_itr->get()); } // 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. ActiveGraphicsContexts::iterator itr = _pager->_activeGraphicsContexts.begin(); DataToCompile* dtc = 0; if (itr != _pager->_activeGraphicsContexts.end()) { dtc = &(databaseRequest->_dataToCompileMap[*itr]); ++itr; } // find all the compileable rendering objects DatabasePager::FindCompileableGLObjectsVisitor frov(dtc, _pager->_changeAutoUnRef, _pager->_valueAutoUnRef, _pager->_changeAnisotropy, _pager->_valueAnisotropy, _pager->_drawablePolicy, _pager); // push the soon to be parent on the nodepath of the NodeVisitor so that // during traversal one can test for where it'll be in the overall scene graph for(osg::NodePath::iterator nitr = nodePath.begin(); nitr != nodePath.end(); ++nitr) { frov.pushOntoNodePath(*nitr); } databaseRequest->_loadedModel->accept(frov); if (_pager->_doPreCompile && !dtc->empty()) { if (_pager->_incrementalCompileOperation.valid()) { //OSG_NOTICE<<"Using IncrementalCompileOperation"<_loadedModel.get()); compileSet->_compileCompletedCallback = new DatabasePagerCompileCompletedCallback(_pager, databaseRequest.get()); _pager->_incrementalCompileOperation->add(compileSet); loadedObjectsNeedToBeCompiled = true; } else if (!_pager->_activeGraphicsContexts.empty()) { // copy the objects from the compile list to the other graphics context list. for(; itr != _pager->_activeGraphicsContexts.end(); ++itr) { databaseRequest->_dataToCompileMap[*itr] = *dtc; } } } // move the databaseRequest from the front of the fileRequest to the end of // dataToCompile or dataToMerge lists. if (loadedObjectsNeedToBeCompiled) { if (!_pager->_incrementalCompileOperation) { _pager->_dataToCompileList->add(databaseRequest.get()); } } else { _pager->_dataToMergeList->add(databaseRequest.get()); } } // Prepare and prune the to-be-compiled list here in // the pager thread rather than in the draw or // graphics context thread(s). if (loadedObjectsNeedToBeCompiled) { loadedObjectsNeedToBeCompiled = ! _pager->_dataToCompileList->pruneOldRequestsAndCheckIfEmpty(); } if (loadedObjectsNeedToBeCompiled && !_pager->_activeGraphicsContexts.empty()) { for(ActiveGraphicsContexts::iterator itr = _pager->_activeGraphicsContexts.begin(); itr != _pager->_activeGraphicsContexts.end(); ++itr) { osg::GraphicsContext* gc = osg::GraphicsContext::getCompileContext(*itr); if (gc) { osg::GraphicsThread* gt = gc->getGraphicsThread(); if (gt) { gt->add(new DatabasePager::CompileOperation(_pager)); } else { gc->makeCurrent(); _pager->compileAllGLObjects(*(gc->getState())); gc->releaseContext(); } } } // OSG_NOTICE<<"Done compiling in paging thread"<getOrCreateSharedStateManager(); //if (osgDB::Registry::instance()->getSharedStateManager()) //osgDB::Registry::instance()->setUseObjectCacheHint(true); _fileRequestQueue = new ReadQueue(this,"fileRequestQueue"); _httpRequestQueue = new ReadQueue(this,"httpRequestQueue"); _dataToCompileList = new RequestQueue(this); _dataToMergeList = new RequestQueue(this); setUpThreads( osg::DisplaySettings::instance()->getNumOfDatabaseThreadsHint(), osg::DisplaySettings::instance()->getNumOfHttpDatabaseThreadsHint()); str = getenv("OSG_DATABASE_PAGER_PRIORITY"); if (str) { if (strcmp(str,"DEFAULT")==0) { setSchedulePriority(OpenThreads::Thread::THREAD_PRIORITY_DEFAULT); } else if (strcmp(str,"MIN")==0) { setSchedulePriority(OpenThreads::Thread::THREAD_PRIORITY_MIN); } else if (strcmp(str,"LOW")==0) { setSchedulePriority(OpenThreads::Thread::THREAD_PRIORITY_LOW); } else if (strcmp(str,"NOMINAL")==0) { setSchedulePriority(OpenThreads::Thread::THREAD_PRIORITY_NOMINAL); } else if (strcmp(str,"HIGH")==0) { setSchedulePriority(OpenThreads::Thread::THREAD_PRIORITY_HIGH); } else if (strcmp(str,"MAX")==0) { setSchedulePriority(OpenThreads::Thread::THREAD_PRIORITY_MAX); } } _activePagedLODList = new SetBasedPagedLODList; _inactivePagedLODList = new SetBasedPagedLODList; } DatabasePager::DatabasePager(const DatabasePager& rhs) { //OSG_INFO<<"Constructing DatabasePager(const DatabasePager& )"<clone(); _inactivePagedLODList = rhs._inactivePagedLODList->clone(); #if 1 // need to set the texture object manager to be able to reuse textures osg::Texture::setMinimumNumberOfTextureObjectsToRetainInCache(100); // need to set the display list manager to be able to reuse display lists osg::Drawable::setMinimumNumberOfDisplayListsToRetainInCache(100); #else // need to set the texture object manager to be able to reuse textures osg::Texture::setMinimumNumberOfTextureObjectsToRetainInCache(0); // need to set the display list manager to be able to reuse display lists osg::Drawable::setMinimumNumberOfDisplayListsToRetainInCache(0); #endif // initialize the stats variables resetStats(); } void DatabasePager::setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation* ico) { _incrementalCompileOperation = ico; } DatabasePager::~DatabasePager() { cancel(); } osg::ref_ptr& DatabasePager::prototype() { static osg::ref_ptr s_DatabasePager = new DatabasePager; return s_DatabasePager; } DatabasePager* DatabasePager::create() { return DatabasePager::prototype().valid() ? DatabasePager::prototype()->clone() : new DatabasePager; } void DatabasePager::setUpThreads(unsigned int totalNumThreads, unsigned int numHttpThreads) { _databaseThreads.clear(); unsigned int numGeneralThreads = numHttpThreads < totalNumThreads ? totalNumThreads - numHttpThreads : 1; if (numHttpThreads==0) { for(unsigned int i=0; istartThread(); } return pos; } int DatabasePager::setSchedulePriority(OpenThreads::Thread::ThreadPriority priority) { int result = 0; for(DatabaseThreadList::iterator dt_itr = _databaseThreads.begin(); dt_itr != _databaseThreads.end(); ++dt_itr) { result = (*dt_itr)->setSchedulePriority(priority); } return result; } bool DatabasePager::isRunning() const { for(DatabaseThreadList::const_iterator dt_itr = _databaseThreads.begin(); dt_itr != _databaseThreads.end(); ++dt_itr) { if ((*dt_itr)->isRunning()) return true; } return false; } int DatabasePager::cancel() { int result = 0; for(DatabaseThreadList::iterator dt_itr = _databaseThreads.begin(); dt_itr != _databaseThreads.end(); ++dt_itr) { (*dt_itr)->setDone(true); } // release the frameBlock and _databasePagerThreadBlock in case its holding up thread cancellation. _fileRequestQueue->release(); _httpRequestQueue->release(); for(DatabaseThreadList::iterator dt_itr = _databaseThreads.begin(); dt_itr != _databaseThreads.end(); ++dt_itr) { (*dt_itr)->cancel(); } _done = true; _startThreadCalled = false; //std::cout<<"DatabasePager::~DatabasePager() stopped running"<clear(); _httpRequestQueue->clear(); _dataToCompileList->clear(); _dataToMergeList->clear(); // note, no need to use a mutex as the list is only accessed from the update thread. _activePagedLODList->clear(); _inactivePagedLODList->clear(); // ?? // _activeGraphicsContexts } void DatabasePager::resetStats() { // initialize the stats variables _minimumTimeToMergeTile = DBL_MAX; _maximumTimeToMergeTile = -DBL_MAX; _totalTimeToMergeTiles = 0.0; _numTilesMerges = 0; } bool DatabasePager::getRequestsInProgress() const { if (getFileRequestListSize()>0) return true; if (getDataToCompileListSize()>0) { return true; } if (getDataToMergeListSize()>0) return true; for(DatabaseThreadList::const_iterator itr = _databaseThreads.begin(); itr != _databaseThreads.end(); ++itr) { if ((*itr)->getActive()) return true; } return false; } void DatabasePager::requestNodeFile(const std::string& fileName,osg::Group* group, float priority, const osg::FrameStamp* framestamp, osg::ref_ptr& databaseRequestRef, const osg::Referenced* options) { osgDB::Options* loadOptions = dynamic_cast(const_cast(options)); if (!loadOptions) { loadOptions = Registry::instance()->getOptions(); // OSG_NOTICE<<"Using options from Registry "<getReferenceTime():0.0; int frameNumber = framestamp?framestamp->getFrameNumber():_frameNumber; // #define WITH_REQUESTNODEFILE_TIMING #ifdef WITH_REQUESTNODEFILE_TIMING osg::Timer_t start_tick = osg::Timer::instance()->tick(); static int previousFrame = -1; static double totalTime = 0.0; if (previousFrame!=frameNumber) { OSG_NOTICE<<"requestNodeFiles for "<_requestQueue; if (requestQueue) { OpenThreads::ScopedLock lock(requestQueue->_requestMutex); databaseRequest->_valid = true; databaseRequest->_frameNumberLastRequest = frameNumber; databaseRequest->_timestampLastRequest = timestamp; databaseRequest->_priorityLastRequest = priority; ++(databaseRequest->_numOfRequests); } else { databaseRequest->_valid = true; databaseRequest->_frameNumberLastRequest = frameNumber; databaseRequest->_timestampLastRequest = timestamp; databaseRequest->_priorityLastRequest = priority; ++(databaseRequest->_numOfRequests); } foundEntry = true; if (databaseRequestRef->referenceCount()==1) { OSG_INFO<<"DatabasePager::requestNodeFile("<_valid = true; databaseRequest->_frameNumberFirstRequest = frameNumber; databaseRequest->_timestampFirstRequest = timestamp; databaseRequest->_priorityFirstRequest = priority; databaseRequest->_frameNumberLastRequest = frameNumber; databaseRequest->_timestampLastRequest = timestamp; databaseRequest->_priorityLastRequest = priority; databaseRequest->_observerNodePath.setNodePathTo(group); databaseRequest->_groupForAddingLoadedSubgraph = group; databaseRequest->_loadOptions = loadOptions; databaseRequest->_requestQueue = _fileRequestQueue.get(); _fileRequestQueue->add(databaseRequest); } } } if (!foundEntry) { OSG_INFO<<"In DatabasePager::requestNodeFile("< lock(_fileRequestQueue->_requestMutex); if (!databaseRequestRef.valid() || databaseRequestRef->referenceCount()==1) { osg::ref_ptr databaseRequest = new DatabaseRequest; databaseRequestRef = databaseRequest.get(); databaseRequest->_valid = true; databaseRequest->_fileName = fileName; databaseRequest->_frameNumberFirstRequest = frameNumber; databaseRequest->_timestampFirstRequest = timestamp; databaseRequest->_priorityFirstRequest = priority; databaseRequest->_frameNumberLastRequest = frameNumber; databaseRequest->_timestampLastRequest = timestamp; databaseRequest->_priorityLastRequest = priority; databaseRequest->_observerNodePath.setNodePathTo(group); databaseRequest->_groupForAddingLoadedSubgraph = group; databaseRequest->_loadOptions = loadOptions; databaseRequest->_requestQueue = _fileRequestQueue.get(); _fileRequestQueue->addNoLock(databaseRequest.get()); } } if (!_startThreadCalled) { OpenThreads::ScopedLock lock(_run_mutex); if (!_startThreadCalled) { _startThreadCalled = true; _done = false; OSG_INFO<<"DatabasePager::startThread()"<getNumOfDatabaseThreadsHint(), osg::DisplaySettings::instance()->getNumOfHttpDatabaseThreadsHint()); } for(DatabaseThreadList::const_iterator dt_itr = _databaseThreads.begin(); dt_itr != _databaseThreads.end(); ++dt_itr) { (*dt_itr)->startThread(); } } } #ifdef WITH_REQUESTNODEFILE_TIMING totalTime += osg::Timer::instance()->delta_m(start_tick, osg::Timer::instance()->tick()); #endif } void DatabasePager::signalBeginFrame(const osg::FrameStamp* framestamp) { if (framestamp) { //OSG_INFO << "signalBeginFrame "<getFrameNumber()<<">>>>>>>>>>>>>>>>"<getFrameNumber(); } //else OSG_INFO << "signalBeginFrame >>>>>>>>>>>>>>>>"<updateBlock(); _httpRequestQueue->updateBlock(); } bool DatabasePager::requiresUpdateSceneGraph() const { return !(_dataToMergeList->empty()); } void DatabasePager::updateSceneGraph(const osg::FrameStamp& frameStamp) { #define UPDATE_TIMING 0 #if UPDATE_TIMING osg::ElapsedTime timer; double timeFor_removeExpiredSubgraphs, timeFor_addLoadedDataToSceneGraph; #endif { removeExpiredSubgraphs(frameStamp); #if UPDATE_TIMING timeFor_removeExpiredSubgraphs = timer.elapsedTime_m(); #endif addLoadedDataToSceneGraph(frameStamp); #if UPDATE_TIMING timeFor_addLoadedDataToSceneGraph = timer.elapsedTime_m() - timeFor_removeExpiredSubgraphs; #endif } #if UPDATE_TIMING double elapsedTime = timer.elapsedTime_m(); if (elapsedTime>0.4) { OSG_NOTICE<<"DatabasePager::updateSceneGraph() total time = "<_frameNumberLastRequest<_groupForAddingLoadedSubgraph; if (osgDB::Registry::instance()->getSharedStateManager()) osgDB::Registry::instance()->getSharedStateManager()->share(databaseRequest->_loadedModel.get()); osg::PagedLOD* plod = dynamic_cast(group); if (plod) { plod->setTimeStamp(plod->getNumChildren(), timeStamp); plod->setFrameNumber(plod->getNumChildren(), frameNumber); plod->getDatabaseRequest(plod->getNumChildren()) = 0; } else { osg::ProxyNode* proxyNode = dynamic_cast(group); if (proxyNode) { proxyNode->getDatabaseRequest(proxyNode->getNumChildren()) = 0; } } group->addChild(databaseRequest->_loadedModel.get()); // Check if parent plod was already registered if not start visitor from parent if( plod && !_activePagedLODList->containsPagedLOD( plod ) && !_inactivePagedLODList->containsPagedLOD( plod ) ) { registerPagedLODs(plod, frameNumber); } else { registerPagedLODs(databaseRequest->_loadedModel.get(), frameNumber); } // OSG_NOTICE<<"merged subgraph"<_fileName<<" after "<_numOfRequests<<" requests and time="<<(timeStamp-databaseRequest->_timestampFirstRequest)*1000.0<_timestampFirstRequest; if (timeToMerge<_minimumTimeToMergeTile) _minimumTimeToMergeTile = timeToMerge; if (timeToMerge>_maximumTimeToMergeTile) _maximumTimeToMergeTile = timeToMerge; _totalTimeToMergeTiles += timeToMerge; ++_numTilesMerges; } else { OSG_INFO<<"DatabasePager::addLoadedDataToSceneGraph() node in parental chain deleted, discarding subgaph."<_loadedModel = 0; // OSG_NOTICE<<"curr = "<delta_m(before,mid)<<"ms,\t"<< osg::Timer::instance()->delta_m(mid,last)<<"ms"<< " objects"<tick(); _activePagedLODList->moveInactivePagedLODTo(*_inactivePagedLODList, frameStamp); _inactivePagedLODList->moveActivePagedLODTo(*_activePagedLODList, frameStamp); int inactivePLOD = _inactivePagedLODList->size(); unsigned int numPagedLODs = _activePagedLODList->size() + inactivePLOD; osg::Timer_t end_a_Tick = osg::Timer::instance()->tick(); double time_a = osg::Timer::instance()->delta_m(startTick,end_a_Tick); s_total_iter_stage_a += 1.0; s_total_time_stage_a += time_a; if (s_total_max_stage_a inactivePLOD) { numToPrune = inactivePLOD; } osg::NodeList childrenRemoved; childrenRemoved.reserve(numToPrune); double expiryTime = frameStamp.getReferenceTime() - 0.1; int expiryFrame = frameStamp.getFrameNumber() - 1; if (numToPrune>0) _inactivePagedLODList->removeExpiredChildren(numToPrune, expiryTime, expiryFrame, childrenRemoved); if (numToPrune>0) _activePagedLODList->removeExpiredChildren(numToPrune, expiryTime, expiryFrame, childrenRemoved); osg::Timer_t end_b_Tick = osg::Timer::instance()->tick(); double time_b = osg::Timer::instance()->delta_m(end_a_Tick,end_b_Tick); s_total_iter_stage_b += 1.0; s_total_time_stage_b += time_b; if (s_total_max_stage_b obs_ptr(&plod); _activePagedLODList.insertPagedLOD(obs_ptr); traverse(plod); } DatabasePager::PagedLODList& _activePagedLODList; int _frameNumber; protected: FindPagedLODsVisitor& operator = (const FindPagedLODsVisitor&) { return *this; } }; void DatabasePager::registerPagedLODs(osg::Node* subgraph, int frameNumber) { if (!subgraph) return; FindPagedLODsVisitor fplv(*_activePagedLODList, frameNumber); subgraph->accept(fplv); } bool DatabasePager::requiresCompileGLObjects() const { 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; } DatabasePager::CompileOperation::CompileOperation(osgDB::DatabasePager* databasePager): osg::GraphicsOperation("DatabasePager::CompileOperation",false), _databasePager(databasePager) { } void DatabasePager::CompileOperation::operator () (osg::GraphicsContext* context) { // OSG_NOTICE<<"Background thread compiling"<compileAllGLObjects(*(context->getState()), true); } bool DatabasePager::requiresExternalCompileGLObjects(unsigned int contextID) const { if (_activeGraphicsContexts.count(contextID)==0) return false; return osg::GraphicsContext::getCompileContext(contextID)==0; } void DatabasePager::compileCompleted(DatabaseRequest* databaseRequest) { //OSG_NOTICE<<"DatabasePager::compileCompleted("<add(databaseRequest); } void DatabasePager::compileAllGLObjects(osg::State& state, bool doFlush) { double availableTime = DBL_MAX; compileGLObjects(state, availableTime, doFlush); } void DatabasePager::compileGLObjects(osg::State& state, double& availableTime, bool doFlush) { // OSG_NOTICE<<"DatabasePager::compileGLObjects "<<_frameNumber<getSharedStateManager(); osg::RenderInfo renderInfo; renderInfo.setState(&state); if (availableTime>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; _dataToCompileList->takeFirst(databaseRequest); unsigned int numObjectsCompiled = 0; // 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() && databaseRequest->valid() && (compileAll || (elapsedTime_dataToCompileMap; DataToCompile& dtc = dcm[state.getContextID()]; if (!dtc._textures.empty() && (elapsedTime+estimatedTextureDuration)get(), state.getContextID()) || (sharedManager && sharedManager->isShared(itr->get()))) { elapsedTime = timer.delta_s(start_tick,timer.tick()); continue; } double startCompileTime = timer.delta_s(start_tick,timer.tick()); (*itr)->apply(state); #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE) GLint p; glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_RESIDENT, &p); #endif elapsedTime = timer.delta_s(start_tick,timer.tick()); // estimate the duration of the compile based on current compile duration. estimatedTextureDuration = (elapsedTime-startCompileTime); ++numObjectsCompiled; } if (osg::getNotifyLevel() >= osg::DEBUG_INFO && numObjectsCompiled > objTemp) OSG_NOTICE<< _frameNumber << " compiled " << numObjectsCompiled - objTemp << " StateSets" << std::endl; // remove the compiled statesets from the list. textures.erase(textures.begin(),itr); } if (!dtc._drawables.empty() && (compileAll || ((elapsedTime+estimatedDrawableDuration)get(), state.getContextID())) { elapsedTime = timer.delta_s(start_tick,timer.tick()); continue; } double startCompileTime = timer.delta_s(start_tick,timer.tick()); (*itr)->compileGLObjects(renderInfo); elapsedTime = timer.delta_s(start_tick,timer.tick()); // estimate the duration of the compile based on current compile duration. estimatedDrawableDuration = (elapsedTime-startCompileTime); ++numObjectsCompiled; } if (osg::getNotifyLevel() >= osg::DEBUG_INFO && numObjectsCompiled > objTemp) { OSG_INFO<< _frameNumber << " compiled " << numObjectsCompiled - objTemp << " Drawables" << std::endl; } // remove the compiled drawables from the list. dwlist.erase(dwlist.begin(),itr); } //OSG_INFO<<"Checking if compiled"<second.empty())) allCompiled=false; } if (numObjectsCompiled > 0) OSG_NOTICE<< "Framenumber "<<_frameNumber << ": compiled " << numObjectsCompiled << " objects" << std::endl; if (allCompiled) { if (doFlush) { glFlush(); } // we've compiled all of the current databaseRequest so we can now pop it off the // to compile list and place it on the merge list. OSG_INFO<<"All compiled"<add(databaseRequest.get()); databaseRequest = 0; _dataToCompileList->takeFirst(databaseRequest); } else { OSG_INFO<<"Not all compiled"<add(databaseRequest.get()); databaseRequest = 0; } elapsedTime = timer.delta_s(start_tick,timer.tick()); } availableTime -= elapsedTime; //OSG_NOTICE<<"elapsedTime="<