From 27158525a9fbcd433b4cc166d7715c91fc9a5d36 Mon Sep 17 00:00:00 2001 From: curt Date: Thu, 25 Jul 2002 23:59:04 +0000 Subject: [PATCH] Restructure the way tile freeing is handled. When a tile is removed from 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 | 136 +++++++++++++++++++++++++++----------- src/Scenery/tileentry.hxx | 20 +++++- src/Scenery/tilemgr.cxx | 21 ++++-- 3 files changed, 128 insertions(+), 49 deletions(-) diff --git a/src/Scenery/tileentry.cxx b/src/Scenery/tileentry.cxx index 9ebeeb283..97d5d372e 100644 --- a/src/Scenery/tileentry.cxx +++ b/src/Scenery/tileentry.cxx @@ -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; } diff --git a/src/Scenery/tileentry.hxx b/src/Scenery/tileentry.hxx index e315d5150..5f413b047 100644 --- a/src/Scenery/tileentry.hxx +++ b/src/Scenery/tileentry.hxx @@ -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) diff --git a/src/Scenery/tilemgr.cxx b/src/Scenery/tilemgr.cxx index 2d3dd9dda..aaf6b0da3 100644 --- a/src/Scenery/tilemgr.cxx +++ b/src/Scenery/tilemgr.cxx @@ -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 -- 2.39.5