framerateEqualizer.cpp

00001 
00002 /* Copyright (c) 2008-2009, Stefan Eilemann <eile@equalizergraphics.com> 
00003  *
00004  * This library is free software; you can redistribute it and/or modify it under
00005  * the terms of the GNU Lesser General Public License version 2.1 as published
00006  * by the Free Software Foundation.
00007  *  
00008  * This library is distributed in the hope that it will be useful, but WITHOUT
00009  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00010  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
00011  * details.
00012  * 
00013  * You should have received a copy of the GNU Lesser General Public License
00014  * along with this library; if not, write to the Free Software Foundation, Inc.,
00015  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00016  */
00017 
00018 #include "framerateEqualizer.h"
00019 
00020 #include "../compound.h"
00021 #include "../compoundVisitor.h"
00022 #include "../config.h"
00023 #include "../log.h"
00024 
00025 #include <eq/base/debug.h>
00026 
00027 #define USE_AVERAGE
00028 #define VSYNC_CAP 60.f
00029 #define SLOWDOWN  1.05f
00030 
00031 namespace eq
00032 {
00033 namespace server
00034 {
00035 
00036 namespace
00037 {
00038 class LoadSubscriber : public CompoundVisitor
00039 {
00040 public:
00041     LoadSubscriber( ChannelListener* listener ) : _listener( listener ) {}
00042 
00043     virtual VisitorResult visit( Compound* compound )
00044         {
00045             Channel*  channel = compound->getChannel();
00046             EQASSERT( channel );
00047             channel->addListener( _listener );
00048 
00049             return TRAVERSE_CONTINUE; 
00050         }
00051 
00052 private:
00053     ChannelListener* const _listener;
00054 };
00055 
00056 class LoadUnsubscriber : public CompoundVisitor
00057 {
00058 public:
00059     LoadUnsubscriber( ChannelListener* listener ) : _listener( listener ) {}
00060 
00061     virtual VisitorResult visit( Compound* compound )
00062         {
00063             Channel*  channel = compound->getChannel();
00064             EQASSERT( channel );
00065             channel->removeListener( _listener );
00066 
00067             return TRAVERSE_CONTINUE; 
00068         }
00069 
00070 private:
00071     ChannelListener* const _listener;
00072 };
00073 
00074 }
00075 
00076 // The smooth load balancer adapts the framerate of the compound to be the
00077 // average frame rate of all children, taking the DPlex period into account.
00078 
00079 FramerateEqualizer::FramerateEqualizer()
00080         : _nSamples( 0 )
00081 {
00082     EQINFO << "New FramerateEqualizer @" << (void*)this << std::endl;
00083 }
00084 
00085 FramerateEqualizer::FramerateEqualizer( const FramerateEqualizer& from )
00086         : Equalizer( from )
00087         , _nSamples( 0 )
00088 {
00089 }
00090 
00091 FramerateEqualizer::~FramerateEqualizer()
00092 {
00093     attach( 0 );
00094 }
00095 
00096 void FramerateEqualizer::attach( Compound* compound )
00097 {
00098     _exit();
00099     Equalizer::attach( compound );
00100 }
00101 
00102 
00103 void FramerateEqualizer::_init()
00104 {
00105     const Compound* compound = getCompound();
00106 
00107     if( _nSamples > 0 || !compound )
00108         return;
00109 
00110     _nSamples = 1;
00111 
00112     // Subscribe to child channel load events
00113     const CompoundVector& children = compound->getChildren();
00114     
00115     EQASSERT( _loadListeners.empty( ));
00116     _loadListeners.resize( children.size( ));
00117     
00118     for( size_t i = 0; i < children.size(); ++i )
00119     {
00120         Compound*      child        = children[i];
00121         const uint32_t period       = child->getInheritPeriod();
00122         LoadListener&  loadListener = _loadListeners[i];
00123         
00124         loadListener.parent = this;
00125         loadListener.period = period;
00126         
00127         LoadSubscriber subscriber( &loadListener );
00128         child->accept( subscriber );
00129         
00130         _nSamples = EQ_MAX( _nSamples, period );
00131     }
00132 
00133     _nSamples = EQ_MIN( _nSamples, 100 );
00134 }
00135 
00136 void FramerateEqualizer::_exit()
00137 {
00138     const Compound* compound = getCompound();
00139     if( !compound || _nSamples == 0 )
00140         return;
00141 
00142     const CompoundVector& children = compound->getChildren();
00143 
00144     EQASSERT( _loadListeners.size() == children.size( ));
00145     for( size_t i = 0; i < children.size(); ++i )
00146     {
00147         Compound*      child        = children[i];
00148         LoadListener&  loadListener = _loadListeners[i];
00149         
00150         LoadUnsubscriber unsubscriber( &loadListener );
00151         child->accept( unsubscriber );
00152     }
00153     
00154     _loadListeners.clear();
00155     _times.clear();
00156     _nSamples = 0;
00157 }
00158 
00159 
00160 void FramerateEqualizer::notifyUpdatePre( Compound* compound, 
00161                                           const uint32_t frameNumber )
00162 {
00163     _init();
00164 
00165     // find starting point of contiguous block
00166     const ssize_t size = static_cast< ssize_t >( _times.size( ));
00167     ssize_t       from = 0;
00168     if( size > 0 )
00169     {
00170         for( ssize_t i = size-1; i >= 0; --i )
00171         {
00172             if( _times[i].second != 0.f )
00173                 continue;
00174 
00175             from = i;
00176             break;
00177         }
00178     }
00179 
00180     // find max / avg time in block
00181     size_t nSamples = 0;
00182 #ifdef USE_AVERAGE
00183     float sumTime = 0.f;
00184 #else
00185     float maxTime  = 0.f;
00186 #endif
00187 
00188     for( ++from; from < size && nSamples < _nSamples; ++from )
00189     {
00190         const FrameTime& time = _times[from];
00191         EQASSERT( time.first > 0 );
00192         EQASSERT( time.second != 0.f );
00193 
00194         ++nSamples;
00195 #ifdef USE_AVERAGE
00196         sumTime += time.second;
00197 #else
00198         maxTime = EQ_MAX( maxTime, time.second );
00199 #endif
00200         EQLOG( LOG_LB2 ) << "Using " << time.first << ", " << time.second << "ms"
00201                         << std::endl;
00202     }
00203 
00204     if( nSamples == _nSamples )       // If we have a full set
00205         while( from < static_cast< ssize_t >( _times.size( )))
00206             _times.pop_back();            //  delete all older samples
00207 
00208     if( isFrozen() || !compound->isActive( ))
00209     {
00210         // always execute code above to not leak memory
00211         compound->setMaxFPS( std::numeric_limits< float >::max( ));
00212         return;
00213     }
00214 
00215     if( nSamples > 0 )
00216     {
00217         //TODO: totalTime *= 1.f - damping;
00218 #ifdef USE_AVERAGE
00219         const float time = (sumTime / nSamples) * SLOWDOWN;
00220 #else
00221         const float time = maxTime * SLOWDOWN;
00222 #endif
00223 
00224         const float fps = 1000.f / time;
00225 #ifdef VSYNC_CAP
00226         if( fps > VSYNC_CAP )
00227             compound->setMaxFPS( std::numeric_limits< float >::max( ));
00228         else
00229 #endif
00230             compound->setMaxFPS( fps );
00231 
00232         EQLOG( LOG_LB2 ) << fps << " Hz from " << nSamples << "/" 
00233                         << _times.size() << " samples, " << time << "ms" 
00234                         << std::endl;
00235     }
00236 
00237     _times.push_front( FrameTime( frameNumber, 0.f ));
00238     EQASSERT( _times.size() < 210 );
00239 }
00240 
00241 void FramerateEqualizer::LoadListener::notifyLoadData( 
00242                             Channel* channel,
00243                             const uint32_t frameNumber, 
00244                             const uint32_t nStatistics,
00245                             const eq::Statistic* statistics  )
00246 {
00247     // gather required load data
00248     float startTime = std::numeric_limits< float >::max();
00249     float endTime   = 0.0f;
00250     for( uint32_t i = 0; i < nStatistics; ++i )
00251     {
00252         const eq::Statistic& data = statistics[i];
00253         switch( data.type )
00254         {
00255             case eq::Statistic::CHANNEL_CLEAR:
00256             case eq::Statistic::CHANNEL_DRAW:
00257             case eq::Statistic::CHANNEL_ASSEMBLE:
00258             case eq::Statistic::CHANNEL_READBACK:
00259                 startTime = EQ_MIN( startTime, data.startTime );
00260                 endTime   = EQ_MAX( endTime, data.endTime );
00261                 break;
00262                 
00263             default:
00264                 break;
00265         }
00266     }
00267     
00268     if( startTime == std::numeric_limits< float >::max( ))
00269         return;
00270     
00271     for( std::deque< FrameTime >::iterator i = parent->_times.begin();
00272          i != parent->_times.end(); ++i )
00273     {
00274         FrameTime& frameTime = *i;
00275         if( frameTime.first != frameNumber )
00276             continue;
00277 
00278         const float time = (endTime - startTime) / period;
00279         frameTime.second = EQ_MAX( frameTime.second, time );
00280         EQLOG( LOG_LB2 ) << "Frame " << frameNumber << " channel " 
00281                         << channel->getName() << " time " << time
00282                         << " period " << period << std::endl;
00283     }
00284 }
00285 
00286 std::ostream& operator << ( std::ostream& os, const FramerateEqualizer* lb )
00287 {
00288     if( lb )
00289         os << "framerate_equalizer {}" << std::endl;
00290     return os;
00291 }
00292 
00293 }
00294 }
Generated on Mon Aug 10 18:58:33 2009 for Equalizer 0.9 by  doxygen 1.5.8