Improved tile cache priority scheme.
Use priorities for loading/unloading. Maintain an expiry time for each tile. Replaced "cache lock" by "current view" flag.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
// newcache.cxx -- routines to handle scenery tile caching
|
// TileCache.cxx -- routines to handle scenery tile caching
|
||||||
//
|
//
|
||||||
// Written by Curtis Olson, started December 2000.
|
// Written by Curtis Olson, started December 2000.
|
||||||
//
|
//
|
||||||
@@ -51,10 +51,9 @@ void TileCache::entry_free( long cache_index ) {
|
|||||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING CACHE ENTRY = " << cache_index );
|
SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING CACHE ENTRY = " << cache_index );
|
||||||
TileEntry *tile = tile_cache[cache_index];
|
TileEntry *tile = tile_cache[cache_index];
|
||||||
tile->removeFromSceneGraph();
|
tile->removeFromSceneGraph();
|
||||||
|
tile_cache.erase( cache_index );
|
||||||
|
|
||||||
delete tile;
|
delete tile;
|
||||||
|
|
||||||
tile_cache.erase( cache_index );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -84,13 +83,12 @@ bool TileCache::exists( const SGBucket& b ) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Return the index of the oldest tile in the cache, return -1 if
|
// Return the index of a tile to be dropped from the cache, return -1 if
|
||||||
// nothing available to be removed.
|
// nothing available to be removed.
|
||||||
long TileCache::get_oldest_tile() {
|
long TileCache::get_drop_tile() {
|
||||||
// we need to free the furthest entry
|
|
||||||
long min_index = -1;
|
long min_index = -1;
|
||||||
double timestamp = 0.0;
|
|
||||||
double min_time = DBL_MAX;
|
double min_time = DBL_MAX;
|
||||||
|
float priority = FLT_MAX;
|
||||||
|
|
||||||
tile_map_iterator current = tile_cache.begin();
|
tile_map_iterator current = tile_cache.begin();
|
||||||
tile_map_iterator end = tile_cache.end();
|
tile_map_iterator end = tile_cache.end();
|
||||||
@@ -98,20 +96,27 @@ long TileCache::get_oldest_tile() {
|
|||||||
for ( ; current != end; ++current ) {
|
for ( ; current != end; ++current ) {
|
||||||
long index = current->first;
|
long index = current->first;
|
||||||
TileEntry *e = current->second;
|
TileEntry *e = current->second;
|
||||||
if (e->get_cache_lock())
|
if (( !e->is_current_view() )&&
|
||||||
|
( e->is_expired(current_time) ))
|
||||||
{
|
{
|
||||||
// tile locked to cache => must not be dropped
|
if (e->is_expired(current_time - 1.0)&&
|
||||||
}
|
!e->is_loaded())
|
||||||
else
|
{
|
||||||
if ( e->is_loaded() ) {
|
/* Immediately drop "empty" tiles which are no longer used/requested, and were last requested > 1 second ago...
|
||||||
timestamp = e->get_timestamp();
|
* Allow a 1 second timeout since an empty tiles may just be loaded...
|
||||||
if ( timestamp < min_time ) {
|
*/
|
||||||
min_time = timestamp;
|
SG_LOG( SG_TERRAIN, SG_DEBUG, " dropping an unused and empty tile");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (( e->get_time_expired() < min_time )||
|
||||||
|
(( e->get_time_expired() == min_time)&&
|
||||||
|
( priority > e->get_priority())))
|
||||||
|
{
|
||||||
|
// drop oldest tile with lowest priority
|
||||||
|
min_time = e->get_time_expired();
|
||||||
|
priority = e->get_priority();
|
||||||
min_index = index;
|
min_index = index;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "loaded = " << e->is_loaded()
|
|
||||||
<< " time stamp = " << e->get_timestamp() );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,35 +127,19 @@ long TileCache::get_oldest_tile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Clear the inner ring flag for all tiles in the cache so that the
|
// Clear all flags indicating tiles belonging to the current view
|
||||||
// external tile scheduler can flag the inner ring correctly.
|
void TileCache::clear_current_view()
|
||||||
void TileCache::clear_inner_ring_flags() {
|
|
||||||
tile_map_iterator current = tile_cache.begin();
|
|
||||||
tile_map_iterator end = tile_cache.end();
|
|
||||||
|
|
||||||
for ( ; current != end; ++current ) {
|
|
||||||
TileEntry *e = current->second;
|
|
||||||
//if ( e->is_loaded() ) {
|
|
||||||
e->set_inner_ring( false );
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear all locked flags for all tiles in the cache.
|
|
||||||
// (Tiles belonging to the current position are locked to
|
|
||||||
// the cache to prevent them from being dropped).
|
|
||||||
void TileCache::clear_cache_lock_flags()
|
|
||||||
{
|
{
|
||||||
tile_map_iterator current = tile_cache.begin();
|
tile_map_iterator current = tile_cache.begin();
|
||||||
tile_map_iterator end = tile_cache.end();
|
tile_map_iterator end = tile_cache.end();
|
||||||
|
|
||||||
for ( ; current != end; ++current ) {
|
for ( ; current != end; ++current ) {
|
||||||
TileEntry *e = current->second;
|
TileEntry *e = current->second;
|
||||||
if (e->get_cache_lock())
|
if (e->is_current_view())
|
||||||
{
|
{
|
||||||
// update timestamps for tiles belonging to most recent position
|
// update expiry time for tiles belonging to most recent position
|
||||||
e->set_timestamp( current_time );
|
e->update_time_expired( current_time );
|
||||||
e->set_cache_lock( false );
|
e->set_current_view( false );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,8 +179,31 @@ bool TileCache::insert_tile( TileEntry *e ) {
|
|||||||
// register tile in the cache
|
// register tile in the cache
|
||||||
long tile_index = e->get_tile_bucket().gen_index();
|
long tile_index = e->get_tile_bucket().gen_index();
|
||||||
tile_cache[tile_index] = e;
|
tile_cache[tile_index] = e;
|
||||||
e->set_timestamp(current_time);
|
e->update_time_expired(current_time);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update tile's priority and expiry time according to current request
|
||||||
|
void TileCache::request_tile(TileEntry* t,float priority,bool current_view,double request_time)
|
||||||
|
{
|
||||||
|
if ((!current_view)&&(request_time<=0.0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// update priority when hire - or old has expired
|
||||||
|
if ((t->is_expired(current_time))||
|
||||||
|
(priority > t->get_priority()))
|
||||||
|
{
|
||||||
|
t->set_priority( priority );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_view)
|
||||||
|
{
|
||||||
|
t->update_time_expired( current_time );
|
||||||
|
t->set_current_view( true );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t->update_time_expired( current_time+request_time );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// newcache.hxx -- routines to handle scenery tile caching
|
// TileCache.hxx -- routines to handle scenery tile caching
|
||||||
//
|
//
|
||||||
// Written by Curtis Olson, started December 2000.
|
// Written by Curtis Olson, started December 2000.
|
||||||
//
|
//
|
||||||
@@ -49,11 +49,11 @@ private:
|
|||||||
// pointers to allow an external linear traversal of cache entries
|
// pointers to allow an external linear traversal of cache entries
|
||||||
tile_map_iterator current;
|
tile_map_iterator current;
|
||||||
|
|
||||||
|
double current_time;
|
||||||
|
|
||||||
// Free a tile cache entry
|
// Free a tile cache entry
|
||||||
void entry_free( long cache_index );
|
void entry_free( long cache_index );
|
||||||
|
|
||||||
double current_time;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
tile_map_iterator begin() { return tile_cache.begin(); }
|
tile_map_iterator begin() { return tile_cache.begin(); }
|
||||||
tile_map_iterator end() { return tile_cache.end(); }
|
tile_map_iterator end() { return tile_cache.end(); }
|
||||||
@@ -72,18 +72,12 @@ public:
|
|||||||
// Check if the specified "bucket" exists in the cache
|
// Check if the specified "bucket" exists in the cache
|
||||||
bool exists( const SGBucket& b ) const;
|
bool exists( const SGBucket& b ) const;
|
||||||
|
|
||||||
// Return the index of the oldest tile in the cache, return -1 if
|
// Return the index of a tile to be dropped from the cache, return -1 if
|
||||||
// nothing available to be removed.
|
// nothing available to be removed.
|
||||||
long get_oldest_tile();
|
long get_drop_tile();
|
||||||
|
|
||||||
// Clear the inner ring flag for all tiles in the cache so that
|
// Clear all flags indicating tiles belonging to the current view
|
||||||
// the external tile scheduler can flag the inner ring correctly.
|
void clear_current_view();
|
||||||
void clear_inner_ring_flags();
|
|
||||||
|
|
||||||
// Clear all locked flags for all tiles in the cache.
|
|
||||||
// (Tiles belonging to the current position are locked to
|
|
||||||
// the cache to prevent them from being dropped).
|
|
||||||
void clear_cache_lock_flags();
|
|
||||||
|
|
||||||
// Clear a cache entry, note that the cache only holds pointers
|
// Clear a cache entry, note that the cache only holds pointers
|
||||||
// and this does not free the object which is pointed to.
|
// and this does not free the object which is pointed to.
|
||||||
@@ -131,6 +125,9 @@ public:
|
|||||||
|
|
||||||
void set_current_time(double val) { current_time = val; }
|
void set_current_time(double val) { current_time = val; }
|
||||||
double get_current_time() const { return current_time; }
|
double get_current_time() const { return current_time; }
|
||||||
|
|
||||||
|
// update tile's priority and expiry time according to current request
|
||||||
|
void request_tile(TileEntry* t,float priority,bool current_view,double requesttime);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,77 +64,16 @@ osgDB::RegisterReaderWriterProxy<ReaderWriterSTG> g_readerWriterSTGProxy;
|
|||||||
ModelRegistryCallbackProxy<LoadOnlyCallback> g_stgCallbackProxy("stg");
|
ModelRegistryCallbackProxy<LoadOnlyCallback> g_stgCallbackProxy("stg");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_CULLCALLBACK_TS
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// Update the timestamp on a tile whenever it is in view.
|
|
||||||
|
|
||||||
class TileCullCallback : public osg::NodeCallback
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TileCullCallback() : _timeStamp(0) {}
|
|
||||||
TileCullCallback(const TileCullCallback& tc, const osg::CopyOp& copyOp) :
|
|
||||||
NodeCallback(tc, copyOp), _timeStamp(tc._timeStamp)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
|
|
||||||
double getTimeStamp() const { return _timeStamp; }
|
|
||||||
void setTimeStamp(double timeStamp) { _timeStamp = timeStamp; }
|
|
||||||
protected:
|
|
||||||
double _timeStamp;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void TileCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
|
|
||||||
{
|
|
||||||
if (nv->getFrameStamp())
|
|
||||||
_timeStamp = nv->getFrameStamp()->getReferenceTime();
|
|
||||||
traverse(node, nv);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
double TileEntry::get_timestamp() const
|
|
||||||
{
|
|
||||||
#ifdef USE_CULLCALLBACK_TS
|
|
||||||
if (_node.valid()) {
|
|
||||||
return (dynamic_cast<TileCullCallback*>(_node->getCullCallback()))
|
|
||||||
->getTimeStamp();
|
|
||||||
} else
|
|
||||||
return DBL_MAX;
|
|
||||||
#else
|
|
||||||
return timestamp;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void TileEntry::set_timestamp(double time_ms)
|
|
||||||
{
|
|
||||||
#ifdef USE_CULLCALLBACK_TS
|
|
||||||
if (_node.valid()) {
|
|
||||||
TileCullCallback* cb
|
|
||||||
= dynamic_cast<TileCullCallback*>(_node->getCullCallback());
|
|
||||||
if (cb)
|
|
||||||
cb->setTimeStamp(time_ms);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
timestamp = time_ms;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
TileEntry::TileEntry ( const SGBucket& b )
|
TileEntry::TileEntry ( const SGBucket& b )
|
||||||
: tile_bucket( b ),
|
: tile_bucket( b ),
|
||||||
tileFileName(b.gen_index_str()),
|
tileFileName(b.gen_index_str()),
|
||||||
_node( new osg::LOD ),
|
_node( new osg::LOD ),
|
||||||
is_inner_ring(false)
|
_priority(-FLT_MAX),
|
||||||
,is_cache_locked(false)
|
_current_view(false),
|
||||||
#ifndef USE_CULLCALLBACK_TS
|
_time_expired(-1.0)
|
||||||
,timestamp(0.0)
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
#ifdef USE_CULLCALLBACK_TS
|
|
||||||
_node->setCullCallback(new TileCullCallback);
|
|
||||||
#endif
|
|
||||||
tileFileName += ".stg";
|
tileFileName += ".stg";
|
||||||
_node->setName(tileFileName);
|
_node->setName(tileFileName);
|
||||||
// Give a default LOD range so that traversals that traverse
|
// Give a default LOD range so that traversals that traverse
|
||||||
@@ -462,4 +401,3 @@ TileEntry::removeFromSceneGraph()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,15 +74,16 @@ private:
|
|||||||
const osgDB::ReaderWriter::Options* options);
|
const osgDB::ReaderWriter::Options* options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* this value is used by the tile scheduler/loader to mark which
|
* This value is used by the tile scheduler/loader to load tiles
|
||||||
* tiles are in the primary ring (i.e. the current tile or the
|
* in a useful sequence. The priority is set to reflect the tiles
|
||||||
* surrounding eight.) Other routines then can use this as an
|
* distance from the center, so all tiles are loaded in an innermost
|
||||||
* optimization and not do some operation to tiles outside of this
|
* to outermost sequence.
|
||||||
* inner ring. (For instance vasi color updating)
|
|
||||||
*/
|
*/
|
||||||
bool is_inner_ring;
|
float _priority;
|
||||||
bool is_cache_locked;
|
/** Flag indicating if tile belongs to current view. */
|
||||||
double timestamp;
|
bool _current_view;
|
||||||
|
/** Time when tile expires. */
|
||||||
|
double _time_expired;
|
||||||
|
|
||||||
static ModelLoadHelper *_modelLoader;
|
static ModelLoadHelper *_modelLoader;
|
||||||
|
|
||||||
@@ -136,13 +137,19 @@ public:
|
|||||||
*/
|
*/
|
||||||
osg::LOD *getNode() const { return _node.get(); }
|
osg::LOD *getNode() const { return _node.get(); }
|
||||||
|
|
||||||
double get_timestamp() const;
|
inline double get_time_expired() const { return _time_expired; }
|
||||||
void set_timestamp( double time_ms );
|
inline void update_time_expired( double time_expired ) { if (_time_expired<time_expired) _time_expired = time_expired; }
|
||||||
|
|
||||||
inline bool get_inner_ring() const { return is_inner_ring; }
|
inline void set_priority(float priority) { _priority=priority; }
|
||||||
inline void set_inner_ring( bool val ) { is_inner_ring = val; }
|
inline float get_priority() const { return _priority; }
|
||||||
inline void set_cache_lock( bool val ) { is_cache_locked = val; }
|
inline void set_current_view(bool current_view) { _current_view = current_view; }
|
||||||
inline bool get_cache_lock() const { return is_cache_locked; }
|
inline bool is_current_view() const { return _current_view; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the tile entry is still needed, otherwise return false
|
||||||
|
* indicating that the tile is no longer in active use.
|
||||||
|
*/
|
||||||
|
inline bool is_expired(double current_time) const { return (_current_view) ? false : (current_time > _time_expired); }
|
||||||
|
|
||||||
// Get the ref_ptr to the DatabaseRequest object, in order to pass
|
// Get the ref_ptr to the DatabaseRequest object, in order to pass
|
||||||
// this to the pager.
|
// this to the pager.
|
||||||
|
|||||||
Reference in New Issue
Block a user