void FramerateEqualizer::_init() { const Compound* compound = getCompound(); if( _nSamples > 0 || !compound ) return; _nSamples = 1; // Subscribe to child channel load events const Compounds& children = compound->getChildren(); EQASSERT( _loadListeners.empty( )); _loadListeners.resize( children.size( )); for( size_t i = 0; i < children.size(); ++i ) { Compound* child = children[i]; const uint32_t period = child->getInheritPeriod(); LoadListener& loadListener = _loadListeners[i]; loadListener.parent = this; loadListener.period = period; LoadSubscriber subscriber( &loadListener ); child->accept( subscriber ); _nSamples = EQ_MAX( _nSamples, period ); } _nSamples = EQ_MIN( _nSamples, 100 ); }
void LoadEqualizer::_assign( Compound* compound, const Viewport& vp, const Range& range ) { LBASSERTINFO( vp == Viewport::FULL || range == Range::ALL, "Mixed 2D/DB load-balancing not implemented" ); compound->setViewport( vp ); compound->setRange( range ); LBLOG( LOG_LB2 ) << compound->getChannel()->getName() << " set " << vp << ", " << range << std::endl; if( getDamping() >= 1.f ) return; // save data for later use Data data; data.vp = vp; data.range = range; data.channel = compound->getChannel(); data.taskID = compound->getTaskID(); const Compound* destCompound = getCompound(); if( destCompound->getChannel() == compound->getChannel( )) data.destTaskID = destCompound->getTaskID(); LBASSERT( data.taskID > 0 ); if( !vp.hasArea() || !range.hasData( )) // will not render data.time = 0; LBFrameData& frameData = _history.back(); LBDatas& items = frameData.second; items.push_back( data ); }
float LoadEqualizer::_getTotalResources( ) const { const Compounds& children = getCompound()->getChildren(); float resources = 0.f; for( CompoundsCIter i = children.begin(); i != children.end(); ++i ) { const Compound* compound = *i; if( compound->isActive( )) resources += compound->getUsage(); } return resources; }
float LoadEqualizer::_getTotalResources( ) const { const Compounds& children = getCompound()->getChildren(); float resources = 0.f; for( Compounds::const_iterator i = children.begin(); i != children.end(); i++ ) { const Compound* compound = *i; if( compound->isRunning( )) resources += compound->getUsage(); } return resources; }
void LoadEqualizer::_computeSplit() { LBASSERT( !_history.empty( )); const LBFrameData& frameData = _history.front(); const Compound* compound = getCompound(); LBLOG( LOG_LB2 ) << "----- balance " << compound->getChannel()->getName() << " using frame " << frameData.first << " tree " << std::endl << _tree; // sort load items for each of the split directions LBDatas items( frameData.second ); _removeEmpty( items ); LBDatas sortedData[3] = { items, items, items }; if( getMode() == MODE_DB ) { LBDatas& rangeData = sortedData[ MODE_DB ]; sort( rangeData.begin(), rangeData.end(), _compareRange ); } else { LBDatas& xData = sortedData[ MODE_VERTICAL ]; sort( xData.begin(), xData.end(), _compareX ); LBDatas& yData = sortedData[ MODE_HORIZONTAL ]; sort( yData.begin(), yData.end(), _compareY ); #ifndef NDEBUG for( LBDatas::const_iterator i = xData.begin(); i != xData.end(); ++i ) { const Data& data = *i; LBLOG( LOG_LB2 ) << " " << data.vp << ", time " << data.time << " (+" << data.assembleTime << ")" << std::endl; } #endif } const float time = float( _getTotalTime( )); LBLOG( LOG_LB2 ) << "Render time " << time << " for " << _tree->resources << " resources" << std::endl; if( _tree->resources > 0.f ) _computeSplit( _tree, time, sortedData, Viewport(), Range( )); }
void MonitorEqualizer::_updateViewports() { if( !_outputFrames.empty( )) return; Compound* compound = getCompound(); if( !compound ) return; const Frames& inputFrames = compound->getInputFrames(); for( Frames::const_iterator i = inputFrames.begin(); i != inputFrames.end(); ++i ) { const Frame* frame = *i; const Compound* root = compound->getRoot(); // find the output frame OutputFrameFinder frameFinder( frame->getName() ); root->accept( frameFinder ); Frame* outputFrame = frameFinder.getResult(); _outputFrames.push_back( outputFrame ); _viewports.push_back( eq::Viewport::FULL ); if( outputFrame ) { const Channel* channel = outputFrame->getChannel(); const Segment* segment = channel->getSegment(); const View* view = channel->getView(); if( view ) { Viewport viewport( segment->getViewport( )); viewport.intersect( view->getViewport( )); _viewports.back() = viewport; } } } }
void FramerateEqualizer::_exit() { const Compound* compound = getCompound(); if( !compound || _nSamples == 0 ) return; const Compounds& children = compound->getChildren(); EQASSERT( _loadListeners.size() == children.size( )); for( size_t i = 0; i < children.size(); ++i ) { Compound* child = children[i]; LoadListener& loadListener = _loadListeners[i]; LoadUnsubscriber unsubscriber( &loadListener ); child->accept( unsubscriber ); } _loadListeners.clear(); _times.clear(); _nSamples = 0; }
void LoadEqualizer::_update( Node* node, const Viewport& vp, const Range& range ) { if( !node ) return; node->mode = getMode(); if( node->mode == MODE_2D ) { PixelViewport pvp = getCompound()->getChannel()->getPixelViewport(); pvp.apply( vp ); if( pvp.w > pvp.h ) // split along longest axis node->mode = MODE_VERTICAL; else node->mode = MODE_HORIZONTAL; } if( node->compound ) _updateLeaf( node ); else _updateNode( node, vp, range ); }
void LoadEqualizer::_computeSplit( Node* node, const float time, LBDatas* datas, const Viewport& vp, const Range& range ) { LBLOG( LOG_LB2 ) << "_computeSplit " << vp << ", " << range << " time " << time << std::endl; LBASSERTINFO( vp.isValid(), vp ); LBASSERTINFO( range.isValid(), range ); LBASSERTINFO( node->resources > 0.f || !vp.hasArea() || !range.hasData(), "Assigning " << node->resources << " work to viewport " << vp << ", " << range ); Compound* compound = node->compound; if( compound ) { _assign( compound, vp, range ); return; } LBASSERT( node->left && node->right ); LBDatas workingSet = datas[ node->mode ]; const float leftTime = node->resources > 0 ? time * node->left->resources / node->resources : 0.f; float timeLeft = LB_MIN( leftTime, time ); // correct for fp rounding error switch( node->mode ) { case MODE_VERTICAL: { LBASSERT( range == Range::ALL ); float splitPos = vp.x; const float end = vp.getXEnd(); while( timeLeft > std::numeric_limits< float >::epsilon() && splitPos < end ) { LBLOG( LOG_LB2 ) << timeLeft << "ms left using " << workingSet.size() << " tiles" << std::endl; // remove all irrelevant items from working set for( LBDatas::iterator i = workingSet.begin(); i != workingSet.end(); ) { const Data& data = *i; if( data.vp.getXEnd() > splitPos ) ++i; else i = workingSet.erase( i ); } if( workingSet.empty( )) break; // find next 'discontinouity' in loads float currentPos = 1.0f; for( LBDatas::const_iterator i = workingSet.begin(); i != workingSet.end(); ++i ) { const Data& data = *i; if( data.vp.x > splitPos && data.vp.x < currentPos ) currentPos = data.vp.x; const float xEnd = data.vp.getXEnd(); if( xEnd > splitPos && xEnd < currentPos ) currentPos = xEnd; } const float width = currentPos - splitPos; LBASSERTINFO( width > 0.f, currentPos << "<=" << splitPos ); LBASSERT( currentPos <= 1.0f ); // accumulate normalized load in splitPos...currentPos LBLOG( LOG_LB2 ) << "Computing load in X " << splitPos << "..." << currentPos << std::endl; float currentTime = 0.f; for( LBDatas::const_iterator i = workingSet.begin(); i != workingSet.end(); ++i ) { const Data& data = *i; if( data.vp.x >= currentPos ) // not yet needed data sets break; float yContrib = data.vp.h; if( data.vp.y < vp.y ) yContrib -= (vp.y - data.vp.y); const float dataEnd = data.vp.getYEnd(); const float vpEnd = vp.getYEnd(); if( dataEnd > vpEnd ) yContrib -= (dataEnd - vpEnd); if( yContrib > 0.f ) { const float percentage = ( width / data.vp.w ) * ( yContrib / data.vp.h ); currentTime += ( data.time * percentage ); LBLOG( LOG_LB2 ) << data.vp << " contributes " << yContrib << " in " << vp.h << " (" << percentage << ") with " << data.time << ": " << ( data.time * percentage ) << " vp.y " << vp.y << " dataEnd " << dataEnd << " vpEnd " << vpEnd << std::endl; LBASSERT( percentage < 1.01f ) } } LBLOG( LOG_LB2 ) << splitPos << "..." << currentPos << ": t=" << currentTime << " of " << timeLeft << std::endl; if( currentTime >= timeLeft ) // found last region { splitPos += ( width * timeLeft / currentTime ); timeLeft = 0.0f; } else { timeLeft -= currentTime; splitPos = currentPos; } } LBLOG( LOG_LB2 ) << "Should split at X " << splitPos << std::endl; if( getDamping() < 1.f ) splitPos = (1.f - getDamping()) * splitPos + getDamping() * node->split; LBLOG( LOG_LB2 ) << "Dampened split at X " << splitPos << std::endl; // There might be more time left due to MIN_PIXEL rounding by parent // LBASSERTINFO( timeLeft <= .001f, timeLeft ); // Ensure minimum size const Compound* root = getCompound(); const float pvpW = static_cast< float >( root->getInheritPixelViewport().w ); const float boundary = static_cast< float >( node->boundary2i.x()) / pvpW; if( node->left->resources == 0.f ) splitPos = vp.x; else if( node->right->resources == 0.f ) splitPos = end; else if( boundary > 0 ) { const float lengthRight = vp.getXEnd() - splitPos; const float lengthLeft = splitPos - vp.x; const float maxRight = static_cast< float >( node->right->maxSize.x( )) / pvpW; const float maxLeft = static_cast< float >( node->left->maxSize.x( )) / pvpW; if( lengthRight > maxRight ) splitPos = end - maxRight; else if( lengthLeft > maxLeft ) splitPos = vp.x + maxLeft; if( (splitPos - vp.x) < boundary ) splitPos = vp.x + boundary; if( (end - splitPos) < boundary ) splitPos = end - boundary; const uint32_t ratio = static_cast< uint32_t >( splitPos / boundary + .5f ); splitPos = ratio * boundary; } splitPos = LB_MAX( splitPos, vp.x ); splitPos = LB_MIN( splitPos, end); const float newPixelW = pvpW * splitPos; const float oldPixelW = pvpW * node->split; if( int( fabs(newPixelW - oldPixelW) ) < node->resistance2i.x( )) splitPos = node->split; else node->split = splitPos; LBLOG( LOG_LB2 ) << "Constrained split " << vp << " at X " << splitPos << std::endl; // balance children Viewport childVP = vp; childVP.w = (splitPos - vp.x); _computeSplit( node->left, leftTime, datas, childVP, range ); childVP.x = childVP.getXEnd(); childVP.w = end - childVP.x; // Fix 2994111: Rounding errors with 2D LB and 16 sources // Floating point rounding may create a width for the 'right' // child which is slightly below the parent width. Correct it. while( childVP.getXEnd() < end ) childVP.w += std::numeric_limits< float >::epsilon(); _computeSplit( node->right, time-leftTime, datas, childVP, range ); break; } case MODE_HORIZONTAL: { LBASSERT( range == Range::ALL ); float splitPos = vp.y; const float end = vp.getYEnd(); while( timeLeft > std::numeric_limits< float >::epsilon() && splitPos < end ) { LBLOG( LOG_LB2 ) << timeLeft << "ms left using " << workingSet.size() << " tiles" << std::endl; // remove all unrelevant items from working set for( LBDatas::iterator i = workingSet.begin(); i != workingSet.end(); ) { const Data& data = *i; if( data.vp.getYEnd() > splitPos ) ++i; else i = workingSet.erase( i ); } if( workingSet.empty( )) break; // find next 'discontinuouity' in loads float currentPos = 1.0f; for( LBDatas::const_iterator i = workingSet.begin(); i != workingSet.end(); ++i ) { const Data& data = *i; if( data.vp.y > splitPos && data.vp.y < currentPos ) currentPos = data.vp.y; const float yEnd = data.vp.getYEnd(); if( yEnd > splitPos && yEnd < currentPos ) currentPos = yEnd; } const float height = currentPos - splitPos; LBASSERTINFO( height > 0.f, currentPos << "<=" << splitPos ); LBASSERT( currentPos <= 1.0f ); // accumulate normalized load in splitPos...currentPos LBLOG( LOG_LB2 ) << "Computing load in Y " << splitPos << "..." << currentPos << std::endl; float currentTime = 0.f; for( LBDatas::const_iterator i = workingSet.begin(); i != workingSet.end(); ++i ) { const Data& data = *i; if( data.vp.y >= currentPos ) // not yet needed data sets break; float xContrib = data.vp.w; if( data.vp.x < vp.x ) xContrib -= (vp.x - data.vp.x); const float dataEnd = data.vp.getXEnd(); const float vpEnd = vp.getXEnd(); if( dataEnd > vpEnd ) xContrib -= (dataEnd - vpEnd); if( xContrib > 0.f ) { const float percentage = ( height / data.vp.h ) * ( xContrib / data.vp.w ); currentTime += ( data.time * percentage ); LBLOG( LOG_LB2 ) << data.vp << " contributes " << xContrib << " in " << vp.w << " (" << percentage << ") with " << data.time << ": " << ( data.time * percentage ) << " total " << currentTime << " vp.x " << vp.x << " dataEnd " << dataEnd << " vpEnd " << vpEnd << std::endl; LBASSERT( percentage < 1.01f ) } } LBLOG( LOG_LB2 ) << splitPos << "..." << currentPos << ": t=" << currentTime << " of " << timeLeft << std::endl; if( currentTime >= timeLeft ) // found last region { splitPos += (height * timeLeft / currentTime ); timeLeft = 0.0f; } else { timeLeft -= currentTime; splitPos = currentPos; } } LBLOG( LOG_LB2 ) << "Should split at Y " << splitPos << std::endl; if( getDamping() < 1.f ) splitPos = (1.f - getDamping( )) * splitPos + getDamping() * node->split; LBLOG( LOG_LB2 ) << "Dampened split at Y " << splitPos << std::endl; const Compound* root = getCompound(); const float pvpH = static_cast< float >( root->getInheritPixelViewport().h ); const float boundary = static_cast< float >(node->boundary2i.y( )) / pvpH; if( node->left->resources == 0.f ) splitPos = vp.y; else if( node->right->resources == 0.f ) splitPos = end; else if ( boundary > 0 ) { const float lengthRight = vp.getYEnd() - splitPos; const float lengthLeft = splitPos - vp.y; const float maxRight = static_cast< float >( node->right->maxSize.y( )) / pvpH; const float maxLeft = static_cast< float >( node->left->maxSize.y( )) / pvpH; if( lengthRight > maxRight ) splitPos = end - maxRight; else if( lengthLeft > maxLeft ) splitPos = vp.y + maxLeft; if( (splitPos - vp.y) < boundary ) splitPos = vp.y + boundary; if( (end - splitPos) < boundary ) splitPos = end - boundary; const uint32_t ratio = static_cast< uint32_t >( splitPos / boundary + .5f ); splitPos = ratio * boundary; } splitPos = LB_MAX( splitPos, vp.y ); splitPos = LB_MIN( splitPos, end ); const float newPixelH = pvpH * splitPos; const float oldPixelH = pvpH * node->split; if( int( fabs(newPixelH - oldPixelH) ) < node->resistance2i.y( )) splitPos = node->split; else node->split = splitPos; LBLOG( LOG_LB2 ) << "Constrained split " << vp << " at Y " << splitPos << std::endl; Viewport childVP = vp; childVP.h = (splitPos - vp.y); _computeSplit( node->left, leftTime, datas, childVP, range ); childVP.y = childVP.getYEnd(); childVP.h = end - childVP.y; while( childVP.getYEnd() < end ) childVP.h += std::numeric_limits< float >::epsilon(); _computeSplit( node->right, time - leftTime, datas, childVP, range); break; } case MODE_DB: { LBASSERT( vp == Viewport::FULL ); float splitPos = range.start; const float end = range.end; while( timeLeft > std::numeric_limits< float >::epsilon() && splitPos < end ) { LBLOG( LOG_LB2 ) << timeLeft << "ms left using " << workingSet.size() << " tiles" << std::endl; // remove all irrelevant items from working set for( LBDatas::iterator i = workingSet.begin(); i != workingSet.end(); ) { const Data& data = *i; if( data.range.end > splitPos ) ++i; else i = workingSet.erase( i ); } if( workingSet.empty( )) break; // find next 'discontinouity' in loads float currentPos = 1.0f; for( LBDatas::const_iterator i = workingSet.begin(); i != workingSet.end(); ++i ) { const Data& data = *i; currentPos = LB_MIN( currentPos, data.range.end ); } const float size = currentPos - splitPos; LBASSERTINFO( size > 0.f, currentPos << "<=" << splitPos ); LBASSERT( currentPos <= 1.0f ); // accumulate normalized load in splitPos...currentPos LBLOG( LOG_LB2 ) << "Computing load in range " << splitPos << "..." << currentPos << std::endl; float currentTime = 0.f; for( LBDatas::const_iterator i = workingSet.begin(); i != workingSet.end(); ++i ) { const Data& data = *i; if( data.range.start >= currentPos ) // not yet needed data break; #if 0 // make sure we cover full area LBASSERTINFO( data.range.start <= splitPos, data.range.start << " > " << splitPos ); LBASSERTINFO( data.range.end >= currentPos, data.range.end << " < " << currentPos); #endif currentTime += data.time * size / data.range.getSize(); } LBLOG( LOG_LB2 ) << splitPos << "..." << currentPos << ": t=" << currentTime << " of " << timeLeft << std::endl; if( currentTime >= timeLeft ) // found last region { const float width = currentPos - splitPos; splitPos += (width * timeLeft / currentTime ); timeLeft = 0.0f; } else { timeLeft -= currentTime; splitPos = currentPos; } } LBLOG( LOG_LB2 ) << "Should split at " << splitPos << std::endl; if( getDamping() < 1.f ) splitPos = (1.f - getDamping( )) * splitPos + getDamping() * node->split; LBLOG( LOG_LB2 ) << "Dampened split at " << splitPos << std::endl; const float boundary( node->boundaryf ); if( node->left->resources == 0.f ) splitPos = range.start; else if( node->right->resources == 0.f ) splitPos = end; const uint32_t ratio = static_cast< uint32_t > ( splitPos / boundary + .5f ); splitPos = ratio * boundary; if( (splitPos - range.start) < boundary ) splitPos = range.start; if( (end - splitPos) < boundary ) splitPos = end; if( fabs( splitPos - node->split ) < node->resistancef ) splitPos = node->split; else node->split = splitPos; LBLOG( LOG_LB2 ) << "Constrained split " << range << " at pos " << splitPos << std::endl; Range childRange = range; childRange.end = splitPos; _computeSplit( node->left, leftTime, datas, vp, childRange ); childRange.start = childRange.end; childRange.end = range.end; _computeSplit( node->right, time - leftTime, datas, vp, childRange); break; } default: LBUNIMPLEMENTED; } }
void TreeEqualizer::_assign( Node* node, const Viewport& vp, const Range& range ) { LBLOG( LOG_LB2 ) << "assign " << vp << ", " << range << " time " << node->time << " split " << node->split << std::endl; LBASSERTINFO( vp.isValid(), vp ); LBASSERTINFO( range.isValid(), range ); LBASSERTINFO( node->resources > 0.f || !vp.hasArea() || !range.hasData(), "Assigning work to unused compound: " << vp << ", " << range); Compound* compound = node->compound; if( compound ) { LBASSERTINFO( vp == Viewport::FULL || range == Range::ALL, "Mixed 2D/DB load-balancing not implemented" ); compound->setViewport( vp ); compound->setRange( range ); LBLOG( LOG_LB2 ) << compound->getChannel()->getName() << " set " << vp << ", " << range << std::endl; return; } switch( node->mode ) { case MODE_VERTICAL: { // Ensure minimum size const Compound* root = getCompound(); const float pvpW = float( root->getInheritPixelViewport().w ); const float end = vp.getXEnd(); const float boundary = float( node->boundary2i.x( )) / pvpW; float absoluteSplit = vp.x + vp.w * node->split; if( node->left->resources == 0.f ) absoluteSplit = vp.x; else if( node->right->resources == 0.f ) absoluteSplit = end; else if( boundary > 0 ) { const float right = vp.getXEnd() - absoluteSplit; const float left = absoluteSplit - vp.x; const float maxRight = float( node->right->maxSize.x( )) / pvpW; const float maxLeft = float( node->left->maxSize.x( )) / pvpW; if( right > maxRight ) absoluteSplit = end - maxRight; else if( left > maxLeft ) absoluteSplit = vp.x + maxLeft; if( (absoluteSplit - vp.x) < boundary ) absoluteSplit = vp.x + boundary; if( (end - absoluteSplit) < boundary ) absoluteSplit = end - boundary; const uint32_t ratio = uint32_t( absoluteSplit / boundary + .5f ); absoluteSplit = ratio * boundary; } absoluteSplit = LB_MAX( absoluteSplit, vp.x ); absoluteSplit = LB_MIN( absoluteSplit, end); node->split = (absoluteSplit - vp.x ) / vp.w; LBLOG( LOG_LB2 ) << "Constrained split " << vp << " at X " << node->split << std::endl; // traverse children Viewport childVP = vp; childVP.w = (absoluteSplit - vp.x); _assign( node->left, childVP, range ); childVP.x = childVP.getXEnd(); childVP.w = end - childVP.x; // Fix 2994111: Rounding errors with 2D LB and 16 sources // Floating point rounding may create a width for the 'right' // child which is slightly below the parent width. Correct it. while( childVP.getXEnd() < end ) childVP.w += std::numeric_limits< float >::epsilon(); _assign( node->right, childVP, range ); break; } case MODE_HORIZONTAL: { // Ensure minimum size const Compound* root = getCompound(); const float pvpH = float( root->getInheritPixelViewport().h ); const float end = vp.getYEnd(); const float boundary = float( node->boundary2i.y( )) / pvpH; float absoluteSplit = vp.y + vp.h * node->split; if( node->left->resources == 0.f ) absoluteSplit = vp.y; else if( node->right->resources == 0.f ) absoluteSplit = end; else if( boundary > 0 ) { const float right = vp.getYEnd() - absoluteSplit; const float left = absoluteSplit - vp.y; const float maxRight = float( node->right->maxSize.y( )) / pvpH; const float maxLeft = float( node->left->maxSize.y( )) / pvpH; if( right > maxRight ) absoluteSplit = end - maxRight; else if( left > maxLeft ) absoluteSplit = vp.y + maxLeft; if( (absoluteSplit - vp.y) < boundary ) absoluteSplit = vp.y + boundary; if( (end - absoluteSplit) < boundary ) absoluteSplit = end - boundary; const uint32_t ratio = uint32_t( absoluteSplit / boundary + .5f ); absoluteSplit = ratio * boundary; } absoluteSplit = LB_MAX( absoluteSplit, vp.y ); absoluteSplit = LB_MIN( absoluteSplit, end); node->split = (absoluteSplit - vp.y ) / vp.h; LBLOG( LOG_LB2 ) << "Constrained split " << vp << " at X " << node->split << std::endl; // traverse children Viewport childVP = vp; childVP.h = (absoluteSplit - vp.y); _assign( node->left, childVP, range ); childVP.y = childVP.getYEnd(); childVP.h = end - childVP.y; // Fix 2994111: Rounding errors with 2D LB and 16 sources // Floating point rounding may create a width for the 'right' // child which is slightly below the parent width. Correct it. while( childVP.getYEnd() < end ) childVP.h += std::numeric_limits< float >::epsilon(); _assign( node->right, childVP, range ); break; } case MODE_DB: { LBASSERT( vp == Viewport::FULL ); const float end = range.end; float absoluteSplit = range.start + (range.end-range.start)*node->split; const float boundary( node->boundaryf ); if( node->left->resources == 0.f ) absoluteSplit = range.start; else if( node->right->resources == 0.f ) absoluteSplit = end; const uint32_t ratio = uint32_t( absoluteSplit / boundary + .5f ); absoluteSplit = ratio * boundary; if( (absoluteSplit - range.start) < boundary ) absoluteSplit = range.start; if( (end - absoluteSplit) < boundary ) absoluteSplit = end; node->split = (absoluteSplit-range.start) / (range.end-range.start); LBLOG( LOG_LB2 ) << "Constrained split " << range << " at pos " << node->split << std::endl; Range childRange = range; childRange.end = absoluteSplit; _assign( node->left, vp, childRange ); childRange.start = childRange.end; childRange.end = range.end; _assign( node->right, vp, childRange); break; } default: LBUNIMPLEMENTED; } }