Files
OpenSceneGraph/src/osgDB/DatabasePager.cpp
2019-01-14 10:24:30 +01:00

1846 lines
62 KiB
C++

/* -*-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 <osgDB/DatabasePager>
#include <osgDB/WriteFile>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/Registry>
#include <osg/Geode>
#include <osg/Timer>
#include <osg/Texture>
#include <osg/Notify>
#include <osg/ProxyNode>
#include <osg/ApplicationUsage>
#include <OpenThreads/ScopedLock>
#include <algorithm>
#include <functional>
#include <set>
#include <iterator>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
using namespace osgDB;
using namespace OpenThreads;
static osg::ApplicationUsageProxy DatabasePager_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_DO_PRE_COMPILE <ON/OFF>","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 <mode>","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 <mode>", "Set the thread priority to DEFAULT, MIN, LOW, NOMINAL, HIGH or MAX.");
static osg::ApplicationUsageProxy DatabasePager_e11(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_MAX_PAGEDLOD <num>","Set the target maximum number of PagedLOD to maintain.");
static osg::ApplicationUsageProxy DatabasePager_e12(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_ASSIGN_PBO_TO_IMAGES <ON/OFF>","Set whether PixelBufferObjects should be assigned to Images to aid download to the GPU.");
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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<osgDB::DatabasePager::DatabaseRequest> _databaseRequest;
};
void DatabasePager::compileCompleted(DatabaseRequest* databaseRequest)
{
//OSG_NOTICE<<"DatabasePager::compileCompleted("<<databaseRequest<<")"<<std::endl;
_dataToCompileList->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; i<removedChildren.size(); ++i)
{
removedChildren[i]->accept(*this);
}
return sizeBefore!=removedChildren.size();
}
typedef std::set<osg::ref_ptr<osg::PagedLOD> > 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<DatabasePager::DatabaseRequest*>(plod->getDatabaseRequest(i).get());
if (request)
request->_groupExpired = true;
}
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SetBasedPagedLODList
//
class SetBasedPagedLODList : public DatabasePager::PagedLODList
{
public:
typedef std::set< osg::observer_ptr<osg::PagedLOD> > 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<osg::PagedLOD> 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<osg::PagedLOD> 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"<<std::endl;
}
}
}
virtual void removeNodes(osg::NodeList& nodesToRemove)
{
for(osg::NodeList::iterator itr = nodesToRemove.begin();
itr != nodesToRemove.end();
++itr)
{
osg::PagedLOD* plod = dynamic_cast<osg::PagedLOD*>(itr->get());
osg::observer_ptr<osg::PagedLOD> obs_ptr(plod);
PagedLODs::iterator plod_itr = _pagedLODs.find(obs_ptr);
if (plod_itr != _pagedLODs.end())
{
OSG_INFO<<"Removing node from PagedLOD list"<<std::endl;
_pagedLODs.erase(plod_itr);
}
}
}
virtual void insertPagedLOD(const osg::observer_ptr<osg::PagedLOD>& plod)
{
if (_pagedLODs.count(plod)!=0)
{
OSG_NOTICE<<"Warning: SetBasedPagedLODList::insertPagedLOD("<<plod.get()<<") already inserted"<<std::endl;
// abort();
return;
}
// OSG_NOTICE<<"OK: SetBasedPagedLODList::insertPagedLOD("<<plod<<") inserting"<<std::endl;
_pagedLODs.insert(plod);
}
virtual bool containsPagedLOD(const osg::observer_ptr<osg::PagedLOD>& 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"<<std::endl;
break;
case DatabasePager::USE_DISPLAY_LISTS:
_mode = _mode | osgUtil::GLObjectsVisitor::SWITCH_ON_DISPLAY_LISTS;
_mode = _mode | osgUtil::GLObjectsVisitor::SWITCH_OFF_VERTEX_BUFFER_OBJECTS;
_mode = _mode & ~osgUtil::GLObjectsVisitor::SWITCH_ON_VERTEX_BUFFER_OBJECTS;
break;
case DatabasePager::USE_VERTEX_BUFFER_OBJECTS:
_mode = _mode | osgUtil::GLObjectsVisitor::SWITCH_ON_VERTEX_BUFFER_OBJECTS;
break;
case DatabasePager::USE_VERTEX_ARRAYS:
_mode = _mode & ~osgUtil::GLObjectsVisitor::SWITCH_ON_DISPLAY_LISTS;
_mode = _mode & ~osgUtil::GLObjectsVisitor::SWITCH_ON_VERTEX_BUFFER_OBJECTS;
_mode = _mode | osgUtil::GLObjectsVisitor::SWITCH_OFF_DISPLAY_LISTS;
_mode = _mode | osgUtil::GLObjectsVisitor::SWITCH_OFF_VERTEX_BUFFER_OBJECTS;
break;
}
if (osgDB::Registry::instance()->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.get()!=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.get()!=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<osg::KdTreeBuilder> _kdTreeBuilder;
protected:
FindCompileableGLObjectsVisitor& operator = (const FindCompileableGLObjectsVisitor&) { return *this; }
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SortFileRequestFunctor
//
struct DatabasePager::SortFileRequestFunctor
{
bool operator() (const osg::ref_ptr<DatabasePager::DatabaseRequest>& lhs,const osg::ref_ptr<DatabasePager::DatabaseRequest>& rhs) const
{
if (lhs->_timestampLastRequest>rhs->_timestampLastRequest) return true;
else if (lhs->_timestampLastRequest<rhs->_timestampLastRequest) return false;
else return (lhs->_priorityLastRequest>rhs->_priorityLastRequest);
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// DatabaseRequest
//
void DatabasePager::DatabaseRequest::invalidate()
{
OSG_INFO<<" DatabasePager::DatabaseRequest::invalidate()."<<std::endl;
_valid = false;
_loadedModel = 0;
_compileSet = 0;
_objectCache = 0;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// RequestQueue
//
DatabasePager::RequestQueue::RequestQueue(DatabasePager* pager):
_pager(pager),
_frameNumberLastPruned(osg::UNINITIALIZED_FRAME_NUMBER)
{
}
DatabasePager::RequestQueue::~RequestQueue()
{
OSG_INFO<<"DatabasePager::RequestQueue::~RequestQueue() Destructing queue."<<std::endl;
for(RequestList::iterator itr = _requestList.begin();
itr != _requestList.end();
++itr)
{
invalidate(itr->get());
}
}
void DatabasePager::RequestQueue::invalidate(DatabaseRequest* dr)
{
// OSG_NOTICE<<"DatabasePager::RequestQueue::invalidate(DatabaseRequest* dr) dr->_compileSet="<<dr->_compileSet.get()<<std::endl;
// XXX _dr_mutex?
osg::ref_ptr<osgUtil::IncrementalCompileOperation::CompileSet> compileSet;
if (dr->_compileSet.lock(compileSet) && _pager->getIncrementalCompileOperation())
{
_pager->getIncrementalCompileOperation()->remove(compileSet.get());
}
dr->invalidate();
}
bool DatabasePager::RequestQueue::pruneOldRequestsAndCheckIfEmpty()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_requestMutex);
unsigned int frameNumber = _pager->_frameNumber;
if (_frameNumberLastPruned != frameNumber)
{
for(RequestQueue::RequestList::iterator citr = _requestList.begin();
citr != _requestList.end();
)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> drLock(_pager->_dr_mutex);
if ((*citr)->isRequestCurrent(frameNumber))
{
++citr;
}
else
{
invalidate(citr->get());
OSG_INFO<<"DatabasePager::RequestQueue::pruneOldRequestsAndCheckIfEmpty(): Pruning "<<(*citr)<<std::endl;
citr = _requestList.erase(citr);
}
}
_frameNumberLastPruned = frameNumber;
updateBlock();
}
return _requestList.empty();
}
bool DatabasePager::RequestQueue::empty()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_requestMutex);
return _requestList.empty();
}
unsigned int DatabasePager::RequestQueue::size()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_requestMutex);
return _requestList.size();
}
void DatabasePager::RequestQueue::clear()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_requestMutex);
for(RequestList::iterator citr = _requestList.begin();
citr != _requestList.end();
++citr)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> drLock(_pager->_dr_mutex);
invalidate(citr->get());
}
_requestList.clear();
_frameNumberLastPruned = _pager->_frameNumber;
updateBlock();
}
void DatabasePager::RequestQueue::add(DatabasePager::DatabaseRequest* databaseRequest)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_requestMutex);
addNoLock(databaseRequest);
}
void DatabasePager::RequestQueue::remove(DatabasePager::DatabaseRequest* databaseRequest)
{
// OSG_NOTICE<<"DatabasePager::RequestQueue::remove(DatabaseRequest* databaseRequest)"<<std::endl;
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_requestMutex);
for(RequestList::iterator citr = _requestList.begin();
citr != _requestList.end();
++citr)
{
if (citr->get()==databaseRequest)
{
// OSG_NOTICE<<" done remove(DatabaseRequest* databaseRequest)"<<std::endl;
_requestList.erase(citr);
return;
}
}
}
void DatabasePager::RequestQueue::addNoLock(DatabasePager::DatabaseRequest* databaseRequest)
{
_requestList.push_back(databaseRequest);
updateBlock();
}
void DatabasePager::RequestQueue::swap(RequestList& requestList)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_requestMutex);
_requestList.swap(requestList);
}
void DatabasePager::RequestQueue::takeFirst(osg::ref_ptr<DatabaseRequest>& databaseRequest)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> 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<OpenThreads::Mutex> 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)<<std::endl;
citr = _requestList.erase(citr);
}
}
_frameNumberLastPruned = frameNumber;
if (selected_itr != _requestList.end())
{
databaseRequest = *selected_itr;
_requestList.erase(selected_itr);
OSG_INFO<<" DatabasePager::RequestQueue::takeFirst() Found DatabaseRequest size()="<<_requestList.size()<<std::endl;
}
else
{
OSG_INFO<<" DatabasePager::RequestQueue::takeFirst() No suitable DatabaseRequest found size()="<<_requestList.size()<<std::endl;
}
updateBlock();
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// ReadQueue
//
DatabasePager::ReadQueue::ReadQueue(DatabasePager* pager, const std::string& name):
RequestQueue(pager),
_name(name)
{
_block = new osg::RefBlock;
}
void DatabasePager::ReadQueue::updateBlock()
{
_block->set((!_requestList.empty() || !_childrenToDeleteList.empty()) &&
!_pager->_databasePagerThreadPaused);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// DatabaseThread
//
DatabasePager::DatabaseThread::DatabaseThread(DatabasePager* pager, Mode mode, const std::string& name):
_done(false),
_active(false),
_pager(pager),
_mode(mode),
_name(name)
{
}
DatabasePager::DatabaseThread::DatabaseThread(const DatabaseThread& dt, DatabasePager* pager):
_done(false),
_active(false),
_pager(pager),
_mode(dt._mode),
_name(dt._name)
{
}
DatabasePager::DatabaseThread::~DatabaseThread()
{
cancel();
}
int DatabasePager::DatabaseThread::cancel()
{
int result = 0;
if( isRunning() )
{
setDone(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;
}
join();
// _startThreadCalled = false;
}
//OSG_NOTICE<<"DatabasePager::DatabaseThread stopped running"<<std::endl;
return result;
}
void DatabasePager::DatabaseThread::run()
{
OSG_INFO<<_name<<": DatabasePager::DatabaseThread::run"<<std::endl;
bool firstTime = true;
osg::ref_ptr<DatabasePager::ReadQueue> read_queue;
osg::ref_ptr<DatabasePager::ReadQueue> 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()= "<<read_queue->size()<<" to delete = "<<read_queue->_childrenToDeleteList.size()<<std::endl;
//
// delete any children if required.
//
if (_pager->_deleteRemovedSubgraphsInDatabaseThread/* && !(read_queue->_childrenToDeleteList.empty())*/)
{
ObjectList deleteList;
{
// Don't hold lock during destruction of deleteList
OpenThreads::ScopedLock<OpenThreads::Mutex> 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> databaseRequest;
read_queue->takeFirst(databaseRequest);
bool readFromFileCache = false;
osg::ref_ptr<FileCache> fileCache = osgDB::Registry::instance()->getFileCache();
osg::ref_ptr<FileLocationCallback> fileLocationCallback = osgDB::Registry::instance()->getFileLocationCallback();
osg::ref_ptr<Options> dr_loadOptions;
std::string fileName;
int frameNumberLastRequest = 0;
bool cacheNodes = false;
if (databaseRequest.valid())
{
{
OpenThreads::ScopedLock<OpenThreads::Mutex> drLock(_pager->_dr_mutex);
dr_loadOptions = databaseRequest->_loadOptions.valid() ? databaseRequest->_loadOptions->cloneOptions() : new osgDB::Options;
dr_loadOptions->setTerrain(databaseRequest->_terrain);
dr_loadOptions->setParentGroup(databaseRequest->_group);
fileName = databaseRequest->_fileName;
frameNumberLastRequest = databaseRequest->_frameNumberLastRequest;
}
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;
cacheNodes = (dr_loadOptions->getObjectCacheHint() & osgDB::Options::CACHE_NODES)!=0;
if (cacheNodes)
{
//OSG_NOTICE<<"Checking main ObjectCache"<<std::endl;
// check the object cache to see if the file we want has already been loaded.
osg::ref_ptr<osg::Object> objectFromCache = osgDB::Registry::instance()->getRefFromObjectCache(fileName);
// if no object with fileName in ObjectCache then try the filename appropriate for fileCache
if (!objectFromCache && (fileCache.valid() && fileCache->isFileAppropriateForFileCache(fileName)))
{
if (fileCache->existsInCache(fileName))
{
objectFromCache = osgDB::Registry::instance()->getRefFromObjectCache(fileCache->createCacheFileName(fileName));
}
}
osg::Node* modelFromCache = dynamic_cast<osg::Node*>(objectFromCache.get());
if (modelFromCache)
{
//OSG_NOTICE<<"Found object in cache "<<fileName<<std::endl;
// assign the cached model to the request
{
OpenThreads::ScopedLock<OpenThreads::Mutex> 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<OpenThreads::Mutex> 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;
}
else
{
//OSG_NOTICE<<"Not Found object in cache "<<fileName<<std::endl;
}
// need to disable any attempt to use the cache when loading as we're handle this ourselves to avoid threading conflicts
{
OpenThreads::ScopedLock<OpenThreads::Mutex> drLock(_pager->_dr_mutex);
databaseRequest->_objectCache = new ObjectCache;
dr_loadOptions->setObjectCache(databaseRequest->_objectCache.get());
}
}
// 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 "<<fileName<<std::endl;
out_queue->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("<<databaseRequest->_fileName<<")"<<std::endl;
//osg::Timer_t before = osg::Timer::instance()->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<osg::Node> loadedModel;
if (rr.validNode()) loadedModel = rr.getNode();
if (!rr.success()) OSG_WARN<<"Error in reading file "<<fileName<<" : "<<rr.statusMessage() << std::endl;
if (loadedModel.valid() &&
fileCache.valid() &&
fileCache->isFileAppropriateForFileCache(fileName) &&
!readFromFileCache)
{
fileCache->writeNode(*(loadedModel), fileName, dr_loadOptions.get());
}
{
OpenThreads::ScopedLock<OpenThreads::Mutex> drLock(_pager->_dr_mutex);
if ((_pager->_frameNumber-databaseRequest->_frameNumberLastRequest)>1)
{
OSG_INFO<<_name<<": Warning DatabaseRquest no longer required."<<std::endl;
loadedModel = 0;
}
}
//OSG_NOTICE<<" node read in "<<osg::Timer::instance()->delta_m(before,osg::Timer::instance()->tick())<<" ms"<<std::endl;
if (loadedModel.valid())
{
loadedModel->getBound();
bool loadedObjectsNeedToBeCompiled = false;
osg::ref_ptr<osgUtil::IncrementalCompileOperation::CompileSet> compileSet = 0;
if (!rr.loadedFromCache())
{
// find all the compileable rendering objects
DatabasePager::FindCompileableGLObjectsVisitor stateToCompile(_pager, _pager->getMarkerObject());
loadedModel->accept(stateToCompile);
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.
if (loadedObjectsNeedToBeCompiled)
{
// OSG_NOTICE<<"Using IncrementalCompileOperation"<<std::endl;
compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(loadedModel.get());
compileSet->buildCompileMap(_pager->_incrementalCompileOperation->getContextSet(), stateToCompile);
compileSet->_compileCompletedCallback = new DatabasePagerCompileCompletedCallback(_pager, databaseRequest.get());
_pager->_incrementalCompileOperation->add(compileSet.get(), false);
}
}
else
{
OSG_NOTICE<<"Loaded from ObjectCache"<<std::endl;
}
{
OpenThreads::ScopedLock<OpenThreads::Mutex> drLock(_pager->_dr_mutex);
databaseRequest->_loadedModel = loadedModel;
databaseRequest->_compileSet = compileSet;
}
// 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<OpenThreads::Mutex> listLock(
_pager->_dataToCompileList->_requestMutex);
_pager->_dataToCompileList->addNoLock(databaseRequest.get());
databaseRequest = 0;
}
else
{
OpenThreads::ScopedLock<OpenThreads::Mutex> 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()"<<std::endl;
_startThreadCalled = false;
_done = false;
_acceptNewRequests = true;
_databasePagerThreadPaused = false;
_numFramesActive = 0;
_frameNumber.exchange(0);
#if __APPLE__
// OSX really doesn't like compiling display lists, and performs poorly when they are used,
// so apply this hack to make up for its short comings.
_drawablePolicy = USE_VERTEX_ARRAYS;
#else
_drawablePolicy = DO_NOT_MODIFY_DRAWABLE_SETTINGS;
#endif
const char* str = getenv("OSG_DATABASE_PAGER_GEOMETRY");
if (!str) str = getenv("OSG_DATABASE_PAGER_DRAWABLE");
if (str)
{
if (strcmp(str,"DoNotModify")==0)
{
_drawablePolicy = DO_NOT_MODIFY_DRAWABLE_SETTINGS;
}
else if (strcmp(str,"DisplayList")==0 || strcmp(str,"DL")==0)
{
_drawablePolicy = USE_DISPLAY_LISTS;
}
else if (strcmp(str,"VBO")==0)
{
_drawablePolicy = USE_VERTEX_BUFFER_OBJECTS;
}
else if (strcmp(str,"VertexArrays")==0 || strcmp(str,"VA")==0 )
{
_drawablePolicy = USE_VERTEX_ARRAYS;
}
}
_assignPBOToImages = false;
if( (str = getenv("OSG_ASSIGN_PBO_TO_IMAGES")) != 0)
{
_assignPBOToImages = strcmp(str,"yes")==0 || strcmp(str,"YES")==0 ||
strcmp(str,"on")==0 || strcmp(str,"ON")==0;
OSG_NOTICE<<"OSG_ASSIGN_PBO_TO_IMAGES set to "<<_assignPBOToImages<<std::endl;
}
_changeAutoUnRef = true;
_valueAutoUnRef = false;
_changeAnisotropy = false;
_valueAnisotropy = 1.0f;
_deleteRemovedSubgraphsInDatabaseThread = true;
if( (str = getenv("OSG_DELETE_IN_DATABASE_THREAD")) != 0)
{
_deleteRemovedSubgraphsInDatabaseThread = strcmp(str,"yes")==0 || strcmp(str,"YES")==0 ||
strcmp(str,"on")==0 || strcmp(str,"ON")==0;
}
_targetMaximumNumberOfPageLOD = 300;
if( (str = getenv("OSG_MAX_PAGEDLOD")) != 0)
{
_targetMaximumNumberOfPageLOD = atoi(str);
OSG_NOTICE<<"_targetMaximumNumberOfPageLOD = "<<_targetMaximumNumberOfPageLOD<<std::endl;
}
_doPreCompile = true;
if( (str = getenv("OSG_DO_PRE_COMPILE")) != 0)
{
_doPreCompile = strcmp(str,"yes")==0 || strcmp(str,"YES")==0 ||
strcmp(str,"on")==0 || strcmp(str,"ON")==0;
}
// initialize the stats variables
resetStats();
_fileRequestQueue = new ReadQueue(this,"fileRequestQueue");
_httpRequestQueue = new ReadQueue(this,"httpRequestQueue");
_dataToCompileList = new RequestQueue(this);
_dataToMergeList = new RequestQueue(this);
// test of setting the database threads affinity.
// _affinity = OpenThreads::Affinity(4,4);
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& )"<<std::endl;
_markerObject = new osg::DummyObject;
_markerObject->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));
}
setProcessorAffinity(rhs.getProcessorAffinity());
_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>& DatabasePager::prototype()
{
static osg::ref_ptr<DatabasePager> s_DatabasePager = new DatabasePager;
return s_DatabasePager;
}
OSG_INIT_SINGLETON_PROXY(ProxyInitDatabasePager, DatabasePager::prototype())
DatabasePager* DatabasePager::create()
{
return DatabasePager::prototype().valid() ?
DatabasePager::prototype()->clone() :
new DatabasePager;
}
void DatabasePager::setProcessorAffinity(const OpenThreads::Affinity& affinity)
{
_affinity = affinity;
for(DatabaseThreadList::iterator itr=_databaseThreads.begin();
itr != _databaseThreads.end();
++itr)
{
(*itr)->setProcessorAffinity(_affinity);
}
}
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; i<numGeneralThreads; ++i)
{
addDatabaseThread(DatabaseThread::HANDLE_ALL_REQUESTS,"HANDLE_ALL_REQUESTS");
}
}
else
{
for(unsigned int i=0; i<numGeneralThreads; ++i)
{
addDatabaseThread(DatabaseThread::HANDLE_NON_HTTP, "HANDLE_NON_HTTP");
}
for(unsigned int i=0; i<numHttpThreads; ++i)
{
addDatabaseThread(DatabaseThread::HANDLE_ONLY_HTTP, "HANDLE_ONLY_HTTP");
}
}
}
unsigned int DatabasePager::addDatabaseThread(DatabaseThread::Mode mode, const std::string& name)
{
OSG_INFO<<"DatabasePager::addDatabaseThread() "<<name<<std::endl;
unsigned int pos = _databaseThreads.size();
DatabaseThread* thread = new DatabaseThread(this, mode,name);
thread->setProcessorAffinity(_affinity);
_databaseThreads.push_back(thread);
if (_startThreadCalled)
{
OSG_INFO<<"DatabasePager::startThread()"<<std::endl;
thread->startThread();
}
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<osg::Referenced>& databaseRequestRef,
const osg::Referenced* options)
{
osgDB::Options* loadOptions = dynamic_cast<osgDB::Options*>(const_cast<osg::Referenced*>(options));
if (!loadOptions)
{
loadOptions = Registry::instance()->getOptions();
}
else
{
// OSG_NOTICE<<"options from requestNodeFile "<<std::endl;
}
if (!_acceptNewRequests) return;
if (nodePath.empty())
{
OSG_NOTICE<<"Warning: DatabasePager::requestNodeFile(..) passed empty NodePath, so nowhere to attach new subgraph to."<<std::endl;
return;
}
osg::Group* group = nodePath.back()->asGroup();
if (!group)
{
OSG_NOTICE<<"Warning: DatabasePager::requestNodeFile(..) passed NodePath without group as last node in path, so nowhere to attach new subgraph to."<<std::endl;
return;
}
osg::Node* terrain = 0;
for(osg::NodePath::reverse_iterator itr = nodePath.rbegin();
itr != nodePath.rend();
++itr)
{
if ((*itr)->asTerrain()) terrain = *itr;
}
double timestamp = framestamp?framestamp->getReferenceTime():0.0;
unsigned int frameNumber = framestamp?framestamp->getFrameNumber():static_cast<unsigned int>(_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 "<<previousFrame<<" time = "<<totalTime<<std::endl;
previousFrame = frameNumber;
totalTime = 0.0;
}
#endif
// search to see if filename already exist in the file loaded list.
bool foundEntry = false;
if (databaseRequestRef.valid())
{
DatabaseRequest* databaseRequest = dynamic_cast<DatabaseRequest*>(databaseRequestRef.get());
bool requeue = false;
if (databaseRequest)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> drLock(_dr_mutex);
if (!(databaseRequest->valid()))
{
OSG_INFO<<"DatabaseRequest has been previously invalidated whilst still attached to scene graph."<<std::endl;
databaseRequest = 0;
}
else
{
OSG_INFO<<"DatabasePager::requestNodeFile("<<fileName<<") updating already assigned."<<std::endl;
databaseRequest->_valid = true;
databaseRequest->_frameNumberLastRequest = frameNumber;
databaseRequest->_timestampLastRequest = timestamp;
databaseRequest->_priorityLastRequest = priority;
++(databaseRequest->_numOfRequests);
foundEntry = true;
if (databaseRequestRef->referenceCount()==1)
{
OSG_INFO<<"DatabasePager::requestNodeFile("<<fileName<<") orphaned, resubmitting."<<std::endl;
databaseRequest->_frameNumberLastRequest = frameNumber;
databaseRequest->_timestampLastRequest = timestamp;
databaseRequest->_priorityLastRequest = priority;
databaseRequest->_group = group;
databaseRequest->_terrain = terrain;
databaseRequest->_loadOptions = loadOptions;
databaseRequest->_objectCache = 0;
requeue = true;
}
}
}
if (requeue)
_fileRequestQueue->add(databaseRequest);
}
if (!foundEntry)
{
OSG_INFO<<"In DatabasePager::requestNodeFile("<<fileName<<")"<<std::endl;
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_fileRequestQueue->_requestMutex);
if (!databaseRequestRef.valid() || databaseRequestRef->referenceCount()==1)
{
osg::ref_ptr<DatabaseRequest> 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->_objectCache = 0;
_fileRequestQueue->addNoLock(databaseRequest.get());
}
}
if (!_startThreadCalled)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_run_mutex);
if (!_startThreadCalled)
{
OSG_INFO<<"DatabasePager::startThread()"<<std::endl;
if (_databaseThreads.empty())
{
setUpThreads(
osg::DisplaySettings::instance()->getNumOfDatabaseThreadsHint(),
osg::DisplaySettings::instance()->getNumOfHttpDatabaseThreadsHint());
}
_startThreadCalled = true;
_done = false;
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()<<std::endl;
#endif
if (framestamp)
{
_dataToCompileList->pruneOldRequestsAndCheckIfEmpty();
//OSG_INFO << "signalBeginFrame "<<framestamp->getFrameNumber()<<">>>>>>>>>>>>>>>>"<<std::endl;
_frameNumber.exchange(framestamp->getFrameNumber());
} //else OSG_INFO << "signalBeginFrame >>>>>>>>>>>>>>>>"<<std::endl;
}
void DatabasePager::signalEndFrame()
{
//OSG_INFO << "signalEndFrame <<<<<<<<<<<<<<<<<<<< "<<std::endl;
}
void DatabasePager::setDatabasePagerThreadPause(bool pause)
{
if (_databasePagerThreadPaused == pause) return;
_databasePagerThreadPaused = pause;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_fileRequestQueue->_requestMutex);
_fileRequestQueue->updateBlock();
}
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_httpRequestQueue->_requestMutex);
_httpRequestQueue->updateBlock();
}
}
bool DatabasePager::requiresUpdateSceneGraph() const
{
return (getDataToMergeListSize()>0);
}
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 = "<<elapsedTime<<"ms"<<std::endl;
OSG_NOTICE<<" timeFor_removeExpiredSubgraphs = "<<timeFor_removeExpiredSubgraphs<<"ms"<<std::endl;
OSG_NOTICE<<" timeFor_addLoadedDataToSceneGraph = "<<timeFor_addLoadedDataToSceneGraph<<"ms"<<std::endl;
OSG_NOTICE<<" _activePagedLODList.size() = "<<_activePagedLODList->size()<<std::endl;
OSG_NOTICE<<" _inactivePagedLODList.size() = "<<_inactivePagedLODList->size()<<std::endl;
OSG_NOTICE<<" total = "<<_activePagedLODList->size() + _inactivePagedLODList->size()<<std::endl;
}
#endif
}
bool DatabasePager::requiresRedraw() const
{
return (getDataToCompileListSize()>0);
}
void DatabasePager::addLoadedDataToSceneGraph(const osg::FrameStamp &frameStamp)
{
double timeStamp = frameStamp.getReferenceTime();
unsigned int frameNumber = frameStamp.getFrameNumber();
osg::Timer_t before = osg::Timer::instance()->tick(), mid, last;
RequestQueue::RequestList localFileLoadedList;
// get the data from the _dataToMergeList, leaving it empty via a std::vector<>.swap.
_dataToMergeList->swap(localFileLoadedList);
mid = osg::Timer::instance()->tick();
// add the loaded data into the scene graph.
for(RequestQueue::RequestList::iterator itr=localFileLoadedList.begin();
itr!=localFileLoadedList.end();
++itr)
{
DatabaseRequest* databaseRequest = itr->get();
// No need to take _dr_mutex. The pager threads are done with
// the request; the cull traversal -- which might redispatch
// the request -- can't run at the sametime as this update traversal.
osg::ref_ptr<osg::Group> group;
if (!databaseRequest->_groupExpired && databaseRequest->_group.lock(group))
{
if (osgDB::Registry::instance()->getSharedStateManager())
osgDB::Registry::instance()->getSharedStateManager()->share(databaseRequest->_loadedModel.get());
osg::PagedLOD* plod = dynamic_cast<osg::PagedLOD*>(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<osg::ProxyNode*>(group.get());
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 ) )
{
registerPagedLODs(plod, frameNumber);
}
else
{
registerPagedLODs(databaseRequest->_loadedModel.get(), frameNumber);
}
// OSG_NOTICE<<"merged subgraph"<<databaseRequest->_fileName<<" after "<<databaseRequest->_numOfRequests<<" requests and time="<<(timeStamp-databaseRequest->_timestampFirstRequest)*1000.0<<std::endl;
double timeToMerge = timeStamp-databaseRequest->_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."<<std::endl;
}
if (databaseRequest->_objectCache.valid() && osgDB::Registry::instance()->getObjectCache())
{
// insert loaded model into Registry ObjectCache
osgDB::Registry::instance()->getObjectCache()->addObjectCache( databaseRequest->_objectCache.get());
databaseRequest->_objectCache->clear();
}
// reset the loadedModel pointer
databaseRequest->_loadedModel = 0;
// OSG_NOTICE<<"curr = "<<timeToMerge<<" min "<<getMinimumTimeToMergeTile()*1000.0<<" max = "<<getMaximumTimeToMergeTile()*1000.0<<" average = "<<getAverageTimToMergeTiles()*1000.0<<std::endl;
}
last = osg::Timer::instance()->tick();
if (!localFileLoadedList.empty())
{
OSG_INFO<<"Done DatabasePager::addLoadedDataToSceneGraph"<<
osg::Timer::instance()->delta_m(before,mid)<<"ms,\t"<<
osg::Timer::instance()->delta_m(mid,last)<<"ms"<<
" objects"<<localFileLoadedList.size()<<std::endl<<std::endl;
}
}
void DatabasePager::removeExpiredSubgraphs(const osg::FrameStamp& frameStamp)
{
static double s_total_iter_stage_a = 0.0;
static double s_total_time_stage_a = 0.0;
static double s_total_max_stage_a = 0.0;
static double s_total_iter_stage_b = 0.0;
static double s_total_time_stage_b = 0.0;
static double s_total_max_stage_b = 0.0;
static double s_total_iter_stage_c = 0.0;
static double s_total_time_stage_c = 0.0;
static double s_total_max_stage_c = 0.0;
if (frameStamp.getFrameNumber()==0)
{
// No need to remove anything on first frame.
return;
}
osg::Timer_t startTick = osg::Timer::instance()->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_a<time_a) s_total_max_stage_a = time_a;
if (numPagedLODs <= _targetMaximumNumberOfPageLOD)
{
// nothing to do
return;
}
int numToPrune = numPagedLODs - _targetMaximumNumberOfPageLOD;
ObjectList childrenRemoved;
double expiryTime = frameStamp.getReferenceTime() - 0.1;
unsigned int expiryFrame = frameStamp.getFrameNumber() - 1;
// First traverse inactive PagedLODs, as their children will
// certainly have expired. Then traverse active nodes if we still
// need to prune.
//OSG_NOTICE<<"numToPrune "<<numToPrune;
if (numToPrune>0)
_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<time_b) s_total_max_stage_b = time_b;
//OSG_NOTICE<<" childrenRemoved.size()="<<childrenRemoved.size()<<std::endl;
if (!childrenRemoved.empty())
{
// pass the objects across to the database pager delete list
if (_deleteRemovedSubgraphsInDatabaseThread)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_fileRequestQueue->_requestMutex);
// splice transfers the entire list in constant time.
_fileRequestQueue->_childrenToDeleteList.splice(
_fileRequestQueue->_childrenToDeleteList.end(),
childrenRemoved);
_fileRequestQueue->updateBlock();
}
else
childrenRemoved.clear();
}
osg::Timer_t end_c_Tick = osg::Timer::instance()->tick();
double time_c = osg::Timer::instance()->delta_m(end_b_Tick,end_c_Tick);
s_total_iter_stage_c += 1.0;
s_total_time_stage_c += time_c;
if (s_total_max_stage_c<time_c) s_total_max_stage_c = time_c;
OSG_INFO<<"active="<<_activePagedLODList->size()<<" overall = "<<osg::Timer::instance()->delta_m(startTick,end_c_Tick)<<
" A="<<time_a<<" avg="<<s_total_time_stage_a/s_total_iter_stage_a<<" max = "<<s_total_max_stage_a<<
" B="<<time_b<<" avg="<<s_total_time_stage_b/s_total_iter_stage_b<<" max = "<<s_total_max_stage_b<<
" C="<<time_c<<" avg="<<s_total_time_stage_c/s_total_iter_stage_c<<" max = "<<s_total_max_stage_c<<std::endl;
}
class DatabasePager::FindPagedLODsVisitor : public osg::NodeVisitor
{
public:
FindPagedLODsVisitor(DatabasePager::PagedLODList& pagedLODList, unsigned int frameNumber):
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_activePagedLODList(pagedLODList),
_frameNumber(frameNumber)
{
}
META_NodeVisitor("osgDB","FindPagedLODsVisitor")
virtual void apply(osg::PagedLOD& plod)
{
plod.setFrameNumberOfLastTraversal(_frameNumber);
osg::observer_ptr<osg::PagedLOD> 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);
}