Beispiel #1
0
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 );
}
Beispiel #3
0
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;
}
Beispiel #5
0
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;
    }
}
Beispiel #7
0
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 );
    }
}
Beispiel #8
0
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;
        }
    }
}
Beispiel #9
0
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;
}
Beispiel #10
0
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;
    }
}
Beispiel #11
0
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.
        }
    }
}
Beispiel #12
0
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;
    }
}
Beispiel #13
0
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;
    }
}