LCOV - code coverage report
Current view: top level - eq/server/equalizers - loadEqualizer.cpp (source / functions) Hit Total Coverage
Test: lcov2.info Lines: 67 687 9.8 %
Date: 2014-06-18 Functions: 10 24 41.7 %

          Line data    Source code
       1             : 
       2             : /* Copyright (c) 2008-2013, Stefan Eilemann <eile@equalizergraphics.com>
       3             :  *                    2011, Cedric Stalder <cedric.stalder@gmail.com>
       4             :  *                    2012, Daniel Nachbaur <danielnachbaur@gmail.com>
       5             :  *
       6             :  * This library is free software; you can redistribute it and/or modify it under
       7             :  * the terms of the GNU Lesser General Public License version 2.1 as published
       8             :  * by the Free Software Foundation.
       9             :  *
      10             :  * This library is distributed in the hope that it will be useful, but WITHOUT
      11             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      12             :  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
      13             :  * details.
      14             :  *
      15             :  * You should have received a copy of the GNU Lesser General Public License
      16             :  * along with this library; if not, write to the Free Software Foundation, Inc.,
      17             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             :  */
      19             : 
      20             : #include "loadEqualizer.h"
      21             : 
      22             : #include "../compound.h"
      23             : #include "../log.h"
      24             : 
      25             : #include <eq/client/statistic.h>
      26             : #include <lunchbox/debug.h>
      27             : 
      28             : namespace eq
      29             : {
      30             : namespace server
      31             : {
      32             : 
      33             : std::ostream& operator << ( std::ostream& os, const LoadEqualizer::Node* );
      34             : 
      35             : // The tree load balancer organizes the children in a binary tree. At each
      36             : // level, a relative split position is determined by balancing the left subtree
      37             : // against the right subtree.
      38             : 
      39          92 : LoadEqualizer::LoadEqualizer()
      40          92 :         : _tree( 0 )
      41             : {
      42          92 :     LBVERB << "New LoadEqualizer @" << (void*)this << std::endl;
      43          92 : }
      44             : 
      45          48 : LoadEqualizer::LoadEqualizer( const fabric::Equalizer& from )
      46             :         : Equalizer( from )
      47          48 :         , _tree( 0 )
      48          48 : {}
      49             : 
      50         420 : LoadEqualizer::~LoadEqualizer()
      51             : {
      52         140 :     _clearTree( _tree );
      53         140 :     delete _tree;
      54         140 :     _tree = 0;
      55             : 
      56         140 :     _history.clear();
      57         280 : }
      58             : 
      59          54 : void LoadEqualizer::notifyUpdatePre( Compound* compound,
      60             :                                      const uint32_t frameNumber )
      61             : {
      62          54 :     _checkHistory(); // execute to not leak memory
      63          54 :     if( isFrozen() || !compound->isActive() || !isActive( ))
      64          45 :         return;
      65             : 
      66           9 :     if( !_tree )
      67             :     {
      68           9 :         LBASSERT( compound == getCompound( ));
      69           9 :         const Compounds& children = compound->getChildren();
      70           9 :         switch( children.size( ))
      71             :         {
      72           0 :           case 0: return; // no child compounds, can't do anything.
      73             :           case 1: // one child, 'balance' it:
      74           9 :               if( getMode() == MODE_DB )
      75           0 :                   children.front()->setRange( Range( ));
      76             :               else
      77           9 :                   children.front()->setViewport( Viewport( ));
      78           9 :               return;
      79             : 
      80             :           default:
      81           0 :               _tree = _buildTree( children );
      82           0 :               break;
      83             :         }
      84             :     }
      85             : 
      86             :     // compute new data
      87           0 :     if( getDamping() < 1.f )
      88             :     {
      89           0 :         _history.push_back( LBFrameData( ));
      90           0 :         _history.back().first = frameNumber;
      91             :     }
      92             : 
      93           0 :     _update( _tree, Viewport(), Range( ));
      94           0 :     _computeSplit();
      95             : }
      96             : 
      97           0 : LoadEqualizer::Node* LoadEqualizer::_buildTree( const Compounds& compounds )
      98             : {
      99           0 :     Node* node = new Node;
     100             : 
     101           0 :     const size_t size = compounds.size();
     102           0 :     if( size == 1 )
     103             :     {
     104           0 :         Compound* compound = compounds.front();
     105             : 
     106           0 :         node->compound = compound;
     107             : 
     108           0 :         Channel* channel = compound->getChannel();
     109           0 :         LBASSERT( channel );
     110           0 :         channel->addListener( this );
     111           0 :         return node;
     112             :     }
     113             : 
     114           0 :     const size_t middle = size >> 1;
     115             : 
     116           0 :     Compounds left;
     117           0 :     for( size_t i = 0; i < middle; ++i )
     118           0 :         left.push_back( compounds[i] );
     119             : 
     120           0 :     Compounds right;
     121           0 :     for( size_t i = middle; i < size; ++i )
     122           0 :         right.push_back( compounds[i] );
     123             : 
     124           0 :     node->left  = _buildTree( left );
     125           0 :     node->right = _buildTree( right );
     126             : 
     127           0 :     return node;
     128             : }
     129             : 
     130         140 : void LoadEqualizer::_clearTree( Node* node )
     131             : {
     132         140 :     if( !node )
     133         280 :         return;
     134             : 
     135           0 :     if( node->compound )
     136             :     {
     137           0 :         Channel* channel = node->compound->getChannel();
     138           0 :         LBASSERTINFO( channel, node->compound );
     139           0 :         channel->removeListener( this );
     140             :     }
     141             :     else
     142             :     {
     143           0 :         _clearTree( node->left );
     144           0 :         _clearTree( node->right );
     145             :     }
     146             : }
     147             : 
     148           0 : void LoadEqualizer::notifyLoadData( Channel* channel,
     149             :                                     const uint32_t frameNumber,
     150             :                                     const Statistics& statistics,
     151             :                                     const Viewport& region )
     152             : {
     153           0 :     LBLOG( LOG_LB2 ) << statistics.size()
     154           0 :                      << " samples from "<< channel->getName()
     155           0 :                      << " @ " << frameNumber << std::endl;
     156           0 :     for( std::deque< LBFrameData >::iterator i = _history.begin();
     157           0 :          i != _history.end(); ++i )
     158             :     {
     159           0 :         LBFrameData& frameData = *i;
     160           0 :         if( frameData.first != frameNumber )
     161           0 :             continue;
     162             : 
     163             :         // Found corresponding historical data set
     164           0 :         LBDatas& items = frameData.second;
     165           0 :         for( LBDatas::iterator j = items.begin(); j != items.end(); ++j )
     166             :         {
     167           0 :             Data& data = *j;
     168           0 :             if( data.channel != channel )
     169           0 :                 continue;
     170             : 
     171             :             // Found corresponding historical data item
     172           0 :             const uint32_t taskID = data.taskID;
     173           0 :             LBASSERTINFO( taskID > 0, channel->getName( ));
     174             : 
     175             :             // gather relevant load data
     176           0 :             int64_t startTime = std::numeric_limits< int64_t >::max();
     177           0 :             int64_t endTime   = 0;
     178           0 :             bool    loadSet   = false;
     179           0 :             int64_t transmitTime = 0;
     180           0 :             for( size_t k = 0; k < statistics.size(); ++k )
     181             :             {
     182           0 :                 const Statistic& stat = statistics[k];
     183           0 :                 if( stat.task == data.destTaskID )
     184           0 :                     _updateAssembleTime( data, stat );
     185             : 
     186             :                 // from different compound
     187           0 :                 if( stat.task != taskID || loadSet )
     188           0 :                     continue;
     189             : 
     190           0 :                 switch( stat.type )
     191             :                 {
     192             :                 case Statistic::CHANNEL_CLEAR:
     193             :                 case Statistic::CHANNEL_DRAW:
     194             :                 case Statistic::CHANNEL_READBACK:
     195           0 :                     startTime = LB_MIN( startTime, stat.startTime );
     196           0 :                     endTime   = LB_MAX( endTime, stat.endTime );
     197           0 :                     break;
     198             : 
     199             :                 case Statistic::CHANNEL_ASYNC_READBACK:
     200             :                 case Statistic::CHANNEL_FRAME_TRANSMIT:
     201           0 :                     transmitTime += stat.endTime - stat.startTime;
     202           0 :                     break;
     203             :                 case Statistic::CHANNEL_FRAME_WAIT_SENDTOKEN:
     204           0 :                     transmitTime -= stat.endTime - stat.startTime;
     205           0 :                     break;
     206             : 
     207             :                 // assemble blocks on input frames, stop using subsequent data
     208             :                 case Statistic::CHANNEL_ASSEMBLE:
     209           0 :                     loadSet = true;
     210           0 :                     break;
     211             : 
     212             :                 default:
     213           0 :                     break;
     214             :                 }
     215             :             }
     216             : 
     217           0 :             if( startTime == std::numeric_limits< int64_t >::max( ))
     218           0 :                 return;
     219             : 
     220           0 :             data.vp.apply( region ); // Update ROI
     221           0 :             data.time = endTime - startTime;
     222           0 :             data.time = LB_MAX( data.time, 1 );
     223           0 :             data.time = LB_MAX( data.time, transmitTime );
     224           0 :             data.assembleTime = LB_MAX( data.assembleTime, 0 );
     225           0 :             LBLOG( LOG_LB2 ) << "Added time " << data.time << " (+"
     226           0 :                              << data.assembleTime << ") for "
     227           0 :                              << channel->getName() << " " << data.vp << ", "
     228           0 :                              << data.range << " @ " << frameNumber << std::endl;
     229           0 :             return;
     230             : 
     231             :             // Note: if the same channel is used twice as a child, the
     232             :             // load-compound association does not work.
     233             :         }
     234             :     }
     235             : }
     236             : 
     237           0 : void LoadEqualizer::_updateAssembleTime( Data& data, const Statistic& stat )
     238             : {
     239           0 :     switch( stat.type )
     240             :     {
     241             :         case Statistic::CHANNEL_FRAME_WAIT_READY:
     242           0 :             data.assembleTime -= stat.endTime - stat.startTime;
     243           0 :             break;
     244             : 
     245             :         case Statistic::CHANNEL_ASSEMBLE:
     246           0 :             data.assembleTime += stat.endTime - stat.startTime;
     247           0 :             break;
     248             : 
     249             :         default:
     250           0 :             break;
     251             :     }
     252           0 : }
     253             : 
     254          54 : void LoadEqualizer::_checkHistory()
     255             : {
     256             :     // 1. Find youngest complete load data set
     257          54 :     uint32_t useFrame = 0;
     258         198 :     for( std::deque< LBFrameData >::reverse_iterator i = _history.rbegin();
     259         198 :          i != _history.rend() && useFrame == 0; ++i )
     260             :     {
     261          12 :         const LBFrameData& frameData = *i;
     262          12 :         const LBDatas& items = frameData.second;
     263          12 :         bool isComplete = true;
     264             : 
     265          72 :         for( LBDatas::const_iterator j = items.begin();
     266          72 :              j != items.end() && isComplete; ++j )
     267             :         {
     268          12 :             const Data& data = *j;
     269             : 
     270          12 :             if( data.time < 0 )
     271           0 :                 isComplete = false;
     272             :         }
     273             : 
     274          12 :         if( isComplete )
     275          12 :             useFrame = frameData.first;
     276             :     }
     277             : 
     278             :     // 2. delete old, unneeded data sets
     279         108 :     while( !_history.empty() && _history.front().first < useFrame )
     280           0 :         _history.pop_front();
     281             : 
     282          54 :     if( _history.empty( )) // insert fake set
     283             :     {
     284          42 :         _history.resize( 1 );
     285             : 
     286          42 :         LBFrameData&  frameData  = _history.front();
     287          42 :         LBDatas& items      = frameData.second;
     288             : 
     289          42 :         frameData.first = 0; // frameNumber
     290          42 :         items.resize( 1 );
     291             : 
     292          42 :         Data& data = items.front();
     293          42 :         data.time = 1;
     294          42 :         LBASSERT( data.taskID == 0 );
     295          42 :         LBASSERT( data.channel == 0 );
     296             :     }
     297          54 : }
     298             : 
     299           0 : float LoadEqualizer::_getTotalResources( ) const
     300             : {
     301           0 :     const Compounds& children = getCompound()->getChildren();
     302             : 
     303           0 :     float resources = 0.f;
     304           0 :     for( CompoundsCIter i = children.begin(); i != children.end(); ++i )
     305             :     {
     306           0 :        const Compound* compound = *i;
     307           0 :        if( compound->isActive( ))
     308           0 :            resources += compound->getUsage();
     309             :     }
     310             : 
     311           0 :     return resources;
     312             : }
     313             : 
     314           0 : void LoadEqualizer::_update( Node* node, const Viewport& vp,
     315             :                              const Range& range )
     316             : {
     317           0 :     if( !node )
     318           0 :         return;
     319             : 
     320           0 :     node->mode = getMode();
     321           0 :     if( node->mode == MODE_2D )
     322             :     {
     323           0 :         PixelViewport pvp = getCompound()->getChannel()->getPixelViewport();
     324           0 :         pvp.apply( vp );
     325             : 
     326           0 :         if( pvp.w > pvp.h ) // split along longest axis
     327           0 :             node->mode = MODE_VERTICAL;
     328             :         else
     329           0 :             node->mode = MODE_HORIZONTAL;
     330             :     }
     331             : 
     332           0 :     if( node->compound )
     333           0 :         _updateLeaf( node );
     334             :     else
     335           0 :         _updateNode( node, vp, range );
     336             : }
     337             : 
     338           0 : void LoadEqualizer::_updateLeaf( Node* node )
     339             : {
     340           0 :     const Compound* compound = node->compound;
     341           0 :     const Channel* channel = compound->getChannel();
     342           0 :     LBASSERT( channel );
     343           0 :     const PixelViewport& pvp = channel->getPixelViewport();
     344           0 :     node->resources = compound->isActive() ? compound->getUsage() : 0.f;
     345           0 :     LBLOG( LOG_LB2 ) << channel->getName() << " active " << compound->isActive()
     346           0 :                      << " using " << node->resources << std::endl;
     347           0 :     LBASSERT( node->resources >= 0.f );
     348             : 
     349           0 :     node->maxSize.x() = pvp.w;
     350           0 :     node->maxSize.y() = pvp.h;
     351           0 :     node->boundaryf = getBoundaryf();
     352           0 :     node->boundary2i = getBoundary2i();
     353           0 :     node->resistancef = getResistancef();
     354           0 :     node->resistance2i = getResistance2i();
     355           0 :     if( !compound->hasDestinationChannel( ))
     356           0 :         return;
     357             : 
     358           0 :     const float nResources = _getTotalResources();
     359           0 :     if( getAssembleOnlyLimit() <= nResources - node->resources )
     360             :     {
     361           0 :         node->resources = 0.f;
     362           0 :         return; // OPT
     363             :     }
     364             : 
     365           0 :     const float time = float( _getTotalTime( ));
     366           0 :     const float assembleTime = float( _getAssembleTime( ));
     367           0 :     if( assembleTime == 0.f || node->resources == 0.f )
     368           0 :         return;
     369             : 
     370           0 :     const float timePerResource = time / ( nResources - node->resources );
     371           0 :     const float renderTime = timePerResource * node->resources ;
     372             : 
     373           0 :     const float clampedAssembleTime = LB_MIN( assembleTime, renderTime );
     374           0 :     const float newTimePerResource = (time + clampedAssembleTime) / nResources;
     375           0 :     node->resources -= ( clampedAssembleTime / newTimePerResource );
     376           0 :     if( node->resources < 0.f ) // may happen due to fp rounding
     377           0 :         node->resources = 0.f;
     378             : }
     379             : 
     380           0 : void LoadEqualizer::_updateNode( Node* node, const Viewport& vp,
     381             :                                  const Range& range )
     382             : {
     383           0 :     Node* left = node->left;
     384           0 :     Node* right = node->right;
     385             : 
     386           0 :     LBASSERT( left );
     387           0 :     LBASSERT( right );
     388             : 
     389           0 :     Viewport leftVP = vp;
     390           0 :     Viewport rightVP = vp;
     391           0 :     Range leftRange = range;
     392           0 :     Range rightRange = range;
     393             : 
     394           0 :     switch( node->mode )
     395             :     {
     396             :       default:
     397           0 :         LBUNIMPLEMENTED;
     398             : 
     399             :       case MODE_VERTICAL:
     400           0 :         leftVP.w = vp.w * .5f;
     401           0 :         rightVP.x = leftVP.getXEnd();
     402           0 :         rightVP.w = vp.getXEnd() - rightVP.x;
     403           0 :         node->split = leftVP.getXEnd();
     404           0 :         break;
     405             : 
     406             :       case MODE_HORIZONTAL:
     407           0 :         leftVP.h = vp.h * .5f;
     408           0 :         rightVP.y = leftVP.getYEnd();
     409           0 :         rightVP.h = vp.getYEnd() - rightVP.y;
     410           0 :         node->split = leftVP.getYEnd();
     411           0 :         break;
     412             : 
     413             :       case MODE_DB:
     414           0 :         leftRange.end = range.start + ( range.end - range.start ) * .5f;
     415           0 :         rightRange.start = leftRange.end;
     416           0 :         node->split = leftRange.end;
     417           0 :         break;
     418             :     }
     419             : 
     420           0 :     _update( left, leftVP, leftRange );
     421           0 :     _update( right, rightVP, rightRange );
     422             : 
     423           0 :     node->resources = left->resources + right->resources;
     424             : 
     425           0 :     if( left->resources == 0.f )
     426             :     {
     427           0 :         node->maxSize    = right->maxSize;
     428           0 :         node->boundary2i = right->boundary2i;
     429           0 :         node->boundaryf  = right->boundaryf;
     430           0 :         node->resistance2i = right->resistance2i;
     431           0 :         node->resistancef = right->resistancef;
     432             :     }
     433           0 :     else if( right->resources == 0.f )
     434             :     {
     435           0 :         node->maxSize = left->maxSize;
     436           0 :         node->boundary2i = left->boundary2i;
     437           0 :         node->boundaryf = left->boundaryf;
     438           0 :         node->resistance2i = left->resistance2i;
     439           0 :         node->resistancef = left->resistancef;
     440             :     }
     441             :     else
     442             :     {
     443           0 :         switch( node->mode )
     444             :         {
     445             :         case MODE_VERTICAL:
     446           0 :             node->maxSize.x() = left->maxSize.x() + right->maxSize.x();
     447           0 :             node->maxSize.y() = LB_MIN( left->maxSize.y(), right->maxSize.y());
     448           0 :             node->boundary2i.x() = left->boundary2i.x()+ right->boundary2i.x();
     449           0 :             node->boundary2i.y() = LB_MAX( left->boundary2i.y(),
     450           0 :                                            right->boundary2i.y());
     451           0 :             node->boundaryf = LB_MAX( left->boundaryf, right->boundaryf );
     452           0 :             node->resistance2i.x() = LB_MAX( left->resistance2i.x(),
     453           0 :                                              right->resistance2i.x( ));
     454           0 :             node->resistance2i.y() = LB_MAX( left->resistance2i.y(),
     455           0 :                                              right->resistance2i.y());
     456           0 :             node->resistancef = LB_MAX( left->resistancef, right->resistancef );
     457           0 :             break;
     458             :         case MODE_HORIZONTAL:
     459           0 :             node->maxSize.x() = LB_MIN( left->maxSize.x(), right->maxSize.x());
     460           0 :             node->maxSize.y() = left->maxSize.y() + right->maxSize.y();
     461           0 :             node->boundary2i.x() = LB_MAX( left->boundary2i.x(),
     462           0 :                                            right->boundary2i.x() );
     463           0 :             node->boundary2i.y() = left->boundary2i.y()+ right->boundary2i.y();
     464           0 :             node->boundaryf = LB_MAX( left->boundaryf, right->boundaryf );
     465           0 :             node->resistance2i.x() = LB_MAX( left->resistance2i.x(),
     466           0 :                                              right->resistance2i.x() );
     467           0 :             node->resistance2i.y() = LB_MAX( left->resistance2i.y(),
     468           0 :                                              right->resistance2i.y( ));
     469           0 :             node->resistancef = LB_MAX( left->resistancef, right->resistancef );
     470           0 :             break;
     471             :         case MODE_DB:
     472           0 :             node->boundary2i.x() = LB_MAX( left->boundary2i.x(),
     473           0 :                                            right->boundary2i.x() );
     474           0 :             node->boundary2i.y() = LB_MAX( left->boundary2i.y(),
     475           0 :                                            right->boundary2i.y() );
     476           0 :             node->boundaryf = left->boundaryf + right->boundaryf;
     477           0 :             node->resistance2i.x() = LB_MAX( left->resistance2i.x(),
     478           0 :                                              right->resistance2i.x() );
     479           0 :             node->resistance2i.y() = LB_MAX( left->resistance2i.y(),
     480           0 :                                              right->resistance2i.y() );
     481           0 :             node->resistancef = LB_MAX( left->resistancef, right->resistancef );
     482           0 :             break;
     483             :         default:
     484           0 :             LBUNIMPLEMENTED;
     485             :         }
     486             :     }
     487           0 : }
     488             : 
     489           0 : int64_t LoadEqualizer::_getTotalTime()
     490             : {
     491           0 :     const LBFrameData& frameData = _history.front();
     492           0 :     LBDatas items = frameData.second;
     493           0 :     _removeEmpty( items );
     494             : 
     495           0 :     int64_t totalTime = 0;
     496           0 :     for( LBDatas::const_iterator i = items.begin(); i != items.end(); ++i )
     497             :     {
     498           0 :         const Data& data = *i;
     499           0 :         totalTime += data.time;
     500             :     }
     501           0 :     return totalTime;
     502             : }
     503             : 
     504           0 : int64_t LoadEqualizer::_getAssembleTime( )
     505             : {
     506           0 :     if( getDamping() >= 1.f )
     507           0 :         return 0;
     508             : 
     509           0 :     const LBFrameData& frameData = _history.front();
     510           0 :     const LBDatas& items = frameData.second;
     511             : 
     512           0 :     int64_t assembleTime = 0;
     513           0 :     for( LBDatas::const_iterator i = items.begin(); i != items.end(); ++i )
     514             :     {
     515           0 :         const Data& data = *i;
     516           0 :         LBASSERT( assembleTime == 0 || data.assembleTime == 0 );
     517           0 :         assembleTime += data.assembleTime;
     518             :     }
     519           0 :     return assembleTime;
     520             : }
     521             : 
     522           0 : void LoadEqualizer::_computeSplit()
     523             : {
     524           0 :     LBASSERT( !_history.empty( ));
     525             : 
     526           0 :     const LBFrameData& frameData = _history.front();
     527           0 :     const Compound* compound = getCompound();
     528           0 :     LBLOG( LOG_LB2 ) << "----- balance " << compound->getChannel()->getName()
     529           0 :                     << " using frame " << frameData.first << " tree "
     530           0 :                      << std::endl << _tree;
     531             : 
     532             :     // sort load items for each of the split directions
     533           0 :     LBDatas items( frameData.second );
     534           0 :     _removeEmpty( items );
     535             : 
     536           0 :     LBDatas sortedData[3] = { items, items, items };
     537             : 
     538           0 :     if( getMode() == MODE_DB )
     539             :     {
     540           0 :         LBDatas& rangeData = sortedData[ MODE_DB ];
     541           0 :         sort( rangeData.begin(), rangeData.end(), _compareRange );
     542             :     }
     543             :     else
     544             :     {
     545           0 :         LBDatas& xData = sortedData[ MODE_VERTICAL ];
     546           0 :         sort( xData.begin(), xData.end(), _compareX );
     547             : 
     548           0 :         LBDatas& yData = sortedData[ MODE_HORIZONTAL ];
     549           0 :         sort( yData.begin(), yData.end(), _compareY );
     550             : 
     551             : #ifndef NDEBUG
     552           0 :         for( LBDatas::const_iterator i = xData.begin(); i != xData.end();
     553             :              ++i )
     554             :         {
     555           0 :             const Data& data = *i;
     556           0 :             LBLOG( LOG_LB2 ) << "  " << data.vp << ", time " << data.time
     557           0 :                              << " (+" << data.assembleTime << ")" << std::endl;
     558             :         }
     559             : #endif
     560             :     }
     561             : 
     562           0 :     const float time = float( _getTotalTime( ));
     563           0 :     LBLOG( LOG_LB2 ) << "Render time " << time << " for "
     564           0 :                      << _tree->resources << " resources" << std::endl;
     565           0 :     if( _tree->resources > 0.f )
     566           0 :         _computeSplit( _tree, time, sortedData, Viewport(), Range( ));
     567           0 : }
     568             : 
     569           0 : void LoadEqualizer::_removeEmpty( LBDatas& items )
     570             : {
     571           0 :     for( LBDatas::iterator i = items.begin(); i != items.end(); )
     572             :     {
     573           0 :         Data& data = *i;
     574             : 
     575           0 :         if( !data.vp.hasArea() || !data.range.hasData( ))
     576           0 :             i = items.erase( i );
     577             :         else
     578           0 :             ++i;
     579             :     }
     580           0 : }
     581             : 
     582           0 : void LoadEqualizer::_computeSplit( Node* node, const float time,
     583             :                                    LBDatas* datas, const Viewport& vp,
     584             :                                    const Range& range )
     585             : {
     586           0 :     LBLOG( LOG_LB2 ) << "_computeSplit " << vp << ", " << range << " time "
     587           0 :                     << time << std::endl;
     588           0 :     LBASSERTINFO( vp.isValid(), vp );
     589           0 :     LBASSERTINFO( range.isValid(), range );
     590           0 :     LBASSERTINFO( node->resources > 0.f || !vp.hasArea() || !range.hasData(),
     591             :                   "Assigning " << node->resources <<
     592             :                   " work to viewport " << vp << ", " << range );
     593             : 
     594           0 :     Compound* compound = node->compound;
     595           0 :     if( compound )
     596             :     {
     597           0 :         _assign( compound, vp, range );
     598           0 :         return;
     599             :     }
     600             : 
     601           0 :     LBASSERT( node->left && node->right );
     602             : 
     603           0 :     LBDatas workingSet = datas[ node->mode ];
     604           0 :     const float leftTime = node->resources > 0 ?
     605           0 :                            time * node->left->resources / node->resources : 0.f;
     606           0 :     float timeLeft = LB_MIN( leftTime, time ); // correct for fp rounding error
     607             : 
     608           0 :     switch( node->mode )
     609             :     {
     610             :         case MODE_VERTICAL:
     611             :         {
     612           0 :             LBASSERT( range == Range::ALL );
     613             : 
     614           0 :             float splitPos = vp.x;
     615           0 :             const float end = vp.getXEnd();
     616             : 
     617           0 :             while( timeLeft > std::numeric_limits< float >::epsilon() &&
     618             :                    splitPos < end )
     619             :             {
     620           0 :                 LBLOG( LOG_LB2 ) << timeLeft << "ms left using "
     621           0 :                                 << workingSet.size() << " tiles" << std::endl;
     622             : 
     623             :                 // remove all irrelevant items from working set
     624           0 :                 for( LBDatas::iterator i = workingSet.begin();
     625           0 :                      i != workingSet.end(); )
     626             :                 {
     627           0 :                     const Data& data = *i;
     628           0 :                     if( data.vp.getXEnd() > splitPos )
     629           0 :                         ++i;
     630             :                     else
     631           0 :                         i = workingSet.erase( i );
     632             :                 }
     633           0 :                 if( workingSet.empty( ))
     634           0 :                     break;
     635             : 
     636             :                 // find next 'discontinouity' in loads
     637           0 :                 float currentPos = 1.0f;
     638           0 :                 for( LBDatas::const_iterator i = workingSet.begin();
     639           0 :                      i != workingSet.end(); ++i )
     640             :                 {
     641           0 :                     const Data& data = *i;
     642           0 :                     if( data.vp.x > splitPos && data.vp.x < currentPos )
     643           0 :                         currentPos = data.vp.x;
     644           0 :                     const float xEnd = data.vp.getXEnd();
     645           0 :                     if( xEnd > splitPos && xEnd < currentPos )
     646           0 :                         currentPos = xEnd;
     647             :                 }
     648             : 
     649           0 :                 const float width = currentPos - splitPos;
     650           0 :                 LBASSERTINFO( width > 0.f, currentPos << "<=" << splitPos );
     651           0 :                 LBASSERT( currentPos <= 1.0f );
     652             : 
     653             :                 // accumulate normalized load in splitPos...currentPos
     654           0 :                 LBLOG( LOG_LB2 ) << "Computing load in X " << splitPos << "..."
     655           0 :                                  << currentPos << std::endl;
     656           0 :                 float currentTime = 0.f;
     657           0 :                 for( LBDatas::const_iterator i = workingSet.begin();
     658           0 :                      i != workingSet.end(); ++i )
     659             :                 {
     660           0 :                     const Data& data = *i;
     661             : 
     662           0 :                     if( data.vp.x >= currentPos ) // not yet needed data sets
     663           0 :                         break;
     664             : 
     665           0 :                     float yContrib = data.vp.h;
     666           0 :                     if( data.vp.y < vp.y )
     667           0 :                         yContrib -= (vp.y - data.vp.y);
     668             : 
     669           0 :                     const float dataEnd = data.vp.getYEnd();
     670           0 :                     const float vpEnd   = vp.getYEnd();
     671           0 :                     if( dataEnd > vpEnd )
     672           0 :                         yContrib -= (dataEnd - vpEnd);
     673             : 
     674           0 :                     if( yContrib > 0.f )
     675             :                     {
     676           0 :                         const float percentage = ( width / data.vp.w ) *
     677           0 :                                                  ( yContrib / data.vp.h );
     678           0 :                         currentTime += ( data.time * percentage );
     679             : 
     680           0 :                         LBLOG( LOG_LB2 ) << data.vp << " contributes "
     681           0 :                                          << yContrib << " in " << vp.h << " ("
     682           0 :                                          << percentage << ") with " << data.time
     683           0 :                                          << ": " << ( data.time * percentage )
     684           0 :                                          << " vp.y " << vp.y << " dataEnd "
     685           0 :                                          << dataEnd << " vpEnd " << vpEnd
     686           0 :                                          << std::endl;
     687           0 :                         LBASSERT( percentage < 1.01f )
     688             :                     }
     689             :                 }
     690             : 
     691           0 :                 LBLOG( LOG_LB2 ) << splitPos << "..." << currentPos << ": t="
     692           0 :                                  << currentTime << " of " << timeLeft
     693           0 :                                  << std::endl;
     694             : 
     695           0 :                 if( currentTime >= timeLeft ) // found last region
     696             :                 {
     697           0 :                     splitPos += ( width * timeLeft / currentTime );
     698           0 :                     timeLeft = 0.0f;
     699             :                 }
     700             :                 else
     701             :                 {
     702           0 :                     timeLeft -= currentTime;
     703           0 :                     splitPos  = currentPos;
     704             :                 }
     705             :             }
     706             : 
     707           0 :             LBLOG( LOG_LB2 ) << "Should split at X " << splitPos << std::endl;
     708           0 :             if( getDamping() < 1.f )
     709           0 :                 splitPos = (1.f - getDamping()) * splitPos +
     710           0 :                             getDamping() * node->split;
     711           0 :             LBLOG( LOG_LB2 ) << "Dampened split at X " << splitPos << std::endl;
     712             : 
     713             :             // There might be more time left due to MIN_PIXEL rounding by parent
     714             :             // LBASSERTINFO( timeLeft <= .001f, timeLeft );
     715             : 
     716             :             // Ensure minimum size
     717           0 :             const Compound* root = getCompound();
     718             :             const float pvpW = static_cast< float >(
     719           0 :                 root->getInheritPixelViewport().w );
     720           0 :             const float boundary = static_cast< float >( node->boundary2i.x()) /
     721           0 :                                        pvpW;
     722           0 :             if( node->left->resources == 0.f )
     723           0 :                 splitPos = vp.x;
     724           0 :             else if( node->right->resources == 0.f )
     725           0 :                 splitPos = end;
     726           0 :             else if( boundary > 0 )
     727             :             {
     728           0 :                 const float lengthRight = vp.getXEnd() - splitPos;
     729           0 :                 const float lengthLeft = splitPos - vp.x;
     730             :                 const float maxRight =
     731           0 :                     static_cast< float >( node->right->maxSize.x( )) / pvpW;
     732             :                 const float maxLeft =
     733           0 :                     static_cast< float >( node->left->maxSize.x( )) / pvpW;
     734           0 :                 if( lengthRight > maxRight )
     735           0 :                     splitPos = end - maxRight;
     736           0 :                 else if( lengthLeft > maxLeft )
     737           0 :                     splitPos = vp.x + maxLeft;
     738             : 
     739           0 :                 if( (splitPos - vp.x) < boundary )
     740           0 :                     splitPos = vp.x + boundary;
     741           0 :                 if( (end - splitPos) < boundary )
     742           0 :                     splitPos = end - boundary;
     743             : 
     744             :                 const uint32_t ratio =
     745           0 :                            static_cast< uint32_t >( splitPos / boundary + .5f );
     746           0 :                 splitPos = ratio * boundary;
     747             :             }
     748             : 
     749           0 :             splitPos = LB_MAX( splitPos, vp.x );
     750           0 :             splitPos = LB_MIN( splitPos, end);
     751             : 
     752           0 :             const float newPixelW = pvpW * splitPos;
     753           0 :             const float oldPixelW = pvpW * node->split;
     754           0 :             if( int( fabs(newPixelW - oldPixelW) ) < node->resistance2i.x( ))
     755           0 :                 splitPos = node->split;
     756             :             else
     757           0 :                 node->split = splitPos;
     758             : 
     759           0 :             LBLOG( LOG_LB2 ) << "Constrained split " << vp << " at X "
     760           0 :                              << splitPos << std::endl;
     761             : 
     762             :             // balance children
     763           0 :             Viewport childVP = vp;
     764           0 :             childVP.w = (splitPos - vp.x);
     765           0 :             _computeSplit( node->left, leftTime, datas, childVP, range );
     766             : 
     767           0 :             childVP.x = childVP.getXEnd();
     768           0 :             childVP.w = end - childVP.x;
     769             :             // Fix 2994111: Rounding errors with 2D LB and 16 sources
     770             :             //   Floating point rounding may create a width for the 'right'
     771             :             //   child which is slightly below the parent width. Correct it.
     772           0 :             while( childVP.getXEnd() < end )
     773           0 :                 childVP.w += std::numeric_limits< float >::epsilon();
     774           0 :             _computeSplit( node->right, time-leftTime, datas, childVP, range );
     775           0 :             break;
     776             :         }
     777             : 
     778             :         case MODE_HORIZONTAL:
     779             :         {
     780           0 :             LBASSERT( range == Range::ALL );
     781           0 :             float splitPos = vp.y;
     782           0 :             const float end = vp.getYEnd();
     783             : 
     784           0 :             while( timeLeft > std::numeric_limits< float >::epsilon() &&
     785             :                    splitPos < end )
     786             :             {
     787           0 :                 LBLOG( LOG_LB2 ) << timeLeft << "ms left using "
     788           0 :                                  << workingSet.size() << " tiles" << std::endl;
     789             : 
     790             :                 // remove all unrelevant items from working set
     791           0 :                 for( LBDatas::iterator i = workingSet.begin();
     792           0 :                      i != workingSet.end(); )
     793             :                 {
     794           0 :                     const Data& data = *i;
     795           0 :                     if( data.vp.getYEnd() > splitPos )
     796           0 :                         ++i;
     797             :                     else
     798           0 :                         i = workingSet.erase( i );
     799             :                 }
     800           0 :                 if( workingSet.empty( ))
     801           0 :                     break;
     802             : 
     803             :                 // find next 'discontinuouity' in loads
     804           0 :                 float currentPos = 1.0f;
     805           0 :                 for( LBDatas::const_iterator i = workingSet.begin();
     806           0 :                      i != workingSet.end(); ++i )
     807             :                 {
     808           0 :                     const Data& data = *i;
     809           0 :                     if( data.vp.y > splitPos && data.vp.y < currentPos )
     810           0 :                         currentPos = data.vp.y;
     811           0 :                     const float yEnd = data.vp.getYEnd();
     812           0 :                     if( yEnd > splitPos && yEnd < currentPos )
     813           0 :                         currentPos = yEnd;
     814             :                 }
     815             : 
     816           0 :                 const float height = currentPos - splitPos;
     817           0 :                 LBASSERTINFO( height > 0.f, currentPos << "<=" << splitPos );
     818           0 :                 LBASSERT( currentPos <= 1.0f );
     819             : 
     820             :                 // accumulate normalized load in splitPos...currentPos
     821           0 :                 LBLOG( LOG_LB2 ) << "Computing load in Y " << splitPos << "..."
     822           0 :                                 << currentPos << std::endl;
     823           0 :                 float currentTime = 0.f;
     824           0 :                 for( LBDatas::const_iterator i = workingSet.begin();
     825           0 :                      i != workingSet.end(); ++i )
     826             :                 {
     827           0 :                     const Data& data = *i;
     828             : 
     829           0 :                     if( data.vp.y >= currentPos ) // not yet needed data sets
     830           0 :                         break;
     831             : 
     832           0 :                     float xContrib = data.vp.w;
     833             : 
     834           0 :                     if( data.vp.x < vp.x )
     835           0 :                         xContrib -= (vp.x - data.vp.x);
     836             : 
     837           0 :                     const float dataEnd = data.vp.getXEnd();
     838           0 :                     const float vpEnd   = vp.getXEnd();
     839           0 :                     if( dataEnd > vpEnd )
     840           0 :                         xContrib -= (dataEnd - vpEnd);
     841             : 
     842           0 :                     if( xContrib > 0.f )
     843             :                     {
     844           0 :                         const float percentage = ( height / data.vp.h ) *
     845           0 :                                                  ( xContrib / data.vp.w );
     846           0 :                         currentTime += ( data.time * percentage );
     847             : 
     848           0 :                         LBLOG( LOG_LB2 ) << data.vp << " contributes "
     849           0 :                                          << xContrib << " in " << vp.w << " ("
     850           0 :                                          << percentage << ") with " << data.time
     851           0 :                                          << ": " << ( data.time * percentage )
     852           0 :                                          << " total " << currentTime << " vp.x "
     853           0 :                                          << vp.x << " dataEnd " << dataEnd
     854           0 :                                          << " vpEnd " << vpEnd << std::endl;
     855           0 :                         LBASSERT( percentage < 1.01f )
     856             :                     }
     857             :                 }
     858             : 
     859           0 :                 LBLOG( LOG_LB2 ) << splitPos << "..." << currentPos << ": t="
     860           0 :                                  << currentTime << " of " << timeLeft
     861           0 :                                  << std::endl;
     862             : 
     863           0 :                 if( currentTime >= timeLeft ) // found last region
     864             :                 {
     865           0 :                     splitPos += (height * timeLeft / currentTime );
     866           0 :                     timeLeft = 0.0f;
     867             :                 }
     868             :                 else
     869             :                 {
     870           0 :                     timeLeft -= currentTime;
     871           0 :                     splitPos  = currentPos;
     872             :                 }
     873             :             }
     874             : 
     875           0 :             LBLOG( LOG_LB2 ) << "Should split at Y " << splitPos << std::endl;
     876           0 :             if( getDamping() < 1.f )
     877           0 :                 splitPos = (1.f - getDamping( )) * splitPos +
     878           0 :                             getDamping() * node->split;
     879           0 :             LBLOG( LOG_LB2 ) << "Dampened split at Y " << splitPos << std::endl;
     880             : 
     881           0 :             const Compound* root = getCompound();
     882             : 
     883             :             const float pvpH = static_cast< float >(
     884           0 :                 root->getInheritPixelViewport().h );
     885           0 :             const float boundary = static_cast< float >(node->boundary2i.y( )) /
     886           0 :                                        pvpH;
     887             : 
     888           0 :             if( node->left->resources == 0.f )
     889           0 :                 splitPos = vp.y;
     890           0 :             else if( node->right->resources == 0.f )
     891           0 :                 splitPos = end;
     892           0 :             else if ( boundary > 0 )
     893             :             {
     894           0 :                 const float lengthRight = vp.getYEnd() - splitPos;
     895           0 :                 const float lengthLeft = splitPos - vp.y;
     896             :                 const float maxRight =
     897           0 :                     static_cast< float >( node->right->maxSize.y( )) / pvpH;
     898             :                 const float maxLeft =
     899           0 :                     static_cast< float >( node->left->maxSize.y( )) / pvpH;
     900           0 :                 if( lengthRight > maxRight )
     901           0 :                     splitPos = end - maxRight;
     902           0 :                 else if( lengthLeft > maxLeft )
     903           0 :                     splitPos = vp.y + maxLeft;
     904             : 
     905           0 :                 if( (splitPos - vp.y) < boundary )
     906           0 :                     splitPos = vp.y + boundary;
     907           0 :                 if( (end - splitPos) < boundary )
     908           0 :                     splitPos = end - boundary;
     909             : 
     910             :                 const uint32_t ratio =
     911           0 :                            static_cast< uint32_t >( splitPos / boundary + .5f );
     912           0 :                 splitPos = ratio * boundary;
     913             :             }
     914             : 
     915           0 :             splitPos = LB_MAX( splitPos, vp.y );
     916           0 :             splitPos = LB_MIN( splitPos, end );
     917             : 
     918           0 :             const float newPixelH = pvpH * splitPos;
     919           0 :             const float oldPixelH = pvpH * node->split;
     920           0 :             if( int( fabs(newPixelH - oldPixelH) ) < node->resistance2i.y( ))
     921           0 :                 splitPos = node->split;
     922             :             else
     923           0 :                 node->split = splitPos;
     924             : 
     925           0 :             LBLOG( LOG_LB2 ) << "Constrained split " << vp << " at Y "
     926           0 :                              << splitPos << std::endl;
     927             : 
     928           0 :             Viewport childVP = vp;
     929           0 :             childVP.h = (splitPos - vp.y);
     930           0 :             _computeSplit( node->left, leftTime, datas, childVP, range );
     931             : 
     932           0 :             childVP.y = childVP.getYEnd();
     933           0 :             childVP.h = end - childVP.y;
     934           0 :             while( childVP.getYEnd() < end )
     935           0 :                 childVP.h += std::numeric_limits< float >::epsilon();
     936           0 :             _computeSplit( node->right, time - leftTime, datas, childVP, range);
     937           0 :             break;
     938             :         }
     939             : 
     940             :         case MODE_DB:
     941             :         {
     942           0 :             LBASSERT( vp == Viewport::FULL );
     943           0 :             float splitPos = range.start;
     944           0 :             const float end = range.end;
     945             : 
     946           0 :             while( timeLeft > std::numeric_limits< float >::epsilon() &&
     947             :                    splitPos < end )
     948             :             {
     949           0 :                 LBLOG( LOG_LB2 ) << timeLeft << "ms left using "
     950           0 :                                  << workingSet.size() << " tiles" << std::endl;
     951             : 
     952             :                 // remove all irrelevant items from working set
     953           0 :                 for( LBDatas::iterator i = workingSet.begin();
     954           0 :                      i != workingSet.end(); )
     955             :                 {
     956           0 :                     const Data& data = *i;
     957           0 :                     if( data.range.end > splitPos )
     958           0 :                         ++i;
     959             :                     else
     960           0 :                         i = workingSet.erase( i );
     961             :                 }
     962           0 :                 if( workingSet.empty( ))
     963           0 :                     break;
     964             : 
     965             :                 // find next 'discontinouity' in loads
     966           0 :                 float currentPos = 1.0f;
     967           0 :                 for( LBDatas::const_iterator i = workingSet.begin();
     968           0 :                      i != workingSet.end(); ++i )
     969             :                 {
     970           0 :                     const Data& data = *i;
     971           0 :                     currentPos = LB_MIN( currentPos, data.range.end );
     972             :                 }
     973             : 
     974           0 :                 const float size = currentPos - splitPos;
     975           0 :                 LBASSERTINFO( size > 0.f, currentPos << "<=" << splitPos );
     976           0 :                 LBASSERT( currentPos <= 1.0f );
     977             : 
     978             :                 // accumulate normalized load in splitPos...currentPos
     979           0 :                 LBLOG( LOG_LB2 ) << "Computing load in range " << splitPos
     980           0 :                                 << "..." << currentPos << std::endl;
     981           0 :                 float currentTime = 0.f;
     982           0 :                 for( LBDatas::const_iterator i = workingSet.begin();
     983           0 :                      i != workingSet.end(); ++i )
     984             :                 {
     985           0 :                     const Data& data = *i;
     986             : 
     987           0 :                     if( data.range.start >= currentPos ) // not yet needed data
     988           0 :                         break;
     989             : #if 0
     990             :                     // make sure we cover full area
     991             :                     LBASSERTINFO(  data.range.start <= splitPos,
     992             :                                    data.range.start << " > " << splitPos );
     993             :                     LBASSERTINFO( data.range.end >= currentPos,
     994             :                                   data.range.end << " < " << currentPos);
     995             : #endif
     996           0 :                     currentTime += data.time * size / data.range.getSize();
     997             :                 }
     998             : 
     999           0 :                 LBLOG( LOG_LB2 ) << splitPos << "..." << currentPos << ": t="
    1000           0 :                                  << currentTime << " of " << timeLeft
    1001           0 :                                  << std::endl;
    1002             : 
    1003           0 :                 if( currentTime >= timeLeft ) // found last region
    1004             :                 {
    1005           0 :                     const float width = currentPos - splitPos;
    1006           0 :                     splitPos += (width * timeLeft / currentTime );
    1007           0 :                     timeLeft = 0.0f;
    1008             :                 }
    1009             :                 else
    1010             :                 {
    1011           0 :                     timeLeft -= currentTime;
    1012           0 :                     splitPos  = currentPos;
    1013             :                 }
    1014             :             }
    1015           0 :             LBLOG( LOG_LB2 ) << "Should split at " << splitPos << std::endl;
    1016           0 :             if( getDamping() < 1.f )
    1017           0 :                 splitPos = (1.f - getDamping( )) * splitPos +
    1018           0 :                             getDamping() * node->split;
    1019           0 :             LBLOG( LOG_LB2 ) << "Dampened split at " << splitPos << std::endl;
    1020             : 
    1021           0 :             const float boundary( node->boundaryf );
    1022           0 :             if( node->left->resources == 0.f )
    1023           0 :                 splitPos = range.start;
    1024           0 :             else if( node->right->resources == 0.f )
    1025           0 :                 splitPos = end;
    1026             : 
    1027             :             const uint32_t ratio = static_cast< uint32_t >
    1028           0 :                       ( splitPos / boundary + .5f );
    1029           0 :             splitPos = ratio * boundary;
    1030           0 :             if( (splitPos - range.start) < boundary )
    1031           0 :                 splitPos = range.start;
    1032           0 :             if( (end - splitPos) < boundary )
    1033           0 :                 splitPos = end;
    1034             : 
    1035           0 :             if( fabs( splitPos - node->split ) < node->resistancef )
    1036           0 :                 splitPos = node->split;
    1037             :             else
    1038           0 :                 node->split = splitPos;
    1039             : 
    1040           0 :             LBLOG( LOG_LB2 ) << "Constrained split " << range << " at pos "
    1041           0 :                              << splitPos << std::endl;
    1042             : 
    1043           0 :             Range childRange = range;
    1044           0 :             childRange.end = splitPos;
    1045           0 :             _computeSplit( node->left, leftTime, datas, vp, childRange );
    1046             : 
    1047           0 :             childRange.start = childRange.end;
    1048           0 :             childRange.end   = range.end;
    1049           0 :             _computeSplit( node->right, time - leftTime, datas, vp, childRange);
    1050           0 :             break;
    1051             :         }
    1052             : 
    1053             :         default:
    1054           0 :             LBUNIMPLEMENTED;
    1055           0 :     }
    1056             : }
    1057             : 
    1058           0 : void LoadEqualizer::_assign( Compound* compound, const Viewport& vp,
    1059             :                              const Range& range )
    1060             : {
    1061           0 :     LBASSERTINFO( vp == Viewport::FULL || range == Range::ALL,
    1062             :                   "Mixed 2D/DB load-balancing not implemented" );
    1063             : 
    1064           0 :     compound->setViewport( vp );
    1065           0 :     compound->setRange( range );
    1066           0 :     LBLOG( LOG_LB2 ) << compound->getChannel()->getName() << " set " << vp
    1067           0 :                      << ", " << range << std::endl;
    1068             : 
    1069           0 :     if( getDamping() >= 1.f )
    1070           0 :         return;
    1071             : 
    1072             :     // save data for later use
    1073           0 :     Data data;
    1074           0 :     data.vp      = vp;
    1075           0 :     data.range   = range;
    1076           0 :     data.channel = compound->getChannel();
    1077           0 :     data.taskID  = compound->getTaskID();
    1078             : 
    1079           0 :     const Compound* destCompound = getCompound();
    1080           0 :     if( destCompound->getChannel() == compound->getChannel( ))
    1081           0 :         data.destTaskID  = destCompound->getTaskID();
    1082             : 
    1083           0 :     LBASSERT( data.taskID > 0 );
    1084             : 
    1085           0 :     if( !vp.hasArea() || !range.hasData( )) // will not render
    1086           0 :         data.time = 0;
    1087             : 
    1088           0 :     LBFrameData& frameData = _history.back();
    1089           0 :     LBDatas& items = frameData.second;
    1090             : 
    1091           0 :     items.push_back( data );
    1092             : }
    1093             : 
    1094           0 : std::ostream& operator << ( std::ostream& os, const LoadEqualizer::Node* node )
    1095             : {
    1096           0 :     if( !node )
    1097           0 :         return os;
    1098             : 
    1099           0 :     os << lunchbox::disableFlush;
    1100             : 
    1101           0 :     if( node->compound )
    1102           0 :         os << node->compound->getChannel()->getName() << " resources "
    1103           0 :            << node->resources << " max size " << node->maxSize << std::endl;
    1104             :     else
    1105           0 :         os << "split " << node->mode << " @ " << node->split << " resources "
    1106           0 :            << node->resources << " max size " << node->maxSize  << std::endl
    1107           0 :            << lunchbox::indent << node->left << node->right << lunchbox::exdent;
    1108             : 
    1109           0 :     os << lunchbox::enableFlush;
    1110           0 :     return os;
    1111             : }
    1112             : 
    1113         142 : std::ostream& operator << ( std::ostream& os, const LoadEqualizer* lb )
    1114             : {
    1115         142 :     if( !lb )
    1116           0 :         return os;
    1117             : 
    1118         142 :     os << lunchbox::disableFlush
    1119         142 :        << "load_equalizer" << std::endl
    1120         142 :        << '{' << std::endl
    1121         284 :        << "    mode    " << lb->getMode() << std::endl;
    1122             : 
    1123         142 :     if( lb->getDamping() != 0.5f )
    1124          34 :         os << "    damping " << lb->getDamping() << std::endl;
    1125             : 
    1126         142 :     if( lb->getBoundary2i() != Vector2i( 1, 1 ) )
    1127           0 :         os << "    boundary [ " << lb->getBoundary2i().x() << " "
    1128           0 :            << lb->getBoundary2i().y() << " ]" << std::endl;
    1129             : 
    1130         142 :     if( lb->getBoundaryf() != std::numeric_limits<float>::epsilon() )
    1131           0 :         os << "    boundary " << lb->getBoundaryf() << std::endl;
    1132             : 
    1133         142 :     if( lb->getResistance2i() != Vector2i( 0, 0 ) )
    1134           0 :         os << "    resistance [ " << lb->getResistance2i().x() << " "
    1135           0 :            << lb->getResistance2i().y() << " ]" << std::endl;
    1136             : 
    1137         142 :     if( lb->getResistancef() != .0f )
    1138           0 :         os << "    resistance " << lb->getResistancef() << std::endl;
    1139             : 
    1140         142 :     os << '}' << std::endl << lunchbox::enableFlush;
    1141         142 :     return os;
    1142             : }
    1143             : 
    1144             : }
    1145          27 : }

Generated by: LCOV version 1.10