osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarth/TextureCompositor.cpp

Go to the documentation of this file.
00001 /* -*-c++-*- */
00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
00003  * Copyright 2008-2010 Pelican Mapping
00004  * http://osgearth.org
00005  *
00006  * osgEarth is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU Lesser General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>
00018  */
00019 
00020 #include <osgEarth/TextureCompositor>
00021 #include <osgEarth/TextureCompositorTexArray>
00022 #include <osgEarth/TextureCompositorMulti>
00023 #include <osgEarth/Capabilities>
00024 #include <osgEarth/ImageUtils>
00025 #include <osgEarth/Registry>
00026 #include <osg/Texture2DArray>
00027 #include <osg/Texture2D>
00028 #include <osg/Texture3D>
00029 #include <vector>
00030 
00031 using namespace osgEarth;
00032 using namespace OpenThreads;
00033 
00034 #define LC "[TextureCompositor] "
00035 
00036 //---------------------------------------------------------------------------
00037 
00038 TextureLayout::TextureLayout()
00039 {
00040     //nop
00041 }
00042 
00043 int
00044 TextureLayout::getSlot( UID layerUID, unsigned which, unsigned maxSlotsToSearch ) const
00045 {
00046     for( unsigned slot = 0; slot < _slots.size() && slot < maxSlotsToSearch; ++slot )
00047     {
00048         if ( _slots[slot] == layerUID )
00049         {
00050             if ( which == 0 )
00051                 return slot;
00052             else
00053                 --which;
00054         }
00055     }
00056     return -1;
00057 }
00058 
00059 int 
00060 TextureLayout::getOrder( UID layerUID ) const
00061 {
00062     int slot = getSlot( layerUID, 0 );
00063     RenderOrderVector::const_iterator i = std::find( _order.begin(), _order.end(), slot );
00064     return i != _order.end() ? (int)(i-_order.begin()) : -1;
00065 }
00066 
00067 int
00068 TextureLayout::getMaxUsedSlot() const
00069 {
00070     for( int i = _slots.size()-1; i >= 0; --i )
00071         if ( i >= 0 )
00072             return i;
00073     return -1;
00074 }
00075 
00076 void
00077 TextureLayout::assignPrimarySlot( ImageLayer* layer, int orderIndex )
00078 {
00079     int slot = -1;
00080 
00081     bool found = false;
00082     for( TextureSlotVector::iterator i = _slots.begin(); i != _slots.end() && !found; ++i )
00083     {
00084         slot = (int)(i - _slots.begin());
00085 
00086         // negative UID means the slot is empty.
00087         bool slotAvailable = (*i < 0) && (_reservedSlots.find(slot) == _reservedSlots.end());
00088         if ( slotAvailable )
00089         {
00090             // record this UID in the new slot:
00091             *i = layer->getUID();
00092 
00093             // record the render order of this slot:
00094             if ( orderIndex >= (int)_order.size() )
00095             {
00096                 _order.resize( orderIndex + 1, -1 );
00097                 _order[orderIndex] = slot;
00098             }
00099             else
00100             {
00101                 if (_order[orderIndex] == -1)
00102                     _order[orderIndex] = slot;
00103                 else
00104                     _order.insert(_order.begin() + orderIndex, slot);
00105             }
00106 
00107             found = true;
00108             break;
00109         }
00110     }
00111 
00112     if ( !found )
00113     {
00114         // put the UID in the next available slot (that's not reserved).
00115         while( _reservedSlots.find(_slots.size()) != _reservedSlots.end() )
00116             _slots.push_back( -1 );
00117 
00118         slot = _slots.size();
00119         _slots.push_back( layer->getUID() );
00120         _order.push_back( _slots.size() - 1 );
00121     }
00122 
00123     OE_INFO << LC << "Allocated SLOT " << slot << "; primary slot for layer \"" << layer->getName() << "\"" << std::endl;
00124 }
00125 
00126 void
00127 TextureLayout::assignSecondarySlot( ImageLayer* layer )
00128 {
00129     int slot = -1;
00130     bool found = false;
00131 
00132     for( TextureSlotVector::iterator i = _slots.begin(); i != _slots.end() && !found; ++i )
00133     {
00134         slot = (int)(i - _slots.begin());
00135 
00136         // negative UID means the slot is empty.
00137         bool slotAvailable = (*i < 0) && (_reservedSlots.find(slot) == _reservedSlots.end());
00138         if ( slotAvailable )
00139         {
00140             // record this UID in the new slot:
00141             *i = layer->getUID();
00142             found = true;
00143             break;
00144         }
00145     }
00146 
00147     if ( !found )
00148     {
00149         // put the UID in the next available slot (that's not reserved).
00150         while( _reservedSlots.find(_slots.size()) != _reservedSlots.end() )
00151             _slots.push_back( -1 );
00152 
00153         slot = _slots.size();
00154         _slots.push_back( layer->getUID() );
00155     }
00156 
00157     OE_INFO << LC << "Allocated SLOT " << slot << "; secondary slot for layer \"" << layer->getName() << "\"" << std::endl;
00158 }
00159 
00160 void
00161 TextureLayout::applyMapModelChange( const MapModelChange& change, bool reserveSeconarySlotIfNecessary )
00162 {
00163     if ( change.getAction() == MapModelChange::ADD_IMAGE_LAYER )
00164     {
00165         assignPrimarySlot( change.getImageLayer(), change.getFirstIndex() );
00166 
00167         bool blendingOn = change.getImageLayer()->getImageLayerOptions().lodBlending() == true;
00168         _lodBlending[ change.getImageLayer()->getUID() ] = blendingOn;
00169 
00170         if ( blendingOn && reserveSeconarySlotIfNecessary )
00171         {
00172             assignSecondarySlot( change.getImageLayer() );
00173         }
00174     }
00175 
00176     else if ( change.getAction() == MapModelChange::REMOVE_IMAGE_LAYER )
00177     {
00178         for( int which = 0; which <= 1; ++which )
00179         {
00180             int slot = getSlot( change.getLayer()->getUID(), which );
00181             if ( slot < 0 )
00182                 break;
00183 
00184             _slots[slot] = -1;
00185 
00186             if ( which == 0 ) // primary slot; remove from render order:
00187             {
00188                 for( RenderOrderVector::iterator j = _order.begin(); j != _order.end(); )
00189                 {
00190                     if ( *j == slot )
00191                         j = _order.erase( j );
00192                     else
00193                         ++j;
00194                 }
00195             }
00196         }
00197     }
00198 
00199     else if ( change.getAction() == MapModelChange::MOVE_IMAGE_LAYER )
00200     {
00201         int fromIndex = getOrder( change.getLayer()->getUID() );
00202         int toIndex = change.getSecondIndex();
00203 
00204         if ( fromIndex != toIndex )
00205         {
00206             int slot = _order[fromIndex];
00207             _order.erase( _order.begin() + fromIndex );
00208             _order.insert( _order.begin() + toIndex, slot );
00209         }
00210     }
00211 
00212     //OE_INFO << LC << "Layout Slots: " << std::endl;
00213     //for( int i=0; i<_slots.size(); ++i )
00214     //    OE_INFO << LC << "  Slot " << i << ": uid=" << _slots[i] << ", order=" << getOrder(_slots[i]) << std::endl;
00215     //OE_INFO << LC << "Layout Order: " << std::endl;
00216     //for( int i=0; i<_order.size(); ++i )
00217     //    OE_INFO << LC << "  Ordr " << i << ": slot=" << _order[i] << std::endl;
00218 }
00219 
00220 void
00221 TextureLayout::setReservedSlots( const std::set<int>& reservedSlots )
00222 {
00223     _reservedSlots = reservedSlots;
00224 }
00225 
00226 bool
00227 TextureLayout::isSlotAvailable( int i ) const
00228 {
00229     if ( (i < (int)_slots.size() && _slots[i] < 0) || i >= (int)_slots.size() )
00230     {
00231         if ( _reservedSlots.find(i) == _reservedSlots.end() )
00232         {
00233             return true;
00234         }
00235     }
00236     return false;
00237 }
00238 
00239 bool
00240 TextureLayout::containsSecondarySlots( unsigned maxSlotsToSearch ) const
00241 {
00242     for( int slot = 0; slot < (int)_slots.size() && slot < (int)maxSlotsToSearch; ++slot )
00243     {
00244         UID uid = _slots[slot];
00245         if ( getSlot(uid, 0) != slot )
00246             return true;
00247     }
00248     return false;
00249 }
00250 
00251 bool
00252 TextureLayout::isBlendingEnabled( UID layerUID ) const
00253 {
00254     std::map<UID,bool>::const_iterator i = _lodBlending.find(layerUID);
00255     return i != _lodBlending.end() ? i->second : false;
00256 }
00257 
00258 //---------------------------------------------------------------------------
00259 
00260 TextureCompositor::TextureCompositor(const TerrainOptions& options) :
00261 osg::Referenced( true ),
00262 _tech( options.compositingTechnique().value() ),
00263 _options( options ),
00264 _forceTech( false )
00265 {
00266     // for debugging:
00267     if ( _tech == TerrainOptions::COMPOSITING_AUTO && ::getenv( "OSGEARTH_COMPOSITOR_TECH" ) )
00268     {
00269         TerrainOptions::CompositingTechnique oldTech = _tech;
00270         std::string t( ::getenv( "OSGEARTH_COMPOSITOR_TECH" ) );
00271         if      ( t == "TEXTURE_ARRAY" )    _tech = TerrainOptions::COMPOSITING_TEXTURE_ARRAY;
00272         else if ( t == "MULTITEXTURE_GPU" ) _tech = TerrainOptions::COMPOSITING_MULTITEXTURE_GPU;
00273         else if ( t == "MULTIPASS" )        _tech = TerrainOptions::COMPOSITING_MULTIPASS;
00274         if ( oldTech != _tech )
00275             _forceTech = true;
00276     }
00277 
00278     init();
00279 }
00280 
00281 bool
00282 TextureCompositor::reserveTextureImageUnit( int& out_unit )
00283 {
00284     //todo: move this into the impls!!
00285 
00286     out_unit = -1;
00287 
00288     //TODO: this only supports GPU texturing....
00289     unsigned maxUnits = osgEarth::Registry::instance()->getCapabilities().getMaxGPUTextureUnits();
00290 
00291     if ( _tech == TerrainOptions::COMPOSITING_MULTITEXTURE_GPU )
00292     {
00293         Threading::ScopedWriteLock exclusiveLock( _layoutMutex );
00294 
00295         for( unsigned i=0; i<maxUnits; ++i )
00296         {
00297             if ( _layout.isSlotAvailable(i) )
00298             {
00299                 out_unit = i;
00300                 _reservedUnits.insert( i );
00301                 _layout.setReservedSlots( _reservedUnits ); // in multitexture, slots == units
00302                 return true;
00303             }
00304         }
00305 
00306         // all taken, return false.
00307         return false;
00308     }
00309 
00310     else if ( _tech == TerrainOptions::COMPOSITING_TEXTURE_ARRAY )
00311     {
00312         // texture array reserved slots 0 and 1 (for primary and blending)
00313         for( unsigned i=2; i<maxUnits; ++i ) // 0 and 1 always reserved.
00314         {
00315             if ( _reservedUnits.find( i ) == _reservedUnits.end() )
00316             {
00317                 out_unit = i;
00318                 _reservedUnits.insert( i );
00319                 return true;
00320             }
00321         }
00322 
00323         // all taken, return false.
00324         return false;
00325     }
00326 
00327     else // multipass... all image layers are locked at unit 0
00328     {
00329         // search for an unused unit.
00330         for( unsigned i=1; i<maxUnits; ++i ) // start at 1 because unit 0 is always reserved
00331         {
00332             if ( _reservedUnits.find( i ) == _reservedUnits.end() )
00333             {
00334                 out_unit = i;
00335                 _reservedUnits.insert( i );
00336                 return true;
00337             }
00338         }
00339 
00340         // all taken, return false.
00341         return false;
00342     }
00343 }
00344 
00345 void
00346 TextureCompositor::releaseTextureImageUnit( int unit )
00347 {
00348     _reservedUnits.erase( unit );
00349 
00350     if ( _tech == TerrainOptions::COMPOSITING_MULTITEXTURE_GPU )
00351     {
00352         Threading::ScopedWriteLock exclusiveLock( _layoutMutex );
00353         _layout.setReservedSlots( _reservedUnits );
00354     }
00355 }
00356 
00357 void
00358 TextureCompositor::applyMapModelChange( const MapModelChange& change )
00359 {
00360     Threading::ScopedWriteLock exclusiveLock( _layoutMutex );
00361 
00362     _layout.applyMapModelChange(
00363         change, 
00364         _impl.valid() ? _impl->blendingRequiresSecondarySlot() : false );
00365 }
00366 
00367 bool
00368 TextureCompositor::supportsLayerUpdate() const
00369 {
00370     return _impl.valid() ? _impl->supportsLayerUpdate() : false;
00371 }
00372 
00373 GeoImage
00374 TextureCompositor::prepareImage( const GeoImage& image, const GeoExtent& tileExtent ) const
00375 {
00376     return _impl.valid() ? _impl->prepareImage( image, tileExtent ) : GeoImage::INVALID;
00377 }
00378 
00379 GeoImage
00380 TextureCompositor::prepareSecondaryImage( const GeoImage& image, const GeoExtent& tileExtent ) const
00381 {
00382     return _impl.valid() ? _impl->prepareSecondaryImage( image, tileExtent ) : GeoImage::INVALID;
00383 }
00384 
00385 void
00386 TextureCompositor::applyLayerUpdate(osg::StateSet* stateSet,
00387                                     UID layerUID,
00388                                     const GeoImage& preparedImage,
00389                                     const TileKey& tileKey,
00390                                     osg::StateSet* parentStateSet) const
00391 {
00392     if ( _impl.valid() )
00393     {
00394         Threading::ScopedReadLock sharedLock( const_cast<TextureCompositor*>(this)->_layoutMutex );
00395         _impl->applyLayerUpdate( stateSet, layerUID, preparedImage, tileKey,
00396                                  _layout,  parentStateSet );
00397     }
00398 }
00399 
00400 void
00401 TextureCompositor::applyLayerRemoval(osg::StateSet* stateSet,
00402                                      UID layerUID ) const
00403 {
00404     if ( _impl.valid() )
00405         _impl->applyLayerRemoval( stateSet, layerUID );
00406 }
00407 
00408 bool
00409 TextureCompositor::requiresUnitTextureSpace() const
00410 {
00411     return _impl.valid() ? _impl->requiresUnitTextureSpace() : false;
00412 }
00413 
00414 bool
00415 TextureCompositor::usesShaderComposition() const
00416 {
00417     return _impl.valid() ? _impl->usesShaderComposition() : false;
00418 }
00419 
00420 void
00421 TextureCompositor::updateMasterStateSet( osg::StateSet* stateSet ) const
00422 {
00423     if ( _impl.valid() )
00424     {
00425         Threading::ScopedReadLock sharedLock( const_cast<TextureCompositor*>(this)->_layoutMutex );
00426         _impl->updateMasterStateSet( stateSet, _layout );
00427     }
00428 }
00429 
00430 void
00431 TextureCompositor::assignTexCoordArray(osg::Geometry* geom,
00432                                        UID layerUID,
00433                                        osg::Vec2Array* texCoords ) const
00434 {
00435     if ( geom && texCoords )
00436     {
00437         if ( _tech == TerrainOptions::COMPOSITING_MULTIPASS )
00438         {
00439             geom->setTexCoordArray( 0, texCoords );
00440         }
00441         else
00442         {
00443             int slot;
00444             {            
00445                 Threading::ScopedReadLock sharedLock( const_cast<TextureCompositor*>(this)->_layoutMutex );
00446                 slot = _layout.getSlot( layerUID );
00447             }
00448             if ( slot >= 0 )
00449                 geom->setTexCoordArray( slot, texCoords );
00450         }
00451     }
00452 }
00453 
00454 int
00455 TextureCompositor::getRenderOrder( UID layerUID ) const
00456 {
00457     Threading::ScopedReadLock sharedLock( const_cast<TextureCompositor*>(this)->_layoutMutex );
00458     return _layout.getOrder( layerUID );
00459 }
00460 
00461 osg::Shader*
00462 TextureCompositor::createSamplerFunction(UID                layerUID, 
00463                                          const std::string& functionName,
00464                                          osg::Shader::Type  type ) const
00465 {
00466     osg::Shader* result = 0L;
00467     if ( _impl.valid() )
00468     {
00469         Threading::ScopedReadLock sharedLock( const_cast<TextureCompositor*>(this)->_layoutMutex );
00470         result = _impl->createSamplerFunction( layerUID, functionName, type, _layout );
00471     }
00472 
00473     if ( !result )
00474     {
00475         std::string fname = !functionName.empty() ? functionName : "defaultSamplerFunction";
00476         std::stringstream buf;
00477         buf << "vec4 " << functionName << "() { \n return vec4(0,0,0,0); \n } \n";
00478         std::string str = buf.str();
00479         result = new osg::Shader( type, str );
00480     }
00481 
00482     return result;
00483 }
00484 
00485 void
00486 TextureCompositor::init()
00487 {        
00488     if ( _impl.valid() ) // double-check pattern
00489     {
00490         return; // already initialized
00491     }
00492 
00493     bool isAuto = _tech == TerrainOptions::COMPOSITING_AUTO;
00494 
00495     const Capabilities& caps = Registry::instance()->getCapabilities();
00496 
00497     // MULTITEXTURE_GPU is the current default.
00498     
00499     if (_tech == TerrainOptions::COMPOSITING_MULTITEXTURE_GPU ||
00500         (isAuto && caps.supportsGLSL(1.20f) && caps.supportsMultiTexture()) ) 
00501     {
00502         _tech = TerrainOptions::COMPOSITING_MULTITEXTURE_GPU;
00503         _impl = new TextureCompositorMultiTexture( true, _options );
00504         OE_INFO << LC << "Compositing technique = MULTITEXTURE/GPU" << std::endl;
00505     }
00506 
00507 #if OSG_VERSION_GREATER_OR_EQUAL( 2, 9, 8 )
00508 
00509     else
00510     if (_tech == TerrainOptions::COMPOSITING_TEXTURE_ARRAY || 
00511         (isAuto && caps.supportsGLSL(1.30f) && caps.supportsTextureArrays()) )
00512     {
00513         _tech = TerrainOptions::COMPOSITING_TEXTURE_ARRAY;
00514         _impl = new TextureCompositorTexArray( _options );
00515         OE_INFO << LC << "Compositing technique = TEXTURE ARRAY" << std::endl;
00516     }
00517 
00518 #endif // OSG_VERSION_GREATER_OR_EQUAL( 2, 9, 8 )
00519 
00520     else
00521     if ( _tech == TerrainOptions::COMPOSITING_MULTITEXTURE_FFP || (isAuto && caps.supportsMultiTexture()) )
00522     {
00523         _tech = TerrainOptions::COMPOSITING_MULTITEXTURE_FFP;
00524         _impl = new TextureCompositorMultiTexture( false, _options );
00525         OE_INFO << LC << "Compositing technique = MULTITEXTURE/FFP" << std::endl;
00526     }
00527 
00528     // Fallback of last resort. The implementation is actually a NO-OP for multipass mode.
00529     else
00530     {
00531         _tech = TerrainOptions::COMPOSITING_MULTIPASS;
00532         _impl = 0L;
00533         OE_INFO << LC << "Compositing technique = MULTIPASS" << std::endl;
00534     }
00535 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines