36de63366b
What was happening was that we screwed up and scheduled tiles for (lon,lon) rather than (lon,lat) ... note the typo. This generated bogus tile id's which the system happily accepted, put into the tile cache system, and attempted to load. The problem was that these bogus tile id's were negative where as all valid tile id's should be >= 0. These negative tile id's up the logic used to remove tiles from the cache. When identifying tiles for removal, we look for the furthest tile away from us by starting out the furthest id at -1 and if we find something further, we update the furthest tile id. Then at the end we check if the furthest tile id >= 0 to see if we found anything we could remove. However, the furthest tile id was these bogus tiles with negative tile id's so the system always assumed there was nothing appropriate for removal. This made it impossible to ever remove a tile from the cache meaning it quickly filled up and no more tiles could be loaded. I fixed the one instance of scheduling tiles for a bogus location, and added a sanity check so if it ever happens again we'll bomb with an appropriate error message.
218 lines
5.8 KiB
C++
218 lines
5.8 KiB
C++
// newcache.cxx -- routines to handle scenery tile caching
|
|
//
|
|
// Written by Curtis Olson, started December 2000.
|
|
//
|
|
// Copyright (C) 2000 Curtis L. Olson - curt@flightgear.org
|
|
//
|
|
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
//
|
|
// $Id$
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_WINDOWS_H
|
|
# include <windows.h>
|
|
#endif
|
|
|
|
#include <GL/glut.h>
|
|
#include <GL/gl.h>
|
|
|
|
#include <plib/ssg.h> // plib include
|
|
|
|
#include <simgear/bucket/newbucket.hxx>
|
|
#include <simgear/debug/logstream.hxx>
|
|
#include <simgear/misc/sg_path.hxx>
|
|
|
|
#include <Main/globals.hxx>
|
|
#include <Main/viewer.hxx>
|
|
#include <Scenery/scenery.hxx> // for scenery.center
|
|
|
|
#include "newcache.hxx"
|
|
#include "tileentry.hxx"
|
|
|
|
|
|
SG_USING_NAMESPACE(std);
|
|
|
|
|
|
// Constructor
|
|
FGNewCache::FGNewCache( void ) :
|
|
max_cache_size(50)
|
|
{
|
|
tile_cache.clear();
|
|
}
|
|
|
|
|
|
// Destructor
|
|
FGNewCache::~FGNewCache( void ) {
|
|
}
|
|
|
|
|
|
// Free a tile cache entry
|
|
void FGNewCache::entry_free( long cache_index ) {
|
|
SG_LOG( SG_TERRAIN, SG_DEBUG, "FREEING CACHE ENTRY = " << cache_index );
|
|
FGTileEntry *tile = tile_cache[cache_index];
|
|
tile->disconnect_ssg_nodes();
|
|
|
|
#ifdef WISH_PLIB_WAS_THREADED // but it isn't
|
|
tile->sched_removal();
|
|
#else
|
|
tile->free_tile();
|
|
delete tile;
|
|
#endif
|
|
|
|
tile_cache.erase( cache_index );
|
|
}
|
|
|
|
|
|
// Initialize the tile cache subsystem
|
|
void FGNewCache::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() );
|
|
|
|
#if 0 // don't clear the cache
|
|
clear_cache();
|
|
#endif
|
|
|
|
SG_LOG( SG_TERRAIN, SG_INFO, " done with init()" );
|
|
}
|
|
|
|
|
|
// Search for the specified "bucket" in the cache
|
|
bool FGNewCache::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() );
|
|
}
|
|
|
|
|
|
// 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_abs_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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Clear all completely loaded tiles (ignores partially loaded tiles)
|
|
void FGNewCache::clear_cache() {
|
|
// This is a hack that should really get cleaned up at some point
|
|
extern ssgBranch *terrain;
|
|
|
|
tile_map_iterator current = tile_cache.begin();
|
|
tile_map_iterator end = tile_cache.end();
|
|
|
|
for ( ; current != end; ++current ) {
|
|
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) ) {
|
|
e->tile_bucket.make_bad();
|
|
entry_free(index);
|
|
}
|
|
}
|
|
|
|
// and ... just in case we missed something ...
|
|
terrain->removeAllKids();
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a new tile and schedule it for loading.
|
|
*/
|
|
bool FGNewCache::insert_tile( FGTileEntry *e ) {
|
|
// clear out a distant entry in the cache if needed.
|
|
if ( make_space() ) {
|
|
// register it in the cache
|
|
long tile_index = e->get_tile_bucket().gen_index();
|
|
tile_cache[tile_index] = e;
|
|
|
|
return true;
|
|
} else {
|
|
// failed to find cache space
|
|
|
|
return false;
|
|
}
|
|
}
|