loadEqualizer.cpp

00001 
00002 /* Copyright (c) 2008-2009, Stefan Eilemann <eile@equalizergraphics.com> 
00003  *
00004  * This library is free software; you can redistribute it and/or modify it under
00005  * the terms of the GNU Lesser General Public License version 2.1 as published
00006  * by the Free Software Foundation.
00007  *  
00008  * This library is distributed in the hope that it will be useful, but WITHOUT
00009  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00010  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
00011  * details.
00012  * 
00013  * You should have received a copy of the GNU Lesser General Public License
00014  * along with this library; if not, write to the Free Software Foundation, Inc.,
00015  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00016  */
00017 
00018 #include "loadEqualizer.h"
00019 
00020 #include "../compound.h"
00021 #include "../log.h"
00022 
00023 #include <eq/client/client.h>
00024 #include <eq/client/server.h>
00025 #include <eq/base/debug.h>
00026 
00027 using namespace eq::base;
00028 using namespace std;
00029 
00030 namespace eq
00031 {
00032 namespace server
00033 {
00034 
00035 #define MIN_PIXELS 8
00036 
00037 std::ostream& operator << ( std::ostream& os, const LoadEqualizer::Node* );
00038 
00039 // The tree load balancer organizes the children in a binary tree. At each
00040 // level, a relative split position is determined by balancing the left subtree
00041 // against the right subtree.
00042 
00043 LoadEqualizer::LoadEqualizer()
00044         : _mode( MODE_2D )
00045         , _damping( .5f )
00046         , _tree( 0 )
00047 {
00048     EQINFO << "New LoadEqualizer @" << (void*)this << endl;
00049 }
00050 
00051 LoadEqualizer::LoadEqualizer( const LoadEqualizer& from )
00052         : Equalizer( from )
00053         , ChannelListener( from )
00054         , _mode( from._mode )
00055         , _damping( from._damping )
00056         , _tree( 0 )
00057 {}
00058 
00059 LoadEqualizer::~LoadEqualizer()
00060 {
00061     _clearTree( _tree );
00062     delete _tree;
00063     _tree = 0;
00064 
00065     _history.clear();
00066 }
00067 
00068 void LoadEqualizer::notifyUpdatePre( Compound* compound,
00069                                      const uint32_t frameNumber )
00070 {
00071     if( !_tree )
00072     {
00073         EQASSERT( compound == getCompound( ));
00074         const CompoundVector& children = compound->getChildren();
00075         if( children.empty( )) // leaf compound, can't do anything.
00076             return;
00077 
00078         _tree = _buildTree( children );
00079         EQLOG( LOG_LB2 ) << "LB tree: " << _tree;
00080     }
00081     _checkHistory();
00082 
00083     if( isFrozen( )) // execute code above to not leak memory
00084         return;
00085 
00086     // compute new data
00087     _history.push_back( LBFrameData( ));
00088     _history.back().first = frameNumber;
00089 
00090     _computeSplit();
00091 }
00092 
00093 LoadEqualizer::Node* LoadEqualizer::_buildTree( const CompoundVector& compounds)
00094 {
00095     Node* node = new Node;
00096 
00097     if( compounds.size() == 1 )
00098     {
00099         Compound*                compound = compounds[0];
00100 
00101         node->compound  = compound;
00102         node->splitMode = ( _mode == MODE_2D ) ? MODE_VERTICAL : _mode;
00103 
00104         Channel* channel = compound->getChannel();
00105         EQASSERT( channel );
00106         channel->addListener( this );
00107         return node;
00108     }
00109 
00110     const size_t middle = compounds.size() / 2;
00111 
00112     CompoundVector left;
00113     for( size_t i = 0; i < middle; ++i )
00114         left.push_back( compounds[i] );
00115 
00116     CompoundVector right;
00117     for( size_t i = middle; i < compounds.size(); ++i )
00118         right.push_back( compounds[i] );
00119 
00120     node->left  = _buildTree( left );
00121     node->right = _buildTree( right );
00122 
00123     if( _mode == MODE_2D )
00124         node->splitMode = ( node->right->splitMode == MODE_VERTICAL ) ? 
00125                               MODE_HORIZONTAL : MODE_VERTICAL;
00126     else
00127         node->splitMode = _mode;
00128 
00129     node->time      = 0.0f;
00130     return node;
00131 }
00132 
00133 void LoadEqualizer::_clearTree( Node* node )
00134 {
00135     if( !node )
00136         return;
00137 
00138     if( node->left )
00139         _clearTree( node->left );
00140     if( node->right )
00141         _clearTree( node->right );
00142 
00143     if( node->compound )
00144     {
00145         Channel* channel = node->compound->getChannel();
00146         EQASSERTINFO( channel, node->compound );
00147         channel->removeListener( this );
00148     }
00149 }
00150 
00151 void LoadEqualizer::notifyLoadData( Channel* channel,
00152                                     const uint32_t frameNumber,
00153                                     const uint32_t nStatistics,
00154                                     const eq::Statistic* statistics )
00155 {
00156     for( deque< LBFrameData >::iterator i = _history.begin();
00157          i != _history.end(); ++i )
00158     {
00159         LBFrameData& frameData = *i;
00160         if( frameData.first != frameNumber )
00161             continue;
00162 
00163         // Found corresponding historical data set
00164         LBDataVector& items = frameData.second;
00165         for( LBDataVector::iterator j = items.begin(); j != items.end(); ++j )
00166         {
00167             Data& data = *j;
00168             if( data.channel != channel )
00169                 continue;
00170 
00171             // Found corresponding historical data item
00172             const uint32_t taskID = data.taskID;
00173             EQASSERTINFO( taskID > 0, channel->getName( ));
00174 
00175             if( data.vp.getArea() <= 0.f )
00176                 return;
00177 
00178             // gather relevant load data
00179             int64_t startTime = numeric_limits< int64_t >::max();
00180             int64_t endTime   = 0;
00181             bool    loadSet   = false;
00182 
00183             for( uint32_t k = 0; k < nStatistics && !loadSet; ++k )
00184             {
00185                 const eq::Statistic& stat = statistics[k];
00186                 if( stat.task != taskID ) // from different compound
00187                     continue;
00188 
00189                 switch( stat.type )
00190                 {
00191                     case eq::Statistic::CHANNEL_CLEAR:
00192                     case eq::Statistic::CHANNEL_DRAW:
00193                     case eq::Statistic::CHANNEL_READBACK:
00194                         startTime = EQ_MIN( startTime, stat.startTime );
00195                         endTime   = EQ_MAX( endTime, stat.endTime );
00196                         break;
00197                 
00198                     // assemble blocks on input frames, stop using subsequent
00199                     // data
00200                     case eq::Statistic::CHANNEL_ASSEMBLE:
00201                         loadSet = true;
00202                         break;
00203                 
00204                     default:
00205                         break;
00206                 }
00207             }
00208     
00209             if( startTime == numeric_limits< int64_t >::max( ))
00210                 return;
00211     
00212             data.time = endTime - startTime;
00213             data.time = EQ_MAX( data.time, 1 );
00214             data.load = static_cast< float >( data.time ) / data.vp.getArea();
00215             EQLOG( LOG_LB2 ) << "Added load " << data.load << " (t=" << data.time
00216                             << ") for " << channel->getName() << " " << data.vp
00217                             << ", " << data.range << " @ " << frameNumber
00218                             << std::endl;
00219             return;
00220 
00221             // Note: if the same channel is used twice as a child, the 
00222             // load-compound association does not work.
00223         }
00224     }
00225 }
00226 
00227 void LoadEqualizer::_checkHistory()
00228 {
00229     // 1. Find youngest complete load data set
00230     uint32_t useFrame = 0;
00231     for( std::deque< LBFrameData >::reverse_iterator i = _history.rbegin();
00232          i != _history.rend() && useFrame == 0; ++i )
00233     {
00234         const LBFrameData&  frameData  = *i;
00235         const LBDataVector& items      = frameData.second;
00236         bool                isComplete = true;
00237 
00238         for( LBDataVector::const_iterator j = items.begin();
00239              j != items.end() && isComplete; ++j )
00240         {
00241             const Data& data = *j;
00242 
00243             if( data.time < 0 )
00244                 isComplete = false;
00245         }
00246 
00247         if( isComplete )
00248             useFrame = frameData.first;
00249     }
00250 
00251     // delete old, unneeded data sets
00252     while( !_history.empty() && _history.front().first < useFrame )
00253         _history.pop_front();
00254     
00255     if( _history.empty( )) // insert fake set
00256     {
00257         _history.resize( 1 );
00258 
00259         LBFrameData&  frameData  = _history.front();
00260         LBDataVector& items      = frameData.second;
00261 
00262         frameData.first = 0; // frameNumber
00263         items.resize( 1 );
00264         
00265         Data& data = items.front();
00266         data.time = 1;
00267         data.load = 1.f;
00268         EQASSERT( data.taskID == 0 );
00269         EQASSERT( data.channel == 0 );
00270     }
00271 }
00272 
00273 void LoadEqualizer::_computeSplit()
00274 {
00275     EQASSERT( !_history.empty( ));
00276     
00277     const LBFrameData& frameData = _history.front();
00278     const Compound* compound = getCompound();
00279     EQLOG( LOG_LB2 ) << "----- balance " << compound->getChannel()->getName()
00280                     << " using frame " << frameData.first << std::endl;
00281 
00282     // sort load items for each of the split directions
00283     LBDataVector items( frameData.second );
00284     _removeEmpty( items );
00285 
00286     LBDataVector sortedData[3] = { items, items, items };
00287 
00288     if( _mode == MODE_DB )
00289     {
00290         LBDataVector& rangeData = sortedData[ MODE_DB ];
00291         sort( rangeData.begin(), rangeData.end(), _compareRange );
00292     }
00293     else
00294     {
00295         LBDataVector& xData = sortedData[ MODE_VERTICAL ];
00296         sort( xData.begin(), xData.end(), _compareX );
00297 
00298         LBDataVector& yData = sortedData[ MODE_HORIZONTAL ];
00299         sort( yData.begin(), yData.end(), _compareY );
00300 
00301 #ifndef NDEBUG
00302         for( LBDataVector::const_iterator i = xData.begin(); i != xData.end();
00303              ++i )
00304         {  
00305             const Data& data = *i;
00306             EQLOG( LOG_LB2 ) << "  " << data.vp << ", load " << data.load 
00307                             << " (t=" << data.load * data.vp.getArea() << ")"
00308                             << std::endl;
00309         }
00310 #endif
00311     }
00312 
00313 
00314     // Compute total rendering time
00315     int64_t totalTime = 0;
00316     for( LBDataVector::const_iterator i = items.begin(); i != items.end(); ++i )
00317     {  
00318         const Data& data = *i;
00319         totalTime += data.time;
00320     }
00321 
00322     const CompoundVector& children = compound->getChildren();
00323     float nResources( 0.f );
00324     for( CompoundVector::const_iterator i = children.begin(); 
00325          i != children.end(); ++i )
00326     {
00327         nResources += (*i)->getUsage();
00328     }
00329 
00330     const float timeLeft = static_cast< float >( totalTime ) /
00331                            static_cast< float >( nResources );
00332     EQLOG( LOG_LB2 ) << "Render time " << totalTime << ", per resource "
00333                     << timeLeft << ", " << nResources << " resources" << endl;
00334 
00335     const float leftover = _assignTargetTimes( _tree, 
00336                                                static_cast<float>( totalTime ),
00337                                                timeLeft );
00338     _assignLeftoverTime( _tree, leftover );
00339     _computeSplit( _tree, sortedData, eq::Viewport(), eq::Range() );
00340 }
00341 
00342 float LoadEqualizer::_assignTargetTimes( Node* node, const float totalTime, 
00343                                          const float resourceTime )
00344 {
00345     const Compound* compound = node->compound;
00346     if( compound )
00347     {
00348         const float usage = compound->getUsage();
00349         float time = resourceTime * usage;
00350 
00351         if( usage > 0.0f )
00352         {
00353             EQASSERT( _damping >= 0.f );
00354             EQASSERT( _damping <= 1.f );
00355 
00356             const LBFrameData&  frameData = _history.front();
00357             const LBDataVector& items     = frameData.second;
00358             for( LBDataVector::const_iterator i = items.begin(); 
00359                  i != items.end(); ++i )
00360             {
00361                 const Data& data = *i;
00362                 const uint32_t taskID = data.taskID;
00363                 
00364                 if( compound->getTaskID() != taskID )
00365                     continue;
00366 
00367                 // found our last rendering time -> use it to smooth the change:
00368                 time = (1.f - _damping) * time + _damping * data.time;
00369                 break;
00370             }
00371         }
00372 
00373         node->time  = EQ_MIN( time, totalTime );
00374         node->usage = usage;
00375         EQLOG( LOG_LB2 ) << compound->getChannel()->getName() << " usage " 
00376                         << compound->getUsage() << " target " << node->time
00377                         << ", left " << totalTime - node->time << std::endl;
00378 
00379         return totalTime - node->time;
00380     }
00381 
00382     EQASSERT( node->left );
00383     EQASSERT( node->right );
00384 
00385     float timeLeft = _assignTargetTimes( node->left, totalTime, resourceTime );
00386     timeLeft       = _assignTargetTimes( node->right, timeLeft, resourceTime );
00387     node->time  = node->left->time + node->right->time;
00388     node->usage = node->left->usage + node->right->usage;
00389     
00390     EQLOG( LOG_LB2 ) << "Node time " << node->time << ", left " << timeLeft
00391                     << endl;
00392     return timeLeft;
00393 }
00394 
00395 void LoadEqualizer::_assignLeftoverTime( Node* node, const float time )
00396 {
00397     const Compound* compound = node->compound;
00398     if( compound )
00399     {
00400         node->time += time;
00401         EQLOG( LOG_LB2 ) << compound->getChannel()->getName() << " usage " 
00402                         << compound->getUsage() << " target " << node->time
00403                         << std::endl;
00404         EQASSERTINFO( node->usage > 0.0f || node->time <= 0.f,
00405                       node->usage << ", " << node->time );
00406     }
00407     else
00408     {
00409         EQASSERT( node->left );
00410         EQASSERT( node->right );
00411 
00412         if( node->usage > 0.f )
00413         {
00414             float leftTime = time * node->left->usage / node->usage;
00415             float rightTime = time - leftTime;
00416             if( time - leftTime < 0.0001f )
00417             {
00418                 leftTime = time;
00419                 rightTime = 0.f;
00420             }
00421             else if( time - rightTime < 0.0001f )
00422             {
00423                 leftTime = 0.f;
00424                 rightTime = time;
00425             }
00426 
00427             _assignLeftoverTime( node->left, leftTime );
00428             _assignLeftoverTime( node->right, rightTime );
00429             node->time = node->left->time + node->right->time;
00430         }
00431         else
00432         {
00433             EQASSERTINFO( time <= 10.f * std::numeric_limits<float>::epsilon(),
00434                           time );
00435         }
00436     }
00437 }
00438 
00439 void LoadEqualizer::_removeEmpty( LBDataVector& items )
00440 {
00441     for( LBDataVector::iterator i = items.begin(); i != items.end(); )
00442     {  
00443         Data& data = *i;
00444 
00445         if( !data.vp.hasArea() || !data.range.hasData( ))
00446             i = items.erase( i );
00447         else
00448             ++i;
00449     }
00450 }
00451 
00452 void LoadEqualizer::_computeSplit( Node* node, LBDataVector* sortedData,
00453                                    const eq::Viewport& vp,
00454                                    const eq::Range& range )
00455 {
00456     const float time = node->time;
00457     EQLOG( LOG_LB2 ) << "_computeSplit " << vp << ", " << range << " time "
00458                     << time << endl;
00459     EQASSERTINFO( vp.isValid(), vp );
00460     EQASSERTINFO( range.isValid(), range );
00461     EQASSERTINFO( node->usage > 0.f || !vp.hasArea() || !range.hasData(),
00462                   "Assigning work to unused compound" );
00463 
00464     Compound* compound = node->compound;
00465     if( compound )
00466     {
00467         EQASSERTINFO( vp == eq::Viewport::FULL || range == eq::Range::ALL,
00468                       "Mixed 2D/DB load-balancing not implemented" );
00469 
00470         // TODO: check that time == vp * load
00471         compound->setViewport( vp );
00472         compound->setRange( range );
00473 
00474         EQLOG( LOG_LB2 ) << compound->getChannel()->getName() << " set "
00475                         << vp << ", " << range << endl;
00476 
00477         // save data for later use
00478         Data data;
00479         data.vp      = vp;
00480         data.range   = range;
00481         data.channel = compound->getChannel();
00482         data.taskID  = compound->getTaskID();
00483         EQASSERT( data.taskID > 0 );
00484 
00485         if( !vp.hasArea() || !range.hasData( )) // will not render
00486             data.time = 0;
00487 
00488         LBFrameData&  frameData = _history.back();
00489         LBDataVector& items     = frameData.second;
00490 
00491         items.push_back( data );
00492         return;
00493     }
00494 
00495     EQASSERT( node->left && node->right );
00496 
00497     switch( node->splitMode )
00498     {
00499         case MODE_VERTICAL:
00500         {
00501             EQASSERT( range == eq::Range::ALL );
00502 
00503             float          timeLeft = node->left->time;
00504             float          splitPos = vp.x;
00505             const float    end      = vp.getXEnd();
00506             LBDataVector workingSet = sortedData[ MODE_VERTICAL ];
00507 
00508             while( timeLeft > std::numeric_limits< float >::epsilon() &&
00509                    splitPos < end && !workingSet.empty())
00510             {
00511                 EQLOG( LOG_LB2 ) << timeLeft << "ms left for "
00512                                 << workingSet.size() << " tiles" << endl;
00513 
00514                 // remove all irrelevant items from working set
00515                 //   Is there a more clever way? Erasing invalidates iter, even
00516                 //   if iter is copied and inc'd beforehand.
00517                 LBDataVector newSet;
00518                 for( LBDataVector::const_iterator i = workingSet.begin();
00519                      i != workingSet.end(); ++i )
00520                 {
00521                     const Data& data = *i;
00522                     if( data.vp.getXEnd() > splitPos )
00523                         newSet.push_back( data );
00524                 }
00525                 workingSet.swap( newSet );
00526                 EQASSERT( !workingSet.empty( ));
00527 
00528                 // find next 'discontinouity' in loads
00529                 float currentPos = 1.0f;
00530                 for( LBDataVector::const_iterator i = workingSet.begin();
00531                      i != workingSet.end(); ++i )
00532                 {
00533                     const Data& data = *i;                        
00534                     currentPos = EQ_MIN( currentPos, data.vp.getXEnd( ));
00535                 }
00536 
00537                 EQASSERTINFO( currentPos > splitPos,
00538                               currentPos << "<=" << splitPos );
00539                 EQASSERT( currentPos <= 1.0f );
00540 
00541                 // accumulate normalized load in splitPos...currentPos
00542                 EQLOG( LOG_LB2 ) << "Computing load in X " << splitPos << "..."
00543                                 << currentPos << endl;
00544                 float currentLoad = 0.f;
00545                 for( LBDataVector::const_iterator i = workingSet.begin();
00546                      i != workingSet.end(); ++i )
00547                 {
00548                     const Data& data = *i;
00549                         
00550                     if( data.vp.x >= currentPos ) // not yet needed data sets
00551                         break;
00552 #if 0
00553                     // make sure we cover full area
00554                     EQASSERTINFO(  data.vp.x <= splitPos, data.vp.x << " > "
00555                                    << splitPos );
00556                     EQASSERTINFO( data.vp.getXEnd() >= currentPos, 
00557                                   data.vp.getXEnd() << " < " << currentPos);
00558 #endif
00559                     float       yContrib = data.vp.h;
00560 
00561                     if( data.vp.y < vp.y )
00562                         yContrib -= (vp.y - data.vp.y);
00563                     
00564                     const float dataEnd = data.vp.getYEnd();
00565                     const float vpEnd   = vp.getYEnd();
00566                     if( dataEnd > vpEnd )
00567                         yContrib -= (dataEnd - vpEnd);
00568 
00569                     if( yContrib > 0.f )
00570                     {
00571                         const float percentage = yContrib / vp.h;
00572                         EQLOG( LOG_LB2 ) << data.vp << " contributes "
00573                                         << yContrib << " of " << data.vp.h
00574                                         << " (" << percentage
00575                                         << ") with " << data.load << ": "
00576                                         << ( data.load * percentage )
00577                                         << " vp.y " << vp.y << " dataEnd " 
00578                                         << dataEnd << " vpEnd " << vpEnd
00579                                         << endl;
00580 
00581                         currentLoad += ( data.load * percentage );
00582                     }
00583                 }
00584 
00585                 const float width        = currentPos - splitPos;
00586                 const float area         = width * vp.h;
00587                 const float currentTime  = area * currentLoad;
00588                     
00589                 EQLOG( LOG_LB2 ) << splitPos << "..." << currentPos 
00590                                 << ": t=" << currentTime << " of " 
00591                                 << timeLeft << endl;
00592 
00593                 if( currentTime >= timeLeft ) // found last region
00594                 {
00595                     splitPos += (width * timeLeft / currentTime );
00596                     timeLeft = 0.0f;
00597                 }
00598                 else
00599                 {
00600                     timeLeft -= currentTime;
00601                     splitPos  = currentPos;
00602                 }
00603             }
00604 
00605             EQLOG( LOG_LB2 ) << "Should split at X " << splitPos << endl;
00606             // There might be more time left due to MIN_PIXEL rounding by parent
00607             // EQASSERTINFO( timeLeft <= .001f, timeLeft );
00608 
00609             // Ensure minimum size
00610             const Compound* root = getCompound();
00611             if( node->left->usage == 0.f )
00612                 splitPos = vp.x;
00613             else if( node->right->usage == 0.f )
00614                 splitPos = end;
00615 #ifdef MIN_PIXELS
00616             else
00617             {
00618                 const float     epsilon = static_cast< float >( MIN_PIXELS ) /
00619                                               root->getInheritPixelViewport().w;
00620 
00621                 if( (splitPos - vp.x) < epsilon )
00622                     splitPos = vp.x + epsilon;
00623                 if( (end - splitPos) < epsilon )
00624                     splitPos = end - epsilon;
00625             }
00626 #endif
00627             const float epsilon = 1.0f / root->getInheritPixelViewport().w;
00628             if( (splitPos - vp.x) < epsilon )
00629                 splitPos = vp.x;
00630             if( (end - splitPos) < epsilon )
00631                 splitPos = end;
00632 
00633             splitPos = EQ_MAX( splitPos, vp.x );
00634             splitPos = EQ_MIN( splitPos, end);
00635 
00636             EQLOG( LOG_LB2 ) << "Split " << vp << " at X " << splitPos << endl;
00637 
00638             // balance children
00639             eq::Viewport childVP = vp;
00640             childVP.w = (splitPos - vp.x);
00641             _computeSplit( node->left, sortedData, childVP, range );
00642 
00643             childVP.x = childVP.getXEnd();
00644             childVP.w = end - childVP.x;
00645             _computeSplit( node->right, sortedData, childVP, range );
00646             break;
00647         }
00648 
00649         case MODE_HORIZONTAL:
00650         {
00651             EQASSERT( range == eq::Range::ALL );
00652             float        timeLeft = node->left->time;
00653             float        splitPos = vp.y;
00654             const float  end      = vp.getYEnd();
00655             LBDataVector workingSet = sortedData[ MODE_HORIZONTAL ];
00656 
00657             while( timeLeft > std::numeric_limits< float >::epsilon() &&
00658                    splitPos < end && !workingSet.empty( ))
00659             {
00660                 EQLOG( LOG_LB2 ) << timeLeft << "ms left for "
00661                                 << workingSet.size() << " tiles" << endl;
00662 
00663                 // remove all unrelevant items from working set
00664                 //   Is there a more clever way? Erasing invalidates iter, even
00665                 //   if iter is copied and inc'd beforehand.
00666                 LBDataVector newSet;
00667                 for( LBDataVector::const_iterator i = workingSet.begin();
00668                      i != workingSet.end(); ++i )
00669                 {
00670                     const Data& data = *i;
00671                     if( data.vp.getYEnd() > splitPos )
00672                         newSet.push_back( data );
00673                 }
00674                 workingSet.swap( newSet );
00675                 EQASSERT( !workingSet.empty( ));
00676 
00677                 // find next 'discontinouity' in loads
00678                 float currentPos = 1.0f;
00679                 for( LBDataVector::const_iterator i = workingSet.begin();
00680                      i != workingSet.end(); ++i )
00681                 {
00682                     const Data& data = *i;                        
00683                     currentPos = EQ_MIN( currentPos, data.vp.getYEnd( ));
00684                 }
00685 
00686                 EQASSERTINFO( currentPos > splitPos,
00687                               currentPos << "<=" << splitPos );
00688                 EQASSERT( currentPos <= 1.0f );
00689 
00690                 // accumulate normalized load in splitPos...currentPos
00691                 EQLOG( LOG_LB2 ) << "Computing load in Y " << splitPos << "..."
00692                                 << currentPos << endl;
00693                 float currentLoad = 0.f;
00694                 for( LBDataVector::const_iterator i = workingSet.begin();
00695                      i != workingSet.end(); ++i )
00696                 {
00697                     const Data& data = *i;
00698                         
00699                     if( data.vp.y >= currentPos ) // not yet needed data sets
00700                         break;
00701 
00702                     float xContrib = data.vp.w;
00703 
00704                     if( data.vp.x < vp.x )
00705                         xContrib -= (vp.x - data.vp.x);
00706                     
00707                     const float dataEnd = data.vp.getXEnd();
00708                     const float vpEnd   = vp.getXEnd();
00709                     if( dataEnd > vpEnd )
00710                         xContrib -= (dataEnd - vpEnd);
00711                     
00712                     if( xContrib > 0.f )
00713                     {
00714                         const float percentage = xContrib / vp.w;
00715                         EQLOG( LOG_LB2 ) << data.vp << " contributes "
00716                                         << xContrib << " of " << data.vp.w
00717                                         << " (" << percentage
00718                                         << ") with " << data.load << ": "
00719                                         << ( data.load * percentage )
00720                                         << " vp.x " << vp.x << " dataEnd " 
00721                                         << dataEnd << " vpEnd " << vpEnd
00722                                         << endl;
00723 
00724                         currentLoad += ( data.load * percentage );
00725                     }
00726                 }
00727 
00728                 const float height       = currentPos - splitPos;
00729                 const float area         = height * vp.w;
00730                 const float currentTime  = area * currentLoad;
00731                     
00732                 EQLOG( LOG_LB2 ) << splitPos << "..." << currentPos 
00733                                 << ": t=" << currentTime << " of " 
00734                                 << timeLeft << endl;
00735 
00736                 if( currentTime >= timeLeft ) // found last region
00737                 {
00738                     splitPos += (height * timeLeft / currentTime );
00739                     timeLeft = 0.0f;
00740                 }
00741                 else
00742                 {
00743                     timeLeft -= currentTime;
00744                     splitPos  = currentPos;
00745                 }
00746             }
00747 
00748             EQLOG( LOG_LB2 ) << "Should split at Y " << splitPos << endl;
00749             // There might be more time left due to MIN_PIXEL rounding by parent
00750             // EQASSERTINFO( timeLeft <= .001f, timeLeft );
00751 
00752             const Compound* root = getCompound();
00753             if( node->left->usage == 0.f )
00754                 splitPos = vp.y;
00755             else if( node->right->usage == 0.f )
00756                 splitPos = end;
00757 #ifdef MIN_PIXELS
00758             else
00759             {
00760                 const float     epsilon = static_cast< float >( MIN_PIXELS ) /
00761                                               root->getInheritPixelViewport().h;
00762 
00763                 if( (splitPos - vp.y) < epsilon )
00764                     splitPos = vp.y + epsilon;
00765                 if( (end - splitPos) < epsilon )
00766                     splitPos = end - epsilon;
00767             }
00768 #endif
00769             const float epsilon = 1.0f / root->getInheritPixelViewport().h;
00770             if( (splitPos - vp.y) < epsilon )
00771                 splitPos = vp.y;
00772             if( (end - splitPos) < epsilon )
00773                 splitPos = end;
00774 
00775             splitPos = EQ_MAX( splitPos, vp.y );
00776             splitPos = EQ_MIN( splitPos, end );
00777 
00778             EQLOG( LOG_LB2 ) << "Split " << vp << " at Y " << splitPos << endl;
00779 
00780             eq::Viewport childVP = vp;
00781             childVP.h = (splitPos - vp.y);
00782             _computeSplit( node->left, sortedData, childVP, range );
00783 
00784             childVP.y = childVP.getYEnd();
00785             childVP.h = end - childVP.y;
00786             _computeSplit( node->right, sortedData, childVP, range );
00787             break;
00788         }
00789 
00790         case MODE_DB:
00791         {
00792             EQASSERT( vp == eq::Viewport::FULL );
00793             float          timeLeft = node->left->time;
00794             float          splitPos = range.start;
00795             const float    end      = range.end;
00796             LBDataVector workingSet = sortedData[ MODE_DB ];
00797 
00798             while( timeLeft > std::numeric_limits< float >::epsilon() && 
00799                    splitPos < end && !workingSet.empty( ))
00800             {
00801                 EQLOG( LOG_LB2 ) << timeLeft << "ms left for "
00802                                 << workingSet.size() << " tiles" << endl;
00803 
00804                 // remove all irrelevant items from working set
00805                 //   Is there a more clever way? Erasing invalidates iter, even
00806                 //   if iter is copied and inc'd beforehand.
00807                 LBDataVector newSet;
00808                 for( LBDataVector::const_iterator i = workingSet.begin();
00809                      i != workingSet.end(); ++i )
00810                 {
00811                     const Data& data = *i;
00812                     if( data.range.end > splitPos )
00813                         newSet.push_back( data );
00814                 }
00815                 workingSet.swap( newSet );
00816                 EQASSERT( !workingSet.empty( ));
00817 
00818                 // find next 'discontinouity' in loads
00819                 float currentPos = 1.0f;
00820                 for( LBDataVector::const_iterator i = workingSet.begin();
00821                      i != workingSet.end(); ++i )
00822                 {
00823                     const Data& data = *i;                        
00824                     currentPos = EQ_MIN( currentPos, data.range.end );
00825                 }
00826 
00827                 EQASSERTINFO( currentPos > splitPos,
00828                               currentPos << "<=" << splitPos );
00829                 EQASSERT( currentPos <= 1.0f );
00830 
00831                 // accumulate normalized load in splitPos...currentPos
00832                 EQLOG( LOG_LB2 ) << "Computing load in range " << splitPos
00833                                 << "..." << currentPos << endl;
00834                 float currentLoad = 0.f;
00835                 for( LBDataVector::const_iterator i = workingSet.begin();
00836                      i != workingSet.end(); ++i )
00837                 {
00838                     const Data& data = *i;
00839                         
00840                     if( data.range.start >= currentPos ) // not yet needed data
00841                         break;
00842 #if 0
00843                     // make sure we cover full area
00844                     EQASSERTINFO(  data.range.start <= splitPos, 
00845                                    data.range.start << " > " << splitPos );
00846                     EQASSERTINFO( data.range.end >= currentPos, 
00847                                   data.range.end << " < " << currentPos);
00848 #endif
00849                     currentLoad += data.load;
00850                 }
00851 
00852                 EQLOG( LOG_LB2 ) << splitPos << "..." << currentPos 
00853                                 << ": t=" << currentLoad << " of " 
00854                                 << timeLeft << endl;
00855 
00856                 if( currentLoad >= timeLeft ) // found last region
00857                 {
00858                     const float width = currentPos - splitPos;
00859                     splitPos += (width * timeLeft / currentLoad );
00860                     timeLeft = 0.0f;
00861                 }
00862                 else
00863                 {
00864                     timeLeft -= currentLoad;
00865                     splitPos  = currentPos;
00866                 }
00867             }
00868             // There might be more time left due to MIN_PIXEL rounding by parent
00869             // EQASSERTINFO( timeLeft <= .001f, timeLeft );
00870 
00871             if( node->left->usage == 0.f )
00872                 splitPos = range.start;
00873             else if( node->right->usage == 0.f )
00874                 splitPos = end;
00875 
00876             const float epsilon( 0.001f );
00877             if( (splitPos - range.start) < epsilon )
00878                 splitPos = range.start;
00879             if( (end - splitPos) < epsilon )
00880                 splitPos = end;
00881 
00882             EQLOG( LOG_LB2 ) << "Split " << range << " at pos " << splitPos
00883                             << endl;
00884 
00885             eq::Range childRange = range;
00886             childRange.end       = splitPos;
00887             _computeSplit( node->left, sortedData, vp, childRange );
00888 
00889             childRange.start = childRange.end;
00890             childRange.end   = range.end;
00891             _computeSplit( node->right, sortedData, vp, childRange );
00892             break;
00893         }
00894 
00895         default:
00896             EQUNIMPLEMENTED;
00897     }
00898 }
00899 
00900 ostream& operator << ( ostream& os, const LoadEqualizer::Node* node )
00901 {
00902     if( !node )
00903         return os;
00904 
00905     os << disableFlush;
00906 
00907     if( node->compound )
00908         os << node->compound->getChannel()->getName() << " target time " 
00909            << node->time << endl;
00910     else
00911         os << "split " << node->splitMode << " target time " << node->time
00912            << endl << indent << node->left << node->right << exdent;
00913 
00914     os << enableFlush;
00915     return os;
00916 }
00917 
00918 std::ostream& operator << ( std::ostream& os, 
00919                             const LoadEqualizer::Mode mode )
00920 {
00921     os << ( mode == LoadEqualizer::MODE_2D         ? "2D" :
00922             mode == LoadEqualizer::MODE_VERTICAL   ? "VERTICAL" :
00923             mode == LoadEqualizer::MODE_HORIZONTAL ? "HORIZONTAL" :
00924             mode == LoadEqualizer::MODE_DB         ? "DB" : "ERROR" );
00925     return os;
00926 }
00927 
00928 std::ostream& operator << ( std::ostream& os, const LoadEqualizer* lb )
00929 {
00930     if( !lb )
00931         return os;
00932 
00933     os << disableFlush
00934        << "load_equalizer" << endl
00935        << '{' << endl
00936        << "    mode    " << lb->getMode() << endl;
00937   
00938     if( lb->getDamping() != 0.5f )
00939         os << "    damping " << lb->getDamping() << endl;
00940 
00941     os << '}' << endl << enableFlush;
00942     return os;
00943 }
00944 
00945 }
00946 }
Generated on Mon Aug 10 18:58:40 2009 for Equalizer 0.9 by  doxygen 1.5.8