]> git.mxchange.org Git - flightgear.git/commitdiff
Restructure the way tile freeing is handled. When a tile is removed from
authorcurt <curt>
Thu, 25 Jul 2002 23:59:04 +0000 (23:59 +0000)
committercurt <curt>
Thu, 25 Jul 2002 23:59:04 +0000 (23:59 +0000)
the tile cache it's ssg elements are disconnected from the main ssg scene
graph, and then the tile is thrown on the end of a delete queue.  The
tilemgr->update() routine runs every frame.  It looks at this queue and if
it is non-empty, it incrementally frees the compents of the first tile
on the queue.  When the tile is completely free it is removed from the queue.

The amount of time to free the memory for even a single tile can be quite
substantial, especially with the increased overhead of dynamic/random
ground objects.  This change allows the system to spread the work of freeing
tile memory out over many frames so you don't get a noticable single frame
hit or stutter.

src/Scenery/tileentry.cxx
src/Scenery/tileentry.hxx
src/Scenery/tilemgr.cxx

index 9ebeeb2833a4699ec4eea6e9bda165249a2af421..97d5d372e3ae2739ccee7fb179f5d384ad065e1b 100644 (file)
@@ -58,7 +58,8 @@ FGTileEntry::FGTileEntry ( const SGBucket& b )
       terra_range( new ssgRangeSelector ),
       rwy_lights_range( new ssgRangeSelector ),
       loaded(false),
