From 616b2bf4f69a5cae8a12b4bf5007e2e2fecc9de1 Mon Sep 17 00:00:00 2001 From: timoore Date: Fri, 14 Dec 2007 22:51:56 +0000 Subject: [PATCH] Use the OSG DatabasePager instead of FGTileLoader Make an OSG file reader for .stg files. New class flightgear::SceneryPager, which is a subclass osg::DatabasePager to handle explicit delete requests. Modify FGNewCache, FGTileEntry, and FGTileManager to use SceneryPager. Mostly this involved removing the queues that talked to FGTileLoader. Calculate accurate tile timestamps from the time they are traversed in the cull stage (which means that they are visible) instead of updating them periodically. Replace tile entry transform and range node with one LOD node --- src/Main/fg_commands.cxx | 3 - src/Main/fg_init.cxx | 7 -- src/Main/fg_os_osgviewer.cxx | 2 + src/Main/fg_os_sdl.cxx | 1 + src/Main/main.cxx | 3 +- src/Scenery/Makefile.am | 2 +- src/Scenery/SceneryPager.cxx | 60 +++++++++ src/Scenery/SceneryPager.hxx | 73 +++++++++++ src/Scenery/newcache.cxx | 167 +------------------------ src/Scenery/newcache.hxx | 10 +- src/Scenery/scenery.cxx | 8 ++ src/Scenery/scenery.hxx | 6 + src/Scenery/tileentry.cxx | 111 +++++++++++------ src/Scenery/tileentry.hxx | 58 ++------- src/Scenery/tilemgr.cxx | 230 ++++++++++------------------------- src/Scenery/tilemgr.hxx | 56 +-------- 16 files changed, 318 insertions(+), 479 deletions(-) create mode 100644 src/Scenery/SceneryPager.cxx create mode 100644 src/Scenery/SceneryPager.hxx diff --git a/src/Main/fg_commands.cxx b/src/Main/fg_commands.cxx index 455baea8b..c17a19c4f 100644 --- a/src/Main/fg_commands.cxx +++ b/src/Main/fg_commands.cxx @@ -452,7 +452,6 @@ do_view_next( bool ) globals->get_current_view()->setHeadingOffset_deg(0.0); globals->get_viewmgr()->next_view(); fix_hud_visibility(); - globals->get_tile_mgr()->refresh_view_timestamps(); } static void @@ -461,7 +460,6 @@ do_view_prev( bool ) globals->get_current_view()->setHeadingOffset_deg(0.0); globals->get_viewmgr()->prev_view(); fix_hud_visibility(); - globals->get_tile_mgr()->refresh_view_timestamps(); } /** @@ -473,7 +471,6 @@ do_view_cycle (const SGPropertyNode * arg) globals->get_current_view()->setHeadingOffset_deg(0.0); globals->get_viewmgr()->next_view(); fix_hud_visibility(); - globals->get_tile_mgr()->refresh_view_timestamps(); return true; } diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 07effac56..1399453c2 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -1633,13 +1633,6 @@ bool fgInitSubsystems() { exit(-1); } - // cause refresh of viewer scenery timestamps every 15 seconds... - globals->get_event_mgr()->addTask( "FGTileMgr::refresh_view_timestamps()", - globals->get_tile_mgr(), - &FGTileMgr::refresh_view_timestamps, - 15 ); - - //////////////////////////////////////////////////////////////////// // Initialize the flight model subsystem. //////////////////////////////////////////////////////////////////// diff --git a/src/Main/fg_os_osgviewer.cxx b/src/Main/fg_os_osgviewer.cxx index 7b7bcc70b..52e2b6d1e 100644 --- a/src/Main/fg_os_osgviewer.cxx +++ b/src/Main/fg_os_osgviewer.cxx @@ -38,6 +38,7 @@ #include #include +#include #include "fg_os.hxx" #include "fg_props.hxx" #include "util.hxx" @@ -65,6 +66,7 @@ void fgOSOpenWindow(int w, int h, int bpp, wsi = osg::GraphicsContext::getWindowingSystemInterface(); viewer = new osgViewer::Viewer; + viewer->setDatabasePager(FGScenery::getPagerSingleton()); // Avoid complications with fg's custom drawables. std::string mode; mode = fgGetString("/sim/rendering/multithreading-mode", "SingleThreaded"); diff --git a/src/Main/fg_os_sdl.cxx b/src/Main/fg_os_sdl.cxx index b3e970fff..018b7dd91 100644 --- a/src/Main/fg_os_sdl.cxx +++ b/src/Main/fg_os_sdl.cxx @@ -82,6 +82,7 @@ void fgOSOpenWindow(int w, int h, int bpp, int realw = screen->w; int realh = screen->h; viewer = new osgViewer::Viewer; + viewer->setDatabasePager(FGScenery::getPagerSingleton()); gw = viewer->setUpViewerAsEmbeddedInWindow(0, 0, realw, realh); // now the main camera ... //osg::ref_ptr camera = new osg::Camera; diff --git a/src/Main/main.cxx b/src/Main/main.cxx index 5c1788d12..942b715fe 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -660,7 +660,8 @@ static void fgMainLoop( void ) { // END Tile Manager udpates - if (!scenery_loaded && globals->get_tile_mgr()->all_queues_empty() && cur_fdm_state->get_inited()) { + if (!scenery_loaded && globals->get_tile_mgr()->isSceneryLoaded() + && cur_fdm_state->get_inited()) { fgSetBool("sim/sceneryloaded",true); fgSetFloat("/sim/sound/volume", init_volume); globals->get_soundmgr()->set_volume(init_volume); diff --git a/src/Scenery/Makefile.am b/src/Scenery/Makefile.am index a9b03eca8..558db4a9b 100644 --- a/src/Scenery/Makefile.am +++ b/src/Scenery/Makefile.am @@ -1,10 +1,10 @@ noinst_LIBRARIES = libScenery.a libScenery_a_SOURCES = \ - FGTileLoader.cxx FGTileLoader.hxx \ newcache.cxx newcache.hxx \ redout.cxx redout.hxx \ scenery.cxx scenery.hxx \ + SceneryPager.cxx SceneryPager.hxx \ tileentry.cxx tileentry.hxx \ tilemgr.cxx tilemgr.hxx diff --git a/src/Scenery/SceneryPager.cxx b/src/Scenery/SceneryPager.cxx new file mode 100644 index 000000000..1d411409b --- /dev/null +++ b/src/Scenery/SceneryPager.cxx @@ -0,0 +1,60 @@ +#include "SceneryPager.hxx" +#include +#include + +using namespace flightgear; +using osg::ref_ptr; +using osg::Node; + +SceneryPager::SceneryPager() +{ + _pagerRequests.reserve(48); + _deleteRequests.reserve(16); +} + +SceneryPager::SceneryPager(const SceneryPager& rhs) : + DatabasePager(rhs) +{ +} + +SceneryPager::~SceneryPager() +{ +} + +void SceneryPager::queueRequest(const std::string& fileName, osg::Group* group, + float priority, osg::FrameStamp* frameStamp) +{ + _pagerRequests.push_back(PagerRequest(fileName, group, priority, + frameStamp)); +} + +void SceneryPager::queueDeleteRequest(osg::ref_ptr& objptr) +{ + _deleteRequests.push_back(objptr); + objptr = 0; +} +void SceneryPager::signalEndFrame() +{ + using namespace std; + bool areDeleteRequests = false; + bool arePagerRequests = false; + if (!_deleteRequests.empty()) { + areDeleteRequests = true; + OpenThreads::ScopedLock + lock(_childrenToDeleteListMutex); + _childrenToDeleteList.insert(_childrenToDeleteList.end(), + _deleteRequests.begin(), + _deleteRequests.end()); + _deleteRequests.clear(); + } + if (!_pagerRequests.empty()) { + arePagerRequests = true; + for_each(_pagerRequests.begin(), _pagerRequests.end(), + bind2nd(mem_fun_ref(&PagerRequest::doRequest), this)); + _pagerRequests.clear(); + } + if (areDeleteRequests && !arePagerRequests) + updateDatabasePagerThreadBlock(); + DatabasePager::signalEndFrame(); +} + diff --git a/src/Scenery/SceneryPager.hxx b/src/Scenery/SceneryPager.hxx new file mode 100644 index 000000000..197202190 --- /dev/null +++ b/src/Scenery/SceneryPager.hxx @@ -0,0 +1,73 @@ +// SceneryPager.hxx -- Interface to OSG database pager +// +// Copyright (C) 2007 Tim Moore timoore@redhat.com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#ifndef FLIGHTGEAR_SCENERYPAGERHXX +#include +#include + +#include +#include +#include + +namespace flightgear +{ +class SceneryPager : public osgDB::DatabasePager +{ +public: + SceneryPager(); + SceneryPager(const SceneryPager& rhs); + void queueRequest(const std::string& fileName, osg::Group* node, + float priority, osg::FrameStamp* frameStamp); + // This is passed a ref_ptr so that it can "take ownership" of the + // node to delete and decrement its refcount while holding the + // lock on the delete list. + void queueDeleteRequest(osg::ref_ptr& objptr); + virtual void signalEndFrame(); +protected: + // Queue up file requests until the end of the frame + struct PagerRequest + { + PagerRequest() {} + PagerRequest(const PagerRequest& rhs) : + _fileName(rhs._fileName), _group(rhs._group), + _priority(rhs._priority), _frameStamp(rhs._frameStamp) {} + PagerRequest(const std::string& fileName, osg::Group* group, + float priority, osg::FrameStamp* frameStamp) : + _fileName(fileName), _group(group), _priority(priority), + _frameStamp(frameStamp) {} + void doRequest(SceneryPager* pager) + { + pager->requestNodeFile(_fileName, _group.get(), _priority, + _frameStamp.get()); + } + std::string _fileName; + osg::ref_ptr _group; + float _priority; + osg::ref_ptr _frameStamp; + }; + typedef std::vector PagerRequestList; + PagerRequestList _pagerRequests; + typedef std::vector > DeleteRequestList; + DeleteRequestList _deleteRequests; + virtual ~SceneryPager(); +}; +} +#define FLIGHTGEAR_SCENERYPAGERHXX 1 +#endif diff --git a/src/Scenery/newcache.cxx b/src/Scenery/newcache.cxx index 643765d76..3c3e56970 100644 --- a/src/Scenery/newcache.cxx +++ b/src/Scenery/newcache.cxx @@ -96,83 +96,12 @@ bool FGNewCache::exists( const SGBucket& b ) const { return ( it != tile_cache.end() ); } - -#if 0 -// Ensure at least one entry is free in the cache -bool FGNewCache::make_space() { - SG_LOG( SG_TERRAIN, SG_DEBUG, "Make space in cache" ); - SG_LOG( SG_TERRAIN, SG_DEBUG, "cache entries = " << tile_cache.size() ); - SG_LOG( SG_TERRAIN, SG_DEBUG, "max size = " << max_cache_size ); - - if ( (int)tile_cache.size() < max_cache_size ) { - // space in the cache, return - return true; - } - - while ( (int)tile_cache.size() >= max_cache_size ) { - sgdVec3 abs_view_pos; - float dist; - double timestamp = 0.0; - int max_index = -1; - double min_time = 2419200000.0f; // one month should be enough - double max_time = 0; - - // we need to free the furthest entry - tile_map_iterator current = tile_cache.begin(); - tile_map_iterator end = tile_cache.end(); - - for ( ; current != end; ++current ) { - long index = current->first; - FGTileEntry *e = current->second; - // if ( e->is_loaded() && (e->get_pending_models() == 0) ) { - if ( e->is_loaded() ) { - - timestamp = e->get_timestamp(); - if ( timestamp < min_time ) { - max_index = index; - min_time = timestamp; - } - if ( timestamp > max_time ) { - max_time = timestamp; - } - - } else { - SG_LOG( SG_TERRAIN, SG_DEBUG, "loaded = " << e->is_loaded() - << " pending models = " << e->get_pending_models() - << " time stamp = " << e->get_timestamp() ); - } - } - - // If we made it this far, then there were no open cache entries. - // We will instead free the oldest cache entry and return true - - SG_LOG( SG_TERRAIN, SG_DEBUG, " min_time = " << min_time ); - SG_LOG( SG_TERRAIN, SG_DEBUG, " index = " << max_index ); - SG_LOG( SG_TERRAIN, SG_DEBUG, " max_time = " << max_time ); - if ( max_index >= 0 ) { - entry_free( max_index ); - return true; - } else { - SG_LOG( SG_TERRAIN, SG_ALERT, "WHOOPS!!! can't make_space(), tile " - "cache is full, but no entries available for removal." ); - return false; - } - } - - SG_LOG( SG_TERRAIN, SG_ALERT, "WHOOPS!!! Hit an unhandled condition in " - "FGNewCache::make_space()." ); - return false; -} -#endif - - // Return the index of the oldest tile in the cache, return -1 if // nothing available to be removed. long FGNewCache::get_oldest_tile() { // we need to free the furthest entry long min_index = -1; double timestamp = 0.0; - double min_time = 2419200000.0f; // one month should be enough double max_time = 0; tile_map_iterator current = tile_cache.begin(); @@ -181,25 +110,19 @@ long FGNewCache::get_oldest_tile() { for ( ; current != end; ++current ) { long index = current->first; FGTileEntry *e = current->second; - if ( e->is_loaded() && (e->get_pending_models() == 0) ) { + if ( e->is_loaded() ) { timestamp = e->get_timestamp(); - if ( timestamp < min_time ) { - min_index = index; - min_time = timestamp; - } if ( timestamp > max_time ) { max_time = timestamp; } } else { SG_LOG( SG_TERRAIN, SG_DEBUG, "loaded = " << e->is_loaded() - << " pending models = " << e->get_pending_models() << " time stamp = " << e->get_timestamp() ); } } - SG_LOG( SG_TERRAIN, SG_DEBUG, " min_time = " << min_time ); SG_LOG( SG_TERRAIN, SG_DEBUG, " index = " << min_index ); SG_LOG( SG_TERRAIN, SG_DEBUG, " max_time = " << max_time ); @@ -215,7 +138,7 @@ void FGNewCache::clear_inner_ring_flags() { for ( ; current != end; ++current ) { FGTileEntry *e = current->second; - if ( e->is_loaded() && (e->get_pending_models() == 0) ) { + if ( e->is_loaded() ) { e->set_inner_ring( false ); } } @@ -238,7 +161,7 @@ void FGNewCache::clear_cache() { long index = current->first; SG_LOG( SG_TERRAIN, SG_DEBUG, "clearing " << index ); FGTileEntry *e = current->second; - if ( e->is_loaded() && (e->get_pending_models() == 0) ) { + 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); @@ -257,10 +180,7 @@ void FGNewCache::clear_cache() { * Create a new tile and schedule it for loading. */ bool FGNewCache::insert_tile( FGTileEntry *e ) { - // set time of insertion for tracking age of tiles... - e->set_timestamp(globals->get_sim_time_sec()); - - // register it in the cache + // register tile in the cache long tile_index = e->get_tile_bucket().gen_index(); tile_cache[tile_index] = e; @@ -268,82 +188,3 @@ bool FGNewCache::insert_tile( FGTileEntry *e ) { } -// Note this is the old version of FGNewCache::make_space(), currently disabled -// It uses distance from a center point to determine tiles to be discarded... -#if 0 -// Ensure at least one entry is free in the cache -bool FGNewCache::make_space() { - SG_LOG( SG_TERRAIN, SG_DEBUG, "Make space in cache" ); - SG_LOG( SG_TERRAIN, SG_DEBUG, "cache entries = " << tile_cache.size() ); - SG_LOG( SG_TERRAIN, SG_DEBUG, "max size = " << max_cache_size ); - - if ( (int)tile_cache.size() < max_cache_size ) { - // space in the cache, return - return true; - } - - while ( (int)tile_cache.size() >= max_cache_size ) { - sgdVec3 abs_view_pos; - float dist; - float max_dist = 0.0; - int max_index = -1; - - // we need to free the furthest entry - tile_map_iterator current = tile_cache.begin(); - tile_map_iterator end = tile_cache.end(); - - for ( ; current != end; ++current ) { - long index = current->first; - FGTileEntry *e = current->second; - - if ( e->is_loaded() && (e->get_pending_models() == 0) ) { - // calculate approximate distance from view point - sgdCopyVec3( abs_view_pos, - globals->get_current_view()->get_absolute_view_pos() ); - - SG_LOG( SG_TERRAIN, SG_DEBUG, "DIST Abs view pos = " - << abs_view_pos[0] << "," - << abs_view_pos[1] << "," - << abs_view_pos[2] ); - SG_LOG( SG_TERRAIN, SG_DEBUG, - " ref point = " << e->center ); - - sgdVec3 center; - sgdSetVec3( center, - e->center.x(), e->center.y(), e->center.z() ); - dist = sgdDistanceVec3( center, abs_view_pos ); - - SG_LOG( SG_TERRAIN, SG_DEBUG, " distance = " << dist ); - - if ( dist > max_dist ) { - max_dist = dist; - max_index = index; - } - } else { - SG_LOG( SG_TERRAIN, SG_INFO, "loaded = " << e->is_loaded() - << " pending models = " << e->get_pending_models() ); - } - } - - // If we made it this far, then there were no open cache entries. - // We will instead free the furthest cache entry and return true - - SG_LOG( SG_TERRAIN, SG_INFO, " max_dist = " << max_dist ); - SG_LOG( SG_TERRAIN, SG_INFO, " index = " << max_index ); - if ( max_index >= 0 ) { - entry_free( max_index ); - return true; - } else { - SG_LOG( SG_TERRAIN, SG_ALERT, "WHOOPS!!! can't make_space(), tile " - "cache is full, but no entries available for removal." ); - return false; - } - } - - SG_LOG( SG_TERRAIN, SG_ALERT, "WHOOPS!!! Hit an unhandled condition in " - "FGNewCache::make_space()." ); - return false; -} -#endif - - diff --git a/src/Scenery/newcache.hxx b/src/Scenery/newcache.hxx index 50bdb8225..5713029fd 100644 --- a/src/Scenery/newcache.hxx +++ b/src/Scenery/newcache.hxx @@ -45,11 +45,11 @@ SG_USING_STD(map); // A class to store and manage a pile of tiles class FGNewCache { - +public: typedef map < long, FGTileEntry * > tile_map; typedef tile_map::iterator tile_map_iterator; typedef tile_map::const_iterator const_tile_map_iterator; - +private: // cache storage space tile_map tile_cache; @@ -63,7 +63,11 @@ class FGNewCache { void entry_free( long cache_index ); public: - + tile_map_iterator begin() { return tile_cache.begin(); } + tile_map_iterator end() { return tile_cache.end(); } + const_tile_map_iterator begin() const { return tile_cache.begin(); } + const_tile_map_iterator end() const { return tile_cache.end(); } + // Constructor FGNewCache(); diff --git a/src/Scenery/scenery.cxx b/src/Scenery/scenery.cxx index 110631b3f..93f5c2f7e 100644 --- a/src/Scenery/scenery.cxx +++ b/src/Scenery/scenery.cxx @@ -42,6 +42,8 @@ #include "scenery.hxx" +using namespace flightgear; + class FGGroundPickCallback : public SGPickCallback { public: virtual bool buttonPressed(int button, const Info& info) @@ -208,3 +210,9 @@ FGScenery::get_cart_ground_intersection(const SGVec3d& pos, const SGVec3d& dir, return hits; } + +SceneryPager* FGScenery::getPagerSingleton() +{ + static osg::ref_ptr pager = new SceneryPager; + return pager.get(); +} diff --git a/src/Scenery/scenery.hxx b/src/Scenery/scenery.hxx index 75512b3aa..0b2d8832b 100644 --- a/src/Scenery/scenery.hxx +++ b/src/Scenery/scenery.hxx @@ -36,6 +36,8 @@ #include #include +#include "SceneryPager.hxx" + class SGMaterial; // Define a structure containing global scenery parameters @@ -99,6 +101,10 @@ public: osg::Group *get_terrain_branch () const { return terrain_branch.get(); } osg::Group *get_models_branch () const { return models_branch.get(); } osg::Group *get_aircraft_branch () const { return aircraft_branch.get(); } + + // Static because access to the pager is needed before the rest of + // the scenery is initialized. + static flightgear::SceneryPager* getPagerSingleton(); }; diff --git a/src/Scenery/tileentry.cxx b/src/Scenery/tileentry.cxx index 1aaa72e81..71c1ce258 100644 --- a/src/Scenery/tileentry.cxx +++ b/src/Scenery/tileentry.cxx @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -93,22 +94,69 @@ public: } }; +namespace +{ +class TileCullCallback : public osg::NodeCallback +{ +public: + TileCullCallback() : _timeStamp(DBL_MAX) {} + TileCullCallback(const TileCullCallback& tc, const osg::CopyOp& copyOp) : + NodeCallback(tc, copyOp), _timeStamp(tc._timeStamp) + { + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + double getTimeStamp() const { return _timeStamp; } + void setTimeStamp(double timeStamp) { _timeStamp = timeStamp; } +protected: + double _timeStamp; +}; +} + +void TileCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + if (nv->getFrameStamp()) + _timeStamp = nv->getFrameStamp()->getReferenceTime(); + traverse(node, nv); +} + +double FGTileEntry::get_timestamp() const +{ + if (_node.valid()) { + return (dynamic_cast(_node->getCullCallback())) + ->getTimeStamp(); + } else + return DBL_MAX; +} + +void FGTileEntry::set_timestamp(double time_ms) +{ + if (_node.valid()) { + TileCullCallback* cb + = dynamic_cast(_node->getCullCallback()); + if (cb) + cb->setTimeStamp(time_ms); + } +} + // Constructor FGTileEntry::FGTileEntry ( const SGBucket& b ) : tile_bucket( b ), - terra_transform( new osg::Group ), - terra_range( new osg::LOD ), - loaded(false), - pending_models(0), + _node( new osg::LOD ), is_inner_ring(false), - free_tracker(0) + free_tracker(0), + tileFileName(b.gen_index_str()) { - terra_transform->setUpdateCallback(new FGTileUpdateCallback); + _node->setUpdateCallback(new FGTileUpdateCallback); + _node->setCullCallback(new TileCullCallback); + tileFileName += ".stg"; + _node->setName(tileFileName); } // Destructor -FGTileEntry::~FGTileEntry () { +FGTileEntry::~FGTileEntry () +{ } static void WorldCoordinate( osg::Matrix& obj_pos, double lat, @@ -181,8 +229,8 @@ bool FGTileEntry::free_tile() { // 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.get(), delete_size ) == 0 ) { - terra_transform = 0; + if ( fgPartialFreeSSGtree( _node.get(), delete_size ) == 0 ) { + _node = 0; free_tracker |= TERRA_NODE; } } else if ( !(free_tracker & LIGHTMAPS) ) { @@ -201,12 +249,12 @@ bool FGTileEntry::free_tile() { // Update the ssg transform node for this tile so it can be // properly drawn relative to our (0,0,0) point void FGTileEntry::prep_ssg_node(float vis) { - if ( !loaded ) return; - + if (!is_loaded()) + return; // visibility can change from frame to frame so we update the // range selector cutoff's each time. - float bounding_radius = terra_range->getChild(0)->getBound().radius(); - terra_range->setRange( 0, 0, vis + bounding_radius ); + float bounding_radius = _node->getChild(0)->getBound().radius(); + _node->setRange( 0, 0, vis + bounding_radius ); } bool FGTileEntry::obj_load( const string& path, @@ -262,18 +310,6 @@ struct Object { // Work in progress... load the tile based entirely by name cuz that's // what we'll want to do with the database pager. -void -FGTileEntry::load( const string_list &path_list, bool is_base ) -{ - osgDB::ReaderWriter::ReadResult result - = osgDB::Registry::instance()->readNode(tile_bucket.gen_index_str() - + ".stg", 0); - if (result.validNode()) { - osg::Node* new_tile = result.getNode(); - terra_range->addChild( new_tile ); - } - terra_transform->addChild( terra_range.get() ); -} osg::Node* FGTileEntry::loadTileByName(const string& index_str, @@ -469,15 +505,13 @@ FGTileEntry::add_ssg_nodes( osg::Group *terrain_branch ) { // bump up the ref count so we can remove this later without // having ssg try to free the memory. - terrain_branch->addChild( terra_transform.get() ); + terrain_branch->addChild( _node.get() ); SG_LOG( SG_TERRAIN, SG_DEBUG, - "connected a tile into scene graph. terra_transform = " - << terra_transform.get() ); + "connected a tile into scene graph. _node = " + << _node.get() ); SG_LOG( SG_TERRAIN, SG_DEBUG, "num parents now = " - << terra_transform->getNumParents() ); - - loaded = true; + << _node->getNumParents() ); } @@ -486,30 +520,27 @@ FGTileEntry::disconnect_ssg_nodes() { SG_LOG( SG_TERRAIN, SG_DEBUG, "disconnecting ssg nodes" ); - if ( ! loaded ) { + if (! is_loaded()) { SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a not-fully loaded tile!" ); } else { - SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a fully loaded tile! terra_transform = " << terra_transform.get() ); + SG_LOG( SG_TERRAIN, SG_DEBUG, "removing a fully loaded tile! _node = " << _node.get() ); } // find the terrain branch parent - int pcount = terra_transform->getNumParents(); + int pcount = _node->getNumParents(); if ( pcount > 0 ) { // find the first parent (should only be one) - osg::Group *parent = terra_transform->getParent( 0 ) ; + osg::Group *parent = _node->getParent( 0 ) ; if( parent ) { // disconnect the tile (we previously ref()'d it so it // won't get freed now) - parent->removeChild( terra_transform.get() ); + parent->removeChild( _node.get() ); } else { + // This should be impossible. SG_LOG( SG_TERRAIN, SG_ALERT, "parent pointer is NULL! Dying" ); exit(-1); } - } else { - SG_LOG( SG_TERRAIN, SG_ALERT, - "Parent count is zero for an ssg tile! Dying" ); - exit(-1); } } diff --git a/src/Scenery/tileentry.hxx b/src/Scenery/tileentry.hxx index c14340836..70bbbc279 100644 --- a/src/Scenery/tileentry.hxx +++ b/src/Scenery/tileentry.hxx @@ -65,7 +65,7 @@ typedef point_list::const_iterator const_point_list_iterator; class FGTileEntry; - +#if 0 /** * A class to hold deferred model loading info */ @@ -102,7 +102,7 @@ public: inline FGTileEntry *get_tile() const { return tile; } inline osg::MatrixTransform *get_obj_trans() const { return obj_trans.get(); } }; - +#endif /** * A class to encapsulate everything we need to know about a scenery tile. @@ -112,35 +112,17 @@ class FGTileEntry { public: // this tile's official location in the world SGBucket tile_bucket; + std::string tileFileName; private: - // pointer to ssg transform for this tile - osg::ref_ptr terra_transform; - // pointer to ssg range selector for this tile - osg::ref_ptr terra_range; + osg::ref_ptr _node; - /** - * Indicates this tile has been loaded from a file and connected - * into the scene graph. Note that this may be set asynchronously - * by another thread. - */ - volatile bool loaded; - - /** - * Count of pending models to load for this tile. This tile - * cannot be removed until this number reaches zero (i.e. no - * pending models to load for this tile.) - */ - volatile int pending_models; - - static bool obj_load( const string& path, + static bool obj_load( const std::string& path, osg::Group* geometry, bool is_base ); - double timestamp; - /** * this value is used by the tile scheduler/loader to mark which * tiles are in the primary ring (i.e. the current tile or the @@ -186,13 +168,6 @@ public: // properly drawn relative to our (0,0,0) point void prep_ssg_node(float vis); - /** - * Load tile data from a file. - * @param base name of directory containing tile data file. - * @param is_base is this a base terrain object for which we should generate - * random ground light points */ - void load( const string_list &base_path, bool is_base ); - /** * Transition to OSG database pager */ @@ -202,17 +177,10 @@ 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; } - - /** - * decrement the pending models count - */ - inline void dec_pending_models() { pending_models--; } - - /** - * return the number of remaining pending models for this tile - */ - inline int get_pending_models() const { return pending_models; } + inline bool is_loaded() const + { + return _node->getNumChildren() > 0; + } /** * Return the "bucket" for this tile @@ -232,12 +200,12 @@ public: /** - * return the SSG Transform node for the terrain + * return the scenegraph node for the terrain */ - osg::Group *get_terra_transform() const { return terra_transform.get(); } + osg::LOD *getNode() const { return _node.get(); } - inline double get_timestamp() const { return timestamp; } - inline void set_timestamp( double time_ms ) { timestamp = time_ms; } + double get_timestamp() const; + void set_timestamp( double time_ms ); inline bool get_inner_ring() const { return is_inner_ring; } inline void set_inner_ring( bool val ) { is_inner_ring = val; } diff --git a/src/Scenery/tilemgr.cxx b/src/Scenery/tilemgr.cxx index 9c9e025be..3df2de9c2 100644 --- a/src/Scenery/tilemgr.cxx +++ b/src/Scenery/tilemgr.cxx @@ -25,6 +25,9 @@ # include #endif +#include +#include + #include #include #include @@ -36,23 +39,19 @@ #include
#include
+#include
#include
#include #include "newcache.hxx" #include "scenery.hxx" +#include "SceneryPager.hxx" #include "tilemgr.hxx" -#define TEST_LAST_HIT_CACHE +using std::for_each; +using flightgear::SceneryPager; -#if defined(ENABLE_THREADS) -SGLockedQueue FGTileMgr::attach_queue; -SGLockedQueue FGTileMgr::model_queue; -#else -queue FGTileMgr::attach_queue; -queue FGTileMgr::model_queue; -#endif // ENABLE_THREADS -queue FGTileMgr::delete_queue; +#define TEST_LAST_HIT_CACHE // Constructor FGTileMgr::FGTileMgr(): @@ -74,29 +73,6 @@ int FGTileMgr::init() { tile_cache.init(); -#if 0 - - // instead it's just a lot easier to let any pending work flush - // through, rather than trying to arrest the queue and nuke all - // the various work at all the various stages and get everything - // cleaned up properly. - - while ( ! attach_queue.empty() ) { - attach_queue.pop(); - } - - while ( ! model_queue.empty() ) { -#if defined(ENABLE_THREADS) - FGDeferredModel* dm = model_queue.pop(); -#else - FGDeferredModel* dm = model_queue.front(); - model_queue.pop(); -#endif - delete dm; - } - loader.reinit(); -#endif - state = Inited; previous_bucket.make_bad(); @@ -115,15 +91,18 @@ void FGTileMgr::sched_tile( const SGBucket& b, const bool is_inner_ring ) { if ( t == NULL ) { // make space in the cache + SceneryPager* pager = FGScenery::getPagerSingleton(); while ( (int)tile_cache.get_size() > tile_cache.get_max_cache_size() ) { long index = tile_cache.get_oldest_tile(); if ( index >= 0 ) { FGTileEntry *old = tile_cache.get_tile( index ); - // OSGFIXME -// shadows->deleteOccluderFromTile( (ssgBranch *) old->get_terra_transform() ); - old->disconnect_ssg_nodes(); - delete_queue.push( old ); tile_cache.clear_entry( index ); + osg::ref_ptr subgraph = old->getNode(); + old->disconnect_ssg_nodes(); + delete old; + // zeros out subgraph ref_ptr, so subgraph is owned by + // the pager and will be deleted in the pager thread. + pager->queueDeleteRequest(subgraph); } else { // nothing to free ?!? forge ahead break; @@ -135,13 +114,14 @@ void FGTileMgr::sched_tile( const SGBucket& b, const bool is_inner_ring ) { // insert the tile into the cache if ( tile_cache.insert_tile( e ) ) { - // Schedule tile for loading - loader.add( e ); + // update_queues will generate load request } else { // insert failed (cache full with no available entries to // delete.) Try again later delete e; } + // Attach to scene graph + e->add_ssg_nodes(globals->get_scenery()->get_terrain_branch()); } else { t->set_inner_ring( is_inner_ring ); } @@ -259,15 +239,6 @@ void FGTileMgr::initialize_queue() #endif } -/** - * return current status of queues - * - */ - -bool FGTileMgr::all_queues_empty() { - return attach_queue.empty() && model_queue.empty(); -} - osg::Node* FGTileMgr::loadTileModel(const string& modelPath, bool cacheModel) { @@ -291,111 +262,42 @@ FGTileMgr::loadTileModel(const string& modelPath, bool cacheModel) return result; } +// Helper class for STL fun +class TileLoad : public std::unary_function +{ +public: + TileLoad(SceneryPager *pager, osg::FrameStamp* framestamp, + osg::Group* terrainBranch) : + _pager(pager), _framestamp(framestamp) {} + TileLoad(const TileLoad& rhs) : + _pager(rhs._pager), _framestamp(rhs._framestamp) {} + void operator()(FGNewCache::tile_map::value_type& tilePair) + { + FGTileEntry* entry = tilePair.second; + if (entry->getNode()->getNumChildren() == 0) { + _pager->queueRequest(entry->tileFileName, + entry->getNode(), + entry->get_inner_ring() ? 10.0f : 1.0f, + _framestamp); + } + } +private: + SceneryPager* _pager; + osg::FrameStamp* _framestamp; +}; + /** * Update the various queues maintained by the tilemagr (private * internal function, do not call directly.) */ void FGTileMgr::update_queues() { - // load the next model in the load queue. Currently this must - // happen in the render thread because model loading can trigger - // texture loading which involves use of the opengl api. Skip any - // models belonging to not loaded tiles (i.e. the tile was removed - // before we were able to load some of the associated models.) - if ( !model_queue.empty() ) { - bool processed_one = false; - - while ( model_queue.size() > 200 || processed_one == false ) { - processed_one = true; - - if ( model_queue.size() > 200 ) { - SG_LOG( SG_TERRAIN, SG_INFO, - "Alert: catching up on model load queue" ); - } - - // cout << "loading next model ..." << endl; - // load the next tile in the queue -#if defined(ENABLE_THREADS) - FGDeferredModel* dm = model_queue.pop(); -#else - FGDeferredModel* dm = model_queue.front(); - model_queue.pop(); -#endif - - // only load the model if the tile still exists in the - // tile cache - FGTileEntry *t = tile_cache.get_tile( dm->get_bucket() ); - if ( t != NULL ) { - //OSGFIXME -// ssgTexturePath( (char *)(dm->get_texture_path().c_str()) ); - try { - osg::Node *obj_model = - globals->get_model_lib()->load_model( ".", - dm->get_model_path(), - globals->get_props(), - globals->get_sim_time_sec(), - dm->get_cache_state(), - new FGNasalModelData ); - if ( obj_model != NULL ) { - dm->get_obj_trans()->addChild( obj_model ); - //OSGFIXME -// shadows->addOccluder( (ssgBranch *) obj_model->getParent(0), -// SGShadowVolume::occluderTypeTileObject, -// (ssgBranch *) dm->get_tile()->get_terra_transform()); - } - } catch (const sg_io_exception& exc) { - string m(exc.getMessage()); - m += " "; - m += exc.getLocation().asString(); - SG_LOG( SG_ALL, SG_ALERT, m ); - } catch (const sg_exception& exc) { // XXX may be redundant - SG_LOG( SG_ALL, SG_ALERT, exc.getMessage()); - } - - dm->get_tile()->dec_pending_models(); - } - delete dm; - } - } - - // Notify the tile loader that it can load another tile - loader.update(); - - if ( !attach_queue.empty() ) { -#if defined(ENABLE_THREADS) - FGTileEntry* e = attach_queue.pop(); -#else - FGTileEntry* e = attach_queue.front(); - attach_queue.pop(); -#endif - e->add_ssg_nodes( globals->get_scenery()->get_terrain_branch() ); - // cout << "Adding ssg nodes for " - } - - if ( !delete_queue.empty() ) { - // cout << "delete queue = " << delete_queue.size() << endl; - bool processed_one = false; - - while ( delete_queue.size() > 30 || processed_one == false ) { - processed_one = true; - - if ( delete_queue.size() > 30 ) { - // uh oh, delete queue is blowing up, we aren't clearing - // it fast enough. Let's just panic, well not panic, but - // get real serious and agressively free up some tiles so - // we don't explode our memory usage. - - SG_LOG( SG_TERRAIN, SG_WARN, - "Warning: catching up on tile delete queue" ); - } - - FGTileEntry* e = delete_queue.front(); - if ( e->free_tile() ) { - delete_queue.pop(); - delete e; - } - } - } + SceneryPager* pager = FGScenery::getPagerSingleton(); + for_each(tile_cache.begin(), tile_cache.end(), + TileLoad(pager, + globals->get_renderer()->getViewer()->getFrameStamp(), + globals->get_scenery()->get_terrain_branch())); } @@ -446,10 +348,6 @@ int FGTileMgr::update( SGLocation *location, double visibility_meters ) SG_LOG( SG_TERRAIN, SG_INFO, "State == Start || Inited" ); // initialize_queue(); state = Running; - - // load the next tile in the load queue (or authorize the next - // load in the case of the threaded tile pager) - loader.update(); } update_queues(); @@ -460,19 +358,6 @@ int FGTileMgr::update( SGLocation *location, double visibility_meters ) return 1; } - -// timer event driven call to scheduler for the purpose of refreshing the tile timestamps -void FGTileMgr::refresh_view_timestamps() { - SG_LOG( SG_TERRAIN, SG_INFO, - "Refreshing timestamps for " << current_bucket.get_center_lon() - << " " << current_bucket.get_center_lat() ); - if ( longitude >= -180.0 && longitude <= 180.0 - && latitude >= -90.0 && latitude <= 90.0 ) - { - schedule_needed(fgGetDouble("/environment/visibility-m"), current_bucket); - } -} - void FGTileMgr::prep_ssg_nodes(float vis) { // traverse the potentially viewable tile list and update range @@ -526,3 +411,22 @@ bool FGTileMgr::scenery_available(double lat, double lon, double range_m) // Survived all tests. return true; } + +namespace +{ +struct IsTileLoaded : + public std::unary_function +{ + bool operator()(const FGNewCache::tile_map::value_type& tilePair) const + { + return tilePair.second->is_loaded(); + } +}; +} + +bool FGTileMgr::isSceneryLoaded() +{ + return (std::find_if(tile_cache.begin(), tile_cache.end(), + std::not1(IsTileLoaded())) + == tile_cache.end()); +} diff --git a/src/Scenery/tilemgr.hxx b/src/Scenery/tilemgr.hxx index 2c376c7fe..63a049273 100644 --- a/src/Scenery/tilemgr.hxx +++ b/src/Scenery/tilemgr.hxx @@ -33,14 +33,7 @@ #include #include -#include - #include -#if defined(ENABLE_THREADS) -# include -#endif // ENABLE_THREADS - -#include "FGTileLoader.hxx" #include "newcache.hxx" #if defined(USE_MEM) || defined(WIN32) @@ -49,12 +42,8 @@ # define FG_MEM_COPY(to,from,n) bcopy(from, to, n) #endif -SG_USING_STD( queue ); - - // forward declaration class FGTileEntry; -class FGDeferredModel; class osg::Node; @@ -100,41 +89,6 @@ private: */ FGNewCache tile_cache; - /** - * Queue tiles for loading. - */ - FGTileLoader loader; - - /** - * Work queues. - * - * attach_queue is the tiles that have been loaded [by the pager] - * that can be attached to the scene graph by the render thread. - * - * model_queue is the set of models that need to be loaded by the - * primary render thread. - */ -#if defined(ENABLE_THREADS) - static SGLockedQueue attach_queue; - static SGLockedQueue model_queue; -#else - static queue attach_queue; - static queue model_queue; -#endif // ENABLE_THREADS - static queue delete_queue; - -public: - - /** - * Add a loaded tile to the 'attach to the scene graph' queue. - */ - static void ready_to_attach( FGTileEntry *t ) { attach_queue.push( t ); } - - /** - * Add a pending model to the 'deferred model load' queue - */ - static void model_ready( FGDeferredModel *dm ) { model_queue.push( dm ); } - public: // Constructor @@ -150,9 +104,6 @@ public: // internal function, do not call directly.) void update_queues(); - // get state of all the scenery loading queues - bool all_queues_empty(); - // given the current lon/lat (in degrees), fill in the array of // local chunks. If the chunk isn't already in the cache, then // read it from disk. @@ -165,10 +116,6 @@ public: // visibility_meters ); void prep_ssg_nodes(float visibility_meters ); - // Set flag with event manager so that non-moving view refreshes - // tiles... - void refresh_view_timestamps(); - const SGBucket& get_current_bucket () const { return current_bucket; } /// Returns true if scenery is avaliable for the given lat, lon position @@ -178,6 +125,9 @@ public: // Load a model for a tile static osg::Node* loadTileModel(const string& modelPath, bool cacheModel); + + // Returns true if all the tiles in the tile cache have been loaded + bool isSceneryLoaded(); }; -- 2.39.5