]> git.mxchange.org Git - flightgear.git/blobdiff - src/Scenery/tilecache.cxx
Fix native protocol crashes.
[flightgear.git] / src / Scenery / tilecache.cxx
index c996087f295f5cc43c7da6fa2786cebbf446a79a..172b604ff5f662dcd6b90cbf3fb18fed8f4bc067 100644 (file)
@@ -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) 1997  Curtis L. Olson  - curt@infoplane.com
+// 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
 //
 // 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 <config.h>
 #endif
 
-#ifdef HAVE_WINDOWS_H
-#  include <windows.h>
-#endif
-
-#include <GL/glut.h>
-#include <XGL/xgl.h>
-
-#include <ssg.h>               // plib include
-
-#include <Debug/logstream.hxx>
-#include <Airports/genapt.hxx>
-#include <Bucket/newbucket.hxx>
-#include <Main/options.hxx>
-#include <Main/views.hxx>
-#include <Misc/fgpath.hxx>
-#include <Objects/obj.hxx>
+#include <simgear/bucket/newbucket.hxx>
+#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/sg_path.hxx>
 
-#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;
-    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();
-    }
-    FG_LOG( FG_TERRAIN, FG_DEBUG, "  done with init()"  );
+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() );
+
+    clear_cache();
+
+    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() );
 }
 
 
-// Fill in a tile cache entry with real data for the specified bucket
-void
-FGTileCache::fill_in( int index, const FGBucket& p )
-{
-    // 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].mark_loaded();
-    tile_cache[index].tile_bucket = p;
-    tile_cache[index].branch_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 );
+// 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;
+            }
+        }
     }
-    tile_cache[index].branch_ptr->addKid( tile_cache[index].range_ptr );
-    terrain->addKid( tile_cache[index].branch_ptr );
-
-    // cout << " ncount before = " << tile_cache[index].ncount << "\n";
-    // cout << " fragments before = " << tile_cache[index].fragment_list.size()
-    //      << "\n";
 
-    string apt_path = tile_path.str();
-    apt_path += ".apt";
-    fgAptGenerate( apt_path, &tile_cache[index] );
+    SG_LOG( SG_TERRAIN, SG_DEBUG, "    index = " << min_index );
+    SG_LOG( SG_TERRAIN, SG_DEBUG, "    min_time = " << min_time );
 
-    // cout << " ncount after = " << tile_cache[index].ncount << "\n";
-    // cout << " fragments after = " << tile_cache[index].fragment_list.size()
-    //      << "\n";
+    return min_index;
 }
 
 
-// Free a tile cache entry
-void
-FGTileCache::entry_free( int index )
+// Clear all flags indicating tiles belonging to the current view
+void TileCache::clear_current_view()
 {
-    tile_cache[index].free_tile();
+    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 );
+        }
+    }
 }
 
+// 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 );
+}
 
-// Return index of next available slot in tile cache
-int
-FGTileCache::next_avail( void )
-{
-    Point3D delta, 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() ) {
-           // 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;
-
-           FG_LOG( FG_TERRAIN, FG_DEBUG, "    distance = " << dist );
-
-           if ( dist > max_dist ) {
-               max_dist = dist;
-               max_index = i;
-           }
-       }
+
+// Clear all completely loaded tiles (ignores partially loaded tiles)
+void TileCache::clear_cache() {
+    std::vector<long> 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);
 
-    // 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.
-    
-    entry_free( max_index );
-    return( max_index );
+    return true;
 }
 
+/**
+ * Reloads a tile when it's already in memory.
+ */
+void TileCache::refresh_tile(long tile_index)
+{
+    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 );
+    }
+}