/* -*-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_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_e11(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_MAX_PAGEDLOD ","Set the target maximum number of PagedLOD to maintain."); static osg::ApplicationUsageProxy DatabasePager_e12(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_ASSIGN_PBO_TO_IMAGES ","Set whether PixelBufferObjects should be assigned to Images to aid download to the GPU."); // 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; }; void DatabasePager::compileCompleted(DatabaseRequest* databaseRequest) { //OSG_NOTICE<<"DatabasePager::compileCompleted("<remove(databaseRequest); _dataToMergeList->add(databaseRequest); } // This class is a helper for the management of SetBasedPagedLODList. class DatabasePager::ExpirePagedLODsVisitor : public osg::NodeVisitor { public: ExpirePagedLODsVisitor(): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) { } META_NodeVisitor("osgDB","ExpirePagedLODsVisitor") virtual void apply(osg::PagedLOD& plod) { _childPagedLODs.insert(&plod); markRequestsExpired(&plod); traverse(plod); } // Remove expired children from a PagedLOD. On return // removedChildren contains the nodes removed by the call to // PagedLOD::removeExpiredChildren, and the _childPagedLODs member // contains all the PagedLOD objects found in those children's // subgraphs. bool removeExpiredChildrenAndFindPagedLODs(osg::PagedLOD* plod, double expiryTime, unsigned int expiryFrame, osg::NodeList& removedChildren) { size_t sizeBefore = removedChildren.size(); plod->removeExpiredChildren(expiryTime, expiryFrame, removedChildren); for(size_t i = sizeBefore; iaccept(*this); } return sizeBefore!=removedChildren.size(); } typedef std::set > PagedLODset; PagedLODset _childPagedLODs; private: void markRequestsExpired(osg::PagedLOD* plod) { unsigned numFiles = plod->getNumFileNames(); for (unsigned i = 0; i < numFiles; ++i) { DatabasePager::DatabaseRequest* request = dynamic_cast(plod->getDatabaseRequest(i).get()); if (request) request->_groupExpired = true; } } }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // 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 removeExpiredChildren( int numberChildrenToRemove, double expiryTime, unsigned int expiryFrame, DatabasePager::ObjectList& childrenRemoved, bool visitActive) { int leftToRemove = numberChildrenToRemove; for(PagedLODs::iterator itr = _pagedLODs.begin(); itr!=_pagedLODs.end() && leftToRemove > 0; ) { osg::ref_ptr plod; if (itr->lock(plod)) { bool plodActive = expiryFrame < plod->getFrameNumberOfLastTraversal(); if (visitActive==plodActive) // true if (visitActive && plodActive) OR (!visitActive &&!plodActive) { DatabasePager::ExpirePagedLODsVisitor expirePagedLODsVisitor; osg::NodeList expiredChildren; // expired PagedLODs expirePagedLODsVisitor.removeExpiredChildrenAndFindPagedLODs( plod.get(), expiryTime, expiryFrame, expiredChildren); // Clear any expired PagedLODs out of the set for (DatabasePager::ExpirePagedLODsVisitor::PagedLODset::iterator citr = expirePagedLODsVisitor._childPagedLODs.begin(), end = expirePagedLODsVisitor._childPagedLODs.end(); citr != end; ++citr) { osg::observer_ptr clod(*citr); // This child PagedLOD cannot be equal to the // PagedLOD pointed to by itr because it must be // in itr's subgraph. Therefore erasing it doesn't // invalidate itr. if (_pagedLODs.erase(clod) > 0) leftToRemove--; } std::copy(expiredChildren.begin(), expiredChildren.end(), std::back_inserter(childrenRemoved)); } // advance the iterator to the next element ++itr; } else { _pagedLODs.erase(itr++); // numberChildrenToRemove includes possibly expired // observer pointers. leftToRemove--; 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 osgUtil::StateToCompile { public: FindCompileableGLObjectsVisitor(const DatabasePager* pager, osg::Object* markerObject): osgUtil::StateToCompile(osgUtil::GLObjectsVisitor::COMPILE_DISPLAY_LISTS|osgUtil::GLObjectsVisitor::COMPILE_STATE_ATTRIBUTES, markerObject), _pager(pager), _changeAutoUnRef(false), _valueAutoUnRef(false), _changeAnisotropy(false), _valueAnisotropy(1.0) { _assignPBOToImages = _pager->_assignPBOToImages; _changeAutoUnRef = _pager->_changeAutoUnRef; _valueAutoUnRef = _pager->_valueAutoUnRef; _changeAnisotropy = _pager->_changeAnisotropy; _valueAnisotropy = _pager->_valueAnisotropy; switch(_pager->_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"<getBuildKdTreesHint()==osgDB::Options::BUILD_KDTREES && osgDB::Registry::instance()->getKdTreeBuilder()) { _kdTreeBuilder = osgDB::Registry::instance()->getKdTreeBuilder()->clone(); } } META_NodeVisitor("osgDB","FindCompileableGLObjectsVisitor") bool requiresCompilation() const { return !empty(); } virtual void apply(osg::Drawable& drawable) { if (_kdTreeBuilder.valid() && _markerObject!=drawable.getUserData()) { drawable.accept(*_kdTreeBuilder); } StateToCompile::apply(drawable); if (drawable.getUserData()==0) { drawable.setUserData(_markerObject.get()); } } void apply(osg::Texture& texture) { // apply any changes if the texture is not static. if (texture.getDataVariance()!=osg::Object::STATIC && _markerObject!=texture.getUserData()) { if (_changeAutoUnRef) { texture.setUnRefImageDataAfterApply(_valueAutoUnRef); } if ((_changeAnisotropy && texture.getMaxAnisotropy() != _valueAnisotropy)) { texture.setMaxAnisotropy(_valueAnisotropy); } } StateToCompile::apply(texture); if (texture.getUserData()==0) { texture.setUserData(_markerObject.get()); } } const DatabasePager* _pager; bool _changeAutoUnRef; bool _valueAutoUnRef; bool _changeAnisotropy; float _valueAnisotropy; 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()."<get()); } } void DatabasePager::RequestQueue::invalidate(DatabaseRequest* dr) { // OSG_NOTICE<<"DatabasePager::RequestQueue::invalidate(DatabaseRequest* dr) dr->_compileSet="<_compileSet.get()< compileSet; if (dr->_compileSet.lock(compileSet) && _pager->getIncrementalCompileOperation()) { _pager->getIncrementalCompileOperation()->remove(compileSet.get()); } dr->invalidate(); } bool DatabasePager::RequestQueue::pruneOldRequestsAndCheckIfEmpty() { OpenThreads::ScopedLock lock(_requestMutex); unsigned int frameNumber = _pager->_frameNumber; if (_frameNumberLastPruned != frameNumber) { for(RequestQueue::RequestList::iterator citr = _requestList.begin(); citr != _requestList.end(); ) { OpenThreads::ScopedLock drLock(_pager->_dr_mutex); if ((*citr)->isRequestCurrent(frameNumber)) { ++citr; } else { invalidate(citr->get()); OSG_INFO<<"DatabasePager::RequestQueue::pruneOldRequestsAndCheckIfEmpty(): Pruning "<<(*citr)< lock(_requestMutex); return _requestList.empty(); } unsigned int DatabasePager::RequestQueue::size() { OpenThreads::ScopedLock lock(_requestMutex); return _requestList.size(); } void DatabasePager::RequestQueue::clear() { OpenThreads::ScopedLock lock(_requestMutex); for(RequestList::iterator citr = _requestList.begin(); citr != _requestList.end(); ++citr) { OpenThreads::ScopedLock drLock(_pager->_dr_mutex); invalidate(citr->get()); } _requestList.clear(); _frameNumberLastPruned = _pager->_frameNumber; updateBlock(); } void DatabasePager::RequestQueue::add(DatabasePager::DatabaseRequest* databaseRequest) { OpenThreads::ScopedLock lock(_requestMutex); addNoLock(databaseRequest); } void DatabasePager::RequestQueue::remove(DatabasePager::DatabaseRequest* databaseRequest) { // OSG_NOTICE<<"DatabasePager::RequestQueue::remove(DatabaseRequest* databaseRequest)"< lock(_requestMutex); for(RequestList::iterator citr = _requestList.begin(); citr != _requestList.end(); ++citr) { if (citr->get()==databaseRequest) { // OSG_NOTICE<<" done remove(DatabaseRequest* databaseRequest)"< lock(_requestMutex); _requestList.swap(requestList); } void DatabasePager::RequestQueue::takeFirst(osg::ref_ptr& databaseRequest) { OpenThreads::ScopedLock lock(_requestMutex); if (!_requestList.empty()) { DatabasePager::SortFileRequestFunctor highPriority; RequestQueue::RequestList::iterator selected_itr = _requestList.end(); int frameNumber = _pager->_frameNumber; for(RequestQueue::RequestList::iterator citr = _requestList.begin(); citr != _requestList.end(); ) { OpenThreads::ScopedLock drLock(_pager->_dr_mutex); if ((*citr)->isRequestCurrent(frameNumber)) { if (selected_itr==_requestList.end() || highPriority(*citr, *selected_itr)) { selected_itr = citr; } ++citr; } else { invalidate(citr->get()); OSG_INFO<<"DatabasePager::RequestQueue::takeFirst(): Pruning "<<(*citr)< 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; read_queue->block(); if (_done) { break; } _active = true; OSG_INFO<<_name<<": _pager->size()= "<size()<<" to delete = "<_childrenToDeleteList.size()<_deleteRemovedSubgraphsInDatabaseThread/* && !(read_queue->_childrenToDeleteList.empty())*/) { ObjectList deleteList; { // Don't hold lock during destruction of deleteList OpenThreads::ScopedLock lock(read_queue->_requestMutex); 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(); osg::ref_ptr dr_loadOptions; std::string fileName; int frameNumberLastRequest = 0; bool cacheNodes = false; if (databaseRequest.valid()) { { OpenThreads::ScopedLock drLock(_pager->_dr_mutex); dr_loadOptions = databaseRequest->_loadOptions.valid() ? databaseRequest->_loadOptions->cloneOptions() : new osgDB::Options; dr_loadOptions->setTerrain(databaseRequest->_terrain); fileName = databaseRequest->_fileName; frameNumberLastRequest = databaseRequest->_frameNumberLastRequest; } cacheNodes = (dr_loadOptions->getObjectCacheHint() & osgDB::Options::CACHE_NODES)!=0; if (cacheNodes) { // check the object cache to see if the file we want has already been loaded. osg::ref_ptr objectFromCache = osgDB::Registry::instance()->getRefFromObjectCache(fileName); osg::Node* modelFromCache = dynamic_cast(objectFromCache.get()); if (modelFromCache) { // OSG_NOTICE<<"Found object in cache "< drLock(_pager->_dr_mutex); databaseRequest->_loadedModel = modelFromCache; } // move the request to the dataToMerge list so it can be merged during the update phase of the frame. { OpenThreads::ScopedLock listLock( _pager->_dataToMergeList->_requestMutex); _pager->_dataToMergeList->addNoLock(databaseRequest.get()); databaseRequest = 0; } // skip the rest of the do/while loop as we have done all the processing we need to do. continue; } // need to disable any attempt to use the cache when loading as we're handle this ourselves to avoid threading conflicts dr_loadOptions->setObjectCacheHint(static_cast(dr_loadOptions->getObjectCacheHint() & ~osgDB::Options::CACHE_NODES)); } if (dr_loadOptions->getFileCache()) fileCache = dr_loadOptions->getFileCache(); if (dr_loadOptions->getFileLocationCallback()) fileLocationCallback = dr_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-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(fileName)) { if (fileCache->existsInCache(fileName)) { readFromFileCache = true; } } break; } case(HANDLE_NON_HTTP): { // check the cache first bool isHighLatencyFileRequest = false; if (fileLocationCallback.valid()) { isHighLatencyFileRequest = fileLocationCallback->fileLocation(fileName, dr_loadOptions.get()) == FileLocationCallback::REMOTE_FILE; } else if (fileCache.valid() && fileCache->isFileAppropriateForFileCache(fileName)) { isHighLatencyFileRequest = true; } if (isHighLatencyFileRequest) { if (fileCache.valid() && fileCache->existsInCache(fileName)) { readFromFileCache = true; } else { OSG_INFO<<_name<<": Passing http requests over "<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(fileName, dr_loadOptions.get(), false) : Registry::instance()->readNode(fileName, dr_loadOptions.get(), false); osg::ref_ptr loadedModel; if (rr.validNode()) loadedModel = rr.getNode(); if (rr.error()) OSG_WARN<<"Error in reading file "<isFileAppropriateForFileCache(fileName) && !readFromFileCache) { fileCache->writeNode(*(loadedModel), fileName, dr_loadOptions.get()); } { OpenThreads::ScopedLock drLock(_pager->_dr_mutex); if ((_pager->_frameNumber-databaseRequest->_frameNumberLastRequest)>1) { OSG_INFO<<_name<<": Warning DatabaseRquest no longer required."<delta_m(before,osg::Timer::instance()->tick())<<" ms"<getBound(); // find all the compileable rendering objects DatabasePager::FindCompileableGLObjectsVisitor stateToCompile(_pager, _pager->getMarkerObject()); loadedModel->accept(stateToCompile); bool loadedObjectsNeedToBeCompiled = _pager->_doPreCompile && _pager->_incrementalCompileOperation.valid() && _pager->_incrementalCompileOperation->requiresCompile(stateToCompile); // move the databaseRequest from the front of the fileRequest to the end of // dataToCompile or dataToMerge lists. osg::ref_ptr compileSet = 0; if (loadedObjectsNeedToBeCompiled) { // OSG_NOTICE<<"Using IncrementalCompileOperation"<buildCompileMap(_pager->_incrementalCompileOperation->getContextSet(), stateToCompile); compileSet->_compileCompletedCallback = new DatabasePagerCompileCompletedCallback(_pager, databaseRequest.get()); _pager->_incrementalCompileOperation->add(compileSet.get(), false); } { OpenThreads::ScopedLock drLock(_pager->_dr_mutex); databaseRequest->_loadedModel = loadedModel; databaseRequest->_compileSet = compileSet; databaseRequest->_insertLoadedSubgraphIntoObjectCache = cacheNodes; } // Dereference the databaseRequest while the queue is // locked. This prevents the request from being // deleted at an unpredictable time within // addLoadedDataToSceneGraph. if (loadedObjectsNeedToBeCompiled) { OpenThreads::ScopedLock listLock( _pager->_dataToCompileList->_requestMutex); _pager->_dataToCompileList->addNoLock(databaseRequest.get()); databaseRequest = 0; } else { OpenThreads::ScopedLock listLock( _pager->_dataToMergeList->_requestMutex); _pager->_dataToMergeList->addNoLock(databaseRequest.get()); databaseRequest = 0; } } // _pager->_dataToCompileList->pruneOldRequestsAndCheckIfEmpty(); } else { OpenThreads::Thread::YieldCurrentThread(); } // 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 circumstances - of which there is no particular pattern. YieldCurrentThread(); firstTime = false; } } while (!testCancel() && !_done); } DatabasePager::DatabasePager() { //OSG_INFO<<"Constructing DatabasePager()"<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; } DatabasePager::DatabasePager(const DatabasePager& rhs) { //OSG_INFO<<"Constructing DatabasePager(const DatabasePager& )"<setName("HasBeenByStateToCompileProcessedMarker"); _startThreadCalled = false; _done = false; _acceptNewRequests = true; _databasePagerThreadPaused = false; _numFramesActive = 0; _frameNumber.exchange(0); _drawablePolicy = rhs._drawablePolicy; _assignPBOToImages = rhs._assignPBOToImages; _changeAutoUnRef = rhs._changeAutoUnRef; _valueAutoUnRef = rhs._valueAutoUnRef; _changeAnisotropy = rhs._changeAnisotropy; _valueAnisotropy = rhs._valueAnisotropy; _deleteRemovedSubgraphsInDatabaseThread = rhs._deleteRemovedSubgraphsInDatabaseThread; _targetMaximumNumberOfPageLOD = rhs._targetMaximumNumberOfPageLOD; _doPreCompile = rhs._doPreCompile; _fileRequestQueue = new ReadQueue(this,"fileRequestQueue"); _httpRequestQueue = new ReadQueue(this,"httpRequestQueue"); _dataToCompileList = new RequestQueue(this); _dataToMergeList = new RequestQueue(this); for(DatabaseThreadList::const_iterator dt_itr = rhs._databaseThreads.begin(); dt_itr != rhs._databaseThreads.end(); ++dt_itr) { _databaseThreads.push_back(new DatabaseThread(**dt_itr,this)); } _activePagedLODList = rhs._activePagedLODList->clone(); #if 1 // need to set the display list manager to be able to reuse display lists osg::Drawable::setMinimumNumberOfDisplayListsToRetainInCache(100); #else // 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; if (_incrementalCompileOperation.valid()) _markerObject = _incrementalCompileOperation->getMarkerObject(); } DatabasePager::~DatabasePager() { // cancel the threads cancel(); // destruct all the threads _databaseThreads.clear(); // destruct all the queues _fileRequestQueue = 0; _httpRequestQueue = 0; _dataToCompileList = 0; _dataToMergeList = 0; // remove reference to the ICO _incrementalCompileOperation = 0; //_activePagedLODList; //_inactivePagedLODList; } 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 queue blocks in case they are 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; return result; } void DatabasePager::clear() { _fileRequestQueue->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(); // ?? // _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::NodePath& nodePath, 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(); } else { // OSG_NOTICE<<"options from requestNodeFile "<asGroup(); if (!group) { OSG_NOTICE<<"Warning: DatabasePager::requestNodeFile(..) passed NodePath without group as last node in path, so nowhere to attach new subgraph to."<asTerrain()) terrain = *itr; } double timestamp = framestamp?framestamp->getReferenceTime():0.0; unsigned int frameNumber = framestamp?framestamp->getFrameNumber():static_cast(_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 "<_valid = true; databaseRequest->_frameNumberLastRequest = frameNumber; databaseRequest->_timestampLastRequest = timestamp; databaseRequest->_priorityLastRequest = priority; ++(databaseRequest->_numOfRequests); foundEntry = true; if (databaseRequestRef->referenceCount()==1) { OSG_INFO<<"DatabasePager::requestNodeFile("<_frameNumberLastRequest = frameNumber; databaseRequest->_timestampLastRequest = timestamp; databaseRequest->_priorityLastRequest = priority; databaseRequest->_group = group; databaseRequest->_terrain = terrain; databaseRequest->_loadOptions = loadOptions; databaseRequest->_insertLoadedSubgraphIntoObjectCache = false; requeue = true; } } } if (requeue) _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->_group = group; databaseRequest->_terrain = terrain; databaseRequest->_loadOptions = loadOptions; databaseRequest->_insertLoadedSubgraphIntoObjectCache = false; _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 0 OSG_NOTICE<<"DatabasePager : _fileRequestQueue->size()="<<_fileRequestQueue->size() <<", _httpRequestQueue->size()= "<<_httpRequestQueue->size() <<", _dataToCompileList->size()= "<<_dataToCompileList->size() <<", _dataToMergeList->size()= "<<_dataToMergeList->size()<pruneOldRequestsAndCheckIfEmpty(); //OSG_INFO << "signalBeginFrame "<getFrameNumber()<<">>>>>>>>>>>>>>>>"<getFrameNumber()); } //else OSG_INFO << "signalBeginFrame >>>>>>>>>>>>>>>>"< lock(_fileRequestQueue->_requestMutex); _fileRequestQueue->updateBlock(); } { OpenThreads::ScopedLock lock(_httpRequestQueue->_requestMutex); _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 = "<_fileName<<") into ObjectCache"<addEntryToObjectCache( databaseRequest->_fileName, databaseRequest->_loadedModel.get(), timeStamp); } // 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(); // numPagedLODs >= actual number of PagedLODs. There can be // invalid observer pointers in _activePagedLODList. unsigned int numPagedLODs = _activePagedLODList->size(); 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_a0) _activePagedLODList->removeExpiredChildren( numToPrune, expiryTime, expiryFrame, childrenRemoved, false); numToPrune = _activePagedLODList->size() - _targetMaximumNumberOfPageLOD; if (numToPrune>0) _activePagedLODList->removeExpiredChildren( numToPrune, expiryTime, expiryFrame, childrenRemoved, true); 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; unsigned int _frameNumber; protected: FindPagedLODsVisitor& operator = (const FindPagedLODsVisitor&) { return *this; } }; void DatabasePager::registerPagedLODs(osg::Node* subgraph, unsigned int frameNumber) { if (!subgraph) return; FindPagedLODsVisitor fplv(*_activePagedLODList, frameNumber); subgraph->accept(fplv); }