Merge lp:~stellarium/stellarium/gz_landscapePreload into lp:stellarium

Proposed by gzotti
Status: Merged
Merged at revision: 9107
Proposed branch: lp:~stellarium/stellarium/gz_landscapePreload
Merge into: lp:stellarium
Diff against target: 724 lines (+230/-61)
6 files modified
src/core/StelTexture.hpp (+4/-1)
src/core/StelTextureMgr.hpp (+1/-1)
src/core/modules/Landscape.cpp (+66/-36)
src/core/modules/Landscape.hpp (+17/-0)
src/core/modules/LandscapeMgr.cpp (+100/-16)
src/core/modules/LandscapeMgr.hpp (+42/-7)
To merge this branch: bzr merge lp:~stellarium/stellarium/gz_landscapePreload
Reviewer Review Type Date Requested Status
Alexander Wolf Approve
Review via email: mp+315181@code.launchpad.net

Commit message

Allow caching for landscapes, including preloading and some other manipulation via scripting.

Description of the change

This branch adds caching for the landscapes, mostly useful for the multi-gigabyte GPUs of today, but (hopefully) not disturbing for older systems. Big landscapes take several seconds to load. Switching rapidly between several landscapes becomes much easier with this. It is also possible to pre-load or delete from the cache via scripting.

Cache size can be configured in config.ini.

