diff --git a/src/Scenery/FGTileLoader.cxx b/src/Scenery/FGTileLoader.cxx index 5123953e6..3465e0f2b 100644 --- a/src/Scenery/FGTileLoader.cxx +++ b/src/Scenery/FGTileLoader.cxx @@ -50,7 +50,7 @@ FGTileLoader::~FGTileLoader() { #ifdef ENABLE_THREADS // Wake up its time to die. - cond.broadcast(); + queue_cond.broadcast(); for (int i = 0; i < MAX_THREADS; ++i) { @@ -86,13 +86,27 @@ FGTileLoader::add( FGTileEntry* tile ) mutex.lock(); tile_queue.push( tile ); // Signal waiting working threads. - cond.signal(); + queue_cond.signal(); mutex.unlock(); #else tile->load( tile_path, true ); #endif // ENABLE_THREADS } +/** + * + */ +void +FGTileLoader::update() +{ +#ifdef ENABLE_THREADS + mutex.lock(); + frame_cond.signal(); + mutex.unlock(); +#endif // ENABLE_THREADS +} + + #ifdef ENABLE_THREADS /** * @@ -106,7 +120,7 @@ FGTileLoader::LoaderThread::run() loader->mutex.lock(); while (loader->empty()) { - loader->cond.wait( loader->mutex ); + loader->queue_cond.wait( loader->mutex ); } // Have we been canceled - exits if yes. @@ -117,6 +131,10 @@ FGTileLoader::LoaderThread::run() pthread_exit( PTHREAD_CANCELED ); } + // Wait for the next frame signal before we load a tile from the queue + // Note that loader->mutex is already locked at this point. + loader->frame_cond.wait( loader->mutex ); + // Grab the tile to load and release the mutex. FGTileEntry* tile = loader->tile_queue.front(); loader->tile_queue.pop(); diff --git a/src/Scenery/FGTileLoader.hxx b/src/Scenery/FGTileLoader.hxx index f74ac3d16..912475abe 100644 --- a/src/Scenery/FGTileLoader.hxx +++ b/src/Scenery/FGTileLoader.hxx @@ -61,6 +61,14 @@ public: */ void add( FGTileEntry* tile ); + /** + * The tile loader thread will only load one tile per call to the + * update() method. This is a way to spread out the work of the + * tile loader and slow it down so it is less intrusive. For + * systems built without thead support this is a no-op. + */ + void update(); + /** * Returns whether the load queue is empty (contains no elements). * @return true if load queue is empty otherwise returns false. @@ -123,7 +131,8 @@ private: * Lock and synchronize access to tile queue. */ SGMutex mutex; - SGCondition cond; + SGCondition queue_cond; + SGCondition frame_cond; /** * Thread cleanup handler. diff --git a/src/Scenery/newcache.cxx b/src/Scenery/newcache.cxx index 46c3ef44b..9d4807243 100644 --- a/src/Scenery/newcache.cxx +++ b/src/Scenery/newcache.cxx @@ -212,18 +212,13 @@ void FGNewCache::make_space() { * Create a new tile and schedule it for loading. */ void -FGNewCache::load_tile( const SGBucket& b ) +FGNewCache::insert_tile( FGTileEntry *e ) { // clear out a distant entry in the cache if needed. make_space(); - // create the entry - FGTileEntry *e = new FGTileEntry( b ); - // register it in the cache - long tile_index = b.gen_index(); + long tile_index = e->get_tile_bucket().gen_index(); tile_cache[tile_index] = e; - // Schedule tile for loading - loader.add( e ); } diff --git a/src/Scenery/newcache.hxx b/src/Scenery/newcache.hxx index 4eb92388e..38dbc71ca 100644 --- a/src/Scenery/newcache.hxx +++ b/src/Scenery/newcache.hxx @@ -46,7 +46,6 @@ #include #include "tileentry.hxx" -#include "FGTileLoader.hxx" SG_USING_STD(map); @@ -69,11 +68,6 @@ class FGNewCache { // Free a tile cache entry void entry_free( long cache_index ); - /** - * Queue tiles for loading. - */ - FGTileLoader loader; - public: // Constructor @@ -128,7 +122,7 @@ public: * Create a new tile and enqueue it for loading. * @param b */ - void load_tile( const SGBucket& b ); + void insert_tile( FGTileEntry* e ); }; diff --git a/src/Scenery/tileentry.hxx b/src/Scenery/tileentry.hxx index d2ff2909f..f9f88d08c 100644 --- a/src/Scenery/tileentry.hxx +++ b/src/Scenery/tileentry.hxx @@ -165,7 +165,13 @@ public: * Return true if the tile entry is loaded, otherwise return false * indicating that the loading thread is still working on this. */ - inline bool is_loaded() const { return loaded; } + inline bool is_loaded() const { return loaded; } + + /** + * Return the "bucket" for this tile + */ + inline SGBucket get_tile_bucket() const { return tile_bucket; } + }; diff --git a/src/Scenery/tilemgr.cxx b/src/Scenery/tilemgr.cxx index 4bdcfda59..44b052695 100644 --- a/src/Scenery/tilemgr.cxx +++ b/src/Scenery/tilemgr.cxx @@ -116,8 +116,14 @@ void FGTileMgr::sched_tile( const SGBucket& b ) { FGTileEntry *t = tile_cache.get_tile( b ); if ( t == NULL ) { - // register a load request - tile_cache.load_tile( b ); + // create a new entry + FGTileEntry *e = new FGTileEntry( b ); + + // insert the tile into the cache + tile_cache.insert_tile( e ); + + // Schedule tile for loading + loader.add( e ); } } @@ -271,8 +277,6 @@ void FGTileMgr::initialize_queue() SG_LOG( SG_TERRAIN, SG_INFO, "Updating Tile list for " << current_bucket ); // cout << "tile cache size = " << tile_cache.get_size() << endl; - int i; - // wipe/initialize tile cache // tile_cache.init(); previous_bucket.make_bad(); @@ -288,6 +292,7 @@ void FGTileMgr::initialize_queue() #if 0 // Now force a load of the center tile and inner ring so we // have something to see in our first frame. + int i; for ( i = 0; i < 9; ++i ) { if ( load_queue.size() ) { SG_LOG( SG_TERRAIN, SG_DEBUG, @@ -402,6 +407,13 @@ int FGTileMgr::update( double lon, double lat ) { last_longitude = longitude; last_latitude = latitude; + // activate loader thread one out of every 5 frames + counter_hack = (counter_hack + 1) % 5; + if ( !counter_hack ) { + // Notify the tile loader that it can load another tile + loader.update(); + } + return 1; } diff --git a/src/Scenery/tilemgr.hxx b/src/Scenery/tilemgr.hxx index e3f49e54c..0bd450fd7 100644 --- a/src/Scenery/tilemgr.hxx +++ b/src/Scenery/tilemgr.hxx @@ -35,6 +35,7 @@ #include +#include "FGTileLoader.hxx" #include "hitlist.hxx" #include "newcache.hxx" @@ -102,10 +103,16 @@ private: double last_latitude; /** - * + * tile cache */ FGNewCache tile_cache; + /** + * Queue tiles for loading. + */ + FGTileLoader loader; + int counter_hack; + public: // Constructor