00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
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
00040
00041
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( ))
00076 return;
00077
00078 _tree = _buildTree( children );
00079 EQLOG( LOG_LB2 ) << "LB tree: " << _tree;
00080 }
00081 _checkHistory();
00082
00083 if( isFrozen( ))
00084 return;
00085
00086
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
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
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
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 )
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
00199
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
00222
00223 }
00224 }
00225 }
00226
00227 void LoadEqualizer::_checkHistory()
00228 {
00229
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
00252 while( !_history.empty() && _history.front().first < useFrame )
00253 _history.pop_front();
00254
00255 if( _history.empty( ))
00256 {
00257 _history.resize( 1 );
00258
00259 LBFrameData& frameData = _history.front();
00260 LBDataVector& items = frameData.second;
00261
00262 frameData.first = 0;
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
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
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
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
00471 compound->setViewport( vp );
00472 compound->setRange( range );
00473
00474 EQLOG( LOG_LB2 ) << compound->getChannel()->getName() << " set "
00475 << vp << ", " << range << endl;
00476
00477
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( ))
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
00515
00516
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
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
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 )
00551 break;
00552 #if 0
00553
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 )
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
00607
00608
00609
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
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
00664
00665
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
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
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 )
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 )
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
00750
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
00805
00806
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
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
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 )
00841 break;
00842 #if 0
00843
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 )
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
00869
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 }