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 }