/* -*-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 #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."); // 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); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // 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::ReaderWriter::Options::BUILD_KDTREES && osgDB::Registry::instance()->getKdTreeBuilder()) { _kdTreeBuilder = osgDB::Registry::instance()->getKdTreeBuilder()->clone(); } } 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)) { compileStateSet = true; if (osg::getNotifyLevel() >= osg::DEBUG_INFO) { osg::notify(osg::DEBUG_INFO) <<"Found compilable texture " << texture << " "; osg::Image* image = texture->getImage(0); if (image) osg::notify(osg::DEBUG_INFO) << image->getFileName(); osg::notify(osg::DEBUG_INFO) << std:: endl; } break; } } } if (compileStateSet) { _dataToCompile.first.insert(stateset); } } } 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::notify(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::notify(osg::NOTICE)<<"USE_VERTEX_BUFFER_OBJECTS"<setUseDisplayList(false); drawable->setUseVertexBufferObjects(false); // osg::notify(osg::NOTICE)<<"USE_VERTEX_ARRAYS"<getUseDisplayList() && !_pager->isCompiled(drawable)) { // osg::notify(osg::NOTICE)<<" Found compilable drawable"< > _textureSet; std::set > _drawableSet; osg::ref_ptr _kdTreeBuilder; }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // 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); } }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // ReadQueue // void DatabasePager::RequestQueue::sort() { std::sort(_requestList.begin(),_requestList.end(),SortFileRequestFunctor()); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // ReadQueue // DatabasePager::ReadQueue::ReadQueue(DatabasePager* pager, const std::string& name): _pager(pager), _name(name) { _block = new osg::RefBlock; } void DatabasePager::ReadQueue::clear() { OpenThreads::ScopedLock lock(_requestMutex); for(RequestList::iterator citr = _requestList.begin(); citr != _requestList.end(); ++citr) { (*citr)->_loadedModel = 0; (*citr)->_requestQueue = 0; } _requestList.clear(); updateBlock(); } void DatabasePager::ReadQueue::add(DatabasePager::DatabaseRequest* databaseRequest) { OpenThreads::ScopedLock lock(_requestMutex); _requestList.push_back(databaseRequest); databaseRequest->_requestQueue = this; updateBlock(); } void DatabasePager::ReadQueue::takeFirst(osg::ref_ptr& databaseRequest) { OpenThreads::ScopedLock lock(_requestMutex); if (!_requestList.empty()) { sort(); databaseRequest = _requestList.front(); databaseRequest->_requestQueue = 0; _requestList.erase(_requestList.begin()); updateBlock(); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // DatabaseThread // DatabasePager::DatabaseThread::DatabaseThread(DatabasePager* pager, Mode mode, const std::string& name): _done(false), _pager(pager), _mode(mode), _name(name) { } DatabasePager::DatabaseThread::DatabaseThread(const DatabaseThread& dt, DatabasePager* pager): _done(false), _pager(pager), _mode(dt._mode), _name(dt._name) { } DatabasePager::DatabaseThread::~DatabaseThread() { cancel(); } int DatabasePager::DatabaseThread::cancel() { int result = 0; if( isRunning() ) { _done = true; switch(_mode) { case(HANDLE_ALL_REQUESTS): _pager->_fileRequestQueue->release(); break; case(HANDLE_NON_HTTP): _pager->_fileRequestQueue->release(); break; case(HANDLE_ONLY_HTTP): _pager->_httpRequestQueue->release(); break; } // release the frameBlock and _databasePagerThreadBlock in case its holding up thread cancellation. // _databasePagerThreadBlock->release(); // then wait for the the thread to stop running. while(isRunning()) { // commenting out debug info as it was cashing crash on exit, presumable // due to osg::notify or std::cout destructing earlier than this destructor. // osg::notify(osg::DEBUG_INFO)<<"Waiting for DatabasePager to cancel"< DatabasePager::DatabaseThread::dpReadRefNodeFile(const std::string& fileName,const ReaderWriter::Options* options) { ReaderWriter::ReadResult rr = Registry::instance()->getReadFileCallback() ? Registry::instance()->getReadFileCallback()->readNode(fileName,options) : Registry::instance()->readNodeImplementation(fileName,options); if (rr.validNode()) return rr.getNode(); if (rr.error()) osg::notify(osg::WARN) << rr.message() << std::endl; return 0; } void DatabasePager::DatabaseThread::run() { osg::notify(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; } //Getting CURL Environment Variables (If found rewrite OSG Options) std::string cacheFilePath; const char* fileCachePath = getenv("OSG_FILE_CACHE"); if (fileCachePath) //Env Cache Directory cacheFilePath = std::string(fileCachePath); do { read_queue->block(); osg::notify(osg::INFO)<<_name<<": _pager->_requestList.size()= "<_requestList.size()<<" to delete = "<_childrenToDeleteList.size()<_deleteRemovedSubgraphsInDatabaseThread && !(read_queue->_childrenToDeleteList.empty())) { ObjectList deleteList; { OpenThreads::ScopedLock lock(read_queue->_childrenToDeleteListMutex); deleteList.swap(read_queue->_childrenToDeleteList); read_queue->updateBlock(); } } // // load any subgraphs that are required. // osg::ref_ptr databaseRequest; read_queue->takeFirst(databaseRequest); if (databaseRequest.valid()) { // 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 (osgDB::containsServerAddress(databaseRequest->_fileName)) { std::string cacheFileName; if (!cacheFilePath.empty()) { cacheFileName = cacheFilePath + "/" + osgDB::getServerAddress(databaseRequest->_fileName) + "/" + osgDB::getServerFileName(databaseRequest->_fileName); std::string path = osgDB::getFilePath(cacheFileName); if (!osgDB::fileExists(path)) { cacheFileName.clear(); } } if (!cacheFilePath.empty() && osgDB::fileExists(cacheFileName)) { osg::notify(osg::INFO)<<_name<<": Reading cache file " << cacheFileName <<", previous path "<_fileName)<_fileName = cacheFileName; } } break; case(HANDLE_NON_HTTP): // check the cache first if (osgDB::containsServerAddress(databaseRequest->_fileName)) { std::string cacheFileName; if (!cacheFilePath.empty()) { cacheFileName = cacheFilePath + "/" + osgDB::getServerAddress(databaseRequest->_fileName) + "/" + osgDB::getServerFileName(databaseRequest->_fileName); std::string path = osgDB::getFilePath(cacheFileName); if (!osgDB::fileExists(path)) { cacheFileName.clear(); } } if (!cacheFilePath.empty() && osgDB::fileExists(cacheFileName)) { osg::notify(osg::INFO)<<_name<<": Reading cache file " << cacheFileName <<", previous path "<_fileName)<_fileName = cacheFileName; } else { osg::notify(osg::INFO)<<_name<<": Passing http requests over "<_fileName<<" cacheFileName="<add(databaseRequest.get()); databaseRequest = 0; } } break; case(HANDLE_ONLY_HTTP): // make sure the request is a http request if (!osgDB::containsServerAddress(databaseRequest->_fileName)) { osg::notify(osg::NOTICE)<<_name<<": Help we have request we shouldn't have "<_fileName<_fileName<<")"<tick(); bool serialize_readNodeFile = false; 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 = dpReadRefNodeFile(databaseRequest->_fileName, databaseRequest->_loadOptions.get()); } else { // assume that we only have one DatabasePager, or that readNodeFile is thread safe... databaseRequest->_loadedModel = dpReadRefNodeFile(databaseRequest->_fileName, databaseRequest->_loadOptions.get()); } if (databaseRequest->_loadedModel.valid() && osgDB::containsServerAddress(databaseRequest->_fileName) && !cacheFilePath.empty()) { std::string cacheFileName = cacheFilePath + "/" + osgDB::getServerAddress(databaseRequest->_fileName) + "/" + osgDB::getServerFileName(databaseRequest->_fileName); std::string path = osgDB::getFilePath(cacheFileName); if (!osgDB::fileExists(path) && !osgDB::makeDirectory(path)) { cacheFileName.clear(); } if (!cacheFileName.empty() && osgDB::fileExists(cacheFileName)) { osg::notify(osg::NOTICE)<<_name<<": Warning, file already in cache file, shouldn't have needed to be reloaded. cache file=" << cacheFileName <<", previous path "<_fileName)<_loadedModel), cacheFileName, databaseRequest->_loadOptions.get()); } } if ((_pager->_frameNumber-databaseRequest->_frameNumberLastRequest)>1) { osg::notify(osg::INFO)<<_name<<": Warning DatabaseRquest no longer required."<_loadedModel = 0; } osg::ref_ptr groupForAddingLoadedSubgraph = databaseRequest->_groupForAddingLoadedSubgraph.get(); if (!groupForAddingLoadedSubgraph) { osg::notify(osg::INFO)<<_name<<": Warning parent of loaded subgraph, deleted."<_loadedModel = 0; } //osg::notify(osg::NOTICE)<<" node read in "<delta_m(before,osg::Timer::instance()->tick())<<" ms"<_loadedModel.valid()) { osg::NodePath nodePath; osg::NodePathList nodePathList = groupForAddingLoadedSubgraph->getParentalNodePaths(); if (!nodePathList.empty()) nodePath = nodePathList.front(); nodePath.push_back(groupForAddingLoadedSubgraph.get()); if (_pager->_doPreCompile && !_pager->_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 = _pager->_activeGraphicsContexts.begin(); DataToCompile& 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 (!dtc.first.empty() || !dtc.second.empty()) { loadedObjectsNeedToBeCompiled = true; // copy the objects from the compile list to the other graphics context list. for(; itr != _pager->_activeGraphicsContexts.end(); ++itr) { databaseRequest->_dataToCompileMap[*itr] = dtc; } } } else { // check to see if we need to run the KdTreeBuilder if (osgDB::Registry::instance()->getBuildKdTreesHint()==osgDB::ReaderWriter::Options::BUILD_KDTREES && osgDB::Registry::instance()->getKdTreeBuilder()) { //osg::Timer_t before = osg::Timer::instance()->tick(); //osg::notify(osg::NOTICE)<<"osgTerrain::GeometryTechnique::build kd tree"< builder = osgDB::Registry::instance()->getKdTreeBuilder()->clone(); for(osg::NodePath::iterator nitr = nodePath.begin(); nitr != nodePath.end(); ++nitr) { builder->pushOntoNodePath(*nitr); } databaseRequest->_loadedModel->accept(*builder); //osg::Timer_t after = osg::Timer::instance()->tick(); //osg::notify(osg::NOTICE)<<"KdTree build time "<delta_m(before, after)< lock(_pager->_dataToCompileList->_requestMutex); databaseRequest->_requestQueue = _pager->_dataToCompileList.get(); _pager->_dataToCompileList->_requestList.push_back(databaseRequest); } else { OpenThreads::ScopedLock lock(_pager->_dataToMergeList->_requestMutex); databaseRequest->_requestQueue = _pager->_dataToMergeList.get(); _pager->_dataToMergeList->_requestList.push_back(databaseRequest); } } // 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) { OpenThreads::ScopedLock lock(_pager->_dataToCompileList->_requestMutex); _pager->_dataToCompileList->sort(); // Prune all the old entries. RequestQueue::RequestList::iterator tooOld = std::find_if(_pager->_dataToCompileList->_requestList.begin(), _pager->_dataToCompileList->_requestList.end(), refPtrAdapt(std::not1(std::bind2nd(std::mem_fun(&DatabaseRequest::isRequestCurrent), _pager->_frameNumber)))); // This is the database thread, so just delete for(RequestQueue::RequestList::iterator citr = tooOld; citr != _pager->_dataToCompileList->_requestList.end(); ++citr) { osg::notify(osg::INFO)<<_name<<": pruning from compile list"<_loadedModel = 0; (*citr)->_requestQueue = 0; } _pager->_dataToCompileList->_requestList.erase(tooOld, _pager->_dataToCompileList->_requestList.end()); loadedObjectsNeedToBeCompiled = !_pager->_dataToCompileList->_requestList.empty(); } 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::notify(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; _dataToMergeList = new RequestQueue; #if 0 _databaseThreads.push_back(new DatabaseThread(this, DatabaseThread::HANDLE_ALL_REQUESTS,"HANDLE_ALL_REQUESTS")); #else #if 1 _databaseThreads.push_back(new DatabaseThread(this, DatabaseThread::HANDLE_NON_HTTP, "HANDLE_NON_HTTP 0")); _databaseThreads.push_back(new DatabaseThread(this, DatabaseThread::HANDLE_ONLY_HTTP, "HANDLE_ONLY_HTTP 1")); #else _databaseThreads.push_back(new DatabaseThread(this, DatabaseThread::HANDLE_NON_HTTP, "HANDLE_NON_HTTP 0")); _databaseThreads.push_back(new DatabaseThread(this, DatabaseThread::HANDLE_NON_HTTP, "HANDLE_NON_HTTP 1")); _databaseThreads.push_back(new DatabaseThread(this, DatabaseThread::HANDLE_ONLY_HTTP, "HANDLE_ONLY_HTTP 2")); _databaseThreads.push_back(new DatabaseThread(this, DatabaseThread::HANDLE_ONLY_HTTP, "HANDLE_ONLY_HTTP 3")); #endif #endif } DatabasePager::DatabasePager(const DatabasePager& rhs) { //osg::notify(osg::INFO)<<"Constructing DatabasePager(const DatabasePager& )"<& 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::setReleaseDelay(double releaseDelay) { _releaseDelay = releaseDelay; if (_releaseDelay==DBL_MAX) { _changeAutoUnRef = true; _valueAutoUnRef = true; } else { // when GLObject release is used make sure Images aren't unref'd as they may be needed later. _changeAutoUnRef = true; _valueAutoUnRef = false; } } 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(); { OpenThreads::ScopedLock lock(_dataToCompileList->_requestMutex); for(RequestQueue::RequestList::iterator citr = _dataToCompileList->_requestList.begin(); citr != _dataToCompileList->_requestList.end(); ++citr) { (*citr)->_loadedModel = 0; (*citr)->_requestQueue = 0; } _dataToCompileList->_requestList.clear(); } { OpenThreads::ScopedLock lock(_dataToMergeList->_requestMutex); for(RequestQueue::RequestList::iterator citr = _dataToMergeList->_requestList.begin(); citr != _dataToMergeList->_requestList.end(); ++citr) { (*citr)->_loadedModel = 0; (*citr)->_requestQueue = 0; } _dataToMergeList->_requestList.clear(); } // note, no need to use a mutex as the list is only accessed from the update thread. _pagedLODList.clear(); // ?? // _activeGraphicsContexts } void DatabasePager::resetStats() { // initialize the stats variables _minimumTimeToMergeTile = DBL_MAX; _maximumTimeToMergeTile = -DBL_MAX; _totalTimeToMergeTiles = 0.0; _numTilesMerges = 0; } void DatabasePager::requestNodeFile(const std::string& fileName,osg::Group* group, float priority, const osg::FrameStamp* framestamp, osg::ref_ptr& databaseRequest) { requestNodeFile(fileName,group,priority,framestamp,databaseRequest,Registry::instance()->getOptions()); } void DatabasePager::requestNodeFile(const std::string& fileName,osg::Group* group, float priority, const osg::FrameStamp* framestamp, osg::ref_ptr& databaseRequestRef, ReaderWriter::Options* loadOptions) { if (!_acceptNewRequests) return; osg::Timer_t start_tick = osg::Timer::instance()->tick(); double timestamp = framestamp?framestamp->getReferenceTime():0.0; int frameNumber = framestamp?framestamp->getFrameNumber():_frameNumber; static int previousFrame = -1; static double totalTime = 0.0; if (previousFrame!=frameNumber) { // osg::notify(osg::NOTICE)<<"requestNodeFiles for "<_requestQueue; if (requestQueue) { OpenThreads::ScopedLock lock(requestQueue->_requestMutex); databaseRequest->_frameNumberLastRequest = frameNumber; databaseRequest->_timestampLastRequest = timestamp; databaseRequest->_priorityLastRequest = priority; ++(databaseRequest->_numOfRequests); } else { databaseRequest->_frameNumberLastRequest = frameNumber; databaseRequest->_timestampLastRequest = timestamp; databaseRequest->_priorityLastRequest = priority; ++(databaseRequest->_numOfRequests); } foundEntry = true; if (databaseRequestRef->referenceCount()==1) { osg::notify(osg::INFO)<<"DatabasePager::fileRquest("<_frameNumberFirstRequest = frameNumber; databaseRequest->_timestampFirstRequest = timestamp; databaseRequest->_priorityFirstRequest = priority; databaseRequest->_frameNumberLastRequest = frameNumber; databaseRequest->_timestampLastRequest = timestamp; databaseRequest->_priorityLastRequest = priority; databaseRequest->_groupForAddingLoadedSubgraph = group; databaseRequest->_loadOptions = loadOptions; databaseRequest->_requestQueue = _fileRequestQueue.get(); _fileRequestQueue->add(databaseRequest); } } } if (!foundEntry) { osg::notify(osg::INFO)<<"In DatabasePager::fileRquest("< lock(_fileRequestQueue->_requestMutex); if (!databaseRequestRef.valid() || databaseRequestRef->referenceCount()==1) { osg::ref_ptr databaseRequest = new DatabaseRequest; databaseRequestRef = databaseRequest.get(); databaseRequest->_fileName = fileName; databaseRequest->_frameNumberFirstRequest = frameNumber; databaseRequest->_timestampFirstRequest = timestamp; databaseRequest->_priorityFirstRequest = priority; databaseRequest->_frameNumberLastRequest = frameNumber; databaseRequest->_timestampLastRequest = timestamp; databaseRequest->_priorityLastRequest = priority; databaseRequest->_groupForAddingLoadedSubgraph = group; databaseRequest->_loadOptions = loadOptions; databaseRequest->_requestQueue = _fileRequestQueue.get(); _fileRequestQueue->_requestList.push_back(databaseRequest.get()); _fileRequestQueue->updateBlock(); } } if (!_startThreadCalled) { OpenThreads::ScopedLock lock(_run_mutex); if (!_startThreadCalled) { _startThreadCalled = true; _done = false; osg::notify(osg::DEBUG_INFO)<<"DatabasePager::startThread()"<startThread(); } } } totalTime += osg::Timer::instance()->delta_m(start_tick, osg::Timer::instance()->tick()); } void DatabasePager::signalBeginFrame(const osg::FrameStamp* framestamp) { if (framestamp) { //osg::notify(osg::INFO) << "signalBeginFrame "<getFrameNumber()<<">>>>>>>>>>>>>>>>"<getFrameNumber(); } //else osg::notify(osg::INFO) << "signalBeginFrame >>>>>>>>>>>>>>>>"<updateBlock(); _httpRequestQueue->updateBlock(); } bool DatabasePager::requiresUpdateSceneGraph() const { { OpenThreads::ScopedLock lock(_dataToMergeList->_requestMutex); if (!_dataToMergeList->_requestList.empty()) return true; } return false; } void DatabasePager::addLoadedDataToSceneGraph(const osg::FrameStamp &frameStamp) { double timeStamp = frameStamp.getReferenceTime(); int frameNumber = frameStamp.getFrameNumber(); // osg::Timer_t before = osg::Timer::instance()->tick(); RequestQueue::RequestList localFileLoadedList; // get the dat for the _dataToCompileList, leaving it empty via a std::vector<>.swap. { OpenThreads::ScopedLock lock(_dataToMergeList->_requestMutex); localFileLoadedList.swap(_dataToMergeList->_requestList); } // add the loaded data into the scene graph. for(RequestQueue::RequestList::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::ref_ptr group = databaseRequest->_groupForAddingLoadedSubgraph.get(); if (group.valid()) { osg::PagedLOD* plod = dynamic_cast(group.get()); if (plod) { plod->setTimeStamp(plod->getNumChildren(), timeStamp); plod->setFrameNumber(plod->getNumChildren(), frameNumber); plod->getDatabaseRequest(plod->getNumChildren()) = 0; } else { osg::ProxyNode* proxyNode = dynamic_cast(group.get()); if (proxyNode) { proxyNode->getDatabaseRequest(proxyNode->getNumChildren()) = 0; } } group->addChild(databaseRequest->_loadedModel.get()); osg::notify(osg::INFO)<<"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; } // reset the loadedModel pointer databaseRequest->_loadedModel = 0; // osg::notify(osg::NOTICE)<<"curr = "<delta_m(before,osg::Timer::instance()->tick())<<" ms objects"<get(); if (_releaseDelay!=DBL_MAX && plod->releaseGLObjectsOnExpiredChildren(releaseTime, releaseFrame)) { osg::notify(osg::INFO)<<"DatabasePager::removeExpiredSubgraphs(), releasing gl objects"<removeExpiredChildren(expiryTime, expiryFrame, childrenRemoved); } if (!childrenRemoved.empty()) { MarkPagedLODsVisitor markerVistor("NeedToRemove"); for(osg::NodeList::iterator critr = childrenRemoved.begin(); critr!=childrenRemoved.end(); ++critr) { (*critr)->accept(markerVistor); } // osg::notify(osg::NOTICE)<<"Children to remove "< lock(_fileRequestQueue->_childrenToDeleteListMutex); for (osg::NodeList::iterator critr = childrenRemoved.begin(); critr!=childrenRemoved.end(); ++critr) { _fileRequestQueue->_childrenToDeleteList.push_back(critr->get()); } _fileRequestQueue->updateBlock(); } // osg::notify(osg::NOTICE)<<" time 2 "<delta_m(before,osg::Timer::instance()->tick())<<" ms "<get(); if (plod && plod->getName() != markerVistor._marker) { ++itr; } else { PagedLODList::iterator itr_to_erase = itr; ++itr; _pagedLODList.erase(itr_to_erase); } } childrenRemoved.clear(); } if (osgDB::Registry::instance()->getSharedStateManager()) osgDB::Registry::instance()->getSharedStateManager()->prune(); // update the Registry object cache. osgDB::Registry::instance()->updateTimeStampOfObjectsInCacheWithExternalReferences(frameStamp.getReferenceTime()); osgDB::Registry::instance()->removeExpiredObjectsInCache(expiryTime); } class DatabasePager::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) { if (!subgraph) return; FindPagedLODsVisitor fplv(_pagedLODList); subgraph->accept(fplv); } bool DatabasePager::requiresCompileGLObjects() const { OpenThreads::ScopedLock lock(_dataToCompileList->_requestMutex); return !_dataToCompileList->_requestList.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::notify(osg::NOTICE)<<"Background thread compiling"<compileAllGLObjects(*(context->getState())); } bool DatabasePager::requiresExternalCompileGLObjects(unsigned int contextID) const { if (_activeGraphicsContexts.count(contextID)==0) return false; return osg::GraphicsContext::getCompileContext(contextID)==0; } void DatabasePager::compileAllGLObjects(osg::State& state) { double availableTime = DBL_MAX; compileGLObjects(state, availableTime); } void DatabasePager::compileGLObjects(osg::State& state, double& availableTime) { // osg::notify(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; // get the first compilable entry. { OpenThreads::ScopedLock lock(_dataToCompileList->_requestMutex); // advance to the next entry to compile if one is available. databaseRequest = _dataToCompileList->_requestList.empty() ? 0 : _dataToCompileList->_requestList.front().get(); }; 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() && (compileAll || (elapsedTime_dataToCompileMap; DataToCompile& dtc = dcm[state.getContextID()]; if (!dtc.first.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)->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; } if (osg::getNotifyLevel() >= osg::DEBUG_INFO && numObjectsCompiled > objTemp) osg::notify(osg::DEBUG_INFO)<< _frameNumber << " compiled " << numObjectsCompiled - objTemp << " StateSets" << std::endl; // remove the compiled statesets from the list. sslist.erase(sslist.begin(),itr); } if (!dtc.second.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::notify(osg::DEBUG_INFO)<< _frameNumber << " compiled " << numObjectsCompiled - objTemp << " Drawables" << std::endl; // 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 (numObjectsCompiled > 0) //osg::notify(osg::NOTICE)<< _frameNumber << "compiled " << numObjectsCompiled << " objects" << std::endl; if (allCompiled) { // 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::notify(osg::NOTICE)<<"All compiled"< lock(_dataToCompileList->_requestMutex); // The request might have been removed from the // _dataToCompile list by another graphics thread, in // which case it's already on the merge list, or by // the pager, which means that the request has // expired. Also, the compile list might have been // shuffled by the pager, so the current request is // not guaranteed to still be at the beginning of the // list. RequestQueue::RequestList::iterator requestIter = std::find(_dataToCompileList->_requestList.begin(), _dataToCompileList->_requestList.end(), databaseRequest); if (requestIter != _dataToCompileList->_requestList.end()) { { OpenThreads::ScopedLock lock(_dataToMergeList->_requestMutex); databaseRequest->_requestQueue = _dataToMergeList.get(); _dataToMergeList->_requestList.push_back(databaseRequest); } _dataToCompileList->_requestList.erase(requestIter); } if (!_dataToCompileList->_requestList.empty()) databaseRequest = _dataToCompileList->_requestList.front(); else databaseRequest = 0; } else { // osg::notify(osg::NOTICE)<<"Not all compiled"<