X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FScenery%2Ftilecache.cxx;h=172b604ff5f662dcd6b90cbf3fb18fed8f4bc067;hb=948aa70af78626584042b70fdf3c3babfb5aa804;hp=2fb9ada68cee73af8c54bac1f612862653c514e9;hpb=58a8b0d10309390e4663b076100dc9db55f835fe;p=flightgear.git diff --git a/src/Scenery/tilecache.cxx b/src/Scenery/tilecache.cxx index 2fb9ada68..172b604ff 100644 --- a/src/Scenery/tilecache.cxx +++ b/src/Scenery/tilecache.cxx @@ -1,8 +1,8 @@ -// tilecache.cxx -- routines to handle scenery tile caching +// TileCache.cxx -- routines to handle scenery tile caching // -// Written by Curtis Olson, started January 1998. +// Written by Curtis Olson, started December 2000. // -// Copyright (C) 1998, 1999 Curtis L. Olson - curt@flightgear.org +// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -16,250 +16,204 @@ // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id$ - #ifdef HAVE_CONFIG_H # include #endif -#ifdef HAVE_WINDOWS_H -# include -#endif +#include +#include +#include -#include -#include +#include "tileentry.hxx" +#include "tilecache.hxx" -#include // plib include +TileCache::TileCache( void ) : + max_cache_size(100), current_time(0.0) +{ + tile_cache.clear(); +} -#include -#include -#include -#include
-#include
-#include -#include -#include "tilecache.hxx" -#include "tileentry.hxx" +TileCache::~TileCache( void ) { + clear_cache(); +} -// a cheesy hack (to be fixed later) -extern ssgBranch *terrain; -extern ssgEntity *penguin; +// Free a tile cache entry +void TileCache::entry_free( long tile_index ) { + SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING CACHE ENTRY = " << tile_index ); + TileEntry *tile = tile_cache[tile_index]; + tile->removeFromSceneGraph(); + tile_cache.erase( tile_index ); + delete tile; +} -// the tile cache -FGTileCache global_tile_cache; +// Initialize the tile cache subsystem +void TileCache::init( void ) { + SG_LOG( SG_TERRAIN, SG_INFO, "Initializing the tile cache." ); + SG_LOG( SG_TERRAIN, SG_INFO, " max cache size = " + << max_cache_size ); + SG_LOG( SG_TERRAIN, SG_INFO, " current cache size = " + << tile_cache.size() ); -// Constructor -FGTileCache::FGTileCache( void ) { - tile_cache.clear(); + clear_cache(); + + SG_LOG( SG_TERRAIN, SG_INFO, " done with init()" ); } -// Initialize the tile cache subsystem -void -FGTileCache::init( void ) -{ - int i; - - FG_LOG( FG_TERRAIN, FG_INFO, "Initializing the tile cache." ); - - // expand cache if needed. For best results ... i.e. to avoid - // tile load problems and blank areas: - // - // target_cache_size >= (current.options.tile_diameter + 1) ** 2 - // - int side = current_options.get_tile_diameter() + 2; - int target_cache_size = (side*side); - FG_LOG( FG_TERRAIN, FG_DEBUG, " target cache size = " - << target_cache_size ); - FG_LOG( FG_TERRAIN, FG_DEBUG, " current cache size = " - << tile_cache.size() ); - FGTileEntry e; - FG_LOG( FG_TERRAIN, FG_DEBUG, " size of tile = " - << sizeof( e ) ); - if ( target_cache_size > (int)tile_cache.size() ) { - // FGTileEntry e; - e.mark_unused(); - int expansion_amt = target_cache_size - (int)tile_cache.size(); - for ( i = 0; i < expansion_amt; ++i ) { - tile_cache.push_back( e ); - FG_LOG( FG_TERRAIN, FG_DEBUG, " expanding cache size = " - << tile_cache.size() ); - } - } - FG_LOG( FG_TERRAIN, FG_DEBUG, " done expanding cache, size = " - << tile_cache.size() ); - - for ( i = 0; i < (int)tile_cache.size(); i++ ) { - if ( !tile_cache[i].is_unused() ) { - entry_free(i); - } - tile_cache[i].mark_unused(); - tile_cache[i].tile_bucket.make_bad(); +// Search for the specified "bucket" in the cache +bool TileCache::exists( const SGBucket& b ) const { + long tile_index = b.gen_index(); + const_tile_map_iterator it = tile_cache.find( tile_index ); + + return ( it != tile_cache.end() ); +} + + +// Return the index of a tile to be dropped from the cache, return -1 if +// nothing available to be removed. +long TileCache::get_drop_tile() { + long min_index = -1; + double min_time = DBL_MAX; + float priority = FLT_MAX; + + tile_map_iterator current = tile_cache.begin(); + tile_map_iterator end = tile_cache.end(); + + for ( ; current != end; ++current ) { + long index = current->first; + TileEntry *e = current->second; + if (( !e->is_current_view() )&& + ( e->is_expired(current_time) )) + { + 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"); + min_index = index; + 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; + } + } } - // and ... just in case we missed something ... - terrain->removeAllKids(); + SG_LOG( SG_TERRAIN, SG_DEBUG, " index = " << min_index ); + SG_LOG( SG_TERRAIN, SG_DEBUG, " min_time = " << min_time ); - FG_LOG( FG_TERRAIN, FG_DEBUG, " done with init()" ); + return min_index; } -// Search for the specified "bucket" in the cache -int -FGTileCache::exists( const FGBucket& p ) +// Clear all flags indicating tiles belonging to the current view +void TileCache::clear_current_view() { - int i; - - for ( i = 0; i < (int)tile_cache.size(); i++ ) { - if ( tile_cache[i].tile_bucket == p ) { - FG_LOG( FG_TERRAIN, FG_DEBUG, - "TILE EXISTS in cache ... index = " << i ); - return( i ); - } + 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_current_view()) + { + // update expiry time for tiles belonging to most recent position + e->update_time_expired( current_time ); + e->set_current_view( false ); + } } - - return( -1 ); +} + +// Clear a cache entry, note that the cache only holds pointers +// and this does not free the object which is pointed to. +void TileCache::clear_entry( long tile_index ) { + tile_cache.erase( tile_index ); } -// Fill in a tile cache entry with real data for the specified bucket -void -FGTileCache::fill_in( int index, const FGBucket& p ) -{ - cout << "FILL IN CACHE ENTRY = " << index << endl; - - // Force some values in case the tile fails to load (i.e. fill - // doesn't exist) - tile_cache[index].center = Point3D( 0.0 ); - tile_cache[index].vtlist = NULL; - tile_cache[index].vnlist = NULL; - tile_cache[index].tclist = NULL; - - // Load the appropriate data file and build tile fragment list - FGPath tile_path( current_options.get_fg_root() ); - tile_path.append( "Scenery" ); - tile_path.append( p.gen_base_path() ); - tile_path.append( p.gen_index_str() ); - - tile_cache[index].tile_bucket = p; - - tile_cache[index].select_ptr = new ssgSelector; - tile_cache[index].transform_ptr = new ssgTransform; - tile_cache[index].range_ptr = new ssgRangeSelector; - - ssgBranch *new_tile = fgObjLoad( tile_path.str(), &tile_cache[index] ); - if ( new_tile != NULL ) { - tile_cache[index].range_ptr->addKid( new_tile ); +// Clear all completely loaded tiles (ignores partially loaded tiles) +void TileCache::clear_cache() { + std::vector indexList; + tile_map_iterator current = tile_cache.begin(); + tile_map_iterator end = tile_cache.end(); + + for ( ; current != end; ++current ) { + long index = current->first; + SG_LOG( SG_TERRAIN, SG_DEBUG, "clearing " << index ); + TileEntry *e = current->second; + if ( e->is_loaded() ) { + e->tile_bucket.make_bad(); + // entry_free modifies tile_cache, so store index and call entry_free() later; + indexList.push_back( index); + } } - tile_cache[index].transform_ptr->addKid( tile_cache[index].range_ptr ); - tile_cache[index].select_ptr->addKid( tile_cache[index].transform_ptr ); - terrain->addKid( tile_cache[index].select_ptr ); - - if ( tile_cache[index].is_scheduled_for_cache() ) { - cout << "FOUND ONE SCHEDULED FOR CACHE" << endl; - // load, but not needed now so disable - tile_cache[index].mark_loaded(); - tile_cache[index].ssg_disable(); - tile_cache[index].select_ptr->select(0); - } else { - cout << "FOUND ONE READY TO LOAD" << endl; - tile_cache[index].mark_loaded(); - tile_cache[index].select_ptr->select(1); + for (unsigned int it = 0; it < indexList.size(); it++) { + entry_free( indexList[ it]); } } +/** + * Create a new tile and schedule it for loading. + */ +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->update_time_expired(current_time); -// Free a tile cache entry -void -FGTileCache::entry_free( int cache_index ) -{ - cout << "FREEING CACHE ENTRY = " << cache_index << endl; - tile_cache[cache_index].free_tile(); + return true; } - -// Return index of next available slot in tile cache -int -FGTileCache::next_avail( void ) +/** + * Reloads a tile when it's already in memory. + */ +void TileCache::refresh_tile(long tile_index) { - // Point3D delta; - Point3D abs_view_pos; - int i; - // float max, med, min, tmp; - float dist, max_dist; - int max_index; - - max_dist = 0.0; - max_index = -1; - - for ( i = 0; i < (int)tile_cache.size(); i++ ) { - // only look at freeing NON-scheduled (i.e. ready to load - // cache entries. This assumes that the cache is always big - // enough for our tile radius! - - if ( tile_cache[i].is_unused() ) { - // favor unused cache slots - return(i); - } else if ( tile_cache[i].is_loaded() || tile_cache[i].is_cached() ) { - // calculate approximate distance from view point - abs_view_pos = current_view.get_abs_view_pos(); - - FG_LOG( FG_TERRAIN, FG_DEBUG, - "DIST Abs view pos = " << abs_view_pos ); - FG_LOG( FG_TERRAIN, FG_DEBUG, - " ref point = " << tile_cache[i].center ); - - /* - delta.setx( fabs(tile_cache[i].center.x() - abs_view_pos.x() ) ); - delta.sety( fabs(tile_cache[i].center.y() - abs_view_pos.y() ) ); - delta.setz( fabs(tile_cache[i].center.z() - abs_view_pos.z() ) ); - - max = delta.x(); med = delta.y(); min = delta.z(); - if ( max < med ) { - tmp = max; max = med; med = tmp; - } - if ( max < min ) { - tmp = max; max = min; min = tmp; - } - dist = max + (med + min) / 4; - */ - - dist = tile_cache[i].center.distance3D( abs_view_pos ); - - FG_LOG( FG_TERRAIN, FG_DEBUG, " distance = " << dist ); - - if ( dist > max_dist ) { - max_dist = dist; - max_index = i; - } - } - } + const_tile_map_iterator it = tile_cache.find( tile_index ); + if ( it == tile_cache.end() ) + return; - // If we made it this far, then there were no open cache entries. - // We will instead free the furthest cache entry and return it's - // index. - - if ( max_index >=0 ) { - FG_LOG( FG_TERRAIN, FG_INFO, " max_dist = " << max_dist ); - FG_LOG( FG_TERRAIN, FG_INFO, " index = " << max_index ); - entry_free( max_index ); - return( max_index ); - } else { - FG_LOG( FG_TERRAIN, FG_ALERT, "WHOOPS!!! Dying in next_avail()" ); - exit( -1 ); - } -} + SG_LOG( SG_TERRAIN, SG_DEBUG, "REFRESHING CACHE ENTRY = " << tile_index ); - -// Destructor -FGTileCache::~FGTileCache( void ) { + if (it->second) + it->second->refresh(); } +// 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 higher - or old request 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 ); + } +}