1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version. The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * OpenSceneGraph Public License for more details.
14#ifndef OSGDB_DATABASEPAGER
15#define OSGDB_DATABASEPAGER 1
17#include <osg/NodeVisitor>
19#include <osg/PagedLOD>
20#include <osg/Drawable>
21#include <osg/GraphicsThread>
22#include <osg/FrameStamp>
23#include <osg/ObserverNodePath>
24#include <osg/observer_ptr>
26#include <OpenThreads/Thread>
27#include <OpenThreads/Mutex>
28#include <OpenThreads/ScopedLock>
29#include <OpenThreads/Condition>
31#include <osgUtil/IncrementalCompileOperation>
33#include <osgDB/SharedStateManager>
34#include <osgDB/ReaderWriter>
35#include <osgDB/Options>
47/** Database paging class which manages the loading of files in a background thread,
48 * and synchronizing of loaded models with the main scene graph.*/
49class OSGDB_EXPORT DatabasePager : public osg::NodeVisitor::DatabaseRequestHandler
53 typedef OpenThreads::Thread::ThreadPriority ThreadPriority;
57 DatabasePager(const DatabasePager& rhs);
59 virtual const char* className() const { return "DatabasePager"; }
61 /** Create a shallow copy on the DatabasePager.*/
62 virtual DatabasePager* clone() const { return new DatabasePager(*this); }
64 /** get the prototype singleton used by DatabasePager::create().*/
65 static osg::ref_ptr<DatabasePager>& prototype();
67 /** create a DatabasePager by cloning DatabasePager::prototype().*/
68 static DatabasePager* create();
72 /** Add a request to load a node file to end the database request list.*/
73 virtual void requestNodeFile(const std::string& fileName, osg::NodePath& nodePath,
74 float priority, const osg::FrameStamp* framestamp,
75 osg::ref_ptr<osg::Referenced>& databaseRequest,
76 const osg::Referenced* options);
78 /** Set the priority of the database pager thread(s).*/
79 int setSchedulePriority(OpenThreads::Thread::ThreadPriority priority);
81 /** Cancel the database pager thread(s).*/
84 virtual bool isRunning() const;
86 /** Clear all internally cached structures.*/
89 class OSGDB_EXPORT DatabaseThread : public osg::Referenced, public OpenThreads::Thread
100 DatabaseThread(DatabasePager* pager, Mode mode, const std::string& name);
102 DatabaseThread(const DatabaseThread& dt, DatabasePager* pager);
104 void setName(const std::string& name) { _name = name; }
105 const std::string& getName() const { return _name; }
107 void setDone(bool done) { _done.exchange(done?1:0); }
108 bool getDone() const { return _done!=0; }
110 void setActive(bool active) { _active = active; }
111 bool getActive() const { return _active; }
113 virtual int cancel();
119 virtual ~DatabaseThread();
121 OpenThreads::Atomic _done;
122 volatile bool _active;
123 DatabasePager* _pager;
129 virtual void setProcessorAffinity(const OpenThreads::Affinity& affinity);
130 OpenThreads::Affinity& getProcessorAffinity() { return _affinity; }
131 const OpenThreads::Affinity& getProcessorAffinity() const { return _affinity; }
133 void setUpThreads(unsigned int totalNumThreads=2, unsigned int numHttpThreads=1);
135 virtual unsigned int addDatabaseThread(DatabaseThread::Mode mode, const std::string& name);
137 DatabaseThread* getDatabaseThread(unsigned int i) { return _databaseThreads[i].get(); }
139 const DatabaseThread* getDatabaseThread(unsigned int i) const { return _databaseThreads[i].get(); }
141 unsigned int getNumDatabaseThreads() const { return static_cast<unsigned int>(_databaseThreads.size()); }
143 /** Set whether the database pager thread should be paused or not.*/
144 void setDatabasePagerThreadPause(bool pause);
146 /** Get whether the database pager thread should is paused or not.*/
147 bool getDatabasePagerThreadPause() const { return _databasePagerThreadPaused; }
149 /** Set whether new database request calls are accepted or ignored.*/
150 void setAcceptNewDatabaseRequests(bool acceptNewRequests) { _acceptNewRequests = acceptNewRequests; }
152 /** Get whether new database request calls are accepted or ignored.*/
153 bool getAcceptNewDatabaseRequests() const { return _acceptNewRequests; }
155 /** Get the number of frames that are currently active.*/
156 int getNumFramesActive() const { return _numFramesActive; }
158 /** Signal the database thread that the update, cull and draw has begun for a new frame.
159 * Note, this is called by the application so that the database pager can go to sleep while the CPU is busy on the main rendering threads. */
160 virtual void signalBeginFrame(const osg::FrameStamp* framestamp);
162 /** Signal the database thread that the update, cull and draw dispatch has completed.
163 * Note, this is called by the application so that the database pager can go to wake back up now the main rendering threads are iddle waiting for the next frame.*/
164 virtual void signalEndFrame();
167 /** Find all PagedLOD nodes in a subgraph and register them with
168 * the DatabasePager so it can keep track of expired nodes.
169 * note, should be only be called from the update thread. */
170 virtual void registerPagedLODs(osg::Node* subgraph, unsigned int frameNumber = 0);
172 /** Set the incremental compile operation.
173 * Used to manage the OpenGL object compilation and merging of subgraphs in a way that avoids overloading
174 * the rendering of frame with too many new objects in one frame. */
175 void setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation* ico);
177 /** Get the incremental compile operation. */
178 osgUtil::IncrementalCompileOperation* getIncrementalCompileOperation() { return _incrementalCompileOperation.get(); }
181 /** Set whether the database pager should pre compile OpenGL objects before allowing
182 * them to be merged into the scene graph.
183 * Pre compilation helps reduce the chances of frame drops, but also slows the
184 * speed at which tiles are merged as they have to be compiled first.*/
185 void setDoPreCompile(bool flag) { _doPreCompile = flag; }
187 /** Get whether the database pager should pre compile OpenGL objects before allowing
188 * them to be merged into the scene graph.*/
189 bool getDoPreCompile() const { return _doPreCompile; }
193 /** Set the target maximum number of PagedLOD to maintain in memory.
194 * Note, if more than the target number are required for rendering of a frame then these active PagedLOD are excempt from being expiried.
195 * But once the number of active drops back below the target the inactive PagedLOD will be trimmed back to the target number.*/
196 void setTargetMaximumNumberOfPageLOD(unsigned int target) { _targetMaximumNumberOfPageLOD = target; }
198 /** Get the target maximum number of PagedLOD to maintain in memory.*/
199 unsigned int getTargetMaximumNumberOfPageLOD() const { return _targetMaximumNumberOfPageLOD; }
202 /** Set whether the removed subgraphs should be deleted in the database thread or not.*/
203 void setDeleteRemovedSubgraphsInDatabaseThread(bool flag) { _deleteRemovedSubgraphsInDatabaseThread = flag; }
205 /** Get whether the removed subgraphs should be deleted in the database thread or not.*/
206 bool getDeleteRemovedSubgraphsInDatabaseThread() const { return _deleteRemovedSubgraphsInDatabaseThread; }
210 DO_NOT_MODIFY_DRAWABLE_SETTINGS,
212 USE_VERTEX_BUFFER_OBJECTS,
216 /** Set how loaded drawables should be handled w.r.t their display list/vertex buffer object/vertex array settings.*/
217 void setDrawablePolicy(DrawablePolicy policy) { _drawablePolicy = policy; }
219 /** Get how loaded drawables should be handled w.r.t their display list/vertex buffer object/vertex array settings.*/
220 DrawablePolicy getDrawablePolicy() const { return _drawablePolicy; }
223 /** Set whether newly loaded textures should have a PixelBufferObject assigned to them to aid download to the GPU.*/
224 void setApplyPBOToImages(bool assignPBOToImages) { _assignPBOToImages = assignPBOToImages; }
226 /** Get whether newly loaded textures should have a PixelBufferObject assigned to them.*/
227 bool getApplyPBOToImages() const { return _assignPBOToImages; }
230 /** Set whether newly loaded textures should have their UnrefImageDataAfterApply set to a specified value.*/
231 void setUnrefImageDataAfterApplyPolicy(bool changeAutoUnRef, bool valueAutoUnRef) { _changeAutoUnRef = changeAutoUnRef; _valueAutoUnRef = valueAutoUnRef; }
233 /** Get whether newly loaded textures should have their UnrefImageDataAfterApply set to a specified value.*/
234 void getUnrefImageDataAfterApplyPolicy(bool& changeAutoUnRef, bool& valueAutoUnRef) const { changeAutoUnRef = _changeAutoUnRef; valueAutoUnRef = _valueAutoUnRef; }
237 /** Set whether newly loaded textures should have their MaxAnisotopy set to a specified value.*/
238 void setMaxAnisotropyPolicy(bool changeAnisotropy, float valueAnisotropy) { _changeAnisotropy = changeAnisotropy; _valueAnisotropy = valueAnisotropy; }
240 /** Set whether newly loaded textures should have their MaxAnisotopy set to a specified value.*/
241 void getMaxAnisotropyPolicy(bool& changeAnisotropy, float& valueAnisotropy) const { changeAnisotropy = _changeAnisotropy; valueAnisotropy = _valueAnisotropy; }
244 /** Return true if there are pending updates to the scene graph that require a call to updateSceneGraph(double). */
245 bool requiresUpdateSceneGraph() const;
247 /** Merge the changes to the scene graph by calling calling removeExpiredSubgraphs then addLoadedDataToSceneGraph.
248 * Note, must only be called from single thread update phase. */
249 virtual void updateSceneGraph(const osg::FrameStamp& frameStamp);
251 /** Return true if there are GL objects that need to be compiled by a draw traversal. */
252 bool requiresRedraw() const;
254 /** Report how many items are in the _fileRequestList queue */
255 unsigned int getFileRequestListSize() const { return static_cast<unsigned int>(_fileRequestQueue->size() + _httpRequestQueue->size()); }
257 /** Report how many items are in the _dataToCompileList queue */
258 unsigned int getDataToCompileListSize() const { return static_cast<unsigned int>(_dataToCompileList->size()); }
260 /** Report how many items are in the _dataToMergeList queue */
261 unsigned int getDataToMergeListSize() const { return static_cast<unsigned int>(_dataToMergeList->size()); }
263 /** Report whether any requests are in the pager.*/
264 bool getRequestsInProgress() const;
266 /** Get the minimum time between the first request for a tile to be loaded and the time of its merge into the main scene graph.*/
267 double getMinimumTimeToMergeTile() const { return _minimumTimeToMergeTile; }
269 /** Get the maximum time between the first request for a tile to be loaded and the time of its merge into the main scene graph.*/
270 double getMaximumTimeToMergeTile() const { return _maximumTimeToMergeTile; }
272 /** Get the average time between the first request for a tile to be loaded and the time of its merge into the main scene graph.*/
273 double getAverageTimeToMergeTiles() const { return (_numTilesMerges > 0) ? _totalTimeToMergeTiles/static_cast<double>(_numTilesMerges) : 0; }
275 /** Reset the Stats variables.*/
278 typedef std::set< osg::ref_ptr<osg::StateSet> > StateSetList;
279 typedef std::vector< osg::ref_ptr<osg::Drawable> > DrawableList;
281 class ExpirePagedLODsVisitor;
283 typedef std::list< osg::ref_ptr<osg::Object> > ObjectList;
285 struct PagedLODList : public osg::Referenced
287 virtual PagedLODList* clone() = 0;
288 virtual void clear() = 0;
289 virtual unsigned int size() = 0;
290 virtual void removeExpiredChildren(int numberChildrenToRemove, double expiryTime, unsigned int expiryFrame, ObjectList& childrenRemoved, bool visitActive) = 0;
291 virtual void removeNodes(osg::NodeList& nodesToRemove) = 0;
292 virtual void insertPagedLOD(const osg::observer_ptr<osg::PagedLOD>& plod) = 0;
293 virtual bool containsPagedLOD(const osg::observer_ptr<osg::PagedLOD>& plod) const = 0;
296 void setMarkerObject(osg::Object* mo) { _markerObject = mo; }
297 osg::Object* getMarkerObject() { return _markerObject.get(); }
298 const osg::Object* getMarkerObject() const { return _markerObject.get(); }
302 virtual ~DatabasePager();
304 friend class DatabaseThread;
305 friend struct DatabaseRequest;
309 struct OSGDB_EXPORT DatabaseRequest : public osg::Referenced
312 osg::Referenced(true),
314 _frameNumberFirstRequest(0),
315 _timestampFirstRequest(0.0),
316 _priorityFirstRequest(0.f),
317 _frameNumberLastRequest(0),
318 _timestampLastRequest(0.0),
319 _priorityLastRequest(0.0f),
326 bool valid() const { return _valid; }
328 bool isRequestCurrent (int frameNumber) const
330 return _valid && (frameNumber - _frameNumberLastRequest <= 1);
334 std::string _fileName;
335 unsigned int _frameNumberFirstRequest;
336 double _timestampFirstRequest;
337 float _priorityFirstRequest;
338 unsigned int _frameNumberLastRequest;
339 double _timestampLastRequest;
340 float _priorityLastRequest;
341 unsigned int _numOfRequests;
343 osg::observer_ptr<osg::Node> _terrain;
344 osg::observer_ptr<osg::Group> _group;
346 osg::ref_ptr<osg::Node> _loadedModel;
347 osg::ref_ptr<Options> _loadOptions;
348 osg::ref_ptr<ObjectCache> _objectCache;
350 osg::observer_ptr<osgUtil::IncrementalCompileOperation::CompileSet> _compileSet;
351 bool _groupExpired; // flag used only in update thread
355 struct OSGDB_EXPORT RequestQueue : public osg::Referenced
359 RequestQueue(DatabasePager* pager);
361 void add(DatabaseRequest* databaseRequest);
362 void remove(DatabaseRequest* databaseRequest);
364 void addNoLock(DatabaseRequest* databaseRequest);
366 void takeFirst(osg::ref_ptr<DatabaseRequest>& databaseRequest);
368 /// prune all the old requests and then return true if requestList left empty
369 bool pruneOldRequestsAndCheckIfEmpty();
371 virtual void updateBlock() {}
373 void invalidate(DatabaseRequest* dr);
382 typedef std::list< osg::ref_ptr<DatabaseRequest> > RequestList;
383 void swap(RequestList& requestList);
385 DatabasePager* _pager;
386 RequestList _requestList;
387 OpenThreads::Mutex _requestMutex;
388 unsigned int _frameNumberLastPruned;
391 virtual ~RequestQueue();
395 typedef std::vector< osg::ref_ptr<DatabaseThread> > DatabaseThreadList;
397 struct OSGDB_EXPORT ReadQueue : public RequestQueue
399 ReadQueue(DatabasePager* pager, const std::string& name);
401 void block() { _block->block(); }
403 void release() { _block->release(); }
405 virtual void updateBlock();
408 osg::ref_ptr<osg::RefBlock> _block;
412 OpenThreads::Mutex _childrenToDeleteListMutex;
413 ObjectList _childrenToDeleteList;
416 // forward declare inner helper classes
417 class FindCompileableGLObjectsVisitor;
418 friend class FindCompileableGLObjectsVisitor;
420 struct DatabasePagerCompileCompletedCallback;
421 friend struct DatabasePagerCompileCompletedCallback;
423 class FindPagedLODsVisitor;
424 friend class FindPagedLODsVisitor;
426 struct SortFileRequestFunctor;
427 friend struct SortFileRequestFunctor;
430 OpenThreads::Mutex _run_mutex;
431 OpenThreads::Mutex _dr_mutex;
432 bool _startThreadCalled;
434 void compileCompleted(DatabaseRequest* databaseRequest);
436 /** Iterate through the active PagedLOD nodes children removing
437 * children which haven't been visited since specified expiryTime.
438 * note, should be only be called from the update thread. */
439 virtual void removeExpiredSubgraphs(const osg::FrameStamp &frameStamp);
441 /** Add the loaded data to the scene graph.*/
442 void addLoadedDataToSceneGraph(const osg::FrameStamp &frameStamp);
445 OpenThreads::Affinity _affinity;
448 bool _acceptNewRequests;
449 bool _databasePagerThreadPaused;
451 DatabaseThreadList _databaseThreads;
453 int _numFramesActive;
454 mutable OpenThreads::Mutex _numFramesActiveMutex;
455 OpenThreads::Atomic _frameNumber;
457 osg::ref_ptr<ReadQueue> _fileRequestQueue;
458 osg::ref_ptr<ReadQueue> _httpRequestQueue;
459 osg::ref_ptr<RequestQueue> _dataToCompileList;
460 osg::ref_ptr<RequestQueue> _dataToMergeList;
462 DrawablePolicy _drawablePolicy;
464 bool _assignPBOToImages;
465 bool _changeAutoUnRef;
466 bool _valueAutoUnRef;
467 bool _changeAnisotropy;
468 float _valueAnisotropy;
470 bool _deleteRemovedSubgraphsInDatabaseThread;
473 osg::ref_ptr<PagedLODList> _activePagedLODList;
475 unsigned int _targetMaximumNumberOfPageLOD;
478 osg::ref_ptr<osgUtil::IncrementalCompileOperation> _incrementalCompileOperation;
481 double _minimumTimeToMergeTile;
482 double _maximumTimeToMergeTile;
483 double _totalTimeToMergeTiles;
484 unsigned int _numTilesMerges;
486 osg::ref_ptr<osg::Object> _markerObject;