1
0
Fork 0
flightgear/src/Scenery/tilecache.cxx
Stuart Buchanan f5b469aee0 Set viewer-[lon|lat]-deg on initial View bind
Previously /sim/current-view/viewer-[lon|lat]-deg were set to
(0,0) when a view was bind() for the first time by the view
manager.  This cause the scenery tile cache to immediately clear
and all scenery to be reloaded.

Now these values are set correctly the first time the view
bind() is called, so the cache behaves correctly.

Also fixed what looks like a possible bug on code read where
the tiles of the current view had a very short expiry time
set.  Seems wrong.
2020-05-10 14:17:40 +01:00

225 lines
6.6 KiB
C++

// TileCache.cxx -- routines to handle scenery tile caching
//
// Written by Curtis Olson, started December 2000.
//
// 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
// 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$
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <simgear/bucket/newbucket.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include "tileentry.hxx"
#include "tilecache.hxx"
TileCache::TileCache( void ) :
max_cache_size(100), current_time(0.0)
{
tile_cache.clear();
}
TileCache::~TileCache( void )
{
tile_map_iterator it = tile_cache.begin();
for (; it != tile_cache.end(); ++it) {
TileEntry* tile = it->second;
tile->removeFromSceneGraph();
delete tile;
}
}
// 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 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
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() );
}
// 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;
}
}
}
SG_LOG( SG_TERRAIN, SG_DEBUG, " index = " << min_index );
SG_LOG( SG_TERRAIN, SG_DEBUG, " min_time = " << min_time );
return min_index;
}
long TileCache::get_first_expired_tile() const
{
const_tile_map_iterator current = tile_cache.begin();
const_tile_map_iterator end = tile_cache.end();
for ( ; current != end; ++current ) {
TileEntry *e = current->second;
if (!e->is_current_view() && e->is_expired(current_time))
{
return current->first;
}
}
return -1; // no expired tile found
}
// Clear all flags indicating tiles belonging to the current view
void TileCache::clear_current_view()
{
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 );
}
// 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;
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);
return true;
}
// 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 + request_time );
t->set_current_view( true );
}
else
{
t->update_time_expired( current_time+request_time );
}
}