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
1=== modified file 'src/core/StelTexture.hpp'
2--- src/core/StelTexture.hpp 2016-11-28 16:01:23 +0000
3+++ src/core/StelTexture.hpp 2017-01-19 22:30:07 +0000
4@@ -91,6 +91,9 @@
5 //! Return whether the image is currently being loaded
6 bool isLoading() const {return (loader || networkReply) && !canBind();}
7
8+ //! Return texture memory size
9+ unsigned int getGlSize() const {return glSize;}
10+
11 signals:
12 //! Emitted when the texture is ready to be bind(), i.e. when downloaded, imageLoading and glLoading is over
13 //! or when an error occured and the texture will never be available
14@@ -171,7 +174,7 @@
15 GLsizei height; //! Texture image height
16
17 //! Size in GL memory
18- int glSize;
19+ unsigned int glSize;
20 };
21
22
23
24=== modified file 'src/core/StelTextureMgr.hpp'
25--- src/core/StelTextureMgr.hpp 2016-11-21 21:04:36 +0000
26+++ src/core/StelTextureMgr.hpp 2017-01-19 22:30:07 +0000
27@@ -61,7 +61,7 @@
28 //! Must be called after the creation of the GLContext.
29 void init();
30
31- int glMemoryUsage;
32+ unsigned int glMemoryUsage;
33 };
34
35
36
37=== modified file 'src/core/modules/Landscape.cpp'
38--- src/core/modules/Landscape.cpp 2017-01-08 16:39:11 +0000
39+++ src/core/modules/Landscape.cpp 2017-01-19 22:30:07 +0000
40@@ -2,6 +2,7 @@
41 * Stellarium
42 * Copyright (C) 2003 Fabien Chereau
43 * Copyright (C) 2011 Bogdan Marinov
44+ * Copyright (C) 2014-17 Georg Zotti
45 *
46 * This program is free software; you can redistribute it and/or
47 * modify it under the terms of the GNU General Public License
48@@ -37,6 +38,7 @@
49
50 Landscape::Landscape(float _radius)
51 : radius(_radius)
52+ , id("uninitialized")
53 , minBrightness(-1.)
54 , landscapeBrightness(1.)
55 , lightScapeBrightness(0.)
56@@ -63,6 +65,7 @@
57 // Load attributes common to all landscapes
58 void Landscape::loadCommon(const QSettings& landscapeIni, const QString& landscapeId)
59 {
60+ id = landscapeId;
61 name = landscapeIni.value("landscape/name").toString();
62 author = landscapeIni.value("landscape/author").toString();
63 description = landscapeIni.value("landscape/description").toString();
64@@ -284,13 +287,14 @@
65 {
66 QString line=in.readLine();
67
68- // TODO: Read entries, construct vectors, put in list.
69- if (line.startsWith('#'))
70+ // Skip comments and all-empty lines (space allowed and ignored)
71+ if (line.startsWith('#') || line.trimmed().isEmpty() )
72 continue;
73+ // Read entries, construct vectors, put in list.
74 QStringList parts=line.split('|');
75 if (parts.count() != 5)
76 {
77- qWarning() << "Invalid line in landscape" << descFileName << ":" << line;
78+ qWarning() << "Invalid line in landscape gazetteer" << descFileName << ":" << line;
79 continue;
80 }
81 LandscapeLabel newLabel;
82@@ -359,6 +363,7 @@
83 , drawGroundFirst(0)
84 , tanMode(false)
85 , calibrated(false)
86+ , memorySize(sizeof(LandscapeOldStyle)) // start with just the known entries.
87 {}
88
89 LandscapeOldStyle::~LandscapeOldStyle()
90@@ -391,7 +396,7 @@
91 {
92 qWarning() << "Landscape type mismatch for landscape " << landscapeId
93 << ", expected old_style, found " << type << ". No landscape in use.";
94- validLandscape = 0;
95+ validLandscape = false;
96 return;
97 }
98
99@@ -421,6 +426,7 @@
100 if ( (!horizonPolygon) && calibrated ) { // for uncalibrated landscapes the texture is currently never queried, so no need to store.
101 QImage *image = new QImage(texturePath);
102 sidesImages.append(image); // indices identical to those in sideTexs
103+ memorySize+=image->byteCount();
104 }
105 // 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!
106 textureKey = QString("landscape/light%1").arg(i);
107@@ -429,6 +435,8 @@
108 {
109 const QString lightTexturePath = getTexturePath(textureName, landscapeId);
110 sideTexs[nbSideTexs+i] = StelApp::getInstance().getTextureManager().createTexture(lightTexturePath);
111+ if(sideTexs[nbSideTexs+i])
112+ memorySize+=sideTexs[nbSideTexs+i].data()->getGlSize();
113 }
114 else
115 sideTexs[nbSideTexs+i].clear();
116@@ -471,28 +479,14 @@
117 QString groundTexName = landscapeIni.value("landscape/groundtex").toString();
118 QString groundTexPath = getTexturePath(groundTexName, landscapeId);
119 groundTex = StelApp::getInstance().getTextureManager().createTexture(groundTexPath, StelTexture::StelTextureParams(true));
120- // GZ 2013/11: I don't see any use of this:
121-// QString description = landscapeIni.value("landscape/ground").toString();
122-// //sscanf(description.toLocal8Bit(),"groundtex:%f:%f:%f:%f",&a,&b,&c,&d);
123-// QStringList parameters = description.split(':');
124-// groundTexCoord.tex = groundTex;
125-// groundTexCoord.texCoords[0] = parameters.at(1).toFloat();
126-// groundTexCoord.texCoords[1] = parameters.at(2).toFloat();
127-// groundTexCoord.texCoords[2] = parameters.at(3).toFloat();
128-// groundTexCoord.texCoords[3] = parameters.at(4).toFloat();
129+ if (groundTex)
130+ memorySize+=groundTex.data()->getGlSize();
131
132 QString fogTexName = landscapeIni.value("landscape/fogtex").toString();
133 QString fogTexPath = getTexturePath(fogTexName, landscapeId);
134 fogTex = StelApp::getInstance().getTextureManager().createTexture(fogTexPath, StelTexture::StelTextureParams(true, GL_LINEAR, GL_REPEAT));
135- // GZ 2013/11: I don't see any use of this:
136-// QString description = landscapeIni.value("landscape/fog").toString();
137-// //sscanf(description.toLocal8Bit(),"fogtex:%f:%f:%f:%f",&a,&b,&c,&d);
138-// QStringList parameters = description.split(':');
139-// fogTexCoord.tex = fogTex;
140-// fogTexCoord.texCoords[0] = parameters.at(1).toFloat();
141-// fogTexCoord.texCoords[1] = parameters.at(2).toFloat();
142-// fogTexCoord.texCoords[2] = parameters.at(3).toFloat();
143-// fogTexCoord.texCoords[3] = parameters.at(4).toFloat();
144+ if (fogTex)
145+ memorySize+=fogTex.data()->getGlSize();
146
147 // Precompute the vertex arrays for ground display
148 // Make slices_per_side=(3<<K) so that the innermost polygon of the fandisk becomes a triangle:
149@@ -520,9 +514,9 @@
150 // GZ: the original code for vertical placement makes unfortunately no sense. There are many approximately-fitted landscapes, though.
151 // I added a switch "calibrated" for the ini file. If true, it works as this landscape apparently was originally intended,
152 // if false (or missing) it uses the original code.
153- // So I corrected the texture coordinates so that decorAltAngle is the total vertical angle, decorAngleShift the lower angle,
154+ // I corrected the texture coordinates so that decorAltAngle is the total vertical angle, decorAngleShift the lower angle,
155 // and the texture in between is correctly stretched.
156- // I located an undocumented switch tan_mode, maybe tan_mode=true means cylindrical panorama projection.
157+ // I located an undocumented switch tan_mode, maybe tan_mode=true means cylindrical panorama projection instead of equirectangular.
158 // Since V0.13, calibrated&&tanMode also works!
159 // In calibrated && !tan_mode, the vertical position is computed correctly, so that quads off the horizon are larger.
160 // in calibrated && tan_mode, d_z can become a constant because the texture is already predistorted in cylindrical projection.
161@@ -619,6 +613,7 @@
162 }
163 }
164 }
165+ //qDebug() << "OldStyleLandscape" << landscapeId << "loaded, mem size:" << memorySize;
166 }
167
168 void LandscapeOldStyle::draw(StelCore* core)
169@@ -836,17 +831,18 @@
170 if(type != "polygonal")
171 {
172 qWarning() << "Landscape type mismatch for landscape "<< landscapeId << ", expected polygonal, found " << type << ". No landscape in use.\n";
173- validLandscape = 0;
174+ validLandscape = false;
175 return;
176 }
177 if (horizonPolygon.isNull())
178 {
179 qWarning() << "Landscape " << landscapeId << " does not declare a valid polygonal_horizon_list. No landscape in use.\n";
180- validLandscape = 0;
181+ validLandscape = false;
182 return;
183 }
184 groundColor=StelUtils::strToVec3f( landscapeIni.value("landscape/ground_color", "0,0,0" ).toString() );
185- validLandscape = 1; // assume ok...
186+ validLandscape = true; // assume ok...
187+ //qDebug() << "PolygonalLandscape" << landscapeId << "loaded, mem size:" << getMemorySize();
188 }
189
190 void LandscapePolygonal::draw(StelCore* core)
191@@ -891,8 +887,12 @@
192
193 LandscapeFisheye::LandscapeFisheye(float _radius)
194 : Landscape(_radius)
195+ , mapTex(StelTextureSP())
196+ , mapTexFog(StelTextureSP())
197+ , mapTexIllum(StelTextureSP())
198 , mapImage(NULL)
199 , texFov(360.)
200+ , memorySize(0)
201 {}
202
203 LandscapeFisheye::~LandscapeFisheye()
204@@ -909,7 +909,7 @@
205 if(type != "fisheye")
206 {
207 qWarning() << "Landscape type mismatch for landscape "<< landscapeId << ", expected fisheye, found " << type << ". No landscape in use.\n";
208- validLandscape = 0;
209+ validLandscape = false;
210 return;
211 }
212 create(name,
213@@ -918,25 +918,38 @@
214 getTexturePath(landscapeIni.value("landscape/maptex_fog").toString(), landscapeId),
215 getTexturePath(landscapeIni.value("landscape/maptex_illum").toString(), landscapeId),
216 landscapeIni.value("landscape/angle_rotatez", 0.f).toFloat());
217+ //qDebug() << "FisheyeLandscape" << landscapeId << "loaded, mem size:" << memorySize;
218 }
219
220
221 void LandscapeFisheye::create(const QString _name, float _texturefov, const QString& _maptex, const QString &_maptexFog, const QString& _maptexIllum, const float _angleRotateZ)
222 {
223 // qDebug() << _name << " " << _fullpath << " " << _maptex << " " << _texturefov;
224- validLandscape = 1; // assume ok...
225+ validLandscape = true; // assume ok...
226 name = _name;
227 texFov = _texturefov*M_PI/180.f;
228 angleRotateZ = _angleRotateZ*M_PI/180.f;
229+
230 if (!horizonPolygon)
231+ {
232 mapImage = new QImage(_maptex);
233+ memorySize+=mapImage->byteCount();
234+ }
235 mapTex = StelApp::getInstance().getTextureManager().createTexture(_maptex, StelTexture::StelTextureParams(true));
236+ memorySize+=mapTex.data()->getGlSize();
237
238- if (_maptexIllum.length())
239+ if (_maptexIllum.length() && (!_maptexIllum.endsWith("/")))
240+ {
241 mapTexIllum = StelApp::getInstance().getTextureManager().createTexture(_maptexIllum, StelTexture::StelTextureParams(true));
242- if (_maptexFog.length())
243+ if (mapTexIllum)
244+ memorySize+=mapTexIllum.data()->getGlSize();
245+ }
246+ if (_maptexFog.length() && (!_maptexFog.endsWith("/")))
247+ {
248 mapTexFog = StelApp::getInstance().getTextureManager().createTexture(_maptexFog, StelTexture::StelTextureParams(true));
249-
250+ if (mapTexFog)
251+ memorySize+=mapTexFog.data()->getGlSize();
252+ }
253 }
254
255
256@@ -1025,6 +1038,9 @@
257
258 LandscapeSpherical::LandscapeSpherical(float _radius)
259 : Landscape(_radius)
260+ , mapTex(StelTextureSP())
261+ , mapTexFog(StelTextureSP())
262+ , mapTexIllum(StelTextureSP())
263 , mapTexTop(0.)
264 , mapTexBottom(0.)
265 , fogTexTop(0.)
266@@ -1032,6 +1048,7 @@
267 , illumTexTop(0.)
268 , illumTexBottom(0.)
269 , mapImage(NULL)
270+ , memorySize(sizeof(LandscapeSpherical))
271 {}
272
273 LandscapeSpherical::~LandscapeSpherical()
274@@ -1054,7 +1071,7 @@
275 qWarning() << "Landscape type mismatch for landscape "<< landscapeId
276 << ", expected spherical, found " << type
277 << ". No landscape in use.\n";
278- validLandscape = 0;
279+ validLandscape = false;
280 return;
281 }
282
283@@ -1069,6 +1086,7 @@
284 landscapeIni.value("landscape/maptex_fog_bottom" , -90.f).toFloat(),
285 landscapeIni.value("landscape/maptex_illum_top" , 90.f).toFloat(),
286 landscapeIni.value("landscape/maptex_illum_bottom", -90.f).toFloat());
287+ //qDebug() << "SphericalLandscape" << landscapeId << "loaded, mem size:" << memorySize;
288 }
289
290
291@@ -1079,7 +1097,7 @@
292 const float _illumTexTop, const float _illumTexBottom)
293 {
294 //qDebug() << "LandscapeSpherical::create():"<< _name << " : " << _maptex << " : " << _maptexFog << " : " << _maptexIllum << " : " << _angleRotateZ;
295- validLandscape = 1; // assume ok...
296+ validLandscape = true; // assume ok...
297 name = _name;
298 angleRotateZ = _angleRotateZ *M_PI/180.f; // Defined in ini --> internal prg value
299 mapTexTop = (90.f-_mapTexTop) *M_PI/180.f; // top 90 --> 0
300@@ -1089,13 +1107,25 @@
301 illumTexTop = (90.f-_illumTexTop) *M_PI/180.f;
302 illumTexBottom= (90.f-_illumTexBottom)*M_PI/180.f;
303 if (!horizonPolygon)
304+ {
305 mapImage = new QImage(_maptex);
306+ memorySize+=mapImage->byteCount();
307+ }
308 mapTex = StelApp::getInstance().getTextureManager().createTexture(_maptex, StelTexture::StelTextureParams(true));
309+ memorySize+=mapTex.data()->getGlSize();
310
311- if (_maptexIllum.length())
312+ if (_maptexIllum.length() && (!_maptexIllum.endsWith("/")))
313+ {
314 mapTexIllum = StelApp::getInstance().getTextureManager().createTexture(_maptexIllum, StelTexture::StelTextureParams(true));
315- if (_maptexFog.length())
316+ if (mapTexIllum)
317+ memorySize+=mapTexIllum.data()->getGlSize();
318+ }
319+ if (_maptexFog.length() && (!_maptexFog.endsWith("/")))
320+ {
321 mapTexFog = StelApp::getInstance().getTextureManager().createTexture(_maptexFog, StelTexture::StelTextureParams(true));
322+ if (mapTexFog)
323+ memorySize+=mapTexFog.data()->getGlSize();
324+ }
325 }
326
327 void LandscapeSpherical::draw(StelCore* core)
328
329=== modified file 'src/core/modules/Landscape.hpp'
330--- src/core/modules/Landscape.hpp 2016-05-22 21:14:58 +0000
331+++ src/core/modules/Landscape.hpp 2017-01-19 22:30:07 +0000
332@@ -75,6 +75,13 @@
333 //! @param landscapeIni A reference to an existing QSettings object which describes the landscape
334 //! @param landscapeId The name of the directory for the landscape files (e.g. "ocean")
335 virtual void load(const QSettings& landscapeIni, const QString& landscapeId) = 0;
336+
337+ //! Return approximate memory footprint in bytes (required for cache cost estimate in LandscapeMgr)
338+ //! The returned value is only approximate, content of QStrings and other small containers like the horizon polygon are not put in in detail.
339+ //! However, texture image sizes must be computed in subclasses.
340+ //! The value returned is a sum of RAM and texture memory requirements.
341+ virtual unsigned int getMemorySize() const {return sizeof(Landscape);}
342+
343 virtual void draw(StelCore* core) = 0;
344 void update(double deltaTime)
345 {
346@@ -124,6 +131,8 @@
347 QString getAuthorName() const {return author;}
348 //! Get landscape description
349 QString getDescription() const {return description;}
350+ //! Get landscape id. This is the landscape directory name, used for cache handling.
351+ QString getId() const {return id;}
352
353 //! Return the associated location (may be empty!)
354 const StelLocation& getLocation() const {return location;}
355@@ -202,6 +211,7 @@
356 QString name; //! Read from landscape.ini:[landscape]name
357 QString author; //! Read from landscape.ini:[landscape]author
358 QString description; //! Read from landscape.ini:[landscape]description
359+ QString id; //! Set during load. Required for consistent caching.
360
361 float minBrightness; //! Read from landscape.ini:[landscape]minimal_brightness. Allows minimum visibility that cannot be underpowered.
362 float landscapeBrightness; //! brightness [0..1] to draw the landscape. Computed by the LandscapeMgr.
363@@ -259,6 +269,7 @@
364 LandscapeOldStyle(float radius = 2.f);
365 virtual ~LandscapeOldStyle();
366 virtual void load(const QSettings& landscapeIni, const QString& landscapeId);
367+ virtual unsigned int getMemorySize() const {return memorySize;}
368 virtual void draw(StelCore* core);
369 //void create(bool _fullpath, QMap<QString, QString> param); // still not implemented
370 virtual float getOpacity(Vec3d azalt) const;
371@@ -302,6 +313,7 @@
372 };
373
374 QList<LOSSide> precomputedSides;
375+ unsigned int memorySize;
376 };
377
378 /////////////////////////////////////////////////////////
379@@ -319,6 +331,7 @@
380 LandscapePolygonal(float radius = 1.f);
381 virtual ~LandscapePolygonal();
382 virtual void load(const QSettings& landscapeIni, const QString& landscapeId);
383+ virtual unsigned int getMemorySize() const {return sizeof(LandscapePolygonal);}
384 virtual void draw(StelCore* core);
385 virtual float getOpacity(Vec3d azalt) const;
386 private:
387@@ -338,6 +351,7 @@
388 LandscapeFisheye(float radius = 1.f);
389 virtual ~LandscapeFisheye();
390 virtual void load(const QSettings& landscapeIni, const QString& landscapeId);
391+ virtual unsigned int getMemorySize() const {return memorySize;}
392 virtual void draw(StelCore* core);
393 //! Sample landscape texture for transparency/opacity. May be used for visibility, sunrise etc.
394 //! @param azalt normalized direction in alt-az frame
395@@ -360,6 +374,7 @@
396 QImage *mapImage; //!< The same image as mapTex, but stored in-mem for sampling.
397
398 float texFov;
399+ unsigned int memorySize;
400 };
401
402 //////////////////////////////////////////////////////////////////////////
403@@ -378,6 +393,7 @@
404 LandscapeSpherical(float radius = 1.f);
405 virtual ~LandscapeSpherical();
406 virtual void load(const QSettings& landscapeIni, const QString& landscapeId);
407+ virtual unsigned int getMemorySize() const {return memorySize;}
408 virtual void draw(StelCore* core);
409 //! Sample landscape texture for transparency/opacity. May be used for visibility, sunrise etc.
410 //! @param azalt normalized direction in alt-az frame
411@@ -414,6 +430,7 @@
412 float illumTexTop; //!< zenithal top angle of the illumination texture, radians
413 float illumTexBottom; //!< zenithal bottom angle of the illumination texture, radians
414 QImage *mapImage; //!< The same image as mapTex, but stored in-mem for opacity sampling.
415+ unsigned int memorySize; //!< holds an approximate value of memory consumption (for cache cost estimate)
416 };
417
418 #endif // _LANDSCAPE_HPP_
419
420=== modified file 'src/core/modules/LandscapeMgr.cpp'
421--- src/core/modules/LandscapeMgr.cpp 2017-01-08 16:39:11 +0000
422+++ src/core/modules/LandscapeMgr.cpp 2017-01-19 22:30:07 +0000
423@@ -142,7 +142,8 @@
424
425
426 LandscapeMgr::LandscapeMgr()
427- : atmosphere(NULL)
428+ : StelModule()
429+ , atmosphere(NULL)
430 , cardinalsPoints(NULL)
431 , landscape(NULL)
432 , oldLandscape(NULL)
433@@ -154,7 +155,7 @@
434 , flagLandscapeSetsMinimalBrightness(false)
435 , flagAtmosphereAutoEnabling(false)
436 {
437- setObjectName("LandscapeMgr");
438+ setObjectName("LandscapeMgr"); // should be done by StelModule's constructor.
439
440 //Note: The first entry in the list is used as the default 'default landscape' in removeLandscape().
441 packagedLandscapeIDs = (QStringList() << "guereins");
442@@ -165,6 +166,7 @@
443 packagedLandscapeIDs << directories.fileName();
444 }
445 packagedLandscapeIDs.removeDuplicates();
446+ landscapeCache.clear();
447 }
448
449 LandscapeMgr::~LandscapeMgr()
450@@ -178,6 +180,8 @@
451 }
452 delete landscape;
453 landscape = NULL;
454+ qDebug() << "LandscapeMgr: Clearing cache of" << landscapeCache.size() << "landscapes totalling about " << landscapeCache.totalCost() << "MB.";
455+ landscapeCache.clear(); // deletes all objects within.
456 }
457
458 /*************************************************************************
459@@ -198,6 +202,7 @@
460 void LandscapeMgr::update(double deltaTime)
461 {
462 atmosphere->update(deltaTime);
463+
464 if (oldLandscape)
465 {
466 // This is only when transitioning to newly loaded landscape. We must draw the old one until the new one is faded in completely.
467@@ -208,7 +213,10 @@
468
469 if (oldLandscape->getEffectiveLandFadeValue()< 0.01f)
470 {
471- delete oldLandscape;
472+ // new logic: try to put old landscape to cache.
473+ //qDebug() << "LandscapeMgr::update: moving oldLandscape " << oldLandscape->getId() << "to Cache. Cost:" << oldLandscape->getMemorySize()/(1024*1024)+1;
474+ landscapeCache.insert(oldLandscape->getId(), oldLandscape, oldLandscape->getMemorySize()/(1024*1024)+1);
475+ //qDebug() << "--> LandscapeMgr::update(): cache now contains " << landscapeCache.size() << "landscapes totalling about " << landscapeCache.totalCost() << "MB.";
476 oldLandscape=NULL;
477 }
478 }
479@@ -368,8 +376,10 @@
480 QSettings* conf = StelApp::getInstance().getSettings();
481 Q_ASSERT(conf);
482
483+ landscapeCache.setMaxCost(conf->value("landscape/cache_size_mb", 100).toInt());
484+ qDebug() << "LandscapeMgr: initialized Cache for" << landscapeCache.maxCost() << "MB.";
485+
486 atmosphere = new Atmosphere();
487- landscape = new LandscapeOldStyle();
488 defaultLandscapeID = conf->value("init_location/landscape_name").toString();
489 setCurrentLandscapeID(defaultLandscapeID);
490 setFlagLandscape(conf->value("landscape/flag_landscape", conf->value("landscape/flag_ground", true).toBool()).toBool());
491@@ -421,13 +431,39 @@
492 if(id==currentLandscapeID)
493 return false;
494
495- // We want to lookup the landscape ID (dir) from the name.
496- Landscape* newLandscape = createFromFile(StelFileMgr::findFile("landscapes/" + id + "/landscape.ini"), id);
497-
498- if (!newLandscape)
499- {
500- qWarning() << "ERROR while loading landscape " << "landscapes/" + id + "/landscape.ini";
501- return false;
502+ Landscape* newLandscape;
503+
504+ // There is a slight chance that we switch back to oldLandscape while oldLandscape is still fading away.
505+ // in this case it is not yet stored in cache, but obviously available. So we just swap places.
506+ if (oldLandscape && oldLandscape->getId()==id)
507+ {
508+ newLandscape=oldLandscape;
509+ }
510+ else
511+ {
512+ // We want to lookup the landscape ID (dir) from the name.
513+ newLandscape= landscapeCache.take(id);
514+
515+ if (newLandscape)
516+ {
517+#ifndef NDEBUG
518+ qDebug() << "LandscapeMgr::setCurrentLandscapeID():: taken " << id << "from cache...";
519+ qDebug() << ".-->LandscapeMgr::setCurrentLandscapeID(): cache contains " << landscapeCache.size() << "landscapes totalling about " << landscapeCache.totalCost() << "MB.";
520+#endif
521+ }
522+ else
523+ {
524+#ifndef NDEBUG
525+ qDebug() << "LandscapeMgr::setCurrentLandscapeID: Loading from file:" << id ;
526+#endif
527+ newLandscape = createFromFile(StelFileMgr::findFile("landscapes/" + id + "/landscape.ini"), id);
528+ }
529+
530+ if (!newLandscape)
531+ {
532+ qWarning() << "ERROR while loading landscape " << "landscapes/" + id + "/landscape.ini";
533+ return false;
534+ }
535 }
536
537 // Keep current landscape for a while, while new landscape fades in!
538@@ -439,15 +475,23 @@
539 newLandscape->setFlagShowFog(landscape->getFlagShowFog());
540 newLandscape->setFlagShowIllumination(landscape->getFlagShowIllumination());
541 newLandscape->setFlagShowLabels(landscape->getFlagShowLabels());
542- //in case we already fade out one old landscape (if switching too rapidly): the old one has to go immediately.
543- if (oldLandscape)
544- delete oldLandscape;
545+
546+ // If we have an oldLandscape that is not just swapped back, put that into cache.
547+ if (oldLandscape && oldLandscape!=newLandscape)
548+ {
549+#ifndef NDEBUG
550+ qDebug() << "LandscapeMgr::setCurrent: moving oldLandscape " << oldLandscape->getId() << "to Cache. Cost:" << oldLandscape->getMemorySize()/(1024*1024)+1;
551+#endif
552+ landscapeCache.insert(oldLandscape->getId(), oldLandscape, oldLandscape->getMemorySize()/(1024*1024)+1);
553+#ifndef NDEBUG
554+ qDebug() << "-->LandscapeMgr::setCurrentLandscapeId(): cache contains " << landscapeCache.size() << "landscapes totalling about " << landscapeCache.totalCost() << "MB.";
555+#endif
556+ }
557 oldLandscape = landscape; // keep old while transitioning!
558 }
559 landscape=newLandscape;
560 currentLandscapeID = id;
561
562-
563 if (getFlagLandscapeSetsLocation() && landscape->hasLocation())
564 {
565 StelCore *core = StelApp::getInstance().getCore();
566@@ -508,6 +552,46 @@
567 }
568 }
569
570+// Load a landscape into cache.
571+// @param id the ID of a landscape
572+// @param replace true if existing landscape entry should be replaced (useful during development to reload after edit)
573+// @return false if landscape could not be found, or existed already and replace was false.
574+bool LandscapeMgr::precacheLandscape(const QString& id, const bool replace)
575+{
576+ if (landscapeCache.contains(id) && (!replace))
577+ return false;
578+
579+ Landscape* newLandscape = createFromFile(StelFileMgr::findFile("landscapes/" + id + "/landscape.ini"), id);
580+ if (!newLandscape)
581+ {
582+ qWarning() << "ERROR while preloading landscape " << "landscapes/" + id + "/landscape.ini";
583+ return false;
584+ }
585+
586+ bool res=landscapeCache.insert(id, newLandscape, newLandscape->getMemorySize()/(1024*1024)+1);
587+#ifndef NDEBUG
588+ if (res)
589+ {
590+ qDebug() << "LandscapeMgr::precacheLandscape(): Successfully added landscape with ID " << id << "to cache";
591+ }
592+ qDebug() << "LandscapeMgr::precacheLandscape(): cache contains " << landscapeCache.size() << "landscapes totalling about " << landscapeCache.totalCost() << "MB.";
593+#endif
594+ return res;
595+}
596+
597+// Remove a landscape from the cache of loaded landscapes.
598+// @param id the ID of a landscape
599+// @return false if landscape could not be found
600+bool LandscapeMgr::removeCachedLandscape(const QString& id)
601+{
602+ bool res= landscapeCache.remove(id);
603+#ifndef NDEBUG
604+ qDebug() << "LandscapeMgr::removeCachedLandscape(): cache contains " << landscapeCache.size() << "landscapes totalling about " << landscapeCache.totalCost() << "MB.";
605+#endif
606+ return res;
607+}
608+
609+
610 // Change the default landscape to the landscape with the ID specified.
611 bool LandscapeMgr::setDefaultLandscapeID(const QString& id)
612 {
613@@ -878,7 +962,7 @@
614 }
615
616 /****************************************************************************
617- get a map of landscape name (from landscape.ini name field) to ID (dir name)
618+ get a map of landscape names (from landscape.ini name field) to ID (dir name)
619 ****************************************************************************/
620 QMap<QString,QString> LandscapeMgr::getNameToDirMap() const
621 {
622
623=== modified file 'src/core/modules/LandscapeMgr.hpp'
624--- src/core/modules/LandscapeMgr.hpp 2016-12-18 06:38:55 +0000
625+++ src/core/modules/LandscapeMgr.hpp 2017-01-19 22:30:07 +0000
626@@ -28,6 +28,7 @@
627
628 #include <QMap>
629 #include <QStringList>
630+#include <QCache>
631
632 class Atmosphere;
633 class Cardinals;
634@@ -41,7 +42,7 @@
635 //! \note
636 //! The Bortle scale index setting was removed from this class, because it was duplicated
637 //! from StelSkyDrawer, complicating code that changes it.
638-//! It is now only in StelSkyDrawer and can be accessed with
639+//! It is now only in StelSkyDrawer and can be accessed
640 //! with \link StelSkyDrawer::getBortleScaleIndex getBortleScaleIndex \endlink
641 //! and \link StelSkyDrawer::setBortleScaleIndex setBortleScaleIndex \endlink.
642 //! Slots setAtmosphereBortleLightPollution and getAtmosphereBortleLightPollution
643@@ -141,9 +142,10 @@
644 ///////////////////////////////////////////////////////////////////////////
645 // Methods specific to the landscape manager
646
647- //! Load a landscape based on a hash of parameters mirroring the landscape.ini
648- //! file and make it the current landscape.
649- bool loadLandscape(QMap<QString, QString>& param);
650+ // Load a landscape based on a hash of parameters mirroring the landscape.ini
651+ // file and make it the current landscape.
652+ // GZ: This was declared, but not implemented(?)
653+ //bool loadLandscape(QMap<QString, QString>& param);
654
655 //! Create a new landscape from the files which describe it.
656 //! Reads a landscape.ini file which is passed as the first parameter, determines
657@@ -177,7 +179,7 @@
658 //! this value explicitly to freeze it during image export. To unfreeze, call this again with any negative value.
659 void setAtmosphereAverageLuminance(const float overrideLuminance);
660
661- //! Return a map of landscape name to landscape ID (directory name).
662+ //! Return a map of landscape names to landscape IDs (directory names).
663 QMap<QString,QString> getNameToDirMap() const;
664
665 //! Retrieve a list of the names of all the available landscapes in
666@@ -212,6 +214,31 @@
667 //! @param changeLocationDuration the duration of the transition animation
668 bool setCurrentLandscapeName(const QString& name, const double changeLocationDuration = 1.0);
669
670+ //! Preload a landscape into cache.
671+ //! @param id the ID of a landscape
672+ //! @param replace true if existing landscape entry should be replaced (useful during development to reload after edit)
673+ //! @return false if landscape could not be found, or if it already existed in cache and replace was false.
674+ bool precacheLandscape(const QString& id, const bool replace=true);
675+ //! Remove a landscape from the cache of landscapes.
676+ //! @param id the ID of a landscape
677+ //! @return false if landscape could not be found
678+ bool removeCachedLandscape(const QString& id);
679+ //! Set size of the landscape cache, in MB.
680+ //! Default size is 100MB, or configured as [landscape/cache_size_mb] from config.ini.
681+ //! The landscape sizes returned in Landscape::getMemorySize() are only approximate, but include image and texture sizes.
682+ //! A big landscape may well take 150MB or more.
683+ //! On a 32bit system, keep this rather small. On 64bit with 16GB RAM and no other tasks, 4GB is no problem.
684+ //! Modern GPUs may have 4 or even 8GB of dedicated texture memory. Most of this may be filled with landscape textures.
685+ //! Example: a museum installation with 20 large (16384x2048) old_stype landscapes can require up to 3.5GB. Allow 4GB cache,
686+ //! and the system will never have to load a landscape during the show when all have been preloaded.
687+ void setCacheSize(int mb) { landscapeCache.setMaxCost(mb);}
688+ //! Retrieve total size of cache (MB).
689+ int getCacheSize() const {return landscapeCache.maxCost();}
690+ //! Retrieve sum of currently used memory in cache (MB, approximate)
691+ int getCacheFilledSize() const {return landscapeCache.totalCost();}
692+ //! Return number of landscapes already in the cache.
693+ int getCacheCount() const {return landscapeCache.count();}
694+
695 //! Get the current landscape object.
696 Landscape* getCurrentLandscape() const { return landscape; }
697
698@@ -505,16 +532,24 @@
699 //! Indicate auto-enable atmosphere for planets with atmospheres in location window
700 bool flagAtmosphereAutoEnabling;
701
702- // The ID of the currently loaded landscape
703+ //! The ID of the currently loaded landscape
704 QString currentLandscapeID;
705
706- // The ID of the default landscape
707+ //! The ID of the default landscape
708 QString defaultLandscapeID;
709
710 //! List of the IDs of the landscapes packaged by default with Stellarium.
711 //! (So that they can't be removed.)
712 QStringList packagedLandscapeIDs;
713
714+ //! QCache of landscapes kept in memory for faster access, esp. when frequently switching between several big landscapes.
715+ //! Example: a 16384-size old_style landscape takes about 10 seconds to load. Kept in cache, it is back instantly.
716+ //! Of course, this requires lots of RAM and GPU texture memory, but in the age of 64bit CPUs and 4GB and more GPU
717+ //! texture memory, it is no problem to keep even 20 or more landscapes.
718+ //! This is esp. useful in a context of automated setup (museum show or such) where a list of landscapes is preloaded
719+ //! at system start (e.g. in the startup.ssc script) and then retrieved while script is running.
720+ //! The key is just the LandscapeID.
721+ QCache<QString,Landscape> landscapeCache;
722 };
723
724 #endif // _LANDSCAPEMGR_HPP_