LCOV - code coverage report
Current view: top level - eq/server/equalizers - viewEqualizer.cpp (source / functions) Hit Total Coverage
Test: Equalizer Lines: 23 391 5.9 %
Date: 2017-12-16 05:07:20 Functions: 8 53 15.1 %

          Line data    Source code
       1             : 
       2             : /* Copyright (c) 2009-2013, Stefan Eilemann <eile@equalizergraphics.com>
       3             :  *
       4             :  * This library is free software; you can redistribute it and/or modify it under
       5             :  * the terms of the GNU Lesser General Public License version 2.1 as published
       6             :  * by the Free Software Foundation.
       7             :  *
       8             :  * This library is distributed in the hope that it will be useful, but WITHOUT
       9             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      10             :  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
      11             :  * details.
      12             :  *
      13             :  * You should have received a copy of the GNU Lesser General Public License
      14             :  * along with this library; if not, write to the Free Software Foundation, Inc.,
      15             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      16             :  */
      17             : 
      18             : #include "viewEqualizer.h"
      19             : 
      20             : #include "../compound.h"
      21             : #include "../compoundVisitor.h"
      22             : #include "../config.h"
      23             : #include "../log.h"
      24             : #include "../pipe.h"
      25             : 
      26             : #include <eq/fabric/statistic.h>
      27             : 
      28             : #include <set>
      29             : 
      30             : #define MIN_USAGE .1f // 10%
      31             : 
      32             : namespace eq
      33             : {
      34             : namespace server
      35             : {
      36          32 : ViewEqualizer::ViewEqualizer()
      37          32 :     : _nPipes(0)
      38             : {
      39          32 :     LBINFO << "New view equalizer @" << (void*)this << std::endl;
      40          32 : }
      41             : 
      42           0 : ViewEqualizer::ViewEqualizer(const ViewEqualizer& from)
      43             :     : Equalizer(from)
      44           0 :     , _nPipes(0)
      45             : {
      46           0 : }
      47             : 
      48          96 : ViewEqualizer::~ViewEqualizer()
      49             : {
      50          32 :     attach(0);
      51          32 :     LBINFO << "Delete view equalizer @" << (void*)this << std::endl;
      52          64 : }
      53             : 
      54           0 : ViewEqualizer::Listener::Listener()
      55             : {
      56           0 : }
      57             : 
      58           0 : ViewEqualizer::Listener::~Listener()
      59             : {
      60           0 : }
      61             : 
      62          96 : void ViewEqualizer::attach(Compound* compound)
      63             : {
      64          96 :     for (Listeners::iterator i = _listeners.begin(); i != _listeners.end(); ++i)
      65           0 :         (*i).clear();
      66             : 
      67          96 :     _listeners.clear();
      68          96 :     Equalizer::attach(compound);
      69          96 : }
      70             : 
      71           0 : void ViewEqualizer::notifyUpdatePre(Compound* compound LB_UNUSED,
      72             :                                     const uint32_t frameNumber)
      73             : {
      74           0 :     LBASSERT(compound == getCompound());
      75             : 
      76           0 :     _updateListeners();
      77           0 :     _updateResources();
      78           0 :     _update(frameNumber);
      79           0 : }
      80             : 
      81             : namespace
      82             : {
      83           0 : class SelfAssigner : public CompoundVisitor
      84             : {
      85             : public:
      86           0 :     SelfAssigner(const Pipe* self, float& nResources,
      87             :                  lunchbox::PtrHash<Pipe*, float>& pipeUsage)
      88           0 :         : _self(self)
      89             :         , _nResources(nResources)
      90             :         , _pipeUsage(pipeUsage)
      91           0 :         , _numChannels(0)
      92             :     {
      93           0 :     }
      94             : 
      95           0 :     virtual VisitorResult visitLeaf(Compound* compound)
      96             :     {
      97           0 :         if (!compound->isActive())
      98           0 :             return TRAVERSE_CONTINUE;
      99             : 
     100           0 :         Pipe* pipe = compound->getPipe();
     101           0 :         LBASSERT(pipe);
     102             : 
     103           0 :         if (pipe != _self)
     104           0 :             return TRAVERSE_CONTINUE;
     105             : 
     106           0 :         if (_pipeUsage.find(pipe) == _pipeUsage.end())
     107           0 :             _pipeUsage[pipe] = 0.0f;
     108             : 
     109           0 :         float& pipeUsage = _pipeUsage[pipe];
     110           0 :         if (pipeUsage >= 1.0f)
     111             :         {
     112           0 :             compound->setUsage(0.f);
     113           0 :             return TRAVERSE_TERMINATE;
     114             :         }
     115             : 
     116           0 :         if (pipeUsage > 0.0f) // pipe already partly used
     117             :         {
     118           0 :             LBASSERT(pipeUsage < 1.0f);
     119             : 
     120           0 :             float use = 1.0f - pipeUsage;
     121           0 :             use = LB_MAX(use, MIN_USAGE);
     122             : 
     123           0 :             compound->setUsage(use);
     124           0 :             _nResources -= use;
     125           0 :             pipeUsage = 1.0f; // Don't use more than twice
     126           0 :             LBLOG(LOG_LB1) << "  Use "
     127           0 :                            << static_cast<unsigned>(use * 100.f + .5f)
     128           0 :                            << "% of " << pipe->getName() << " task "
     129           0 :                            << compound->getTaskID() << ", "
     130           0 :                            << _nResources * 100.f << "% left" << std::endl;
     131             :         }
     132             :         else
     133             :         {
     134           0 :             LBASSERT(pipeUsage == 0.0f);
     135             : 
     136           0 :             float use = LB_MIN(1.0f, _nResources);
     137             : 
     138           0 :             compound->setUsage(use);
     139           0 :             _nResources -= use;
     140           0 :             pipeUsage = use;
     141           0 :             LBLOG(LOG_LB1) << "  Use "
     142           0 :                            << static_cast<unsigned>(use * 100.f + .5f)
     143           0 :                            << "% of " << pipe->getName() << " task "
     144           0 :                            << compound->getTaskID() << ", "
     145           0 :                            << _nResources * 100.f << "% left" << std::endl;
     146             :         }
     147           0 :         ++_numChannels;
     148             : 
     149           0 :         return TRAVERSE_TERMINATE;
     150             :     }
     151             : 
     152           0 :     uint32_t getNumChannels() const { return _numChannels; }
     153             : private:
     154             :     const Pipe* const _self;
     155             :     float& _nResources;
     156             :     lunchbox::PtrHash<Pipe*, float>& _pipeUsage;
     157             :     uint32_t _numChannels;
     158             : };
     159             : 
     160           0 : class PreviousAssigner : public CompoundVisitor
     161             : {
     162             : public:
     163           0 :     PreviousAssigner(const Pipe* self, float& nResources,
     164             :                      lunchbox::PtrHash<Pipe*, float>& pipeUsage)
     165           0 :         : _self(self)
     166             :         , _nResources(nResources)
     167             :         , _pipeUsage(pipeUsage)
     168           0 :         , _numChannels(0)
     169             :     {
     170           0 :     }
     171             : 
     172           0 :     virtual VisitorResult visitLeaf(Compound* compound)
     173             :     {
     174           0 :         if (!compound->isActive())
     175           0 :             return TRAVERSE_CONTINUE;
     176             : 
     177           0 :         Pipe* pipe = compound->getPipe();
     178           0 :         LBASSERT(pipe);
     179             : 
     180           0 :         if (compound->getUsage() == 0.0f || // not previously used
     181           0 :             pipe == _self)                  // already assigned above
     182             :         {
     183           0 :             return TRAVERSE_CONTINUE;
     184             :         }
     185             : 
     186           0 :         compound->setUsage(0.0f);     // reset to unused
     187           0 :         if (_nResources <= MIN_USAGE) // done
     188           0 :             return TRAVERSE_CONTINUE;
     189             : 
     190           0 :         if (_pipeUsage.find(pipe) == _pipeUsage.end())
     191           0 :             _pipeUsage[pipe] = 0.0f;
     192             : 
     193           0 :         float& pipeUsage = _pipeUsage[pipe];
     194           0 :         if (pipeUsage > 0.0f) // pipe already partly used
     195           0 :             return TRAVERSE_CONTINUE;
     196             : 
     197           0 :         float use = LB_MIN(1.0f, _nResources);
     198           0 :         if (use + MIN_USAGE > 1.0f)
     199           0 :             use = 1.0f;
     200             : 
     201           0 :         pipeUsage = use;
     202           0 :         compound->setUsage(use);
     203           0 :         _nResources -= use;
     204           0 :         ++_numChannels;
     205             : 
     206           0 :         LBLOG(LOG_LB1) << "  Use " << static_cast<unsigned>(use * 100.f + .5f)
     207           0 :                        << "% of " << pipe->getName() << " task "
     208           0 :                        << compound->getTaskID() << ", " << _nResources * 100.f
     209           0 :                        << "% left" << std::endl;
     210           0 :         return TRAVERSE_CONTINUE;
     211             :     }
     212             : 
     213           0 :     uint32_t getNumChannels() const { return _numChannels; }
     214             : private:
     215             :     const Pipe* const _self;
     216             :     float& _nResources;
     217             :     lunchbox::PtrHash<Pipe*, float>& _pipeUsage;
     218             :     uint32_t _numChannels;
     219             : };
     220             : 
     221           0 : class NewAssigner : public CompoundVisitor
     222             : {
     223             : public:
     224           0 :     NewAssigner(float& nResources, lunchbox::PtrHash<Pipe*, float>& pipeUsage)
     225           0 :         : _nResources(nResources)
     226             :         , _pipeUsage(pipeUsage)
     227             :         , _numChannels(0)
     228           0 :         , _fallback(0)
     229             :     {
     230           0 :     }
     231             : 
     232           0 :     virtual VisitorResult visitLeaf(Compound* compound)
     233             :     {
     234           0 :         if (!compound->isActive())
     235           0 :             return TRAVERSE_CONTINUE;
     236             : 
     237           0 :         if (!_fallback)
     238           0 :             _fallback = compound;
     239             : 
     240           0 :         if (compound->getUsage() != 0.0f) // already used
     241           0 :             return TRAVERSE_CONTINUE;
     242             : 
     243           0 :         Pipe* pipe = compound->getPipe();
     244           0 :         LBASSERT(pipe);
     245             : 
     246           0 :         if (_pipeUsage.find(pipe) == _pipeUsage.end())
     247           0 :             _pipeUsage[pipe] = 0.0f;
     248             : 
     249           0 :         float& pipeUsage = _pipeUsage[pipe];
     250           0 :         if (pipeUsage >= 1.0f)
     251           0 :             return TRAVERSE_CONTINUE;
     252             : 
     253           0 :         if (pipeUsage > 0.0f) // pipe already partly used
     254             :         {
     255           0 :             LBASSERT(pipeUsage < 1.0f);
     256             : 
     257           0 :             float use = 1.0f - pipeUsage;
     258           0 :             use = LB_MAX(use, MIN_USAGE);
     259             : 
     260           0 :             compound->setUsage(use);
     261           0 :             _nResources -= use;
     262           0 :             pipeUsage = 1.0f; // Don't use more than twice
     263           0 :             LBLOG(LOG_LB1) << "  Use "
     264           0 :                            << static_cast<unsigned>(use * 100.f + .5f)
     265           0 :                            << "% of " << pipe->getName() << " task "
     266           0 :                            << compound->getTaskID() << ", "
     267           0 :                            << _nResources * 100.f << "% left" << std::endl;
     268             :         }
     269             :         else
     270             :         {
     271           0 :             LBASSERT(pipeUsage == 0.0f);
     272             : 
     273           0 :             float use = LB_MIN(1.0f, _nResources);
     274             : 
     275           0 :             compound->setUsage(use);
     276           0 :             _nResources -= use;
     277           0 :             pipeUsage = use;
     278           0 :             LBLOG(LOG_LB1) << "  Use "
     279           0 :                            << static_cast<unsigned>(use * 100.f + .5f)
     280           0 :                            << "% of " << pipe->getName() << " task "
     281           0 :                            << compound->getTaskID() << ", "
     282           0 :                            << _nResources * 100.f << "% left" << std::endl;
     283             :         }
     284           0 :         ++_numChannels;
     285             : 
     286           0 :         if (_nResources <= MIN_USAGE)
     287           0 :             return TRAVERSE_TERMINATE; // done
     288           0 :         return TRAVERSE_CONTINUE;
     289             :     }
     290             : 
     291           0 :     uint32_t getNumChannels() const { return _numChannels; }
     292           0 :     Compound* getFallback() { return _fallback; }
     293             : private:
     294             :     float& _nResources;
     295             :     lunchbox::PtrHash<Pipe*, float>& _pipeUsage;
     296             :     uint32_t _numChannels;
     297             :     Compound* _fallback;
     298             : };
     299             : }
     300             : 
     301           0 : void ViewEqualizer::_update(const uint32_t frameNumber)
     302             : {
     303           0 :     const uint32_t frame = _findInputFrameNumber();
     304           0 :     LBLOG(LOG_LB1) << "Using data from frame " << frame << std::endl;
     305             : 
     306             :     //----- Gather data for frame
     307           0 :     Loads loads;
     308           0 :     int64_t totalTime(0);
     309             : 
     310           0 :     for (Listeners::iterator i = _listeners.begin(); i != _listeners.end(); ++i)
     311             :     {
     312           0 :         Listener& listener = *i;
     313           0 :         const Listener::Load& load = listener.useLoad(frame);
     314             : 
     315           0 :         totalTime += load.time;
     316           0 :         loads.push_back(load);
     317             :     }
     318             : 
     319           0 :     const Compound* compound = getCompound();
     320             : 
     321           0 :     if (isFrozen() || !compound->isActive() || _nPipes == 0)
     322             :         // always execute code above to not leak memory
     323           0 :         return;
     324             : 
     325           0 :     if (totalTime == 0) // no data
     326           0 :         totalTime = 1;
     327             : 
     328           0 :     const float resourceTime(static_cast<float>(totalTime) /
     329           0 :                              static_cast<float>(_nPipes));
     330           0 :     LBLOG(LOG_LB1) << resourceTime << "ms/resource" << std::endl;
     331             : 
     332             :     //----- Assign new resource usage
     333           0 :     const Compounds& children = compound->getChildren();
     334           0 :     const size_t size(_listeners.size());
     335           0 :     LBASSERT(children.size() == size);
     336           0 :     lunchbox::PtrHash<Pipe*, float> pipeUsage;
     337           0 :     float* leftOvers = static_cast<float*>(alloca(size * sizeof(float)));
     338             : 
     339             :     // use self
     340           0 :     for (size_t i = 0; i < size; ++i)
     341             :     {
     342           0 :         Listener::Load& load = loads[i];
     343           0 :         LBASSERT(load.missing == 0);
     344             : 
     345           0 :         Compound* child = children[i];
     346           0 :         if (!child->isActive())
     347           0 :             continue;
     348             : 
     349           0 :         float segmentResources(load.time / resourceTime);
     350             : 
     351           0 :         LBLOG(LOG_LB1) << "----- balance step 1 for view " << i << " ("
     352           0 :                        << child->getChannel()->getName() << " "
     353           0 :                        << child->getChannel()->getSerial() << ") using "
     354           0 :                        << segmentResources << " resources" << std::endl;
     355           0 :         SelfAssigner assigner(child->getPipe(), segmentResources, pipeUsage);
     356             : 
     357           0 :         child->accept(assigner);
     358           0 :         load.missing = assigner.getNumChannels();
     359           0 :         leftOvers[i] = segmentResources;
     360             :     }
     361             : 
     362             :     // use previous' frames resources
     363           0 :     for (size_t i = 0; i < size; ++i)
     364             :     {
     365           0 :         Listener::Load& load = loads[i];
     366           0 :         Compound* child = children[i];
     367           0 :         if (!child->isActive())
     368           0 :             continue;
     369             : 
     370           0 :         float& leftOver = leftOvers[i];
     371           0 :         LBLOG(LOG_LB1) << "----- balance step 2 for view " << i << " ("
     372           0 :                        << child->getChannel()->getName() << " "
     373           0 :                        << child->getChannel()->getSerial() << ") using "
     374           0 :                        << leftOver << " resources" << std::endl;
     375           0 :         PreviousAssigner assigner(child->getPipe(), leftOver, pipeUsage);
     376             : 
     377           0 :         child->accept(assigner);
     378           0 :         load.missing += assigner.getNumChannels();
     379             :     }
     380             : 
     381             :     // satisfy left-overs
     382           0 :     for (size_t i = 0; i < size; ++i)
     383             :     {
     384           0 :         Listener& listener = _listeners[i];
     385           0 :         LBASSERTINFO(listener.getNLoads() <= getConfig()->getLatency() + 3,
     386             :                      listener);
     387             : 
     388           0 :         float& leftOver = leftOvers[i];
     389           0 :         Listener::Load& load = loads[i];
     390           0 :         Compound* child = children[i];
     391             : 
     392           0 :         if (!child->isActive())
     393           0 :             continue;
     394             : 
     395           0 :         if (leftOver > MIN_USAGE || load.missing == 0)
     396             :         {
     397           0 :             LBLOG(LOG_LB1) << "----- balance step 3 for view " << i << " ("
     398           0 :                            << child->getChannel()->getName() << ") using "
     399           0 :                            << leftOver << " resources" << std::endl;
     400             : 
     401           0 :             NewAssigner assigner(leftOver, pipeUsage);
     402           0 :             child->accept(assigner);
     403           0 :             load.missing += assigner.getNumChannels();
     404             : 
     405           0 :             if (load.missing == 0) // assign at least one resource
     406             :             {
     407           0 :                 Compound* fallback = assigner.getFallback();
     408           0 :                 LBASSERT(fallback);
     409           0 :                 LBASSERT(leftOver > 0);
     410             : 
     411           0 :                 fallback->setUsage(leftOver);
     412           0 :                 load.missing = 1;
     413           0 :                 LBLOG(LOG_LB1)
     414           0 :                     << "  Use " << static_cast<unsigned>(leftOver * 100.f + .5f)
     415           0 :                     << "% of " << fallback->getPipe()->getName() << " task "
     416           0 :                     << fallback->getTaskID() << std::endl;
     417             :             }
     418             :         }
     419             : 
     420           0 :         listener.newLoad(frameNumber, load.missing);
     421             :     }
     422             : }
     423             : 
     424           0 : uint32_t ViewEqualizer::_findInputFrameNumber() const
     425             : {
     426           0 :     LBASSERT(!_listeners.empty());
     427             : 
     428           0 :     uint32_t frame = std::numeric_limits<uint32_t>::max();
     429           0 :     const Compound* compound = getCompound();
     430           0 :     const Compounds& children = compound->getChildren();
     431           0 :     const size_t nChildren = children.size();
     432           0 :     LBASSERT(nChildren == _listeners.size());
     433             : 
     434           0 :     bool change = true;
     435           0 :     while (change)
     436             :     {
     437           0 :         change = false;
     438           0 :         for (size_t i = 0; i < nChildren; ++i)
     439             :         {
     440           0 :             const Compound* child = children[i];
     441           0 :             if (!child->isActive())
     442           0 :                 continue;
     443             : 
     444           0 :             const Listener& listener = _listeners[i];
     445           0 :             const uint32_t youngest = listener.findYoungestLoad(frame);
     446           0 :             if (frame > youngest)
     447             :             {
     448           0 :                 change = true;
     449           0 :                 frame = youngest;
     450             :             }
     451             :         }
     452             :     }
     453             : 
     454           0 :     return frame;
     455             : }
     456             : 
     457           0 : void ViewEqualizer::_updateListeners()
     458             : {
     459           0 :     if (!_listeners.empty())
     460             :     {
     461           0 :         LBASSERT(getCompound()->getChildren().size() == _listeners.size());
     462           0 :         return;
     463             :     }
     464             : 
     465           0 :     Compound* compound = getCompound();
     466           0 :     const Compounds& children = compound->getChildren();
     467           0 :     const size_t nChildren = children.size();
     468             : 
     469           0 :     _listeners.resize(nChildren);
     470           0 :     for (size_t i = 0; i < nChildren; ++i)
     471             :     {
     472           0 :         LBLOG(LOG_LB1) << lunchbox::disableFlush << "Tasks for view " << i
     473           0 :                        << ": ";
     474           0 :         Listener& listener = _listeners[i];
     475           0 :         listener.update(children[i]);
     476           0 :         LBLOG(LOG_LB1) << std::endl << lunchbox::enableFlush;
     477             :     }
     478             : }
     479             : 
     480             : namespace
     481             : {
     482           0 : class PipeCounter : public CompoundVisitor
     483             : {
     484             : public:
     485           0 :     virtual VisitorResult visitPre(const Compound* compound)
     486             :     {
     487           0 :         return compound->isActive() ? TRAVERSE_CONTINUE : TRAVERSE_PRUNE;
     488             :     }
     489             : 
     490           0 :     virtual VisitorResult visitLeaf(const Compound* compound)
     491             :     {
     492           0 :         if (!compound->isActive())
     493           0 :             return TRAVERSE_PRUNE;
     494             : 
     495           0 :         const Pipe* pipe = compound->getPipe();
     496           0 :         LBASSERT(pipe);
     497           0 :         _pipes.insert(pipe);
     498           0 :         return TRAVERSE_CONTINUE;
     499             :     }
     500             : 
     501           0 :     size_t getNPipes() const { return _pipes.size(); }
     502             : private:
     503             :     std::set<const Pipe*> _pipes;
     504             : };
     505             : }
     506             : 
     507           0 : void ViewEqualizer::_updateResources()
     508             : {
     509           0 :     PipeCounter counter;
     510           0 :     const Compound* compound = getCompound();
     511           0 :     compound->accept(counter);
     512           0 :     _nPipes = counter.getNPipes();
     513           0 : }
     514             : 
     515             : //---------------------------------------------------------------------------
     516             : // Per-child listener implementation
     517             : //---------------------------------------------------------------------------
     518             : namespace
     519             : {
     520           0 : class LoadSubscriber : public CompoundVisitor
     521             : {
     522             : public:
     523           0 :     LoadSubscriber(ChannelListener* listener,
     524             :                    lunchbox::PtrHash<Channel*, uint32_t>& taskIDs)
     525           0 :         : _listener(listener)
     526           0 :         , _taskIDs(taskIDs)
     527             :     {
     528           0 :     }
     529             : 
     530           0 :     virtual VisitorResult visitLeaf(Compound* compound)
     531             :     {
     532           0 :         Channel* channel = compound->getChannel();
     533           0 :         LBASSERT(channel);
     534             : 
     535           0 :         if (_taskIDs.find(channel) == _taskIDs.end())
     536             :         {
     537           0 :             channel->addListener(_listener);
     538           0 :             _taskIDs[channel] = compound->getTaskID();
     539           0 :             LBLOG(LOG_LB1) << _taskIDs[channel] << ' ';
     540             :         }
     541             :         else
     542             :         {
     543           0 :             LBASSERTINFO(0, "View equalizer does not support using channel "
     544             :                                 << channel->getName()
     545             :                                 << " multiple times in one branch");
     546             :         }
     547           0 :         return TRAVERSE_CONTINUE;
     548             :     }
     549             : 
     550             : private:
     551             :     ChannelListener* const _listener;
     552             :     lunchbox::PtrHash<Channel*, uint32_t>& _taskIDs;
     553             : };
     554             : }
     555             : 
     556           0 : void ViewEqualizer::Listener::update(Compound* compound)
     557             : {
     558           0 :     LBASSERT(_taskIDs.empty());
     559           0 :     LoadSubscriber subscriber(this, _taskIDs);
     560           0 :     compound->accept(subscriber);
     561           0 : }
     562             : 
     563           0 : void ViewEqualizer::Listener::clear()
     564             : {
     565           0 :     for (TaskIDHash::const_iterator i = _taskIDs.begin(); i != _taskIDs.end();
     566             :          ++i)
     567             :     {
     568           0 :         i->first->removeListener(this);
     569             :     }
     570           0 :     _taskIDs.clear();
     571           0 : }
     572             : 
     573          20 : ViewEqualizer::Listener::Load ViewEqualizer::Listener::Load::NONE(0, 0, 1);
     574          20 : ViewEqualizer::Listener::Load::Load(const uint32_t frame_,
     575             :                                     const uint32_t missing_,
     576          20 :                                     const int64_t time_)
     577             :     : frame(frame_)
     578             :     , missing(missing_)
     579             :     , nResources(missing_)
     580          20 :     , time(time_)
     581             : {
     582          20 : }
     583             : 
     584           0 : bool ViewEqualizer::Listener::Load::operator==(const Load& rhs) const
     585             : {
     586           0 :     return (frame == rhs.frame && missing == rhs.missing && time == rhs.time);
     587             : }
     588             : 
     589           0 : void ViewEqualizer::Listener::notifyLoadData(Channel* channel,
     590             :                                              const uint32_t frameNumber,
     591             :                                              const Statistics& statistics,
     592             :                                              const Viewport& /*region*/)
     593             : {
     594           0 :     Load& load = _getLoad(frameNumber);
     595           0 :     if (load == Load::NONE)
     596           0 :         return;
     597             : 
     598           0 :     LBASSERT(_taskIDs.find(channel) != _taskIDs.end());
     599           0 :     const uint32_t taskID = _taskIDs[channel];
     600             : 
     601             :     // gather relevant load data
     602           0 :     int64_t startTime = std::numeric_limits<int64_t>::max();
     603           0 :     int64_t endTime = 0;
     604           0 :     bool loadSet = false;
     605           0 :     int64_t transmitTime = 0;
     606           0 :     for (size_t i = 0; i < statistics.size() && !loadSet; ++i)
     607             :     {
     608           0 :         const Statistic& data = statistics[i];
     609           0 :         if (data.task != taskID) // data from another compound
     610           0 :             continue;
     611             : 
     612           0 :         switch (data.type)
     613             :         {
     614             :         case Statistic::CHANNEL_CLEAR:
     615             :         case Statistic::CHANNEL_DRAW:
     616             :         case Statistic::CHANNEL_READBACK:
     617           0 :             startTime = LB_MIN(startTime, data.startTime);
     618           0 :             endTime = LB_MAX(endTime, data.endTime);
     619           0 :             break;
     620             : 
     621             :         case Statistic::CHANNEL_ASYNC_READBACK:
     622             :         case Statistic::CHANNEL_FRAME_TRANSMIT:
     623           0 :             transmitTime += data.startTime - data.endTime;
     624           0 :             break;
     625             :         case Statistic::CHANNEL_FRAME_WAIT_SENDTOKEN:
     626           0 :             transmitTime -= data.endTime - data.startTime;
     627           0 :             break;
     628             : 
     629             :         // assemble blocks on input frames, stop using subsequent data
     630             :         case Statistic::CHANNEL_ASSEMBLE:
     631           0 :             loadSet = true;
     632           0 :             break;
     633             : 
     634             :         default:
     635           0 :             break;
     636             :         }
     637             :     }
     638             : 
     639           0 :     if (startTime == std::numeric_limits<int64_t>::max())
     640           0 :         return;
     641             : 
     642           0 :     LBASSERTINFO(load.missing > 0, load << " for " << channel->getName() << " "
     643             :                                         << channel->getSerial());
     644             : 
     645           0 :     const int64_t time = LB_MAX(endTime - startTime, transmitTime);
     646           0 :     load.time += time;
     647           0 :     --load.missing;
     648             : 
     649           0 :     if (load.missing == 0)
     650             :     {
     651           0 :         const float rTime = float(load.time) / float(load.nResources);
     652           0 :         load.time = int64_t(rTime * sqrtf(float(load.nResources)));
     653             :     }
     654             : 
     655           0 :     LBLOG(LOG_LB1) << "Task " << taskID << ", added time " << time << " to "
     656           0 :                    << load << " from " << channel->getName() << " "
     657           0 :                    << channel->getSerial() << std::endl;
     658             : }
     659             : 
     660           0 : uint32_t ViewEqualizer::Listener::findYoungestLoad(const uint32_t frame) const
     661             : {
     662           0 :     for (LoadDeque::const_iterator i = _loads.begin(); i != _loads.end(); ++i)
     663             :     {
     664           0 :         const Load& load = *i;
     665           0 :         if (load.missing == 0 && load.frame <= frame)
     666           0 :             return load.frame;
     667             :     }
     668           0 :     return 0;
     669             : }
     670             : 
     671           0 : const ViewEqualizer::Listener::Load& ViewEqualizer::Listener::useLoad(
     672             :     const uint32_t frame)
     673             : {
     674           0 :     for (LoadDeque::iterator i = _loads.begin(); i != _loads.end(); ++i)
     675             :     {
     676           0 :         Load& load = *i;
     677           0 :         if (load.frame == frame)
     678             :         {
     679           0 :             LBASSERT(load.missing == 0);
     680           0 :             if (load.time == 0)
     681           0 :                 load.time = 1;
     682             : 
     683           0 :             ++i;
     684           0 :             _loads.erase(i, _loads.end());
     685           0 :             return load;
     686             :         }
     687             :     }
     688             : 
     689           0 :     return Load::NONE;
     690             : }
     691             : 
     692           0 : ViewEqualizer::Listener::Load& ViewEqualizer::Listener::_getLoad(
     693             :     const uint32_t frame)
     694             : {
     695           0 :     for (LoadDeque::iterator i = _loads.begin(); i != _loads.end(); ++i)
     696             :     {
     697           0 :         const Load& load = *i;
     698           0 :         if (load.frame == frame)
     699           0 :             return *i;
     700             :     }
     701             : 
     702           0 :     return Load::NONE;
     703             : }
     704             : 
     705           0 : void ViewEqualizer::Listener::newLoad(const uint32_t frameNumber,
     706             :                                       const uint32_t nChannels)
     707             : {
     708           0 :     LBASSERT(nChannels > 0);
     709           0 :     _loads.push_front(Load(frameNumber, nChannels, 0));
     710           0 : }
     711             : 
     712          16 : std::ostream& operator<<(std::ostream& os, const ViewEqualizer* equalizer)
     713             : {
     714          16 :     if (equalizer)
     715          16 :         os << "view_equalizer {}" << std::endl;
     716          16 :     return os;
     717             : }
     718             : 
     719           0 : std::ostream& operator<<(std::ostream& os,
     720             :                          const ViewEqualizer::Listener& listener)
     721             : {
     722           0 :     os << lunchbox::disableFlush << "Listener" << std::endl << lunchbox::indent;
     723           0 :     for (ViewEqualizer::Listener::LoadDeque::const_iterator i =
     724           0 :              listener._loads.begin();
     725           0 :          i != listener._loads.end(); ++i)
     726             :     {
     727           0 :         os << *i << std::endl;
     728             :     }
     729           0 :     os << lunchbox::exdent << lunchbox::enableFlush;
     730           0 :     return os;
     731             : }
     732             : 
     733           0 : std::ostream& operator<<(std::ostream& os,
     734             :                          const ViewEqualizer::Listener::Load& load)
     735             : {
     736           0 :     os << "frame " << load.frame << " missing " << load.missing << " t "
     737           0 :        << load.time;
     738           0 :     return os;
     739             : }
     740             : }
     741          60 : }

Generated by: LCOV version 1.11