To post a comment you must log in.
Revision history for this message
Alexander Wolf (alexwolf) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/core/StelTexture.hpp'
--- src/core/StelTexture.hpp 2016-11-28 16:01:23 +0000
+++ src/core/StelTexture.hpp 2017-01-19 22:30:07 +0000
@@ -91,6 +91,9 @@
91 //! Return whether the image is currently being loaded91 //! Return whether the image is currently being loaded
92 bool isLoading() const {return (loader || networkReply) && !canBind();}92 bool isLoading() const {return (loader || networkReply) && !canBind();}
9393
94 //! Return texture memory size
95 unsigned int getGlSize() const {return glSize;}
96
94signals:97signals:
95 //! Emitted when the texture is ready to be bind(), i.e. when downloaded, imageLoading and glLoading is over98 //! Emitted when the texture is ready to be bind(), i.e. when downloaded, imageLoading and glLoading is over
96 //! or when an error occured and the texture will never be available99 //! or when an error occured and the texture will never be available
@@ -171,7 +174,7 @@
171 GLsizei height; //! Texture image height174 GLsizei height; //! Texture image height
172175
173 //! Size in GL memory176 //! Size in GL memory
174 int glSize;177 unsigned int glSize;
175};178};
176179
177180
178181
=== modified file 'src/core/StelTextureMgr.hpp'
--- src/core/StelTextureMgr.hpp 2016-11-21 21:04:36 +0000
+++ src/core/StelTextureMgr.hpp 2017-01-19 22:30:07 +0000
@@ -61,7 +61,7 @@
61 //! Must be called after the creation of the GLContext.61 //! Must be called after the creation of the GLContext.
62 void init();62 void init();
6363
64 int glMemoryUsage;64 unsigned int glMemoryUsage;
65};65};
6666
6767
6868
=== modified file 'src/core/modules/Landscape.cpp'
--- src/core/modules/Landscape.cpp 2017-01-08 16:39:11 +0000
+++ src/core/modules/Landscape.cpp 2017-01-19 22:30:07 +0000
@@ -2,6 +2,7 @@
2 * Stellarium2 * Stellarium
3 * Copyright (C) 2003 Fabien Chereau3 * Copyright (C) 2003 Fabien Chereau
4 * Copyright (C) 2011 Bogdan Marinov4 * Copyright (C) 2011 Bogdan Marinov
5 * Copyright (C) 2014-17 Georg Zotti
5 *6 *
6 * This program is free software; you can redistribute it and/or7 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License8 * modify it under the terms of the GNU General Public License
@@ -37,6 +38,7 @@
3738
38Landscape::Landscape(float _radius)39Landscape::Landscape(float _radius)
39 : radius(_radius)40 : radius(_radius)
41 , id("uninitialized")
40 , minBrightness(-1.)42 , minBrightness(-1.)
41 , landscapeBrightness(1.)43 , landscapeBrightness(1.)
42 , lightScapeBrightness(0.)44 , lightScapeBrightness(0.)
@@ -63,6 +65,7 @@
63// Load attributes common to all landscapes65// Load attributes common to all landscapes
64void Landscape::loadCommon(const QSettings& landscapeIni, const QString& landscapeId)66void Landscape::loadCommon(const QSettings& landscapeIni, const QString& landscapeId)
65{67{
68 id = landscapeId;
66 name = landscapeIni.value("landscape/name").toString();69 name = landscapeIni.value("landscape/name").toString();
67 author = landscapeIni.value("landscape/author").toString();70 author = landscapeIni.value("landscape/author").toString();
68 description = landscapeIni.value("landscape/description").toString();71 description = landscapeIni.value("landscape/description").toString();
@@ -284,13 +287,14 @@
284 {287 {
285 QString line=in.readLine();288 QString line=in.readLine();
286289
287 // TODO: Read entries, construct vectors, put in list.290 // Skip comments and all-empty lines (space allowed and ignored)
288 if (line.startsWith('#'))291 if (line.startsWith('#') || line.trimmed().isEmpty() )
289 continue;292 continue;
293 // Read entries, construct vectors, put in list.
290 QStringList parts=line.split('|');294 QStringList parts=line.split('|');
291 if (parts.count() != 5)295 if (parts.count() != 5)
292 {296 {
293 qWarning() << "Invalid line in landscape" << descFileName << ":" << line;297 qWarning() << "Invalid line in landscape gazetteer" << descFileName << ":" << line;
294 continue;298 continue;
295 }299 }
296 LandscapeLabel newLabel;300 LandscapeLabel newLabel;
@@ -359,6 +363,7 @@
359 , drawGroundFirst(0)363 , drawGroundFirst(0)
360 , tanMode(false)364 , tanMode(false)
361 , calibrated(false)365 , calibrated(false)
366 , memorySize(sizeof(LandscapeOldStyle)) // start with just the known entries.
362{}367{}
363368
364LandscapeOldStyle::~LandscapeOldStyle()369LandscapeOldStyle::~LandscapeOldStyle()
@@ -391,7 +396,7 @@
391 {396 {
392 qWarning() << "Landscape type mismatch for landscape " << landscapeId397 qWarning() << "Landscape type mismatch for landscape " << landscapeId
393 << ", expected old_style, found " << type << ". No landscape in use.";398 << ", expected old_style, found " << type << ". No landscape in use.";
394 validLandscape = 0;399 validLandscape = false;
395 return;400 return;
396 }401 }
397402
@@ -421,6 +426,7 @@
421 if ( (!horizonPolygon) && calibrated ) { // for uncalibrated landscapes the texture is currently never queried, so no need to store.426 if ( (!horizonPolygon) && calibrated ) { // for uncalibrated landscapes the texture is currently never queried, so no need to store.
422 QImage *image = new QImage(texturePath);427 QImage *image = new QImage(texturePath);
423 sidesImages.append(image); // indices identical to those in sideTexs428 sidesImages.append(image); // indices identical to those in sideTexs
429 memorySize+=image->byteCount();
424 }430 }
425 // Also allow light textures. The light textures must cover the same geometry as the sides. It is allowed that not all or even any light textures are present!431 // Also allow light textures. The light textures must cover the same geometry as the sides. It is allowed that not all or even any light textures are present!
426 textureKey = QString("landscape/light%1").arg(i);432 textureKey = QString("landscape/light%1").arg(i);
@@ -429,6 +435,8 @@
429 {435 {
430 const QString lightTexturePath = getTexturePath(textureName, landscapeId);436 const QString lightTexturePath = getTexturePath(textureName, landscapeId);
431 sideTexs[nbSideTexs+i] = StelApp::getInstance().getTextureManager().createTexture(lightTexturePath);437 sideTexs[nbSideTexs+i] = StelApp::getInstance().getTextureManager().createTexture(lightTexturePath);
438 if(sideTexs[nbSideTexs+i])
439 memorySize+=sideTexs[nbSideTexs+i].data()->getGlSize();
432 }440 }
433 else441 else
434 sideTexs[nbSideTexs+i].clear();442 sideTexs[nbSideTexs+i].clear();
@@ -471,28 +479,14 @@
471 QString groundTexName = landscapeIni.value("landscape/groundtex").toString();479 QString groundTexName = landscapeIni.value("landscape/groundtex").toString();
472 QString groundTexPath = getTexturePath(groundTexName, landscapeId);480 QString groundTexPath = getTexturePath(groundTexName, landscapeId);
473 groundTex = StelApp::getInstance().getTextureManager().createTexture(groundTexPath, StelTexture::StelTextureParams(true));481 groundTex = StelApp::getInstance().getTextureManager().createTexture(groundTexPath, StelTexture::StelTextureParams(true));
474 // GZ 2013/11: I don't see any use of this:482 if (groundTex)
475// QString description = landscapeIni.value("landscape/ground").toString();483 memorySize+=groundTex.data()->getGlSize();
476// //sscanf(description.toLocal8Bit(),"groundtex:%f:%f:%f:%f",&a,&b,&c,&d);
477// QStringList parameters = description.split(':');
478// groundTexCoord.tex = groundTex;
479// groundTexCoord.texCoords[0] = parameters.at(1).toFloat();
480// groundTexCoord.texCoords[1] = parameters.at(2).toFloat();
481// groundTexCoord.texCoords[2] = parameters.at(3).toFloat();
482// groundTexCoord.texCoords[3] = parameters.at(4).toFloat();
483484
484 QString fogTexName = landscapeIni.value("landscape/fogtex").toString();485 QString fogTexName = landscapeIni.value("landscape/fogtex").toString();
485 QString fogTexPath = getTexturePath(fogTexName, landscapeId);486 QString fogTexPath = getTexturePath(fogTexName, landscapeId);
486 fogTex = StelApp::getInstance().getTextureManager().createTexture(fogTexPath, StelTexture::StelTextureParams(true, GL_LINEAR, GL_REPEAT));487 fogTex = StelApp::getInstance().getTextureManager().createTexture(fogTexPath, StelTexture::StelTextureParams(true, GL_LINEAR, GL_REPEAT));
487 // GZ 2013/11: I don't see any use of this:488 if (fogTex)
488// QString description = landscapeIni.value("landscape/fog").toString();489 memorySize+=fogTex.data()->getGlSize();
489// //sscanf(description.toLocal8Bit(),"fogtex:%f:%f:%f:%f",&a,&b,&c,&d);
490// QStringList parameters = description.split(':');
491// fogTexCoord.tex = fogTex;
492// fogTexCoord.texCoords[0] = parameters.at(1).toFloat();
493// fogTexCoord.texCoords[1] = parameters.at(2).toFloat();
494// fogTexCoord.texCoords[2] = parameters.at(3).toFloat();
495// fogTexCoord.texCoords[3] = parameters.at(4).toFloat();
496490
497 // Precompute the vertex arrays for ground display491 // Precompute the vertex arrays for ground display
498 // Make slices_per_side=(3<<K) so that the innermost polygon of the fandisk becomes a triangle:492 // Make slices_per_side=(3<<K) so that the innermost polygon of the fandisk becomes a triangle:
@@ -520,9 +514,9 @@
520 // GZ: the original code for vertical placement makes unfortunately no sense. There are many approximately-fitted landscapes, though.514 // GZ: the original code for vertical placement makes unfortunately no sense. There are many approximately-fitted landscapes, though.
521 // I added a switch "calibrated" for the ini file. If true, it works as this landscape apparently was originally intended,515 // I added a switch "calibrated" for the ini file. If true, it works as this landscape apparently was originally intended,
522 // if false (or missing) it uses the original code.516 // if false (or missing) it uses the original code.
523 // So I corrected the texture coordinates so that decorAltAngle is the total vertical angle, decorAngleShift the lower angle,517 // I corrected the texture coordinates so that decorAltAngle is the total vertical angle, decorAngleShift the lower angle,
524 // and the texture in between is correctly stretched.518 // and the texture in between is correctly stretched.
525 // I located an undocumented switch tan_mode, maybe tan_mode=true means cylindrical panorama projection.519 // I located an undocumented switch tan_mode, maybe tan_mode=true means cylindrical panorama projection instead of equirectangular.
526 // Since V0.13, calibrated&&tanMode also works!520 // Since V0.13, calibrated&&tanMode also works!
527 // In calibrated && !tan_mode, the vertical position is computed correctly, so that quads off the horizon are larger.521 // In calibrated && !tan_mode, the vertical position is computed correctly, so that quads off the horizon are larger.
528 // in calibrated && tan_mode, d_z can become a constant because the texture is already predistorted in cylindrical projection.522 // in calibrated && tan_mode, d_z can become a constant because the texture is already predistorted in cylindrical projection.
@@ -619,6 +613,7 @@
619 }613 }
620 }614 }
621 }615 }
616 //qDebug() << "OldStyleLandscape" << landscapeId << "loaded, mem size:" << memorySize;
622}617}
623618
624void LandscapeOldStyle::draw(StelCore* core)619void LandscapeOldStyle::draw(StelCore* core)
@@ -836,17 +831,18 @@
836 if(type != "polygonal")831 if(type != "polygonal")
837 {832 {
838 qWarning() << "Landscape type mismatch for landscape "<< landscapeId << ", expected polygonal, found " << type << ". No landscape in use.\n";833 qWarning() << "Landscape type mismatch for landscape "<< landscapeId << ", expected polygonal, found " << type << ". No landscape in use.\n";
839 validLandscape = 0;834 validLandscape = false;
840 return;835 return;
841 }836 }
842 if (horizonPolygon.isNull())837 if (horizonPolygon.isNull())
843 {838 {
844 qWarning() << "Landscape " << landscapeId << " does not declare a valid polygonal_horizon_list. No landscape in use.\n";839 qWarning() << "Landscape " << landscapeId << " does not declare a valid polygonal_horizon_list. No landscape in use.\n";
845 validLandscape = 0;840 validLandscape = false;
846 return;841 return;
847 }842 }
848 groundColor=StelUtils::strToVec3f( landscapeIni.value("landscape/ground_color", "0,0,0" ).toString() );843 groundColor=StelUtils::strToVec3f( landscapeIni.value("landscape/ground_color", "0,0,0" ).toString() );
849 validLandscape = 1; // assume ok...844 validLandscape = true; // assume ok...
845 //qDebug() << "PolygonalLandscape" << landscapeId << "loaded, mem size:" << getMemorySize();
850}846}
851847
852void LandscapePolygonal::draw(StelCore* core)848void LandscapePolygonal::draw(StelCore* core)
@@ -891,8 +887,12 @@
891887
892LandscapeFisheye::LandscapeFisheye(float _radius)888LandscapeFisheye::LandscapeFisheye(float _radius)
893 : Landscape(_radius)889 : Landscape(_radius)
890 , mapTex(StelTextureSP())
891 , mapTexFog(StelTextureSP())
892 , mapTexIllum(StelTextureSP())
894 , mapImage(NULL)893 , mapImage(NULL)
895 , texFov(360.)894 , texFov(360.)
895 , memorySize(0)
896{}896{}
897897
898LandscapeFisheye::~LandscapeFisheye()898LandscapeFisheye::~LandscapeFisheye()
@@ -909,7 +909,7 @@
909 if(type != "fisheye")909 if(type != "fisheye")
910 {910 {
911 qWarning() << "Landscape type mismatch for landscape "<< landscapeId << ", expected fisheye, found " << type << ". No landscape in use.\n";911 qWarning() << "Landscape type mismatch for landscape "<< landscapeId << ", expected fisheye, found " << type << ". No landscape in use.\n";
912 validLandscape = 0;912 validLandscape = false;
913 return;913 return;
914 }914 }
915 create(name,915 create(name,
@@ -918,25 +918,38 @@
918 getTexturePath(landscapeIni.value("landscape/maptex_fog").toString(), landscapeId),918 getTexturePath(landscapeIni.value("landscape/maptex_fog").toString(), landscapeId),
919 getTexturePath(landscapeIni.value("landscape/maptex_illum").toString(), landscapeId),919 getTexturePath(landscapeIni.value("landscape/maptex_illum").toString(), landscapeId),
920 landscapeIni.value("landscape/angle_rotatez", 0.f).toFloat());920 landscapeIni.value("landscape/angle_rotatez", 0.f).toFloat());
921 //qDebug() << "FisheyeLandscape" << landscapeId << "loaded, mem size:" << memorySize;
921}922}
922923
923924
924void LandscapeFisheye::create(const QString _name, float _texturefov, const QString& _maptex, const QString &_maptexFog, const QString& _maptexIllum, const float _angleRotateZ)925void LandscapeFisheye::create(const QString _name, float _texturefov, const QString& _maptex, const QString &_maptexFog, const QString& _maptexIllum, const float _angleRotateZ)
925{926{
926 // qDebug() << _name << " " << _fullpath << " " << _maptex << " " << _texturefov;927 // qDebug() << _name << " " << _fullpath << " " << _maptex << " " << _texturefov;
927 validLandscape = 1; // assume ok...928 validLandscape = true; // assume ok...
928 name = _name;929 name = _name;
929 texFov = _texturefov*M_PI/180.f;930 texFov = _texturefov*M_PI/180.f;
930 angleRotateZ = _angleRotateZ*M_PI/180.f;931 angleRotateZ = _angleRotateZ*M_PI/180.f;
932
931 if (!horizonPolygon)933 if (!horizonPolygon)
934 {
932 mapImage = new QImage(_maptex);935 mapImage = new QImage(_maptex);
936 memorySize+=mapImage->byteCount();
937 }
933 mapTex = StelApp::getInstance().getTextureManager().createTexture(_maptex, StelTexture::StelTextureParams(true));938 mapTex = StelApp::getInstance().getTextureManager().createTexture(_maptex, StelTexture::StelTextureParams(true));
939 memorySize+=mapTex.data()->getGlSize();
934940
935 if (_maptexIllum.length())941 if (_maptexIllum.length() && (!_maptexIllum.endsWith("/")))
942 {
936 mapTexIllum = StelApp::getInstance().getTextureManager().createTexture(_maptexIllum, StelTexture::StelTextureParams(true));943 mapTexIllum = StelApp::getInstance().getTextureManager().createTexture(_maptexIllum, StelTexture::StelTextureParams(true));
937 if (_maptexFog.length())944 if (mapTexIllum)
945 memorySize+=mapTexIllum.data()->getGlSize();
946 }
947 if (_maptexFog.length() && (!_maptexFog.endsWith("/")))
948 {
938 mapTexFog = StelApp::getInstance().getTextureManager().createTexture(_maptexFog, StelTexture::StelTextureParams(true));949 mapTexFog = StelApp::getInstance().getTextureManager().createTexture(_maptexFog, StelTexture::StelTextureParams(true));
939950 if (mapTexFog)
951 memorySize+=mapTexFog.data()->getGlSize();
952 }
940}953}
941954
942955
@@ -1025,6 +1038,9 @@
10251038
1026LandscapeSpherical::LandscapeSpherical(float _radius)1039LandscapeSpherical::LandscapeSpherical(float _radius)
1027 : Landscape(_radius)1040 : Landscape(_radius)
1041 , mapTex(StelTextureSP())
1042 , mapTexFog(StelTextureSP())
1043 , mapTexIllum(StelTextureSP())
1028 , mapTexTop(0.)1044 , mapTexTop(0.)
1029 , mapTexBottom(0.)1045 , mapTexBottom(0.)
1030 , fogTexTop(0.)1046 , fogTexTop(0.)
@@ -1032,6 +1048,7 @@
1032 , illumTexTop(0.)1048 , illumTexTop(0.)
1033 , illumTexBottom(0.)1049 , illumTexBottom(0.)
1034 , mapImage(NULL)1050 , mapImage(NULL)
1051 , memorySize(sizeof(LandscapeSpherical))
1035{}1052{}
10361053
1037LandscapeSpherical::~LandscapeSpherical()1054LandscapeSpherical::~LandscapeSpherical()
@@ -1054,7 +1071,7 @@
1054 qWarning() << "Landscape type mismatch for landscape "<< landscapeId1071 qWarning() << "Landscape type mismatch for landscape "<< landscapeId
1055 << ", expected spherical, found " << type1072 << ", expected spherical, found " << type
1056 << ". No landscape in use.\n";1073 << ". No landscape in use.\n";
1057 validLandscape = 0;1074 validLandscape = false;
1058 return;1075 return;
1059 }1076 }
10601077
@@ -1069,6 +1086,7 @@
1069 landscapeIni.value("landscape/maptex_fog_bottom" , -90.f).toFloat(),1086 landscapeIni.value("landscape/maptex_fog_bottom" , -90.f).toFloat(),
1070 landscapeIni.value("landscape/maptex_illum_top" , 90.f).toFloat(),1087 landscapeIni.value("landscape/maptex_illum_top" , 90.f).toFloat(),
1071 landscapeIni.value("landscape/maptex_illum_bottom", -90.f).toFloat());1088 landscapeIni.value("landscape/maptex_illum_bottom", -90.f).toFloat());
1089 //qDebug() << "SphericalLandscape" << landscapeId << "loaded, mem size:" << memorySize;
1072}1090}
10731091
10741092
@@ -1079,7 +1097,7 @@
1079 const float _illumTexTop, const float _illumTexBottom)1097 const float _illumTexTop, const float _illumTexBottom)
1080{1098{
1081 //qDebug() << "LandscapeSpherical::create():"<< _name << " : " << _maptex << " : " << _maptexFog << " : " << _maptexIllum << " : " << _angleRotateZ;1099 //qDebug() << "LandscapeSpherical::create():"<< _name << " : " << _maptex << " : " << _maptexFog << " : " << _maptexIllum << " : " << _angleRotateZ;
1082 validLandscape = 1; // assume ok...1100 validLandscape = true; // assume ok...
1083 name = _name;1101 name = _name;
1084 angleRotateZ = _angleRotateZ *M_PI/180.f; // Defined in ini --> internal prg value1102 angleRotateZ = _angleRotateZ *M_PI/180.f; // Defined in ini --> internal prg value
1085 mapTexTop = (90.f-_mapTexTop) *M_PI/180.f; // top 90 --> 01103 mapTexTop = (90.f-_mapTexTop) *M_PI/180.f; // top 90 --> 0
@@ -1089,13 +1107,25 @@
1089 illumTexTop = (90.f-_illumTexTop) *M_PI/180.f;1107 illumTexTop = (90.f-_illumTexTop) *M_PI/180.f;
1090 illumTexBottom= (90.f-_illumTexBottom)*M_PI/180.f;1108 illumTexBottom= (90.f-_illumTexBottom)*M_PI/180.f;
1091 if (!horizonPolygon)1109 if (!horizonPolygon)
1110 {
1092 mapImage = new QImage(_maptex);1111 mapImage = new QImage(_maptex);
1112 memorySize+=mapImage->byteCount();
1113 }
1093 mapTex = StelApp::getInstance().getTextureManager().createTexture(_maptex, StelTexture::StelTextureParams(true));1114 mapTex = StelApp::getInstance().getTextureManager().createTexture(_maptex, StelTexture::StelTextureParams(true));
1115 memorySize+=mapTex.data()->getGlSize();
10941116
1095 if (_maptexIllum.length())1117 if (_maptexIllum.length() && (!_maptexIllum.endsWith("/")))
1118 {
1096 mapTexIllum = StelApp::getInstance().getTextureManager().createTexture(_maptexIllum, StelTexture::StelTextureParams(true));1119 mapTexIllum = StelApp::getInstance().getTextureManager().createTexture(_maptexIllum, StelTexture::StelTextureParams(true));
1097 if (_maptexFog.length())1120 if (mapTexIllum)
1121 memorySize+=mapTexIllum.data()->getGlSize();
1122 }
1123 if (_maptexFog.length() && (!_maptexFog.endsWith("/")))
1124 {
1098 mapTexFog = StelApp::getInstance().getTextureManager().createTexture(_maptexFog, StelTexture::StelTextureParams(true));1125 mapTexFog = StelApp::getInstance().getTextureManager().createTexture(_maptexFog, StelTexture::StelTextureParams(true));
1126 if (mapTexFog)
1127 memorySize+=mapTexFog.data()->getGlSize();
1128 }
1099}1129}
11001130
1101void LandscapeSpherical::draw(StelCore* core)1131void LandscapeSpherical::draw(StelCore* core)
11021132
=== modified file 'src/core/modules/Landscape.hpp'
--- src/core/modules/Landscape.hpp 2016-05-22 21:14:58 +0000
+++ src/core/modules/Landscape.hpp 2017-01-19 22:30:07 +0000
@@ -75,6 +75,13 @@
75 //! @param landscapeIni A reference to an existing QSettings object which describes the landscape75 //! @param landscapeIni A reference to an existing QSettings object which describes the landscape
76 //! @param landscapeId The name of the directory for the landscape files (e.g. "ocean")76 //! @param landscapeId The name of the directory for the landscape files (e.g. "ocean")
77 virtual void load(const QSettings& landscapeIni, const QString& landscapeId) = 0;77 virtual void load(const QSettings& landscapeIni, const QString& landscapeId) = 0;
78
79 //! Return approximate memory footprint in bytes (required for cache cost estimate in LandscapeMgr)
80 //! The returned value is only approximate, content of QStrings and other small containers like the horizon polygon are not put in in detail.
81 //! However, texture image sizes must be computed in subclasses.
82 //! The value returned is a sum of RAM and texture memory requirements.
83 virtual unsigned int getMemorySize() const {return sizeof(Landscape);}
84
78 virtual void draw(StelCore* core) = 0;85 virtual void draw(StelCore* core) = 0;
79 void update(double deltaTime)86 void update(double deltaTime)
80 {87 {
@@ -124,6 +131,8 @@
124 QString getAuthorName() const {return author;}131 QString getAuthorName() const {return author;}
125 //! Get landscape description132 //! Get landscape description
126 QString getDescription() const {return description;}133 QString getDescription() const {return description;}
134 //! Get landscape id. This is the landscape directory name, used for cache handling.
135 QString getId() const {return id;}
127136
128 //! Return the associated location (may be empty!)137 //! Return the associated location (may be empty!)
129 const StelLocation& getLocation() const {return location;}138 const StelLocation& getLocation() const {return location;}
@@ -202,6 +211,7 @@
202 QString name; //! Read from landscape.ini:[landscape]name211 QString name; //! Read from landscape.ini:[landscape]name
203 QString author; //! Read from landscape.ini:[landscape]author212 QString author; //! Read from landscape.ini:[landscape]author
204 QString description; //! Read from landscape.ini:[landscape]description213 QString description; //! Read from landscape.ini:[landscape]description
214 QString id; //! Set during load. Required for consistent caching.
205215
206 float minBrightness; //! Read from landscape.ini:[landscape]minimal_brightness. Allows minimum visibility that cannot be underpowered.216 float minBrightness; //! Read from landscape.ini:[landscape]minimal_brightness. Allows minimum visibility that cannot be underpowered.
207 float landscapeBrightness; //! brightness [0..1] to draw the landscape. Computed by the LandscapeMgr.217 float landscapeBrightness; //! brightness [0..1] to draw the landscape. Computed by the LandscapeMgr.
@@ -259,6 +269,7 @@
259 LandscapeOldStyle(float radius = 2.f);269 LandscapeOldStyle(float radius = 2.f);
260 virtual ~LandscapeOldStyle();270 virtual ~LandscapeOldStyle();
261 virtual void load(const QSettings& landscapeIni, const QString& landscapeId);271 virtual void load(const QSettings& landscapeIni, const QString& landscapeId);
272 virtual unsigned int getMemorySize() const {return memorySize;}
262 virtual void draw(StelCore* core);273 virtual void draw(StelCore* core);
263 //void create(bool _fullpath, QMap<QString, QString> param); // still not implemented274 //void create(bool _fullpath, QMap<QString, QString> param); // still not implemented
264 virtual float getOpacity(Vec3d azalt) const;275 virtual float getOpacity(Vec3d azalt) const;
@@ -302,6 +313,7 @@
302 };313 };
303314
304 QList<LOSSide> precomputedSides;315 QList<LOSSide> precomputedSides;
316 unsigned int memorySize;
305};317};
306318
307/////////////////////////////////////////////////////////319/////////////////////////////////////////////////////////
@@ -319,6 +331,7 @@
319 LandscapePolygonal(float radius = 1.f);331 LandscapePolygonal(float radius = 1.f);
320 virtual ~LandscapePolygonal();332 virtual ~LandscapePolygonal();
321 virtual void load(const QSettings& landscapeIni, const QString& landscapeId);333 virtual void load(const QSettings& landscapeIni, const QString& landscapeId);
334 virtual unsigned int getMemorySize() const {return sizeof(LandscapePolygonal);}
322 virtual void draw(StelCore* core);335 virtual void draw(StelCore* core);
323 virtual float getOpacity(Vec3d azalt) const;336 virtual float getOpacity(Vec3d azalt) const;
324private:337private:
@@ -338,6 +351,7 @@
338 LandscapeFisheye(float radius = 1.f);351 LandscapeFisheye(float radius = 1.f);
339 virtual ~LandscapeFisheye();352 virtual ~LandscapeFisheye();
340 virtual void load(const QSettings& landscapeIni, const QString& landscapeId);353 virtual void load(const QSettings& landscapeIni, const QString& landscapeId);
354 virtual unsigned int getMemorySize() const {return memorySize;}
341 virtual void draw(StelCore* core);355 virtual void draw(StelCore* core);
342 //! Sample landscape texture for transparency/opacity. May be used for visibility, sunrise etc.356 //! Sample landscape texture for transparency/opacity. May be used for visibility, sunrise etc.
343 //! @param azalt normalized direction in alt-az frame357 //! @param azalt normalized direction in alt-az frame
@@ -360,6 +374,7 @@
360 QImage *mapImage; //!< The same image as mapTex, but stored in-mem for sampling.374 QImage *mapImage; //!< The same image as mapTex, but stored in-mem for sampling.
361375
362 float texFov;376 float texFov;
377 unsigned int memorySize;
363};378};
364379
365//////////////////////////////////////////////////////////////////////////380//////////////////////////////////////////////////////////////////////////
@@ -378,6 +393,7 @@
378 LandscapeSpherical(float radius = 1.f);393 LandscapeSpherical(float radius = 1.f);
379 virtual ~LandscapeSpherical();394 virtual ~LandscapeSpherical();
380 virtual void load(const QSettings& landscapeIni, const QString& landscapeId);395 virtual void load(const QSettings& landscapeIni, const QString& landscapeId);
396 virtual unsigned int getMemorySize() const {return memorySize;}
381 virtual void draw(StelCore* core);397 virtual void draw(StelCore* core);
382 //! Sample landscape texture for transparency/opacity. May be used for visibility, sunrise etc.398 //! Sample landscape texture for transparency/opacity. May be used for visibility, sunrise etc.
383 //! @param azalt normalized direction in alt-az frame399 //! @param azalt normalized direction in alt-az frame
@@ -414,6 +430,7 @@
414 float illumTexTop; //!< zenithal top angle of the illumination texture, radians430 float illumTexTop; //!< zenithal top angle of the illumination texture, radians
415 float illumTexBottom; //!< zenithal bottom angle of the illumination texture, radians431 float illumTexBottom; //!< zenithal bottom angle of the illumination texture, radians
416 QImage *mapImage; //!< The same image as mapTex, but stored in-mem for opacity sampling.432 QImage *mapImage; //!< The same image as mapTex, but stored in-mem for opacity sampling.
433 unsigned int memorySize; //!< holds an approximate value of memory consumption (for cache cost estimate)
417};434};
418435
419#endif // _LANDSCAPE_HPP_436#endif // _LANDSCAPE_HPP_
420437
=== modified file 'src/core/modules/LandscapeMgr.cpp'
--- src/core/modules/LandscapeMgr.cpp 2017-01-08 16:39:11 +0000
+++ src/core/modules/LandscapeMgr.cpp 2017-01-19 22:30:07 +0000
@@ -142,7 +142,8 @@
142142
143143
144LandscapeMgr::LandscapeMgr()144LandscapeMgr::LandscapeMgr()
145 : atmosphere(NULL)145 : StelModule()
146 , atmosphere(NULL)
146 , cardinalsPoints(NULL)147 , cardinalsPoints(NULL)
147 , landscape(NULL)148 , landscape(NULL)
148 , oldLandscape(NULL)149 , oldLandscape(NULL)
@@ -154,7 +155,7 @@
154 , flagLandscapeSetsMinimalBrightness(false)155 , flagLandscapeSetsMinimalBrightness(false)
155 , flagAtmosphereAutoEnabling(false)156 , flagAtmosphereAutoEnabling(false)
156{157{
157 setObjectName("LandscapeMgr");158 setObjectName("LandscapeMgr"); // should be done by StelModule's constructor.
158159
159 //Note: The first entry in the list is used as the default 'default landscape' in removeLandscape().160 //Note: The first entry in the list is used as the default 'default landscape' in removeLandscape().
160 packagedLandscapeIDs = (QStringList() << "guereins");161 packagedLandscapeIDs = (QStringList() << "guereins");
@@ -165,6 +166,7 @@
165 packagedLandscapeIDs << directories.fileName();166 packagedLandscapeIDs << directories.fileName();
166 }167 }
167 packagedLandscapeIDs.removeDuplicates();168 packagedLandscapeIDs.removeDuplicates();
169 landscapeCache.clear();
168}170}
169171
170LandscapeMgr::~LandscapeMgr()172LandscapeMgr::~LandscapeMgr()
@@ -178,6 +180,8 @@
178 }180 }
179 delete landscape;181 delete landscape;
180 landscape = NULL;182 landscape = NULL;
183 qDebug() << "LandscapeMgr: Clearing cache of" << landscapeCache.size() << "landscapes totalling about " << landscapeCache.totalCost() << "MB.";
184 landscapeCache.clear(); // deletes all objects within.
181}185}
182186
183/*************************************************************************187/*************************************************************************
@@ -198,6 +202,7 @@
198void LandscapeMgr::update(double deltaTime)202void LandscapeMgr::update(double deltaTime)
199{203{
200 atmosphere->update(deltaTime);204 atmosphere->update(deltaTime);
205
201 if (oldLandscape)206 if (oldLandscape)
202 {207 {
203 // This is only when transitioning to newly loaded landscape. We must draw the old one until the new one is faded in completely.208 // This is only when transitioning to newly loaded landscape. We must draw the old one until the new one is faded in completely.
@@ -208,7 +213,10 @@
208213
209 if (oldLandscape->getEffectiveLandFadeValue()< 0.01f)214 if (oldLandscape->getEffectiveLandFadeValue()< 0.01f)
210 {215 {
211 delete oldLandscape;216 // new logic: try to put old landscape to cache.
217 //qDebug() << "LandscapeMgr::update: moving oldLandscape " << oldLandscape->getId() << "to Cache. Cost:" << oldLandscape->getMemorySize()/(1024*1024)+1;
218 landscapeCache.insert(oldLandscape->getId(), oldLandscape, oldLandscape->getMemorySize()/(1024*1024)+1);
219 //qDebug() << "--> LandscapeMgr::update(): cache now contains " << landscapeCache.size() << "landscapes totalling about " << landscapeCache.totalCost() << "MB.";
212 oldLandscape=NULL;220 oldLandscape=NULL;
213 }221 }
214 }222 }
@@ -368,8 +376,10 @@
368 QSettings* conf = StelApp::getInstance().getSettings();376 QSettings* conf = StelApp::getInstance().getSettings();
369 Q_ASSERT(conf);377 Q_ASSERT(conf);
370378
379 landscapeCache.setMaxCost(conf->value("landscape/cache_size_mb", 100).toInt());
380 qDebug() << "LandscapeMgr: initialized Cache for" << landscapeCache.maxCost() << "MB.";
381
371 atmosphere = new Atmosphere();382 atmosphere = new Atmosphere();
372 landscape = new LandscapeOldStyle();
373 defaultLandscapeID = conf->value("init_location/landscape_name").toString();383 defaultLandscapeID = conf->value("init_location/landscape_name").toString();
374 setCurrentLandscapeID(defaultLandscapeID);384 setCurrentLandscapeID(defaultLandscapeID);
375 setFlagLandscape(conf->value("landscape/flag_landscape", conf->value("landscape/flag_ground", true).toBool()).toBool());385 setFlagLandscape(conf->value("landscape/flag_landscape", conf->value("landscape/flag_ground", true).toBool()).toBool());
@@ -421,13 +431,39 @@
421 if(id==currentLandscapeID)431 if(id==currentLandscapeID)
422 return false;432 return false;
423433
424 // We want to lookup the landscape ID (dir) from the name.434 Landscape* newLandscape;
425 Landscape* newLandscape = createFromFile(StelFileMgr::findFile("landscapes/" + id + "/landscape.ini"), id);435
426 436 // There is a slight chance that we switch back to oldLandscape while oldLandscape is still fading away.
427 if (!newLandscape)437 // in this case it is not yet stored in cache, but obviously available. So we just swap places.
428 {438 if (oldLandscape && oldLandscape->getId()==id)
429 qWarning() << "ERROR while loading landscape " << "landscapes/" + id + "/landscape.ini";439 {
430 return false;440 newLandscape=oldLandscape;
441 }
442 else
443 {
444 // We want to lookup the landscape ID (dir) from the name.
445 newLandscape= landscapeCache.take(id);
446
447 if (newLandscape)
448 {
449#ifndef NDEBUG
450 qDebug() << "LandscapeMgr::setCurrentLandscapeID():: taken " << id << "from cache...";
451 qDebug() << ".-->LandscapeMgr::setCurrentLandscapeID(): cache contains " << landscapeCache.size() << "landscapes totalling about " << landscapeCache.totalCost() << "MB.";
452#endif
453 }
454 else
455 {
456#ifndef NDEBUG
457 qDebug() << "LandscapeMgr::setCurrentLandscapeID: Loading from file:" << id ;
458#endif
459 newLandscape = createFromFile(StelFileMgr::findFile("landscapes/" + id + "/landscape.ini"), id);
460 }
461
462 if (!newLandscape)
463 {
464 qWarning() << "ERROR while loading landscape " << "landscapes/" + id + "/landscape.ini";
465 return false;
466 }
431 }467 }
432468
433 // Keep current landscape for a while, while new landscape fades in!469 // Keep current landscape for a while, while new landscape fades in!
@@ -439,15 +475,23 @@
439 newLandscape->setFlagShowFog(landscape->getFlagShowFog());475 newLandscape->setFlagShowFog(landscape->getFlagShowFog());
440 newLandscape->setFlagShowIllumination(landscape->getFlagShowIllumination());476 newLandscape->setFlagShowIllumination(landscape->getFlagShowIllumination());
441 newLandscape->setFlagShowLabels(landscape->getFlagShowLabels());477 newLandscape->setFlagShowLabels(landscape->getFlagShowLabels());
442 //in case we already fade out one old landscape (if switching too rapidly): the old one has to go immediately.478
443 if (oldLandscape)479 // If we have an oldLandscape that is not just swapped back, put that into cache.
444 delete oldLandscape;480 if (oldLandscape && oldLandscape!=newLandscape)
481 {
482#ifndef NDEBUG
483 qDebug() << "LandscapeMgr::setCurrent: moving oldLandscape " << oldLandscape->getId() << "to Cache. Cost:" << oldLandscape->getMemorySize()/(1024*1024)+1;
484#endif
485 landscapeCache.insert(oldLandscape->getId(), oldLandscape, oldLandscape->getMemorySize()/(1024*1024)+1);
486#ifndef NDEBUG
487 qDebug() << "-->LandscapeMgr::setCurrentLandscapeId(): cache contains " << landscapeCache.size() << "landscapes totalling about " << landscapeCache.totalCost() << "MB.";
488#endif
489 }
445 oldLandscape = landscape; // keep old while transitioning!490 oldLandscape = landscape; // keep old while transitioning!
446 }491 }
447 landscape=newLandscape;492 landscape=newLandscape;
448 currentLandscapeID = id;493 currentLandscapeID = id;
449494
450
451 if (getFlagLandscapeSetsLocation() && landscape->hasLocation())495 if (getFlagLandscapeSetsLocation() && landscape->hasLocation())
452 {496 {
453 StelCore *core = StelApp::getInstance().getCore();497 StelCore *core = StelApp::getInstance().getCore();
@@ -508,6 +552,46 @@
508 }552 }
509}553}
510554
555// Load a landscape into cache.
556// @param id the ID of a landscape
557// @param replace true if existing landscape entry should be replaced (useful during development to reload after edit)
558// @return false if landscape could not be found, or existed already and replace was false.
559bool LandscapeMgr::precacheLandscape(const QString& id, const bool replace)
560{
561 if (landscapeCache.contains(id) && (!replace))
562 return false;
563
564 Landscape* newLandscape = createFromFile(StelFileMgr::findFile("landscapes/" + id + "/landscape.ini"), id);
565 if (!newLandscape)
566 {
567 qWarning() << "ERROR while preloading landscape " << "landscapes/" + id + "/landscape.ini";
568 return false;
569 }
570
571 bool res=landscapeCache.insert(id, newLandscape, newLandscape->getMemorySize()/(1024*1024)+1);
572#ifndef NDEBUG
573 if (res)
574 {
575 qDebug() << "LandscapeMgr::precacheLandscape(): Successfully added landscape with ID " << id << "to cache";
576 }
577 qDebug() << "LandscapeMgr::precacheLandscape(): cache contains " << landscapeCache.size() << "landscapes totalling about " << landscapeCache.totalCost() << "MB.";
578#endif
579 return res;
580}
581
582// Remove a landscape from the cache of loaded landscapes.
583// @param id the ID of a landscape
584// @return false if landscape could not be found
585bool LandscapeMgr::removeCachedLandscape(const QString& id)
586{
587 bool res= landscapeCache.remove(id);
588#ifndef NDEBUG
589 qDebug() << "LandscapeMgr::removeCachedLandscape(): cache contains " << landscapeCache.size() << "landscapes totalling about " << landscapeCache.totalCost() << "MB.";
590#endif
591 return res;
592}
593
594
511// Change the default landscape to the landscape with the ID specified.595// Change the default landscape to the landscape with the ID specified.
512bool LandscapeMgr::setDefaultLandscapeID(const QString& id)596bool LandscapeMgr::setDefaultLandscapeID(const QString& id)
513{597{
@@ -878,7 +962,7 @@
878}962}
879963
880/****************************************************************************964/****************************************************************************
881 get a map of landscape name (from landscape.ini name field) to ID (dir name)965 get a map of landscape names (from landscape.ini name field) to ID (dir name)
882 ****************************************************************************/966 ****************************************************************************/
883QMap<QString,QString> LandscapeMgr::getNameToDirMap() const967QMap<QString,QString> LandscapeMgr::getNameToDirMap() const
884{968{
885969
=== modified file 'src/core/modules/LandscapeMgr.hpp'
--- src/core/modules/LandscapeMgr.hpp 2016-12-18 06:38:55 +0000
+++ src/core/modules/LandscapeMgr.hpp 2017-01-19 22:30:07 +0000
@@ -28,6 +28,7 @@
2828
29#include <QMap>29#include <QMap>
30#include <QStringList>30#include <QStringList>
31#include <QCache>
3132
32class Atmosphere;33class Atmosphere;
33class Cardinals;34class Cardinals;
@@ -41,7 +42,7 @@
41//! \note42//! \note
42//! The Bortle scale index setting was removed from this class, because it was duplicated43//! The Bortle scale index setting was removed from this class, because it was duplicated
43//! from StelSkyDrawer, complicating code that changes it.44//! from StelSkyDrawer, complicating code that changes it.
44//! It is now only in StelSkyDrawer and can be accessed with45//! It is now only in StelSkyDrawer and can be accessed
45//! with \link StelSkyDrawer::getBortleScaleIndex getBortleScaleIndex \endlink46//! with \link StelSkyDrawer::getBortleScaleIndex getBortleScaleIndex \endlink
46//! and \link StelSkyDrawer::setBortleScaleIndex setBortleScaleIndex \endlink.47//! and \link StelSkyDrawer::setBortleScaleIndex setBortleScaleIndex \endlink.
47//! Slots setAtmosphereBortleLightPollution and getAtmosphereBortleLightPollution48//! Slots setAtmosphereBortleLightPollution and getAtmosphereBortleLightPollution
@@ -141,9 +142,10 @@
141 ///////////////////////////////////////////////////////////////////////////142 ///////////////////////////////////////////////////////////////////////////
142 // Methods specific to the landscape manager143 // Methods specific to the landscape manager
143144
144 //! Load a landscape based on a hash of parameters mirroring the landscape.ini145 // Load a landscape based on a hash of parameters mirroring the landscape.ini
145 //! file and make it the current landscape.146 // file and make it the current landscape.
146 bool loadLandscape(QMap<QString, QString>& param);147 // GZ: This was declared, but not implemented(?)
148 //bool loadLandscape(QMap<QString, QString>& param);
147149
148 //! Create a new landscape from the files which describe it.150 //! Create a new landscape from the files which describe it.
149 //! Reads a landscape.ini file which is passed as the first parameter, determines151 //! Reads a landscape.ini file which is passed as the first parameter, determines
@@ -177,7 +179,7 @@
177 //! this value explicitly to freeze it during image export. To unfreeze, call this again with any negative value.179 //! this value explicitly to freeze it during image export. To unfreeze, call this again with any negative value.
178 void setAtmosphereAverageLuminance(const float overrideLuminance);180 void setAtmosphereAverageLuminance(const float overrideLuminance);
179181
180 //! Return a map of landscape name to landscape ID (directory name).182 //! Return a map of landscape names to landscape IDs (directory names).
181 QMap<QString,QString> getNameToDirMap() const;183 QMap<QString,QString> getNameToDirMap() const;
182184
183 //! Retrieve a list of the names of all the available landscapes in185 //! Retrieve a list of the names of all the available landscapes in
@@ -212,6 +214,31 @@
212 //! @param changeLocationDuration the duration of the transition animation214 //! @param changeLocationDuration the duration of the transition animation
213 bool setCurrentLandscapeName(const QString& name, const double changeLocationDuration = 1.0);215 bool setCurrentLandscapeName(const QString& name, const double changeLocationDuration = 1.0);
214216
217 //! Preload a landscape into cache.
218 //! @param id the ID of a landscape
219 //! @param replace true if existing landscape entry should be replaced (useful during development to reload after edit)
220 //! @return false if landscape could not be found, or if it already existed in cache and replace was false.
221 bool precacheLandscape(const QString& id, const bool replace=true);
222 //! Remove a landscape from the cache of landscapes.
223 //! @param id the ID of a landscape
224 //! @return false if landscape could not be found
225 bool removeCachedLandscape(const QString& id);
226 //! Set size of the landscape cache, in MB.
227 //! Default size is 100MB, or configured as [landscape/cache_size_mb] from config.ini.
228 //! The landscape sizes returned in Landscape::getMemorySize() are only approximate, but include image and texture sizes.
229 //! A big landscape may well take 150MB or more.
230 //! On a 32bit system, keep this rather small. On 64bit with 16GB RAM and no other tasks, 4GB is no problem.
231 //! Modern GPUs may have 4 or even 8GB of dedicated texture memory. Most of this may be filled with landscape textures.
232 //! Example: a museum installation with 20 large (16384x2048) old_stype landscapes can require up to 3.5GB. Allow 4GB cache,
233 //! and the system will never have to load a landscape during the show when all have been preloaded.
234 void setCacheSize(int mb) { landscapeCache.setMaxCost(mb);}
235 //! Retrieve total size of cache (MB).
236 int getCacheSize() const {return landscapeCache.maxCost();}
237 //! Retrieve sum of currently used memory in cache (MB, approximate)
238 int getCacheFilledSize() const {return landscapeCache.totalCost();}
239 //! Return number of landscapes already in the cache.
240 int getCacheCount() const {return landscapeCache.count();}
241
215 //! Get the current landscape object.242 //! Get the current landscape object.
216 Landscape* getCurrentLandscape() const { return landscape; }243 Landscape* getCurrentLandscape() const { return landscape; }
217244
@@ -505,16 +532,24 @@
505 //! Indicate auto-enable atmosphere for planets with atmospheres in location window532 //! Indicate auto-enable atmosphere for planets with atmospheres in location window
506 bool flagAtmosphereAutoEnabling;533 bool flagAtmosphereAutoEnabling;
507534
508 // The ID of the currently loaded landscape535 //! The ID of the currently loaded landscape
509 QString currentLandscapeID;536 QString currentLandscapeID;
510537
511 // The ID of the default landscape538 //! The ID of the default landscape
512 QString defaultLandscapeID;539 QString defaultLandscapeID;
513540
514 //! List of the IDs of the landscapes packaged by default with Stellarium.541 //! List of the IDs of the landscapes packaged by default with Stellarium.
515 //! (So that they can't be removed.)542 //! (So that they can't be removed.)
516 QStringList packagedLandscapeIDs;543 QStringList packagedLandscapeIDs;
517544
545 //! QCache of landscapes kept in memory for faster access, esp. when frequently switching between several big landscapes.
546 //! Example: a 16384-size old_style landscape takes about 10 seconds to load. Kept in cache, it is back instantly.
547 //! Of course, this requires lots of RAM and GPU texture memory, but in the age of 64bit CPUs and 4GB and more GPU
548 //! texture memory, it is no problem to keep even 20 or more landscapes.
549 //! This is esp. useful in a context of automated setup (museum show or such) where a list of landscapes is preloaded
550 //! at system start (e.g. in the startup.ssc script) and then retrieved while script is running.
551 //! The key is just the LandscapeID.
552 QCache<QString,Landscape> landscapeCache;
518};553};
519554
520#endif // _LANDSCAPEMGR_HPP_555#endif // _LANDSCAPEMGR_HPP_