1 /* -*-c++-*- */
2 /* osgEarth - Geospatial SDK for OpenSceneGraph
3 * Copyright 2019 Pelican Mapping
4 * http://osgearth.org
5 *
6 * osgEarth is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
16 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
17 * IN THE SOFTWARE.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>
21 */
22 #include "TileNodeRegistry"
23
24 using namespace osgEarth::Drivers::MPTerrainEngine;
25 using namespace osgEarth;
26
27 #define LC "[TileNodeRegistry] "
28
29 #define OE_TEST OE_NULL
30 //#define OE_TEST OE_INFO
31
32
33 //----------------------------------------------------------------------------
34
TileNodeRegistry(const std::string & name,Terrain * terrain)35 TileNodeRegistry::TileNodeRegistry(const std::string& name, Terrain* terrain) :
36 _name ( name ),
37 _revisioningEnabled( false ),
38 _frameNumber ( 0u ),
39 _terrain( terrain )
40 {
41 //nop
42 }
43
44
45 void
setRevisioningEnabled(bool value)46 TileNodeRegistry::setRevisioningEnabled(bool value)
47 {
48 _revisioningEnabled = value;
49 }
50
51
52 void
setMapRevision(const Revision & rev,bool setToDirty)53 TileNodeRegistry::setMapRevision(const Revision& rev,
54 bool setToDirty)
55 {
56 if ( _revisioningEnabled )
57 {
58 if ( _maprev != rev || setToDirty )
59 {
60 Threading::ScopedMutexLock exclusive( _tilesMutex );
61
62 if ( _maprev != rev || setToDirty )
63 {
64 _maprev = rev;
65
66 for( TileNodeMap::iterator i = _tiles.begin(); i != _tiles.end(); ++i )
67 {
68 i->second->setMapRevision( _maprev );
69 if ( setToDirty )
70 i->second->setDirty();
71 }
72 }
73 }
74 }
75 }
76
77
78 //NOTE: this method assumes the input extent is the same SRS as
79 // the terrain profile SRS.
80 void
setDirty(const GeoExtent & extent,unsigned minLevel,unsigned maxLevel)81 TileNodeRegistry::setDirty(const GeoExtent& extent,
82 unsigned minLevel,
83 unsigned maxLevel)
84 {
85 Threading::ScopedMutexLock exclusive( _tilesMutex );
86
87 bool checkSRS = false;
88 for( TileNodeMap::iterator i = _tiles.begin(); i != _tiles.end(); ++i )
89 {
90 const TileKey& key = i->first;
91 if (minLevel <= key.getLOD() &&
92 maxLevel >= key.getLOD() &&
93 extent.intersects(i->first.getExtent(), checkSRS) )
94 {
95 i->second->setDirty();
96 }
97 }
98 }
99
100
101 void
add(TileNode * tile)102 TileNodeRegistry::add( TileNode* tile )
103 {
104 if ( tile )
105 {
106 osg::ref_ptr<Terrain> terrain;
107 if (_terrain.lock(terrain))
108 {
109 terrain->notifyTileAdded(tile->getKey(), tile);
110 }
111
112 Threading::ScopedMutexLock exclusive( _tilesMutex );
113 _tiles[ tile->getKey() ] = tile;
114 if ( _revisioningEnabled )
115 tile->setMapRevision( _maprev );
116
117 OE_TEST << LC << _name << ": tiles=" << _tiles.size() << std::endl;
118
119 // check for waiters.
120 Notifications::iterator i = _notifications.find(tile->getKey());
121 if ( i != _notifications.end() )
122 {
123 TileKeySet& waiters = i->second;
124
125 for (TileKeySet::iterator j = waiters.begin(); j != waiters.end(); ++j)
126 {
127 const TileKey& waiter = *j;
128 TileNodeMap::iterator k = _tiles.find(waiter);
129 if ( k != _tiles.end() )
130 {
131 // notify the listener:
132 k->second->notifyOfArrival( tile );
133 }
134 }
135
136 // clear the wait list for this tile.
137 // if there were waiters whose keys weren't found in the registry,
138 // they should not have been there anyway!
139 _notifications.erase(i);
140 }
141
142 // Listen for east and south neighbors of the new tile:
143 const TileKey& key = tile->getTileNode()->getKey();
144 startListeningFor( key.createNeighborKey(1, 0), tile->getTileNode() );
145 startListeningFor( key.createNeighborKey(0, 1), tile->getTileNode() );
146 }
147 }
148
149 void
remove(TileNode * tile)150 TileNodeRegistry::remove( TileNode* tile )
151 {
152 if ( tile )
153 {
154 Threading::ScopedMutexLock exclusive( _tilesMutex );
155 _tiles.erase( tile->getKey() );
156 OE_TEST << LC << _name << ": tiles=" << _tiles.size() << std::endl;
157
158 // remove neighbor listeners:
159 const TileKey& key = tile->getTileNode()->getKey();
160 stopListeningFor( key.createNeighborKey(1, 0), tile->getTileNode() );
161 stopListeningFor( key.createNeighborKey(0, 1), tile->getTileNode() );
162 }
163 }
164
165
166 bool
get(const TileKey & key,osg::ref_ptr<TileNode> & out_tile)167 TileNodeRegistry::get( const TileKey& key, osg::ref_ptr<TileNode>& out_tile )
168 {
169 Threading::ScopedMutexLock shared( _tilesMutex );
170
171 TileNodeMap::iterator i = _tiles.find(key);
172 if ( i != _tiles.end() && i->second.valid() )
173 {
174 out_tile = i->second.get();
175 return true;
176 }
177 return false;
178 }
179
180
181 bool
take(const TileKey & key,osg::ref_ptr<TileNode> & out_tile)182 TileNodeRegistry::take( const TileKey& key, osg::ref_ptr<TileNode>& out_tile )
183 {
184 Threading::ScopedMutexLock exclusive( _tilesMutex );
185
186 TileNodeMap::iterator i = _tiles.find(key);
187 if ( i != _tiles.end() )
188 {
189 out_tile = i->second.get();
190 _tiles.erase( i );
191 OE_TEST << LC << _name << ": tiles=" << _tiles.size() << std::endl;
192 return true;
193 }
194 return false;
195 }
196
197 void
run(const TileNodeRegistry::ConstOperation & op) const198 TileNodeRegistry::run( const TileNodeRegistry::ConstOperation& op ) const
199 {
200 Threading::ScopedMutexLock lock( _tilesMutex );
201 op.operator()( _tiles );
202 }
203
204
205 bool
empty() const206 TileNodeRegistry::empty() const
207 {
208 // don't bother mutex-protecteding this.
209 return _tiles.empty();
210 }
211
212 void
startListeningFor(const TileKey & tileToWaitFor,TileNode * waiter)213 TileNodeRegistry::startListeningFor(const TileKey& tileToWaitFor, TileNode* waiter)
214 {
215 //Threading::ScopedMutexLock lock( _tilesMutex );
216 // ASSUME EXCLUSIVE LOCK
217
218 TileNodeMap::iterator i = _tiles.find( tileToWaitFor );
219 if ( i != _tiles.end() )
220 {
221 OE_DEBUG << LC << waiter->getKey().str() << " listened for " << tileToWaitFor.str()
222 << ", but it was already in the repo.\n";
223
224 waiter->notifyOfArrival( i->second.get() );
225 }
226 else
227 {
228 OE_DEBUG << LC << waiter->getKey().str() << " listened for " << tileToWaitFor.str() << ".\n";
229 _notifications[tileToWaitFor].insert( waiter->getKey() );
230 }
231 }
232
233 void
stopListeningFor(const TileKey & tileToWaitFor,TileNode * waiter)234 TileNodeRegistry::stopListeningFor(const TileKey& tileToWaitFor, TileNode* waiter)
235 {
236 //Threading::ScopedMutexLock lock( _tilesMutex );
237 // ASSUME EXCLUSIVE LOCK
238
239 Notifications::iterator i = _notifications.find(tileToWaitFor);
240 if (i != _notifications.end())
241 {
242 // remove the waiter from this set:
243 i->second.erase(waiter->getKey());
244
245 // if the set is now empty, remove the set entirely
246 if (i->second.empty())
247 {
248 _notifications.erase(i);
249 }
250 }
251 }
252
253 void
releaseAll(ResourceReleaser * releaser)254 TileNodeRegistry::releaseAll(ResourceReleaser* releaser)
255 {
256 ResourceReleaser::ObjectList objects;
257 {
258 Threading::ScopedMutexLock exclusive(_tilesMutex);
259
260 for (TileNodeMap::iterator i = _tiles.begin(); i != _tiles.end(); ++i)
261 {
262 objects.push_back(i->second.get());
263 }
264
265 _tiles.clear();
266 _notifications.clear();
267 }
268
269 releaser->push(objects);
270 }