-      pending_models(0)
+      pending_models(0),
+      free_tracker(0)
 {
     nodes.clear();
 
@@ -691,60 +692,115 @@ void FGTileEntry::sched_removal() {
 #endif
 
 
+// Free "n" leaf elements of an ssg tree.  returns the number of
+// elements freed.  An empty branch node is considered a leaf.  This
+// is intended to spread the load of freeing a complex tile out over
+// several frames.
+static int fgPartialFreeSSGtree( ssgBranch *b, int n ) {
+    if ( n > 0 ) {
+        // we still have some delete budget left
+        int num_deletes = 0;
+        for ( int i = 0; i < b->getNumKids(); ++i ) {
+            ssgEntity *kid = b->getKid(i);
+            if ( kid->isAKindOf( ssgTypeBranch() ) ) {
+                int result = fgPartialFreeSSGtree( (ssgBranch *)kid, n );
+                num_deletes += result;
+                n -= result;
+                if ( kid->getNumKids() == 0 ) {
+                    b->removeKid(i);
+                    num_deletes++;
+                    n--;
+                }
+                if ( n < 0 ) {
+                    break;
+                }
+            } else {
+                b->removeKid(i);
+                num_deletes++;
+            }
+        }
+        return num_deletes;
+    } else {
+        return 0;
+    }
+}
+
+
 // Clean up the memory used by this tile and delete the arrays used by
 // ssg as well as the whole ssg branch
-void FGTileEntry::free_tile() {
+bool FGTileEntry::free_tile() {
     int i;
-    SG_LOG( SG_TERRAIN, SG_INFO,
-           "FREEING TILE = (" << tile_bucket << ")" );
-
+    int delete_size = 100;
     SG_LOG( SG_TERRAIN, SG_DEBUG,
-           "  deleting " << nodes.size() << " nodes" );
-    nodes.clear();
+           "FREEING TILE = (" << tile_bucket << ")" );
 
-    // delete the ssg structures
-    SG_LOG( SG_TERRAIN, SG_DEBUG,
-           "  deleting (leaf data) vertex, normal, and "
-           << " texture coordinate arrays" );
+    if ( !(free_tracker & NODES) ) {
+        SG_LOG( SG_TERRAIN, SG_DEBUG,
+                "  deleting " << nodes.size() << " nodes" );
+        nodes.clear();
 
-    for ( i = 0; i < (int)vec3_ptrs.size(); ++i ) {
-       delete [] vec3_ptrs[i];
-    }
-    vec3_ptrs.clear();
+        free_tracker |= NODES;
+    } else if ( !(free_tracker & VEC_PTRS) ) {
+        // delete the vector pointers
+        SG_LOG( SG_TERRAIN, SG_DEBUG,
+                "  deleting (leaf data) vertex, normal, and "
+                << " texture coordinate arrays" );
 
-    for ( i = 0; i < (int)vec2_ptrs.size(); ++i ) {
-       delete [] vec2_ptrs[i];
-    }
-    vec2_ptrs.clear();
-
-    for ( i = 0; i < (int)index_ptrs.size(); ++i ) {
-       delete index_ptrs[i];
-    }
-    index_ptrs.clear();
+        for ( i = 0; i < (int)vec3_ptrs.size(); ++i ) {
+            delete [] vec3_ptrs[i];
+        }
+        vec3_ptrs.clear();
 
-    // delete the terrain branch (this should already have been
-    // disconnected from the scene graph)
-    ssgDeRefDelete( terra_transform );
+        for ( i = 0; i < (int)vec2_ptrs.size(); ++i ) {
+            delete [] vec2_ptrs[i];
+        }
+        vec2_ptrs.clear();
 
-    if ( gnd_lights_transform ) {
-       // delete the terrain lighting branch (this should already have been
-       // disconnected from the scene graph)
-       ssgDeRefDelete( gnd_lights_transform );
-    }
+        for ( i = 0; i < (int)index_ptrs.size(); ++i ) {
+            delete index_ptrs[i];
+        }
+        index_ptrs.clear();
 
-    if ( rwy_lights_transform ) {
+        free_tracker |= VEC_PTRS;
+    } else if ( !(free_tracker & TERRA_NODE) ) {
+        // delete the terrain branch (this should already have been
+        // disconnected from the scene graph)
+        SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING terra_transform" );
+        if ( fgPartialFreeSSGtree( terra_transform, delete_size ) == 0 ) {
+            ssgDeRefDelete( terra_transform ); // polish off the parent
+            free_tracker |= TERRA_NODE;
+        }
+    } else if ( !(free_tracker & GROUND_LIGHTS) && gnd_lights_transform ) {
+        // delete the terrain lighting branch (this should already have been
+        // disconnected from the scene graph)
+        SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING gnd_lights_transform" );
+        if ( fgPartialFreeSSGtree( gnd_lights_transform, delete_size ) == 0 ) {
+            ssgDeRefDelete( gnd_lights_transform ); // polish off the parent
+            free_tracker |= GROUND_LIGHTS;
+        }
+    } else if ( !(free_tracker & RWY_LIGHTS) && rwy_lights_transform ) {
        // delete the terrain lighting branch (this should already have been
        // disconnected from the scene graph)
-       ssgDeRefDelete( rwy_lights_transform );
-    }
-
-    // ADA
-    if ( lightmaps_transform ) {
+        SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING rwy_lights_transform" );
+       if ( fgPartialFreeSSGtree( rwy_lights_transform, delete_size ) == 0 ) {
+            ssgDeRefDelete( rwy_lights_transform ); // polish off the parent
+            free_tracker |= RWY_LIGHTS;
+        }
+    } else if ( !(free_tracker & LIGHTMAPS) && lightmaps_transform ) {
+        // ADA
        // delete the terrain lighting branch (this should already have been
         // disconnected from the scene graph)
-       ssgDeRefDelete( lightmaps_transform );
+        SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING lightmaps_transform" );
+       if ( fgPartialFreeSSGtree( lightmaps_transform, delete_size ) == 0 ) {
+            ssgDeRefDelete( lightmaps_transform ); // polish off the parent
+            free_tracker |= LIGHTMAPS;
+        }
+    } else {
+        return true;
     }
-    // ADA
+
+    // if we fall down to here, we still have work todo, return false
+    return false;
 }
 
 
index e315d515071cb97c50b0bb5b029ffddce5fe3827..5f413b04732b0454032d9dcc19f54fc49dfe4d51 100644 (file)
@@ -192,6 +192,18 @@ private:
 
     double timestamp;
 
+
+    // this variable tracks the status of the incremental memory freeing.
+    enum {
+        NODES = 0x01,
+        VEC_PTRS = 0x02,
+        TERRA_NODE = 0x04,
+        GROUND_LIGHTS = 0x08,
+        RWY_LIGHTS = 0x10,
+        LIGHTMAPS = 0x20
+    };
+    int free_tracker;
+
 public:
 
     // ADA --->
@@ -212,8 +224,12 @@ public:
 #endif
 
     // Clean up the memory used by this tile and delete the arrays
-    // used by ssg as well as the whole ssg branch
-    void free_tile();
+    // used by ssg as well as the whole ssg branch.  This does a
+    // partial clean up and exits so we can spread the load across
+    // multiple frames.  Returns false if work remaining to be done,
+    // true if dynamically allocated memory used by this tile is
+    // completely freed.
+    bool free_tile();
 
     // Calculate this tile's offset
     void SetOffset( const Point3D& p)
index 2d3dd9dda301e63e293780be21fae19a9c319b44..aaf6b0da3c5fad781b2195e7d4060bc5b193d4a6 100644 (file)
@@ -70,8 +70,8 @@ FGTileMgr::FGTileMgr():
     state( Start ),
     current_tile( NULL ),
     vis( 16000 ),
-    counter_hack(0),
-    max_cache_size(100)
+    max_cache_size(100),
+    counter_hack(0)
 {
 }
 
@@ -131,10 +131,11 @@ void FGTileMgr::sched_tile( const SGBucket& b ) {
 
     if ( t == NULL ) {
         // make space in the cache
-        while ( tile_cache.get_size() > max_cache_size ) {
+        while ( (int)tile_cache.get_size() > max_cache_size ) {
             long index = tile_cache.get_oldest_tile();
             if ( index >= 0 ) {
                 FGTileEntry *old = tile_cache.get_tile( index );
+                old->disconnect_ssg_nodes();
                 delete_queue.push( old );
                 tile_cache.clear_entry( index );
             } else {
@@ -271,6 +272,7 @@ int FGTileMgr::update( double lon, double lat, double visibility_meters ) {
                        globals->get_scenery()->get_center() );
 }
 
+
 int FGTileMgr::update( double lon, double lat, double visibility_meters,
                        sgdVec3 abs_pos_vector, SGBucket p_current,
                        SGBucket p_previous, Point3D center ) {
@@ -360,12 +362,13 @@ int FGTileMgr::update( double lon, double lat, double visibility_meters,
        // cout << "Adding ssg nodes for "
     }
 
+    // cout << "delete queue = " << delete_queue.size() << endl;
     if ( !delete_queue.empty() ) {
        FGTileEntry* e = delete_queue.front();
-       delete_queue.pop();
-        e->disconnect_ssg_nodes();
-        e->free_tile();
-        delete e;
+        if ( e->free_tile() ) {
+            delete_queue.pop();
+            delete e;
+        }
     }
 
     // no reason to update this if we haven't moved...
@@ -392,6 +395,7 @@ void FGTileMgr::refresh_view_timestamps() {
     schedule_needed(fgGetDouble("/environment/visibility-m"), current_bucket);
 }
 
+
 // check and set current tile and scenery center...
 void FGTileMgr::setCurrentTile(double longitude, double latitude) {
 
@@ -406,6 +410,7 @@ void FGTileMgr::setCurrentTile(double longitude, double latitude) {
     }
 }
 
+
 int FGTileMgr::updateCurrentElevAtPos(sgdVec3 abs_pos_vector, Point3D center) {
 
   sgdVec3 sc;
@@ -446,6 +451,7 @@ int FGTileMgr::updateCurrentElevAtPos(sgdVec3 abs_pos_vector, Point3D center) {
     return hit;
 }
 
+
 void FGTileMgr::prep_ssg_nodes(float vis) {
 
     // traverse the potentially viewable tile list and update range
@@ -462,6 +468,7 @@ void FGTileMgr::prep_ssg_nodes(float vis) {
 
 }
 
+
 void FGTileMgr::prep_ssg_nodes(float vis, sgVec3 up, Point3D center) {
 
     // traverse the potentially viewable tile list and update range