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.
|
||||
//
|
||||
@@ -51,10 +51,9 @@ void TileCache::entry_free( long cache_index ) {
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING CACHE ENTRY = " << cache_index );
|
||||
TileEntry *tile = tile_cache[cache_index];
|
||||
tile->removeFromSceneGraph();
|
||||
tile_cache.erase( cache_index );
|
||||
|
||||
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.
|
||||
long TileCache::get_oldest_tile() {
|
||||
// we need to free the furthest entry
|
||||
long TileCache::get_drop_tile() {
|
||||
long min_index = -1;
|
||||
double timestamp = 0.0;
|
||||
double min_time = DBL_MAX;
|
||||
float priority = FLT_MAX;
|
||||
|
||||
tile_map_iterator current = tile_cache.begin();
|
||||
tile_map_iterator end = tile_cache.end();
|
||||
@@ -98,20 +96,27 @@ long TileCache::get_oldest_tile() {
|
||||
for ( ; current != end; ++current ) {
|
||||
long index = current->first;
|
||||
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
|
||||
}
|
||||
else
|
||||
if ( e->is_loaded() ) {
|
||||
timestamp = e->get_timestamp();
|
||||
if ( timestamp < min_time ) {
|
||||
min_time = timestamp;
|
||||
if (e->is_expired(current_time - 1.0)&&
|
||||
!e->is_loaded())
|
||||
{
|
||||
/* Immediately drop "empty" tiles which are no longer used/requested, and were last requested > 1 second ago...
|
||||
* Allow a 1 second timeout since an empty tiles may just be loaded...
|
||||
*/
|
||||
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;
|
||||
}
|
||||
} 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
|
||||
// external tile scheduler can flag the inner ring correctly.
|
||||
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()
|
||||
// Clear all flags indicating tiles belonging to the current view
|
||||
void TileCache::clear_current_view()
|
||||
{
|
||||
tile_map_iterator current = tile_cache.begin();
|
||||
tile_map_iterator end = tile_cache.end();
|
||||
|
||||
for ( ; current != end; ++current ) {
|
||||
TileEntry *e = current->second;
|
||||
if (e->get_cache_lock())
|
||||
if (e->is_current_view())
|
||||
{
|
||||
// update timestamps for tiles belonging to most recent position
|
||||
e->set_timestamp( current_time );
|
||||
e->set_cache_lock( false );
|
||||
// update expiry time for tiles belonging to most recent position
|
||||
e->update_time_expired( current_time );
|
||||
e->set_current_view( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,8 +179,31 @@ bool TileCache::insert_tile( TileEntry *e ) {
|
||||
// register tile in the cache
|
||||
long tile_index = e->get_tile_bucket().gen_index();
|
||||
tile_cache[tile_index] = e;
|
||||
e->set_timestamp(current_time);
|
||||
e->update_time_expired(current_time);
|
||||
|
||||
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.
|
||||
//
|
||||
@@ -49,11 +49,11 @@ private:
|
||||
// pointers to allow an external linear traversal of cache entries
|
||||
tile_map_iterator current;
|
||||
|
||||
double current_time;
|
||||
|
||||
// Free a tile cache entry
|
||||
void entry_free( long cache_index );
|
||||
|
||||
double current_time;
|
||||
|
||||
public:
|
||||
tile_map_iterator begin() { return tile_cache.begin(); }
|
||||
tile_map_iterator end() { return tile_cache.end(); }
|
||||
@@ -72,18 +72,12 @@ public:
|
||||
// Check if the specified "bucket" exists in the cache
|
||||
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.
|
||||
long get_oldest_tile();
|
||||
|
||||
// Clear the inner ring flag for all tiles in the cache so that
|
||||
// the external tile scheduler can flag the inner ring correctly.
|
||||
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();
|
||||
long get_drop_tile();
|
||||
|
||||
// Clear all flags indicating tiles belonging to the current view
|
||||
void clear_current_view();
|
||||
|
||||
// Clear a cache entry, note that the cache only holds pointers
|
||||
// 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; }
|
||||
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");
|
||||
}
|
||||
|
||||
#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
|
||||
TileEntry::TileEntry ( const SGBucket& b )
|
||||
: tile_bucket( b ),
|
||||
tileFileName(b.gen_index_str()),
|
||||
_node( new osg::LOD ),
|
||||
is_inner_ring(false)
|
||||
,is_cache_locked(false)
|
||||
#ifndef USE_CULLCALLBACK_TS
|
||||
,timestamp(0.0)
|
||||
#endif
|
||||
_priority(-FLT_MAX),
|
||||
_current_view(false),
|
||||
_time_expired(-1.0)
|
||||
{
|
||||
#ifdef USE_CULLCALLBACK_TS
|
||||
_node->setCullCallback(new TileCullCallback);
|
||||
#endif
|
||||
tileFileName += ".stg";
|
||||
_node->setName(tileFileName);
|
||||
// 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);
|
||||
|
||||
/**
|
||||
* this value is used by the tile scheduler/loader to mark which
|
||||
* tiles are in the primary ring (i.e. the current tile or the
|
||||
* surrounding eight.) Other routines then can use this as an
|
||||
* optimization and not do some operation to tiles outside of this
|
||||
* inner ring. (For instance vasi color updating)
|
||||
* This value is used by the tile scheduler/loader to load tiles
|
||||
* in a useful sequence. The priority is set to reflect the tiles
|
||||
* distance from the center, so all tiles are loaded in an innermost
|
||||
* to outermost sequence.
|
||||
*/
|
||||
bool is_inner_ring;
|
||||
bool is_cache_locked;
|
||||
double timestamp;
|
||||
float _priority;
|
||||
/** Flag indicating if tile belongs to current view. */
|
||||
bool _current_view;
|
||||
/** Time when tile expires. */
|
||||
double _time_expired;
|
||||
|
||||
static ModelLoadHelper *_modelLoader;
|
||||
|
||||
@@ -136,13 +137,19 @@ public:
|
||||
*/
|
||||
osg::LOD *getNode() const { return _node.get(); }
|
||||
|
||||
double get_timestamp() const;
|
||||
void set_timestamp( double time_ms );
|
||||
inline double get_time_expired() const { return _time_expired; }
|
||||
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_inner_ring( bool val ) { is_inner_ring = val; }
|
||||
inline void set_cache_lock( bool val ) { is_cache_locked = val; }
|
||||
inline bool get_cache_lock() const { return is_cache_locked; }
|
||||
inline void set_priority(float priority) { _priority=priority; }
|
||||
inline float get_priority() const { return _priority; }
|
||||
inline void set_current_view(bool current_view) { _current_view = current_view; }
|
||||
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
|
||||
// this to the pager.
|
||||
|
||||
Reference in New Issue
Block a user