X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FScenery%2Ftilecache.cxx;h=172b604ff5f662dcd6b90cbf3fb18fed8f4bc067;hb=948aa70af78626584042b70fdf3c3babfb5aa804;hp=bed8713911da7fe598d88dcb4b447a48d2190dee;hpb=095c069b3966c251b8554d8c2b76d74ff30f9621;p=flightgear.git diff --git a/src/Scenery/tilecache.cxx b/src/Scenery/tilecache.cxx index bed871391..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,323 +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 // plib include +#include +#include +#include -#include -#include -#include -#include -#include -#include
-#include
-#include -#include -#include // for scenery.center - -#include "tilecache.hxx" #include "tileentry.hxx" +#include "tilecache.hxx" - -// a cheesy hack (to be fixed later) -extern ssgBranch *terrain; -extern ssgEntity *penguin; +TileCache::TileCache( void ) : + max_cache_size(100), current_time(0.0) +{ + tile_cache.clear(); +} -// the tile cache -FGTileCache global_tile_cache; +TileCache::~TileCache( void ) { + clear_cache(); +} -// Constructor -FGTileCache::FGTileCache( void ) { - tile_cache.clear(); +// 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; } // 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; - e.mark_unused(); - e.vec3_ptrs.clear(); - e.vec2_ptrs.clear(); - e.index_ptrs.clear(); - - FG_LOG( FG_TERRAIN, FG_DEBUG, " size of tile = " - << sizeof( e ) ); - if ( target_cache_size > (int)tile_cache.size() ) { - // FGTileEntry e; - 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(); - } +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() ); - // and ... just in case we missed something ... - terrain->removeAllKids(); + clear_cache(); - FG_LOG( FG_TERRAIN, FG_DEBUG, " done with init()" ); + SG_LOG( SG_TERRAIN, SG_INFO, " done with init()" ); } // Search for the specified "bucket" in the cache -int -FGTileCache::exists( const FGBucket& p ) -{ - 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 ); - } - } - - return( -1 ); +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() ); } -#if 0 -static void print_refs( ssgSelector *sel, ssgTransform *trans, - ssgRangeSelector *range) -{ - cout << "selector -> " << sel->getRef() - << " transform -> " << trans->getRef() - << " range -> " << range->getRef() << endl; +// 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; + } + } + } + + SG_LOG( SG_TERRAIN, SG_DEBUG, " index = " << min_index ); + SG_LOG( SG_TERRAIN, SG_DEBUG, " min_time = " << min_time ); + + return min_index; } -#endif -// Fill in a tile cache entry with real data for the specified bucket -void -FGTileCache::fill_in( int index, const FGBucket& p ) +// Clear all flags indicating tiles belonging to the current view +void TileCache::clear_current_view() { - // cout << "FILL IN CACHE ENTRY = " << index << endl; - - tile_cache[index].center = Point3D( 0.0 ); - if ( tile_cache[index].vec3_ptrs.size() || - tile_cache[index].vec2_ptrs.size() || - tile_cache[index].index_ptrs.size() ) - { - FG_LOG( FG_TERRAIN, FG_ALERT, - "Attempting to overwrite existing or" - << " not properly freed leaf data." ); - exit(-1); + 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 ); + } } +} - tile_cache[index].select_ptr = new ssgSelector; - tile_cache[index].transform_ptr = new ssgTransform; - tile_cache[index].range_ptr = new ssgRangeSelector; - tile_cache[index].tile_bucket = p; - - FGPath tile_path( current_options.get_fg_root() ); - tile_path.append( "Scenery" ); - tile_path.append( p.gen_base_path() ); - - // Load the appropriate data file and build tile fragment list - FGPath tile_base = tile_path; - tile_base.append( p.gen_index_str() ); - ssgBranch *new_tile = fgObjLoad( tile_base.str(), &tile_cache[index], - true ); - - if ( new_tile != NULL ) { - tile_cache[index].range_ptr->addKid( new_tile ); - } - - // load custom objects - cout << "CUSTOM OBJECTS" << endl; - - FGPath index_path = tile_path; - index_path.append( p.gen_index_str() ); - index_path.concat( ".ind" ); - - cout << "Looking in " << index_path.str() << endl; - - fg_gzifstream in( index_path.str() ); - - if ( in.is_open() ) { - string token, name; - - while ( ! in.eof() ) { - in >> token; - in >> name; - in >> skipws; - cout << "token = " << token << " name = " << name << endl; - - FGPath custom_path = tile_path; - custom_path.append( name ); - ssgBranch *custom_obj = fgObjLoad( custom_path.str(), - &tile_cache[index], false ); - if ( (new_tile != NULL) && (custom_obj != NULL) ) { - new_tile -> addKid( custom_obj ); - } - } - } +// 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 ); +} - // generate cloud layer - if ( current_options.get_clouds() ) { - ssgLeaf *cloud_layer = fgGenCloudLayer( &tile_cache[index], - current_options.get_clouds_asl() ); - cloud_layer->clrTraversalMaskBits( SSGTRAV_HOT ); - new_tile -> addKid( cloud_layer ); - } - tile_cache[index].transform_ptr->addKid( tile_cache[index].range_ptr ); - - // calculate initial tile offset - tile_cache[index].SetOffset( scenery.center ); - sgCoord sgcoord; - sgSetCoord( &sgcoord, - tile_cache[index].offset.x(), - tile_cache[index].offset.y(), tile_cache[index].offset.z(), - 0.0, 0.0, 0.0 ); - tile_cache[index].transform_ptr->setTransform( &sgcoord ); - - 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); +// 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); + } + } + 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; - } - } - } - - // 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_DEBUG, " max_dist = " << max_dist ); - FG_LOG( FG_TERRAIN, FG_DEBUG, " index = " << max_index ); - entry_free( max_index ); - return( max_index ); - } else { - FG_LOG( FG_TERRAIN, FG_ALERT, "WHOOPS!!! Dying in next_avail()" ); - exit( -1 ); - } -} + const_tile_map_iterator it = tile_cache.find( tile_index ); + if ( it == tile_cache.end() ) + return; + 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 ); + } +}