void LoadEqualizer::_updateNode( Node* node ) { Node* left = node->left; Node* right = node->right; EQASSERT( left ); EQASSERT( right ); _update( left ); _update( right ); node->resources = left->resources + right->resources; if( left->resources == 0.f ) { node->maxSize = right->maxSize; node->boundary2i = right->boundary2i; node->boundaryf = right->boundaryf; } else if( right->resources == 0.f ) { node->maxSize = left->maxSize; node->boundary2i = left->boundary2i; node->boundaryf = left->boundaryf; } else { switch( node->mode ) { case MODE_VERTICAL: node->maxSize.x() = left->maxSize.x() + right->maxSize.x(); node->maxSize.y() = EQ_MIN( left->maxSize.y(), right->maxSize.y()); node->boundary2i.x() = left->boundary2i.x()+ right->boundary2i.x(); node->boundary2i.y() = EQ_MAX( left->boundary2i.y(), right->boundary2i.y()); node->boundaryf = EQ_MAX( left->boundaryf, right->boundaryf ); break; case MODE_HORIZONTAL: node->maxSize.x() = EQ_MIN( left->maxSize.x(), right->maxSize.x()); node->maxSize.y() = left->maxSize.y() + right->maxSize.y(); node->boundary2i.x() = EQ_MAX( left->boundary2i.x(), right->boundary2i.x() ); node->boundary2i.y() = left->boundary2i.y()+ right->boundary2i.y(); node->boundaryf = EQ_MAX( left->boundaryf, right->boundaryf ); break; case MODE_DB: node->boundary2i.x() = EQ_MAX( left->boundary2i.x(), right->boundary2i.x() ); node->boundary2i.y() = EQ_MAX( left->boundary2i.y(), right->boundary2i.y() ); node->boundaryf = left->boundaryf + right->boundaryf; break; default: EQUNIMPLEMENTED; } } }
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 ROITracker::updateDelay( const PixelViewports& pvps, const uint8_t* ticket ) { EQASSERT( _needsUpdate ); EQASSERTINFO( ticket == _ticket, "Wrong ticket" ); if( ticket != _ticket ) { EQERROR << "Wrong ticket" << std::endl; return; } uint32_t totalAreaFound = 0; for( uint32_t i = 0; i < pvps.size(); i++ ) totalAreaFound += pvps[ i ].getArea(); Area& area = (*_curFrame)[ _lastStage ].areas.back(); if( totalAreaFound < area.pvp.getArea()*4/5 ) { // ROI cutted enough, reset failure statistics area.lastSkip = 0; }else { // disable ROI for next frames, if it was failing before, // increase number of frames to skip area.lastSkip = EQ_MIN( area.lastSkip*2 + 1, 64 ); area.skip = area.lastSkip; } _needsUpdate = false; }
void FrameData::adjustQuality( const float delta ) { _quality += delta; _quality = EQ_MAX( _quality, 0.1f ); _quality = EQ_MIN( _quality, 1.0f ); setDirty( DIRTY_FLAGS ); EQINFO << "Set non-idle image quality to " << _quality << std::endl; }
void TreeEqualizer::_notifyLoadData( Node* node, Channel* channel, const uint32_t nStatistics, const Statistic* statistics ) { if( !node ) return; _notifyLoadData( node->left, channel, nStatistics, statistics ); _notifyLoadData( node->right, channel, nStatistics, statistics ); if( !node->compound || node->compound->getChannel() != channel ) return; // gather relevant load data const uint32_t taskID = node->compound->getTaskID(); int64_t startTime = std::numeric_limits< int64_t >::max(); int64_t endTime = 0; bool loadSet = false; int64_t timeTransmit = 0; for( uint32_t i = 0; i < nStatistics && !loadSet; ++i ) { const Statistic& stat = statistics[ i ]; if( stat.task != taskID ) // from different compound continue; switch( stat.type ) { case Statistic::CHANNEL_CLEAR: case Statistic::CHANNEL_DRAW: case Statistic::CHANNEL_READBACK: startTime = EQ_MIN( startTime, stat.startTime ); endTime = EQ_MAX( endTime, stat.endTime ); break; case Statistic::CHANNEL_FRAME_TRANSMIT: timeTransmit += stat.endTime - stat.startTime; break; // assemble blocks on input frames, stop using subsequent data case Statistic::CHANNEL_ASSEMBLE: loadSet = true; break; default: break; } } if( startTime == std::numeric_limits< int64_t >::max( )) return; node->time = endTime - startTime; node->time = EQ_MAX( node->time, 1 ); node->time = EQ_MAX( node->time, timeTransmit ); }
void FramerateEqualizer::LoadListener::notifyLoadData( Channel* channel, const uint32_t frameNumber, const uint32_t nStatistics, const eq::Statistic* statistics ) { // gather required load data int64_t startTime = std::numeric_limits< int64_t >::max(); int64_t endTime = 0; for( uint32_t i = 0; i < nStatistics; ++i ) { const eq::Statistic& data = statistics[i]; switch( data.type ) { case eq::Statistic::CHANNEL_CLEAR: case eq::Statistic::CHANNEL_DRAW: case eq::Statistic::CHANNEL_ASSEMBLE: case eq::Statistic::CHANNEL_READBACK: startTime = EQ_MIN( startTime, data.startTime ); endTime = EQ_MAX( endTime, data.endTime ); break; default: break; } } if( startTime == std::numeric_limits< int64_t >::max( )) return; if( startTime == endTime ) // very fast draws might report 0 times ++endTime; for( std::deque< FrameTime >::iterator i = parent->_times.begin(); i != parent->_times.end(); ++i ) { FrameTime& frameTime = *i; if( frameTime.first != frameNumber ) continue; const float time = static_cast< float >( endTime - startTime ) / period; frameTime.second = EQ_MAX( frameTime.second, time ); EQLOG( LOG_LB2 ) << "Frame " << frameNumber << " channel " << channel->getName() << " time " << time << " period " << period << std::endl; } }
void Channel::_updateNearFar( const mesh::BoundingSphere& boundingSphere ) { // compute dynamic near/far plane of whole model const FrameData& frameData = _getFrameData(); const eq::Matrix4f& rotation = frameData.getCameraRotation(); const eq::Matrix4f headTransform = getHeadTransform() * rotation; eq::Matrix4f modelInv; compute_inverse( headTransform, modelInv ); const eq::Vector3f zero = modelInv * eq::Vector3f::ZERO; eq::Vector3f front = modelInv * eq::Vector3f( 0.0f, 0.0f, -1.0f ); front -= zero; front.normalize(); front *= boundingSphere.w(); const eq::Vector3f center = frameData.getCameraPosition().get_sub_vector< 3 >() - boundingSphere.get_sub_vector< 3 >(); const eq::Vector3f nearPoint = headTransform * ( center - front ); const eq::Vector3f farPoint = headTransform * ( center + front ); if( useOrtho( )) { EQASSERTINFO( fabs( farPoint.z() - nearPoint.z() ) > std::numeric_limits< float >::epsilon(), nearPoint << " == " << farPoint ); setNearFar( -nearPoint.z(), -farPoint.z() ); } else { // estimate minimal value of near plane based on frustum size const eq::Frustumf& frustum = getFrustum(); const float width = fabs( frustum.right() - frustum.left() ); const float height = fabs( frustum.top() - frustum.bottom() ); const float size = EQ_MIN( width, height ); const float minNear = frustum.near_plane() / size * .001f; const float zNear = EQ_MAX( minNear, -nearPoint.z() ); const float zFar = EQ_MAX( zNear * 2.f, -farPoint.z() ); setNearFar( zNear, zFar ); } }
void MessagePump::dispatchOne( const uint32_t timeout ) { _initReceiverQueue(); _clock.reset(); for( int64_t timeLeft = timeout; timeleft >= 0; timeleft = timeout - _clock.getTime64( )) { if( _needGlobalLock ) Global::enterCarbon(); EventRef event; const float wait = EQ_MIN( float( timeLeft ) * .001f, .05f /* 50ms */ ); const OSStatus status = ReceiveNextEvent( 0, 0, wait, true, &event ); if( status == noErr ) { LBVERB << "Dispatch Carbon event " << event << std::endl; if( !_needGlobalLock ) Global::enterCarbon(); const EventTargetRef target = GetEventDispatcherTarget(); SendEventToEventTarget( event, target ); Global::leaveCarbon(); ReleaseEvent( event ); return; } if( _needGlobalLock ) Global::leaveCarbon(); if( status != eventLoopTimedOutErr ) { LBWARN << "ReceiveNextEvent failed: " << status << std::endl; return; } } }
void LoadEqualizer::_updateLeaf( Node* node ) { const Compound* compound = node->compound; const Channel* channel = compound->getChannel(); EQASSERT( channel ); const PixelViewport& pvp = channel->getPixelViewport(); node->resources = compound->isRunning() ? compound->getUsage() : 0.f; EQASSERT( node->resources >= 0.f ); node->maxSize.x() = pvp.w; node->maxSize.y() = pvp.h; node->boundaryf = _boundaryf; node->boundary2i = _boundary2i; if( !compound->hasDestinationChannel( )) return; const float nResources = _getTotalResources(); if( _assembleOnlyLimit <= nResources - node->resources ) { node->resources = 0.f; return; // OPT } const float time = float( _getTotalTime( )); const float assembleTime = float( _getAssembleTime( )); if( assembleTime == 0 || node->resources == 0.f ) return; const float timePerResource = time / ( nResources - node->resources ); const float renderTime = timePerResource * node->resources ; const float clampedAssembleTime = EQ_MIN( assembleTime, renderTime ); const float newTimePerResource = (time + clampedAssembleTime) / nResources; node->resources -= ( clampedAssembleTime / newTimePerResource ); if( node->resources < 0.f ) // may happen due to fp rounding node->resources = 0.f; }
void LoadEqualizer::_computeSplit( Node* node, const float time, LBDatas* datas, const Viewport& vp, const Range& range ) { EQLOG( LOG_LB2 ) << "_computeSplit " << vp << ", " << range << " time " << time << std::endl; EQASSERTINFO( vp.isValid(), vp ); EQASSERTINFO( range.isValid(), range ); EQASSERTINFO( 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; } EQASSERT( 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 = EQ_MIN( leftTime, time ); // correct for fp rounding error switch( node->mode ) { case MODE_VERTICAL: { EQASSERT( range == Range::ALL ); float splitPos = vp.x; const float end = vp.getXEnd(); while( timeLeft > std::numeric_limits< float >::epsilon() && splitPos < end ) { EQLOG( 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; EQASSERTINFO( width > 0.f, currentPos << "<=" << splitPos ); EQASSERT( currentPos <= 1.0f ); // accumulate normalized load in splitPos...currentPos EQLOG( 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 ); EQLOG( 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; EQASSERT( percentage < 1.01f ) } } EQLOG( 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; } } EQLOG( LOG_LB2 ) << "Should split at X " << splitPos << std::endl; splitPos = (1.f - _damping) * splitPos + _damping * node->split; EQLOG( LOG_LB2 ) << "Dampened split at X " << splitPos << std::endl; // There might be more time left due to MIN_PIXEL rounding by parent // EQASSERTINFO( 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 = EQ_MAX( splitPos, vp.x ); splitPos = EQ_MIN( splitPos, end); EQLOG( LOG_LB2 ) << "Constrained split " << vp << " at X " << splitPos << std::endl; node->split = splitPos; // 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: { EQASSERT( range == Range::ALL ); float splitPos = vp.y; const float end = vp.getYEnd(); while( timeLeft > std::numeric_limits< float >::epsilon() && splitPos < end ) { EQLOG( 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; EQASSERTINFO( height > 0.f, currentPos << "<=" << splitPos ); EQASSERT( currentPos <= 1.0f ); // accumulate normalized load in splitPos...currentPos EQLOG( 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 ); EQLOG( 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; EQASSERT( percentage < 1.01f ) } } EQLOG( 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; } } EQLOG( LOG_LB2 ) << "Should split at Y " << splitPos << std::endl; splitPos = (1.f - _damping) * splitPos + _damping * node->split; EQLOG( 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 = EQ_MAX( splitPos, vp.y ); splitPos = EQ_MIN( splitPos, end ); EQLOG( LOG_LB2 ) << "Constrained split " << vp << " at Y " << splitPos << std::endl; node->split = splitPos; 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: { EQASSERT( vp == Viewport::FULL ); float splitPos = range.start; const float end = range.end; while( timeLeft > std::numeric_limits< float >::epsilon() && splitPos < end ) { EQLOG( 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 = EQ_MIN( currentPos, data.range.end ); } const float size = currentPos - splitPos; EQASSERTINFO( size > 0.f, currentPos << "<=" << splitPos ); EQASSERT( currentPos <= 1.0f ); // accumulate normalized load in splitPos...currentPos EQLOG( 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 EQASSERTINFO( data.range.start <= splitPos, data.range.start << " > " << splitPos ); EQASSERTINFO( data.range.end >= currentPos, data.range.end << " < " << currentPos); #endif currentTime += data.time * size / data.range.getSize(); } EQLOG( 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; } } EQLOG( LOG_LB2 ) << "Should split at " << splitPos << std::endl; splitPos = (1.f - _damping) * splitPos + _damping * node->split; EQLOG( 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; EQLOG( LOG_LB2 ) << "Constrained split " << range << " at pos " << splitPos << std::endl; node->split = splitPos; 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: EQUNIMPLEMENTED; } }
void LoadEqualizer::notifyLoadData( Channel* channel, const uint32_t frameNumber, const uint32_t nStatistics, const Statistic* statistics, const Viewport& region ) { EQLOG( LOG_LB2 ) << nStatistics << " samples from "<< channel->getName() << " @ " << frameNumber << std::endl; for( std::deque< LBFrameData >::iterator i = _history.begin(); i != _history.end(); ++i ) { LBFrameData& frameData = *i; if( frameData.first != frameNumber ) continue; // Found corresponding historical data set LBDatas& items = frameData.second; for( LBDatas::iterator j = items.begin(); j != items.end(); ++j ) { Data& data = *j; if( data.channel != channel ) continue; // Found corresponding historical data item const uint32_t taskID = data.taskID; EQASSERTINFO( taskID > 0, channel->getName( )); // gather relevant load data int64_t startTime = std::numeric_limits< int64_t >::max(); int64_t endTime = 0; bool loadSet = false; int64_t transmitTime = 0; for( uint32_t k = 0; k < nStatistics; ++k ) { const Statistic& stat = statistics[k]; if( stat.task == data.destTaskID ) _updateAssembleTime( data, stat ); // from different compound if( stat.task != taskID || loadSet ) continue; switch( stat.type ) { case Statistic::CHANNEL_CLEAR: case Statistic::CHANNEL_DRAW: case Statistic::CHANNEL_READBACK: startTime = EQ_MIN( startTime, stat.startTime ); endTime = EQ_MAX( endTime, stat.endTime ); break; case Statistic::CHANNEL_FRAME_TRANSMIT: transmitTime += stat.endTime - stat.startTime; break; case Statistic::CHANNEL_FRAME_WAIT_SENDTOKEN: transmitTime -= stat.endTime - stat.startTime; break; // assemble blocks on input frames, stop using subsequent data case Statistic::CHANNEL_ASSEMBLE: loadSet = true; break; default: break; } } if( startTime == std::numeric_limits< int64_t >::max( )) return; data.vp.apply( region ); // Update ROI data.time = endTime - startTime; data.time = EQ_MAX( data.time, 1 ); data.time = EQ_MAX( data.time, transmitTime ); data.assembleTime = EQ_MAX( data.assembleTime, 0 ); EQLOG( LOG_LB2 ) << "Added time " << data.time << " (+" << data.assembleTime << ") for " << channel->getName() << " " << data.vp << ", " << data.range << " @ " << frameNumber << std::endl; return; // Note: if the same channel is used twice as a child, the // load-compound association does not work. } } }
void TreeEqualizer::_assign( Node* node, const Viewport& vp, const Range& range ) { EQLOG( LOG_LB2 ) << "assign " << vp << ", " << range << " time " << node->time << " split " << node->split << std::endl; EQASSERTINFO( vp.isValid(), vp ); EQASSERTINFO( range.isValid(), range ); EQASSERTINFO( node->resources > 0.f || !vp.hasArea() || !range.hasData(), "Assigning work to unused compound: " << vp << ", " << range); Compound* compound = node->compound; if( compound ) { EQASSERTINFO( vp == Viewport::FULL || range == Range::ALL, "Mixed 2D/DB load-balancing not implemented" ); compound->setViewport( vp ); compound->setRange( range ); EQLOG( 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 = EQ_MAX( absoluteSplit, vp.x ); absoluteSplit = EQ_MIN( absoluteSplit, end); node->split = (absoluteSplit - vp.x ) / vp.w; EQLOG( 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 = EQ_MAX( absoluteSplit, vp.y ); absoluteSplit = EQ_MIN( absoluteSplit, end); node->split = (absoluteSplit - vp.y ) / vp.h; EQLOG( 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: { EQASSERT( 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); EQLOG( 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: EQUNIMPLEMENTED; } }
void TreeEqualizer::_update( Node* node ) { if( !node ) return; const Compound* compound = node->compound; if( compound ) { const Channel* channel = compound->getChannel(); const PixelViewport& pvp = channel->getPixelViewport(); EQASSERT( channel ); node->resources = compound->isRunning() ? compound->getUsage() : 0.f; node->maxSize.x() = pvp.w; node->maxSize.y() = pvp.h; node->boundaryf = _boundaryf; node->boundary2i = _boundary2i; return; } // else EQASSERT( node->left ); EQASSERT( node->right ); _update( node->left ); _update( node->right ); node->resources = node->left->resources + node->right->resources; if( node->left->resources == 0.f ) { node->maxSize = node->right->maxSize; node->boundary2i = node->right->boundary2i; node->boundaryf = node->right->boundaryf; node->time = node->right->time; } else if( node->right->resources == 0.f ) { node->maxSize = node->left->maxSize; node->boundary2i = node->left->boundary2i; node->boundaryf = node->left->boundaryf; node->time = node->left->time; } else { switch( node->mode ) { case MODE_VERTICAL: node->maxSize.x() = node->left->maxSize.x() + node->right->maxSize.x(); node->maxSize.y() = EQ_MIN( node->left->maxSize.y(), node->right->maxSize.y() ); node->boundary2i.x() = node->left->boundary2i.x() + node->right->boundary2i.x(); node->boundary2i.y() = EQ_MAX( node->left->boundary2i.y(), node->right->boundary2i.y()); node->boundaryf = EQ_MAX( node->left->boundaryf, node->right->boundaryf ); break; case MODE_HORIZONTAL: node->maxSize.x() = EQ_MIN( node->left->maxSize.x(), node->right->maxSize.x() ); node->maxSize.y() = node->left->maxSize.y() + node->right->maxSize.y(); node->boundary2i.x() = EQ_MAX( node->left->boundary2i.x(), node->right->boundary2i.x() ); node->boundary2i.y() = node->left->boundary2i.y() + node->right->boundary2i.y(); node->boundaryf = EQ_MAX( node->left->boundaryf, node->right->boundaryf ); break; case MODE_DB: node->boundary2i.x() = EQ_MAX( node->left->boundary2i.x(), node->right->boundary2i.x() ); node->boundary2i.y() = EQ_MAX( node->left->boundary2i.y(), node->right->boundary2i.y() ); node->boundaryf = node->left->boundaryf +node->right->boundaryf; break; default: EQUNIMPLEMENTED; } node->time = node->left->time + node->right->time; } }