LCOV - code coverage report
Current view: top level - eq/server - compound.cpp (source / functions) Hit Total Coverage
Test: Equalizer Lines: 753 987 76.3 %
Date: 2017-12-16 05:07:20 Functions: 63 77 81.8 %

          Line data    Source code
       1             : 
       2             : /* Copyright (c) 2005-2017, Stefan Eilemann <eile@equalizergraphics.com>
       3             :  *                          Daniel Nachbaur <danielnachbaur@gmail.com>
       4             :  *                          Cedric Stalder <cedric.stalder@gmail.com>
       5             :  *
       6             :  * This library is free software; you can redistribute it and/or modify it under
       7             :  * the terms of the GNU Lesser General Public License version 2.1 as published
       8             :  * by the Free Software Foundation.
       9             :  *
      10             :  * This library is distributed in the hope that it will be useful, but WITHOUT
      11             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      12             :  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
      13             :  * details.
      14             :  *
      15             :  * You should have received a copy of the GNU Lesser General Public License
      16             :  * along with this library; if not, write to the Free Software Foundation, Inc.,
      17             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             :  */
      19             : 
      20             : #include "compound.h"
      21             : 
      22             : #include "canvas.h"
      23             : #include "channel.h"
      24             : #include "colorMask.h"
      25             : #include "compoundInitVisitor.h"
      26             : #include "compoundListener.h"
      27             : #include "compoundUpdateDataVisitor.h"
      28             : #include "compoundUpdateInputVisitor.h"
      29             : #include "compoundUpdateOutputVisitor.h"
      30             : #include "config.h"
      31             : #include "equalizers/equalizer.h"
      32             : #include "frame.h"
      33             : #include "frameData.h"
      34             : #include "global.h"
      35             : #include "layout.h"
      36             : #include "log.h"
      37             : #include "observer.h"
      38             : #include "segment.h"
      39             : #include "tileQueue.h"
      40             : #include "view.h"
      41             : 
      42             : #include <boost/foreach.hpp>
      43             : #include <eq/fabric/paths.h>
      44             : #include <lunchbox/algorithm.h>
      45             : #include <lunchbox/os.h>
      46             : 
      47             : #include <algorithm>
      48             : #include <math.h>
      49             : #include <vector>
      50             : 
      51             : #include "compoundActivateVisitor.h"
      52             : #include "compoundExitVisitor.h"
      53             : #include "compoundUpdateActivateVisitor.h"
      54             : 
      55             : namespace eq
      56             : {
      57             : namespace server
      58             : {
      59             : #define MAKE_ATTR_STRING(attr) (std::string("EQ_COMPOUND_") + #attr)
      60             : ;
      61             : 
      62         670 : Compound::Compound(Config* parent)
      63             :     : _config(parent)
      64             :     , _parent(0)
      65             :     , _usage(1.0f)
      66             :     , _taskID(0)
      67         670 :     , _frustum(_data.frustumData)
      68             : {
      69         670 :     LBASSERT(parent);
      70         670 :     parent->addCompound(this);
      71         670 :     LBLOG(LOG_INIT) << "New root compound @" << (void*)this << std::endl;
      72         670 : }
      73             : 
      74        3542 : Compound::Compound(Compound* parent)
      75             :     : _config(0)
      76             :     , _parent(parent)
      77             :     , _usage(1.0f)
      78             :     , _taskID(0)
      79        3542 :     , _frustum(_data.frustumData)
      80             : {
      81        3542 :     LBASSERT(parent);
      82        3542 :     parent->_addChild(this);
      83        3542 :     LBLOG(LOG_INIT) << "New compound child @" << (void*)this << std::endl;
      84        3542 : }
      85             : 
      86       12636 : Compound::~Compound()
      87             : {
      88        4212 :     _swapBarrier = 0;
      89             : 
      90       13518 :     for (Equalizers::const_iterator i = _equalizers.begin();
      91        9012 :          i != _equalizers.end(); ++i)
      92             :     {
      93         294 :         Equalizer* equalizer = *i;
      94         294 :         equalizer->attach(0);
      95         294 :         delete equalizer;
      96             :     }
      97        4212 :     _equalizers.clear();
      98             : 
      99       11296 :     while (!_children.empty())
     100             :     {
     101        3542 :         Compound* compound = _children.back();
     102        3542 :         _removeChild(compound);
     103        3542 :         delete compound;
     104             :     }
     105             : 
     106        4212 :     if (_config)
     107         670 :         _config->removeCompound(this);
     108             :     else
     109             :     {
     110        3542 :         LBASSERT(_parent);
     111        3542 :         _parent->_removeChild(this);
     112             :     }
     113             : 
     114        7020 :     for (FramesCIter i = _inputFrames.begin(); i != _inputFrames.end(); ++i)
     115        2808 :         delete *i;
     116        4212 :     _inputFrames.clear();
     117             : 
     118        6952 :     for (FramesCIter i = _outputFrames.begin(); i != _outputFrames.end(); ++i)
     119        2740 :         delete *i;
     120        4212 :     _outputFrames.clear();
     121             : 
     122       12636 :     for (TileQueuesCIter i = _inputTileQueues.begin();
     123        8424 :          i != _inputTileQueues.end(); ++i)
     124             :     {
     125           0 :         delete *i;
     126             :     }
     127        4212 :     _inputTileQueues.clear();
     128             : 
     129       12636 :     for (TileQueuesCIter i = _outputTileQueues.begin();
     130        8424 :          i != _outputTileQueues.end(); ++i)
     131             :     {
     132           0 :         delete *i;
     133             :     }
     134        4212 :     _outputTileQueues.clear();
     135        8424 : }
     136             : 
     137       12636 : Compound::Data::Data()
     138             :     : channel(0)
     139             :     , buffers(Frame::Buffer::undefined)
     140             :     , eyes(EYE_UNDEFINED)
     141             :     , tasks(fabric::TASK_DEFAULT)
     142             :     , period(LB_UNDEFINED_UINT32)
     143             :     , phase(LB_UNDEFINED_UINT32)
     144       12636 :     , maxFPS(std::numeric_limits<float>::max())
     145             : {
     146       12636 :     const Global* global = Global::instance();
     147       75816 :     for (int i = 0; i < IATTR_ALL; ++i)
     148       63180 :         iAttributes[i] =
     149       63180 :             global->getCompoundIAttribute(static_cast<IAttribute>(i));
     150       50544 :     for (size_t i = 0; i < NUM_EYES; ++i)
     151       37908 :         active[i] = 0;
     152       12636 : }
     153             : 
     154        3542 : void Compound::_addChild(Compound* child)
     155             : {
     156        3542 :     LBASSERT(child->_parent == this);
     157        3542 :     _children.push_back(child);
     158        3542 :     _fireChildAdded(child);
     159        3542 : }
     160             : 
     161        7084 : bool Compound::_removeChild(Compound* child)
     162             : {
     163        7084 :     Compounds::iterator i = lunchbox::find(_children, child);
     164        7084 :     if (i == _children.end())
     165        3542 :         return false;
     166             : 
     167        3542 :     _fireChildRemove(child);
     168        3542 :     _children.erase(i);
     169        3542 :     return true;
     170             : }
     171             : 
     172       12866 : Compound* Compound::getNext() const
     173             : {
     174       12866 :     if (!_parent)
     175        5180 :         return 0;
     176             : 
     177        7686 :     Compounds& siblings = _parent->_children;
     178             :     Compounds::iterator result =
     179        7686 :         std::find(siblings.begin(), siblings.end(), this);
     180        7686 :     if (result == siblings.end())
     181           0 :         return 0;
     182        7686 :     ++result;
     183        7686 :     if (result == siblings.end())
     184        3906 :         return 0;
     185             : 
     186        3780 :     return *result;
     187             : }
     188             : 
     189           8 : Node* Compound::getNode()
     190             : {
     191           8 :     Channel* channel = getChannel();
     192           8 :     return channel ? channel->getNode() : 0;
     193             : }
     194             : 
     195         264 : ServerPtr Compound::getServer()
     196             : {
     197         264 :     return getConfig()->getServer();
     198             : }
     199             : 
     200        2928 : void Compound::setChannel(Channel* channel)
     201             : {
     202        2928 :     _data.channel = channel;
     203             : 
     204             :     // Update swap barrier
     205        2928 :     if (!isDestination())
     206        3388 :         return;
     207             : 
     208        1240 :     Segment* segment = channel ? channel->getSegment() : 0;
     209        1240 :     if (!segment)
     210          12 :         return;
     211             : 
     212        2456 :     SwapBarrierPtr swapBarrier = segment->getSwapBarrier();
     213        1228 :     if (swapBarrier)
     214          72 :         setSwapBarrier(swapBarrier);
     215             : }
     216             : 
     217       15602 : const Channel* Compound::getChannel() const
     218             : {
     219       15602 :     if (_data.channel)
     220        8548 :         return _data.channel;
     221        7054 :     if (_parent)
     222        4460 :         return _parent->getChannel();
     223        2594 :     return 0;
     224             : }
     225             : 
     226       24452 : Channel* Compound::getChannel()
     227             : {
     228       24452 :     if (_data.channel)
     229       14438 :         return _data.channel;
     230       10014 :     if (_parent)
     231        7420 :         return _parent->getChannel();
     232        2594 :     return 0;
     233             : }
     234             : 
     235           0 : Window* Compound::getWindow()
     236             : {
     237           0 :     Channel* channel = getChannel();
     238           0 :     if (channel)
     239           0 :         return channel->getWindow();
     240           0 :     return 0;
     241             : }
     242             : 
     243           0 : const Window* Compound::getWindow() const
     244             : {
     245           0 :     const Channel* channel = getChannel();
     246           0 :     if (channel)
     247           0 :         return channel->getWindow();
     248           0 :     return 0;
     249             : }
     250             : 
     251           0 : Pipe* Compound::getPipe()
     252             : {
     253           0 :     Channel* channel = getChannel();
     254           0 :     if (channel)
     255           0 :         return channel->getPipe();
     256           0 :     return 0;
     257             : }
     258             : 
     259           0 : const Pipe* Compound::getPipe() const
     260             : {
     261           0 :     const Channel* channel = getChannel();
     262           0 :     if (channel)
     263           0 :         return channel->getPipe();
     264           0 :     return 0;
     265             : }
     266             : 
     267         294 : void Compound::addEqualizer(Equalizer* equalizer)
     268             : {
     269         294 :     if (equalizer)
     270         294 :         equalizer->attach(this);
     271             : 
     272         294 :     _equalizers.push_back(equalizer);
     273         294 : }
     274             : 
     275         240 : bool Compound::isInheritActive(const Eye eye) const
     276             : {
     277         240 :     const int32_t index = lunchbox::getIndexOfLastBit(eye);
     278         240 :     LBASSERT(index >= 0);
     279         240 :     LBASSERT(index < NUM_EYES);
     280         240 :     return _inherit.active[index];
     281             : }
     282             : 
     283           4 : bool Compound::isLastInheritEye(const Eye eye) const
     284             : {
     285           4 :     int32_t index = lunchbox::getIndexOfLastBit(eye);
     286           4 :     LBASSERT(index >= 0);
     287             : 
     288          20 :     while (++index < NUM_EYES)
     289           8 :         if (_inherit.active[index])
     290           0 :             return false;
     291           4 :     return true;
     292             : }
     293             : 
     294         296 : bool Compound::isActive() const
     295             : {
     296         296 :     bool active = false;
     297        1184 :     for (size_t i = 0; i < NUM_EYES; ++i)
     298         888 :         active = active || _inherit.active[i];
     299             : 
     300         296 :     if (!active)
     301         180 :         return false;
     302             : 
     303         116 :     const Channel* channel = getChannel();
     304         116 :     if (!channel)
     305          56 :         return true;
     306             : 
     307          60 :     if (!channel->isRunning())
     308           0 :         return false;
     309             : 
     310          60 :     LBASSERT(_inherit.channel);
     311          60 :     const View* view = _inherit.channel->getView();
     312          60 :     return channel->supportsView(view);
     313             : }
     314             : 
     315             : //---------------------------------------------------------------------------
     316             : // Listener interface
     317             : //---------------------------------------------------------------------------
     318         294 : void Compound::addListener(CompoundListener* listener)
     319             : {
     320         588 :     LB_TS_SCOPED(_serverThread);
     321         294 :     _listeners.push_back(listener);
     322         294 : }
     323             : 
     324         294 : void Compound::removeListener(CompoundListener* listener)
     325             : {
     326         588 :     LB_TS_SCOPED(_serverThread);
     327             :     CompoundListeners::iterator i =
     328         294 :         find(_listeners.begin(), _listeners.end(), listener);
     329         294 :     if (i != _listeners.end())
     330         294 :         _listeners.erase(i);
     331         294 : }
     332             : 
     333         264 : void Compound::fireUpdatePre(const uint32_t frameNumber)
     334             : {
     335         528 :     LB_TS_SCOPED(_serverThread);
     336             : 
     337         300 :     BOOST_FOREACH (CompoundListener* listener, _listeners)
     338          36 :         listener->notifyUpdatePre(this, frameNumber);
     339         264 : }
     340             : 
     341        3060 : const std::string& Compound::getIAttributeString(
     342             :     const Compound::IAttribute attr)
     343             : {
     344             :     static std::string iAttributeStrings[] = {
     345          12 :         MAKE_ATTR_STRING(IATTR_STEREO_MODE),
     346          12 :         MAKE_ATTR_STRING(IATTR_STEREO_ANAGLYPH_LEFT_MASK),
     347          12 :         MAKE_ATTR_STRING(IATTR_STEREO_ANAGLYPH_RIGHT_MASK),
     348        3102 :         MAKE_ATTR_STRING(IATTR_FILL1), MAKE_ATTR_STRING(IATTR_FILL2)};
     349        3060 :     return iAttributeStrings[attr];
     350             : }
     351             : 
     352        3542 : void Compound::_fireChildAdded(Compound* child)
     353             : {
     354        7084 :     LB_TS_SCOPED(_serverThread);
     355             : 
     356       13446 :     for (CompoundListeners::const_iterator i = _listeners.begin();
     357        8964 :          i != _listeners.end(); ++i)
     358             : 
     359         940 :         (*i)->notifyChildAdded(this, child);
     360        3542 : }
     361             : 
     362        3542 : void Compound::_fireChildRemove(Compound* child)
     363             : {
     364        7084 :     LB_TS_SCOPED(_serverThread);
     365             : 
     366       10626 :     for (CompoundListeners::const_iterator i = _listeners.begin();
     367        7084 :          i != _listeners.end(); ++i)
     368             : 
     369           0 :         (*i)->notifyChildRemove(this, child);
     370        3542 : }
     371             : 
     372             : //---------------------------------------------------------------------------
     373             : // I/O objects access
     374             : //---------------------------------------------------------------------------
     375         500 : void Compound::setSwapBarrier(SwapBarrierPtr barrier)
     376             : {
     377         500 :     if (barrier && barrier->getName().empty())
     378             :     {
     379         196 :         const Compound* root = getRoot();
     380         196 :         const std::string& rootName = root->getName();
     381         196 :         if (rootName.empty())
     382         192 :             barrier->setName("barrier");
     383             :         else
     384           4 :             barrier->setName("barrier." + rootName);
     385             :     }
     386             : 
     387         500 :     _swapBarrier = barrier;
     388         500 : }
     389             : 
     390        2808 : void Compound::addInputFrame(Frame* frame)
     391             : {
     392        2808 :     LBASSERT(frame);
     393        2808 :     if (frame->getName().empty())
     394           0 :         _setDefaultFrameName(frame);
     395        2808 :     _inputFrames.push_back(frame);
     396        2808 :     frame->setCompound(this);
     397        2808 : }
     398             : 
     399        2740 : void Compound::addOutputFrame(Frame* frame)
     400             : {
     401        2740 :     if (frame->getName().empty())
     402         544 :         _setDefaultFrameName(frame);
     403        2740 :     _outputFrames.push_back(frame);
     404        2740 :     frame->setCompound(this);
     405        2740 : }
     406             : 
     407           0 : void Compound::addInputTileQueue(TileQueue* tileQueue)
     408             : {
     409           0 :     LBASSERT(tileQueue);
     410           0 :     if (tileQueue->getName().empty())
     411           0 :         _setDefaultTileQueueName(tileQueue);
     412           0 :     _inputTileQueues.push_back(tileQueue);
     413           0 :     tileQueue->setCompound(this);
     414           0 : }
     415             : 
     416           0 : void Compound::removeInputTileQueue(TileQueue* tileQueue)
     417             : {
     418           0 :     TileQueuesIter i;
     419           0 :     i = find(_inputTileQueues.begin(), _inputTileQueues.end(), tileQueue);
     420           0 :     if (i != _inputTileQueues.end())
     421           0 :         _inputTileQueues.erase(i);
     422           0 : }
     423             : 
     424           0 : void Compound::addOutputTileQueue(TileQueue* tileQueue)
     425             : {
     426           0 :     if (tileQueue->getName().empty())
     427           0 :         _setDefaultTileQueueName(tileQueue);
     428           0 :     _outputTileQueues.push_back(tileQueue);
     429           0 :     tileQueue->setCompound(this);
     430           0 : }
     431             : 
     432           0 : void Compound::removeOutputTileQueue(TileQueue* tileQueue)
     433             : {
     434           0 :     TileQueuesIter i;
     435           0 :     i = find(_outputTileQueues.begin(), _outputTileQueues.end(), tileQueue);
     436           0 :     if (i != _outputTileQueues.end())
     437           0 :         _outputTileQueues.erase(i);
     438           0 : }
     439             : 
     440         544 : void Compound::_setDefaultFrameName(Frame* frame)
     441             : {
     442         544 :     for (Compound* compound = this; compound; compound = compound->getParent())
     443             :     {
     444         544 :         if (!compound->getName().empty())
     445             :         {
     446           0 :             frame->setName("frame." + compound->getName());
     447           0 :             return;
     448             :         }
     449             : 
     450         544 :         const Channel* channel = compound->getChannel();
     451         544 :         if (channel && !channel->getName().empty())
     452             :         {
     453         544 :             frame->setName("frame." + channel->getName());
     454         544 :             return;
     455             :         }
     456             :     }
     457           0 :     frame->setName("frame");
     458             : }
     459             : 
     460           0 : void Compound::_setDefaultTileQueueName(TileQueue* tileQueue)
     461             : {
     462           0 :     for (Compound* compound = this; compound; compound = compound->getParent())
     463             :     {
     464           0 :         if (!compound->getName().empty())
     465             :         {
     466           0 :             tileQueue->setName("queue." + compound->getName());
     467           0 :             return;
     468             :         }
     469             : 
     470           0 :         const Channel* channel = compound->getChannel();
     471           0 :         if (channel && !channel->getName().empty())
     472             :         {
     473           0 :             tileQueue->setName("queue." + channel->getName());
     474           0 :             return;
     475             :         }
     476             :     }
     477           0 :     tileQueue->setName("queue");
     478             : }
     479             : 
     480           0 : void Compound::adopt(Compound* child)
     481             : {
     482           0 :     if (child->_config)
     483             :     {
     484           0 :         child->_config->removeCompound(child);
     485           0 :         const_cast<Config*&>(child->_config) = 0;
     486             :     }
     487             :     else
     488             :     {
     489           0 :         LBASSERT(child->_parent);
     490           0 :         child->_parent->_removeChild(child);
     491             :     }
     492             : 
     493           0 :     const_cast<Compound*&>(child->_parent) = this;
     494           0 :     _addChild(child);
     495           0 : }
     496             : 
     497        6706 : bool Compound::isDestination() const
     498             : {
     499        6706 :     if (!getChannel())
     500          84 :         return false;
     501             : 
     502        8744 :     for (const Compound* compound = getParent(); compound;
     503             :          compound = compound->getParent())
     504             :     {
     505        6236 :         if (compound->getChannel())
     506        4114 :             return false;
     507             :     }
     508             : 
     509        2508 :     return true;
     510             : }
     511             : 
     512           8 : bool Compound::hasDestinationChannel() const
     513             : {
     514           8 :     return getChannel() && getChannel() == getInheritChannel();
     515             : }
     516             : 
     517          16 : RenderContext Compound::setupRenderContext(const Eye eye) const
     518             : {
     519          16 :     RenderContext context;
     520          16 :     context.pvp = _inherit.pvp;
     521          16 :     context.overdraw = _inherit.overdraw;
     522          16 :     context.vp = _inherit.vp;
     523          16 :     context.range = _inherit.range;
     524          16 :     context.pixel = _inherit.pixel;
     525          16 :     context.subPixel = _inherit.subPixel;
     526          16 :     context.zoom = _inherit.zoom;
     527          16 :     context.period = _inherit.period;
     528          16 :     context.phase = _inherit.phase;
     529          16 :     context.offset.x() = context.pvp.x;
     530          16 :     context.offset.y() = context.pvp.y;
     531          16 :     context.eye = eye;
     532          16 :     context.taskID = _taskID;
     533          16 :     _computeFrustum(context);
     534          16 :     return context;
     535             : }
     536             : 
     537             : //---------------------------------------------------------------------------
     538             : // frustum operations
     539             : //---------------------------------------------------------------------------
     540         174 : void Compound::setWall(const Wall& wall)
     541             : {
     542         174 :     _frustum.setWall(wall);
     543         174 :     LBVERB << "Wall: " << _data.frustumData << std::endl;
     544         174 : }
     545             : 
     546           0 : void Compound::setProjection(const Projection& projection)
     547             : {
     548           0 :     _frustum.setProjection(projection);
     549           0 :     LBVERB << "Projection: " << _data.frustumData << std::endl;
     550           0 : }
     551             : 
     552         160 : void Compound::updateFrustum(const Vector3f& eye, const float ratio)
     553             : {
     554         160 :     if (!isDestination()) // only set view/segment frusta on destination
     555         202 :         return;
     556             : 
     557         118 :     Channel* channel = getChannel();
     558         118 :     Segment* segment = channel->getSegment();
     559         118 :     const View* view = channel->getView();
     560         118 :     if (!segment || !view)
     561           0 :         return;
     562             : 
     563         118 :     if (view->getCurrentType() != Frustum::TYPE_NONE) // frustum from view:
     564             :     {
     565             :         // set compound frustum =
     566             :         //         segment frustum X channel/view coverage
     567         118 :         const Viewport& segmentVP = segment->getViewport();
     568         118 :         const Viewport& viewVP = view->getViewport();
     569         118 :         const Viewport coverage = viewVP.getCoverage(segmentVP);
     570             : 
     571         118 :         Wall wall(view->getWall());
     572             : 
     573         118 :         wall.apply(coverage);
     574         118 :         wall.moveFocus(eye, ratio);
     575         118 :         _updateOverdraw(wall);
     576         118 :         wall.scale(view->getModelUnit());
     577             : 
     578         118 :         switch (view->getCurrentType())
     579             :         {
     580             :         case Frustum::TYPE_WALL:
     581         118 :             setWall(wall);
     582         118 :             LBLOG(LOG_VIEW) << "View wall for " << channel->getName() << ": "
     583         118 :                             << wall << std::endl;
     584         236 :             return;
     585             : 
     586             :         case Frustum::TYPE_PROJECTION:
     587             :         {
     588           0 :             Projection projection(view->getProjection()); // keep distance
     589           0 :             projection = wall;
     590           0 :             setProjection(projection);
     591           0 :             LBLOG(LOG_VIEW) << "View projection for " << channel->getName()
     592           0 :                             << ": " << projection << std::endl;
     593           0 :             return;
     594             :         }
     595             : 
     596             :         default:
     597           0 :             LBUNIMPLEMENTED;
     598             :         }
     599             :     }
     600             :     // else frustum from segment
     601             : 
     602           0 :     segment->inheritFrustum();
     603             : 
     604             :     // set compound frustum =
     605             :     //         segment frustum X channel/segment coverage
     606           0 :     const Channel* outputChannel = segment->getChannel();
     607           0 :     LBASSERT(outputChannel);
     608             : 
     609           0 :     const Viewport& outputVP = outputChannel->getViewport();
     610           0 :     const Viewport& channelVP = channel->getViewport();
     611           0 :     const Viewport coverage = outputVP.getCoverage(channelVP);
     612             : 
     613           0 :     Wall wall(segment->getWall());
     614           0 :     wall.moveFocus(eye, ratio);
     615           0 :     wall.apply(coverage);
     616           0 :     _updateOverdraw(wall);
     617           0 :     wall.scale(view->getModelUnit());
     618             : 
     619           0 :     switch (segment->getCurrentType())
     620             :     {
     621             :     case Frustum::TYPE_WALL:
     622             :     {
     623           0 :         setWall(wall);
     624           0 :         LBLOG(LOG_VIEW) << "Segment wall for " << channel->getName() << ": "
     625           0 :                         << wall << std::endl;
     626           0 :         return;
     627             :     }
     628             : 
     629             :     case Frustum::TYPE_PROJECTION:
     630             :     {
     631           0 :         Projection projection(segment->getProjection());
     632           0 :         projection = wall;
     633           0 :         setProjection(projection);
     634           0 :         LBLOG(LOG_VIEW) << "Segment projection for " << channel->getName()
     635           0 :                         << ": " << projection << std::endl;
     636           0 :         return;
     637             :     }
     638             :     default:
     639           0 :         LBUNIMPLEMENTED;
     640             :     }
     641             : }
     642             : 
     643          16 : void Compound::_computeFrustum(RenderContext& context) const
     644             : {
     645             :     // compute eye position in screen space
     646          16 :     const Vector3f& eyeWorld = _getEyePosition(context.eye);
     647          16 :     const FrustumData& frustumData = _inherit.frustumData;
     648          16 :     const Matrix4f& xfm = frustumData.getTransform();
     649          16 :     const Vector3f eyeWall = xfm * eyeWorld;
     650             : 
     651          16 :     LBVERB << "Eye position world: " << eyeWorld << " wall " << eyeWall
     652          16 :            << std::endl;
     653          16 :     _computePerspective(context, eyeWall);
     654          16 :     _computeOrtho(context, eyeWall);
     655          16 : }
     656             : 
     657           0 : void Compound::computeTileFrustum(Frustumf& frustum, const Eye eye, Viewport vp,
     658             :                                   bool ortho) const
     659             : {
     660           0 :     const Vector3f& eyeWorld = _getEyePosition(eye);
     661           0 :     const FrustumData& frustumData = _inherit.frustumData;
     662           0 :     const Matrix4f& xfm = frustumData.getTransform();
     663           0 :     const Vector3f eyeWall = xfm * eyeWorld;
     664             : 
     665           0 :     _computeFrustumCorners(frustum, frustumData, eyeWall, ortho, &vp);
     666           0 : }
     667             : 
     668             : namespace
     669             : {
     670          32 : static void _computeHeadTransform(Matrix4f& result, const Matrix4f& xfm,
     671             :                                   const Vector3f& eye)
     672             : {
     673             :     // headTransform = -trans(eye) * view matrix (frustum position)
     674         160 :     for (int i = 0; i < 16; i += 4)
     675             :     {
     676         128 :         result.array[i] = xfm.array[i] - eye[0] * xfm.array[i + 3];
     677         128 :         result.array[i + 1] = xfm.array[i + 1] - eye[1] * xfm.array[i + 3];
     678         128 :         result.array[i + 2] = xfm.array[i + 2] - eye[2] * xfm.array[i + 3];
     679         128 :         result.array[i + 3] = xfm.array[i + 3];
     680             :     }
     681          32 : }
     682             : }
     683             : 
     684          16 : void Compound::_computePerspective(RenderContext& context,
     685             :                                    const Vector3f& eye) const
     686             : {
     687          16 :     const FrustumData& frustumData = _inherit.frustumData;
     688             : 
     689          16 :     _computeFrustumCorners(context.frustum, frustumData, eye, false);
     690          16 :     _computeHeadTransform(context.headTransform, frustumData.getTransform(),
     691          16 :                           eye);
     692             : 
     693          16 :     const bool isHMD = (frustumData.getType() != Wall::TYPE_FIXED);
     694          16 :     if (isHMD)
     695           0 :         context.headTransform *= _getInverseHeadMatrix();
     696          16 : }
     697             : 
     698          16 : void Compound::_computeOrtho(RenderContext& context, const Vector3f& eye) const
     699             : {
     700             :     // Compute corners for cyclop eye without perspective correction:
     701          16 :     const Vector3f& cyclopWorld = _getEyePosition(EYE_CYCLOP);
     702          16 :     const FrustumData& frustumData = _inherit.frustumData;
     703          16 :     const Matrix4f& xfm = frustumData.getTransform();
     704          16 :     const Vector3f cyclopWall = xfm * cyclopWorld;
     705             : 
     706          16 :     _computeFrustumCorners(context.ortho, frustumData, cyclopWall, true);
     707          16 :     _computeHeadTransform(context.orthoTransform, xfm, eye);
     708             : 
     709             :     // Apply stereo shearing
     710          16 :     context.orthoTransform.array[8] += (cyclopWall[0] - eye[0]) / eye[2];
     711          16 :     context.orthoTransform.array[9] += (cyclopWall[1] - eye[1]) / eye[2];
     712             : 
     713          16 :     const bool isHMD = (frustumData.getType() != Wall::TYPE_FIXED);
     714          16 :     if (isHMD)
     715           0 :         context.orthoTransform *= _getInverseHeadMatrix();
     716          16 : }
     717             : 
     718          32 : Vector3f Compound::_getEyePosition(const Eye eye) const
     719             : {
     720          32 :     const FrustumData& frustumData = _inherit.frustumData;
     721          32 :     const Channel* destChannel = getInheritChannel();
     722          32 :     const View* view = destChannel->getView();
     723          32 :     const Observer* observer = view ? view->getObserver() : 0;
     724          32 :     const float modelUnit = view ? view->getModelUnit() : 1.f;
     725             : 
     726          32 :     if (observer)
     727          32 :         return modelUnit * (frustumData.getType() == Wall::TYPE_FIXED
     728             :                                 ? observer->getEyeWorld(eye)
     729          32 :                                 : observer->getEyePosition(eye));
     730             : 
     731             :     const float eyeBase_2 =
     732           0 :         0.5f * modelUnit * getConfig()->getFAttribute(Config::FATTR_EYE_BASE);
     733           0 :     switch (eye)
     734             :     {
     735             :     case EYE_LEFT:
     736           0 :         return Vector3f(-eyeBase_2, 0.f, 0.f);
     737             :     case EYE_RIGHT:
     738           0 :         return Vector3f(eyeBase_2, 0.f, 0.f);
     739             : 
     740             :     default:
     741           0 :         LBUNIMPLEMENTED;
     742             :     case EYE_CYCLOP:
     743           0 :         return Vector3f();
     744             :     }
     745             : }
     746             : 
     747           0 : const Matrix4f& Compound::_getInverseHeadMatrix() const
     748             : {
     749           0 :     const Channel* destChannel = getInheritChannel();
     750           0 :     const View* view = destChannel->getView();
     751             :     const Observer* observer =
     752           0 :         static_cast<const Observer*>(view ? view->getObserver() : 0);
     753             : 
     754           0 :     if (observer)
     755           0 :         return observer->getInverseHeadMatrix();
     756             : 
     757           0 :     static const Matrix4f identity;
     758           0 :     return identity;
     759             : }
     760             : 
     761          32 : void Compound::_computeFrustumCorners(Frustumf& frustum,
     762             :                                       const FrustumData& frustumData,
     763             :                                       const Vector3f& eye, const bool ortho,
     764             :                                       const Viewport* const invp) const
     765             : {
     766          32 :     const Channel* destination = getInheritChannel();
     767          32 :     frustum = destination->getFrustum();
     768             : 
     769          32 :     const float ratio = ortho ? 1.0f : frustum.nearPlane() / eye.z();
     770          32 :     const float width_2 = frustumData.getWidth() * .5f;
     771          32 :     const float height_2 = frustumData.getHeight() * .5f;
     772             : 
     773          32 :     if (eye.z() > 0 || ortho)
     774             :     {
     775          32 :         frustum.left() = (-width_2 - eye.x()) * ratio;
     776          32 :         frustum.right() = (width_2 - eye.x()) * ratio;
     777          32 :         frustum.bottom() = (-height_2 - eye.y()) * ratio;
     778          32 :         frustum.top() = (height_2 - eye.y()) * ratio;
     779             :     }
     780             :     else // eye behind near plane - 'mirror' x
     781             :     {
     782           0 :         frustum.left() = (width_2 - eye.x()) * ratio;
     783           0 :         frustum.right() = (-width_2 - eye.x()) * ratio;
     784           0 :         frustum.bottom() = (height_2 + eye.y()) * ratio;
     785           0 :         frustum.top() = (-height_2 + eye.y()) * ratio;
     786             :     }
     787             : 
     788             :     // move frustum according to pixel decomposition
     789          32 :     const Pixel& pixel = getInheritPixel();
     790          32 :     if (pixel != Pixel::ALL && pixel.isValid())
     791             :     {
     792           0 :         const Channel* inheritChannel = getInheritChannel();
     793           0 :         const PixelViewport& destPVP = inheritChannel->getPixelViewport();
     794             : 
     795           0 :         if (pixel.w > 1)
     796             :         {
     797           0 :             const float frustumWidth = frustum.right() - frustum.left();
     798             :             const float pixelWidth =
     799           0 :                 frustumWidth / static_cast<float>(destPVP.w);
     800           0 :             const float jitter = pixelWidth * pixel.x - pixelWidth * .5f;
     801             : 
     802           0 :             frustum.left() += jitter;
     803           0 :             frustum.right() += jitter;
     804             :         }
     805           0 :         if (pixel.h > 1)
     806             :         {
     807           0 :             const float frustumHeight = frustum.bottom() - frustum.top();
     808           0 :             const float pixelHeight = frustumHeight / float(destPVP.h);
     809           0 :             const float jitter = pixelHeight * pixel.y + pixelHeight * .5f;
     810             : 
     811           0 :             frustum.top() -= jitter;
     812           0 :             frustum.bottom() -= jitter;
     813             :         }
     814             :     }
     815             : 
     816             :     // adjust to viewport (screen-space decomposition)
     817             :     // Note: vp is computed pixel-correct by Compound::updateInheritData()
     818          32 :     const Viewport& vp = invp ? *invp : _inherit.vp;
     819          32 :     if (vp != Viewport::FULL && vp.isValid())
     820             :     {
     821          16 :         const float frustumWidth = frustum.right() - frustum.left();
     822          16 :         frustum.left() += frustumWidth * vp.x;
     823          16 :         frustum.right() = frustum.left() + frustumWidth * vp.w;
     824             : 
     825          16 :         const float frustumHeight = frustum.top() - frustum.bottom();
     826          16 :         frustum.bottom() += frustumHeight * vp.y;
     827          16 :         frustum.top() = frustum.bottom() + frustumHeight * vp.h;
     828             :     }
     829          32 : }
     830             : 
     831         118 : void Compound::_updateOverdraw(Wall& wall)
     832             : {
     833         118 :     Channel* channel = getChannel();
     834         118 :     LBASSERT(channel);
     835         118 :     if (!channel)
     836           0 :         return;
     837             : 
     838         118 :     const Segment* segment = channel->getSegment();
     839         118 :     const View* view = channel->getView();
     840         118 :     LBASSERT(segment && view);
     841         118 :     if (!segment || !view)
     842           0 :         return;
     843             : 
     844         118 :     const Viewport& segmentVP = segment->getViewport();
     845         118 :     const Viewport& viewVP = view->getViewport();
     846         118 :     const Vector2i& overdraw = view->getOverdraw();
     847         118 :     Vector4i channelOverdraw;
     848             : 
     849             :     // compute overdraw
     850         118 :     if (overdraw.x() && viewVP.x < segmentVP.x)
     851           0 :         channelOverdraw.x() = overdraw.x();
     852             : 
     853         118 :     if (overdraw.x() && viewVP.getXEnd() > segmentVP.getXEnd())
     854           0 :         channelOverdraw.z() = overdraw.x();
     855             : 
     856         118 :     if (overdraw.y() && viewVP.y < segmentVP.y)
     857           0 :         channelOverdraw.y() = overdraw.y();
     858             : 
     859         118 :     if (overdraw.y() && viewVP.getYEnd() > segmentVP.getYEnd())
     860           0 :         channelOverdraw.w() = overdraw.y();
     861             : 
     862             :     // apply to frustum
     863         118 :     if (channelOverdraw.x() > 0)
     864             :     {
     865           0 :         const PixelViewport& pvp = channel->getPixelViewport();
     866           0 :         const float ratio = static_cast<float>(pvp.w + channelOverdraw.x()) /
     867           0 :                             static_cast<float>(pvp.w);
     868           0 :         wall.resizeLeft(ratio);
     869             :     }
     870             : 
     871         118 :     if (channelOverdraw.z() > 0)
     872             :     {
     873           0 :         const PixelViewport& pvp = channel->getPixelViewport();
     874           0 :         const float ratio = static_cast<float>(pvp.w + channelOverdraw.x() +
     875           0 :                                                channelOverdraw.z()) /
     876           0 :                             static_cast<float>(pvp.w + channelOverdraw.x());
     877           0 :         wall.resizeRight(ratio);
     878             :     }
     879             : 
     880         118 :     if (channelOverdraw.y() > 0)
     881             :     {
     882           0 :         const PixelViewport& pvp = channel->getPixelViewport();
     883           0 :         const float ratio = static_cast<float>(pvp.h + channelOverdraw.y()) /
     884           0 :                             static_cast<float>(pvp.h);
     885           0 :         wall.resizeBottom(ratio);
     886             :     }
     887             : 
     888         118 :     if (channelOverdraw.w() > 0)
     889             :     {
     890           0 :         const PixelViewport& pvp = channel->getPixelViewport();
     891           0 :         const float ratio = static_cast<float>(pvp.h + +channelOverdraw.y() +
     892           0 :                                                channelOverdraw.w()) /
     893           0 :                             static_cast<float>(pvp.h + channelOverdraw.y());
     894           0 :         wall.resizeTop(ratio);
     895             :     }
     896             : 
     897         118 :     channel->setOverdraw(channelOverdraw);
     898             : }
     899             : 
     900             : //---------------------------------------------------------------------------
     901             : // accept
     902             : //---------------------------------------------------------------------------
     903             : namespace
     904             : {
     905             : template <class C>
     906        3260 : VisitorResult _accept(C* compound, CompoundVisitor& visitor)
     907             : {
     908        3260 :     if (compound->isLeaf())
     909          14 :         return visitor.visitLeaf(compound);
     910             : 
     911        3246 :     C* current = compound;
     912        3246 :     VisitorResult result = TRAVERSE_CONTINUE;
     913             : 
     914        6454 :     while (true)
     915             :     {
     916        9700 :         C* parent = current->getParent();
     917        9700 :         C* next = current->getNext();
     918             : 
     919        9700 :         const Compounds& children = current->getChildren();
     920        9700 :         C* child = children.empty() ? 0 : children[0];
     921             : 
     922             :         //---------- down-right traversal
     923        9700 :         if (!child) // leaf
     924             :         {
     925        3986 :             switch (visitor.visitLeaf(current))
     926             :             {
     927             :             case TRAVERSE_TERMINATE:
     928         174 :                 return TRAVERSE_TERMINATE;
     929             : 
     930             :             case TRAVERSE_PRUNE:
     931        1826 :                 result = TRAVERSE_PRUNE;
     932        1826 :                 current = next;
     933        1826 :                 break;
     934             : 
     935             :             case TRAVERSE_CONTINUE:
     936        1986 :                 current = next;
     937        1986 :                 break;
     938             : 
     939             :             default:
     940           0 :                 LBASSERTINFO(0, "Unreachable");
     941             :             }
     942             :         }
     943             :         else // node
     944             :         {
     945        5714 :             switch (visitor.visitPre(current))
     946             :             {
     947             :             case TRAVERSE_TERMINATE:
     948         244 :                 return TRAVERSE_TERMINATE;
     949             : 
     950             :             case TRAVERSE_PRUNE:
     951        2072 :                 result = TRAVERSE_PRUNE;
     952        2072 :                 current = next;
     953        2072 :                 break;
     954             : 
     955             :             case TRAVERSE_CONTINUE:
     956        3398 :                 current = child;
     957        3398 :                 break;
     958             : 
     959             :             default:
     960           0 :                 LBASSERTINFO(0, "Unreachable");
     961             :             }
     962             :         }
     963             : 
     964             :         //---------- up-right traversal
     965        9282 :         if (!current && !parent)
     966         886 :             return TRAVERSE_CONTINUE;
     967             : 
     968       10844 :         while (!current)
     969             :         {
     970        3166 :             current = parent;
     971        3166 :             parent = current->getParent();
     972        3166 :             next = current->getNext();
     973             : 
     974        3166 :             switch (visitor.visitPost(current))
     975             :             {
     976             :             case TRAVERSE_TERMINATE:
     977           0 :                 return TRAVERSE_TERMINATE;
     978             : 
     979             :             case TRAVERSE_PRUNE:
     980           0 :                 result = TRAVERSE_PRUNE;
     981           0 :                 break;
     982             : 
     983             :             case TRAVERSE_CONTINUE:
     984        3166 :                 break;
     985             : 
     986             :             default:
     987           0 :                 LBASSERTINFO(0, "Unreachable");
     988             :             }
     989             : 
     990        3166 :             if (current == compound)
     991        1942 :                 return result;
     992             : 
     993        1224 :             current = next;
     994             :         }
     995             :     }
     996             :     return result;
     997             : }
     998             : }
     999             : 
    1000        3176 : VisitorResult Compound::accept(CompoundVisitor& visitor)
    1001             : {
    1002        3176 :     return _accept(this, visitor);
    1003             : }
    1004             : 
    1005          84 : VisitorResult Compound::accept(CompoundVisitor& visitor) const
    1006             : {
    1007          84 :     return _accept(this, visitor);
    1008             : }
    1009             : 
    1010             : //---------------------------------------------------------------------------
    1011             : // Operations
    1012             : //---------------------------------------------------------------------------
    1013             : 
    1014          16 : void Compound::activate(const uint32_t eyes)
    1015             : {
    1016          64 :     for (size_t i = 0; i < NUM_EYES; ++i)
    1017             :     {
    1018          48 :         const Eye eye = Eye(1 << i);
    1019          48 :         if (!(eyes & eye))
    1020          50 :             continue;
    1021             : 
    1022          44 :         ++_data.active[i];
    1023          44 :         if (!getChannel()) // non-dest root compound
    1024          42 :             continue;
    1025             : 
    1026           4 :         CompoundActivateVisitor channelActivate(true, eye);
    1027           2 :         accept(channelActivate);
    1028             :     }
    1029          16 : }
    1030             : 
    1031          16 : void Compound::deactivate(const uint32_t eyes)
    1032             : {
    1033          64 :     for (size_t i = 0; i < NUM_EYES; ++i)
    1034             :     {
    1035          48 :         const fabric::Eye eye = static_cast<Eye>(1 << i);
    1036          48 :         if (!(eyes & eye))
    1037          50 :             continue;
    1038             : 
    1039          44 :         LBASSERT(_data.active[i]);
    1040          44 :         --_data.active[i];
    1041          44 :         if (!getChannel()) // non-dest root compound
    1042          42 :             continue;
    1043             : 
    1044           4 :         CompoundActivateVisitor channelDeactivate(false, eye);
    1045           2 :         accept(channelDeactivate);
    1046             :     }
    1047          16 : }
    1048             : 
    1049          14 : void Compound::init()
    1050             : {
    1051          28 :     CompoundInitVisitor initVisitor;
    1052          14 :     accept(initVisitor);
    1053          14 : }
    1054             : 
    1055          14 : void Compound::exit()
    1056             : {
    1057          28 :     CompoundExitVisitor visitor;
    1058          14 :     accept(visitor);
    1059          14 : }
    1060             : 
    1061         132 : void Compound::register_()
    1062             : {
    1063         264 :     ServerPtr server = getServer();
    1064         132 :     const uint32_t latency = getConfig()->getLatency();
    1065             : 
    1066         492 :     for (Frames::const_iterator i = _outputFrames.begin();
    1067         328 :          i != _outputFrames.end(); ++i)
    1068             :     {
    1069          32 :         Frame* frame = *i;
    1070          32 :         server->registerObject(frame);
    1071          32 :         frame->setAutoObsolete(latency);
    1072          32 :         LBLOG(LOG_ASSEMBLY) << "Output frame \"" << frame->getName() << "\" id "
    1073          32 :                             << frame->getID() << std::endl;
    1074             :     }
    1075             : 
    1076         492 :     for (Frames::const_iterator i = _inputFrames.begin();
    1077         328 :          i != _inputFrames.end(); ++i)
    1078             :     {
    1079          32 :         Frame* frame = *i;
    1080          32 :         server->registerObject(frame);
    1081          32 :         frame->setAutoObsolete(latency);
    1082          32 :         LBLOG(LOG_ASSEMBLY) << "Input frame \"" << frame->getName() << "\" id "
    1083          32 :                             << frame->getID() << std::endl;
    1084             :     }
    1085             : 
    1086         396 :     for (TileQueuesCIter i = _inputTileQueues.begin();
    1087         264 :          i != _inputTileQueues.end(); ++i)
    1088             :     {
    1089           0 :         TileQueue* queue = *i;
    1090           0 :         server->registerObject(queue);
    1091           0 :         queue->setAutoObsolete(latency);
    1092           0 :         LBLOG(LOG_ASSEMBLY) << "Input queue \"" << queue->getName() << "\" id "
    1093           0 :                             << queue->getID() << std::endl;
    1094             :     }
    1095             : 
    1096         396 :     for (TileQueuesCIter i = _outputTileQueues.begin();
    1097         264 :          i != _outputTileQueues.end(); ++i)
    1098             :     {
    1099           0 :         TileQueue* queue = *i;
    1100           0 :         server->registerObject(queue);
    1101           0 :         queue->setAutoObsolete(latency);
    1102           0 :         LBLOG(LOG_ASSEMBLY) << "Output queue \"" << queue->getName() << "\" id "
    1103           0 :                             << queue->getID() << std::endl;
    1104             :     }
    1105         132 : }
    1106             : 
    1107         132 : void Compound::deregister()
    1108             : {
    1109         264 :     ServerPtr server = getServer();
    1110             : 
    1111         492 :     for (Frames::const_iterator i = _outputFrames.begin();
    1112         328 :          i != _outputFrames.end(); ++i)
    1113             :     {
    1114          32 :         Frame* frame = *i;
    1115          32 :         frame->flush();
    1116          32 :         server->deregisterObject(frame);
    1117             :     }
    1118             : 
    1119         492 :     for (Frames::const_iterator i = _inputFrames.begin();
    1120         328 :          i != _inputFrames.end(); ++i)
    1121             :     {
    1122          32 :         Frame* frame = *i;
    1123          32 :         server->deregisterObject(frame);
    1124             :     }
    1125             : 
    1126         396 :     for (TileQueuesCIter i = _inputTileQueues.begin();
    1127         264 :          i != _inputTileQueues.end(); ++i)
    1128             :     {
    1129           0 :         TileQueue* queue = *i;
    1130           0 :         server->deregisterObject(queue);
    1131             :     }
    1132             : 
    1133         396 :     for (TileQueuesCIter i = _outputTileQueues.begin();
    1134         264 :          i != _outputTileQueues.end(); ++i)
    1135             :     {
    1136           0 :         TileQueue* queue = *i;
    1137           0 :         queue->flush();
    1138           0 :         server->deregisterObject(queue);
    1139             :     }
    1140         132 : }
    1141             : 
    1142          82 : void Compound::backup()
    1143             : {
    1144          82 :     _backup = _data;
    1145             : 
    1146         100 :     for (EqualizersCIter i = _equalizers.begin(); i != _equalizers.end(); ++i)
    1147          18 :         (*i)->backup();
    1148          82 : }
    1149             : 
    1150           0 : void Compound::restore()
    1151             : {
    1152           0 :     _data = _backup;
    1153             : 
    1154           0 :     for (EqualizersCIter i = _equalizers.begin(); i != _equalizers.end(); ++i)
    1155           0 :         (*i)->restore();
    1156           0 : }
    1157             : 
    1158             : //---------------------------------------------------------------------------
    1159             : // pre-render compound state update
    1160             : //---------------------------------------------------------------------------
    1161          28 : void Compound::update(const uint32_t frameNumber)
    1162             : {
    1163             :     // https://github.com/Eyescale/Equalizer/issues/76
    1164          56 :     CompoundUpdateActivateVisitor updateActivateVisitor(frameNumber);
    1165          28 :     accept(updateActivateVisitor);
    1166             : 
    1167          56 :     CompoundUpdateDataVisitor updateDataVisitor(frameNumber);
    1168          28 :     accept(updateDataVisitor);
    1169             : 
    1170          56 :     CompoundUpdateOutputVisitor updateOutputVisitor(frameNumber);
    1171          28 :     accept(updateOutputVisitor);
    1172             : 
    1173          28 :     const FrameMap& outputFrames = updateOutputVisitor.getOutputFrames();
    1174          28 :     const TileQueueMap& outputQueues = updateOutputVisitor.getOutputQueues();
    1175          56 :     CompoundUpdateInputVisitor updateInputVisitor(outputFrames, outputQueues);
    1176          28 :     accept(updateInputVisitor);
    1177             : 
    1178             :     // commit output frames after input frames have been set
    1179          32 :     for (FrameMapCIter i = outputFrames.begin(); i != outputFrames.end(); ++i)
    1180             :     {
    1181           4 :         Frame* frame = i->second;
    1182           4 :         frame->commit();
    1183             :     }
    1184             : 
    1185          28 :     const BarrierMap& swapBarriers = updateOutputVisitor.getSwapBarriers();
    1186          28 :     for (BarrierMapCIter i = swapBarriers.begin(); i != swapBarriers.end(); ++i)
    1187             :     {
    1188           0 :         co::Barrier* barrier = i->second;
    1189           0 :         LBASSERT(barrier->isGood());
    1190           0 :         if (barrier->getHeight() > 1)
    1191           0 :             barrier->commit();
    1192             :     }
    1193          28 : }
    1194             : 
    1195         660 : void Compound::updateInheritData(const uint32_t frameNumber)
    1196             : {
    1197         660 :     _data.pixel.validate();
    1198         660 :     _data.subPixel.validate();
    1199         660 :     _data.zoom.validate();
    1200             : 
    1201         660 :     if (isRoot())
    1202          70 :         _updateInheritRoot();
    1203             :     else
    1204         590 :         _updateInheritNode();
    1205             : 
    1206         660 :     if (_inherit.channel)
    1207             :     {
    1208         590 :         _updateInheritStereo();
    1209         590 :         _updateInheritActive(frameNumber);
    1210             :     }
    1211             : 
    1212         660 :     if (_inherit.pvp.isValid())
    1213             :     {
    1214         590 :         _inherit.pvp.apply(_data.pixel);
    1215             : 
    1216             :         // Zoom
    1217         590 :         const PixelViewport unzoomedPVP(_inherit.pvp);
    1218         590 :         _inherit.pvp.apply(_data.zoom);
    1219             : 
    1220             :         // update inherit zoom to be pixel-correct with the integer-rounded pvp
    1221         590 :         const Zoom zoom = _inherit.pvp.getZoom(unzoomedPVP);
    1222         590 :         _inherit.zoom *= zoom;
    1223             :     }
    1224             : 
    1225             :     // Tasks
    1226         660 :     updateInheritTasks();
    1227             : 
    1228         660 :     const View* view = _inherit.channel ? _inherit.channel->getView() : 0;
    1229         660 :     const Channel* channel = getChannel();
    1230         660 :     if (channel && !channel->supportsView(view))
    1231           0 :         _inherit.tasks = fabric::TASK_NONE;
    1232             : 
    1233         660 :     if (!_inherit.pvp.hasArea() || !_inherit.range.hasData())
    1234             :         // Channels with no PVP or range do not execute tasks
    1235          70 :         _inherit.tasks = fabric::TASK_NONE;
    1236         660 : }
    1237             : 
    1238          70 : void Compound::_updateInheritRoot()
    1239             : {
    1240          70 :     LBASSERT(!_parent);
    1241             : 
    1242          70 :     const PixelViewport oldPVP(_inherit.pvp);
    1243          70 :     _inherit = _data;
    1244          70 :     _inherit.pvp = oldPVP;
    1245             : 
    1246          70 :     _inherit.zoom = Zoom::NONE; // will be reapplied by parent method
    1247          70 :     _updateInheritPVP();
    1248             : 
    1249          70 :     if (_inherit.eyes == fabric::EYE_UNDEFINED)
    1250          70 :         _inherit.eyes = fabric::EYES_ALL;
    1251           0 :     else if (_inherit.channel)
    1252             :     {
    1253           0 :         const View* view = _inherit.channel->getView();
    1254           0 :         if (!view)
    1255           0 :             _inherit.eyes = EYE_CYCLOP;
    1256             :     }
    1257             : 
    1258          70 :     if (_inherit.period == LB_UNDEFINED_UINT32)
    1259          70 :         _inherit.period = 1;
    1260             : 
    1261          70 :     if (_inherit.phase == LB_UNDEFINED_UINT32)
    1262          70 :         _inherit.phase = 0;
    1263             : 
    1264          70 :     if (_inherit.buffers == Frame::Buffer::undefined)
    1265          70 :         _inherit.buffers = Frame::Buffer::color;
    1266             : 
    1267          70 :     if (_inherit.iAttributes[IATTR_STEREO_MODE] == UNDEFINED)
    1268          70 :         _inherit.iAttributes[IATTR_STEREO_MODE] = fabric::AUTO;
    1269             : 
    1270          70 :     if (_inherit.iAttributes[IATTR_STEREO_ANAGLYPH_LEFT_MASK] == UNDEFINED)
    1271          70 :         _inherit.iAttributes[IATTR_STEREO_ANAGLYPH_LEFT_MASK] = COLOR_MASK_RED;
    1272             : 
    1273          70 :     if (_inherit.iAttributes[IATTR_STEREO_ANAGLYPH_RIGHT_MASK] == UNDEFINED)
    1274          70 :         _inherit.iAttributes[IATTR_STEREO_ANAGLYPH_RIGHT_MASK] =
    1275             :             COLOR_MASK_GREEN | COLOR_MASK_BLUE;
    1276          70 : }
    1277             : 
    1278         590 : void Compound::_updateInheritNode()
    1279             : {
    1280         590 :     LBASSERT(_parent);
    1281         590 :     const PixelViewport oldPVP(_inherit.pvp);
    1282         590 :     _inherit = _parent->_inherit;
    1283             : 
    1284         590 :     if (!_inherit.channel)
    1285             :     {
    1286          70 :         _inherit.pvp = oldPVP;
    1287          70 :         _updateInheritPVP();
    1288          70 :         _inherit.vp.apply(_data.vp);
    1289             :     }
    1290         520 :     else if (_inherit.pvp.isValid())
    1291             :     {
    1292         520 :         LBASSERT(_data.vp.isValid());
    1293         520 :         _inherit.pvp.apply(_data.vp);
    1294             : 
    1295             :         // Compute the inherit viewport to be pixel-correct with the integer-
    1296             :         // rounded pvp. This is needed to calculate the frustum correctly.
    1297         520 :         const Viewport vp = _inherit.pvp / _parent->_inherit.pvp;
    1298         520 :         _inherit.vp.apply(vp);
    1299             : 
    1300         520 :         _updateInheritOverdraw();
    1301             :     }
    1302             :     else
    1303             :     {
    1304           0 :         LBASSERT(!_inherit.channel->isRunning());
    1305             :     }
    1306             : 
    1307         590 :     if (_data.frustumData.isValid())
    1308          70 :         _inherit.frustumData = _data.frustumData;
    1309             : 
    1310         590 :     _inherit.range.apply(_data.range);
    1311         590 :     _inherit.pixel.apply(_data.pixel);
    1312         590 :     _inherit.subPixel.apply(_data.subPixel);
    1313             : 
    1314         590 :     if (_data.eyes != fabric::EYE_UNDEFINED)
    1315         240 :         _inherit.eyes = _data.eyes;
    1316         350 :     else if (_inherit.channel)
    1317             :     {
    1318         350 :         const View* view = _inherit.channel->getView();
    1319         350 :         if (!view)
    1320           0 :             _inherit.eyes = EYE_CYCLOP;
    1321             :     }
    1322             : 
    1323         590 :     if (_data.period != LB_UNDEFINED_UINT32)
    1324           0 :         _inherit.period = _data.period;
    1325             : 
    1326         590 :     if (_data.phase != LB_UNDEFINED_UINT32)
    1327           0 :         _inherit.phase = _data.phase;
    1328             : 
    1329         590 :     _inherit.maxFPS = _data.maxFPS;
    1330             : 
    1331         590 :     if (_data.buffers != Frame::Buffer::undefined)
    1332          60 :         _inherit.buffers = _data.buffers;
    1333             : 
    1334         590 :     if (_data.iAttributes[IATTR_STEREO_MODE] != UNDEFINED)
    1335           0 :         _inherit.iAttributes[IATTR_STEREO_MODE] =
    1336           0 :             _data.iAttributes[IATTR_STEREO_MODE];
    1337             : 
    1338         590 :     if (_data.iAttributes[IATTR_STEREO_ANAGLYPH_LEFT_MASK] != UNDEFINED)
    1339           0 :         _inherit.iAttributes[IATTR_STEREO_ANAGLYPH_LEFT_MASK] =
    1340           0 :             _data.iAttributes[IATTR_STEREO_ANAGLYPH_LEFT_MASK];
    1341             : 
    1342         590 :     if (_data.iAttributes[IATTR_STEREO_ANAGLYPH_RIGHT_MASK] != UNDEFINED)
    1343           0 :         _inherit.iAttributes[IATTR_STEREO_ANAGLYPH_RIGHT_MASK] =
    1344           0 :             _data.iAttributes[IATTR_STEREO_ANAGLYPH_RIGHT_MASK];
    1345         590 : }
    1346             : 
    1347         140 : void Compound::_updateInheritPVP()
    1348             : {
    1349         140 :     Channel* channel = _data.channel;
    1350         140 :     if (!channel)
    1351         140 :         return;
    1352             : 
    1353          70 :     const PixelViewport oldPVP(_inherit.pvp);
    1354          70 :     _inherit.channel = channel;
    1355          70 :     _inherit.pvp = channel->getPixelViewport();
    1356             : 
    1357          70 :     View* view = channel->getView();
    1358          70 :     if (!view || !_inherit.pvp.isValid())
    1359             :     {
    1360           0 :         LBASSERT(channel->getOverdraw() == Vector4i());
    1361           0 :         return;
    1362             :     }
    1363          70 :     LBASSERT(channel == getChannel());
    1364             : 
    1365             :     // enlarge pvp by overdraw
    1366          70 :     const Vector4i& overdraw = channel->getOverdraw();
    1367          70 :     _inherit.pvp.w += overdraw.x() + overdraw.z();
    1368          70 :     _inherit.pvp.h += overdraw.y() + overdraw.w();
    1369             : 
    1370          70 :     if (oldPVP != _inherit.pvp) // channel PVP changed
    1371             :     {
    1372          14 :         view->updateFrusta();
    1373          14 :         LBASSERT(overdraw == channel->getOverdraw());
    1374             :     }
    1375             : 
    1376          70 :     _inherit.overdraw = overdraw;
    1377             : }
    1378             : 
    1379         520 : void Compound::_updateInheritOverdraw()
    1380             : {
    1381         520 :     const PixelViewport& pvp = _inherit.pvp;
    1382         520 :     const PixelViewport& parentPVP = _parent->_inherit.pvp;
    1383             : 
    1384         520 :     _inherit.overdraw.x() -= pvp.x - parentPVP.x;
    1385         520 :     _inherit.overdraw.y() -= pvp.y - parentPVP.y;
    1386         520 :     _inherit.overdraw.z() -= parentPVP.getXEnd() - pvp.getXEnd();
    1387         520 :     _inherit.overdraw.w() -= parentPVP.getYEnd() - pvp.getYEnd();
    1388             : 
    1389         520 :     _inherit.overdraw.x() = LB_MAX(_inherit.overdraw.x(), 0);
    1390         520 :     _inherit.overdraw.y() = LB_MAX(_inherit.overdraw.y(), 0);
    1391         520 :     _inherit.overdraw.z() = LB_MAX(_inherit.overdraw.z(), 0);
    1392         520 :     _inherit.overdraw.w() = LB_MAX(_inherit.overdraw.w(), 0);
    1393             : 
    1394         520 :     _inherit.overdraw.x() = LB_MIN(_inherit.overdraw.x(), pvp.w);
    1395         520 :     _inherit.overdraw.y() = LB_MIN(_inherit.overdraw.y(), pvp.h);
    1396         520 :     _inherit.overdraw.z() = LB_MIN(_inherit.overdraw.z(), pvp.w);
    1397         520 :     _inherit.overdraw.w() = LB_MIN(_inherit.overdraw.w(), pvp.h);
    1398             : 
    1399         520 :     LBASSERTINFO(pvp.w >= _inherit.overdraw.x() + _inherit.overdraw.z(),
    1400             :                  pvp.w << " < "
    1401             :                        << _inherit.overdraw.x() + _inherit.overdraw.z());
    1402         520 :     LBASSERTINFO(pvp.h >= _inherit.overdraw.y() + _inherit.overdraw.w(),
    1403             :                  pvp.h << " < "
    1404             :                        << _inherit.overdraw.y() + _inherit.overdraw.w());
    1405         520 : }
    1406             : 
    1407         786 : void Compound::updateInheritTasks()
    1408             : {
    1409         786 :     if (_data.tasks == fabric::TASK_DEFAULT)
    1410             :     {
    1411         786 :         if (isLeaf())
    1412             :         {
    1413         304 :             _inherit.tasks = fabric::TASK_ALL;
    1414             :             // check if a parent compound has cleared us
    1415        1384 :             for (Compound* compound = getParent(); compound;
    1416             :                  compound = compound->getParent())
    1417             :             {
    1418        1080 :                 Channel* channel = compound->getChannel();
    1419        1080 :                 if (channel == getChannel())
    1420         412 :                     _inherit.tasks &= ~fabric::TASK_CLEAR; // done already
    1421             :             }
    1422             :         }
    1423             :         else
    1424         482 :             _inherit.tasks = fabric::TASK_CLEAR | fabric::TASK_ASSEMBLE |
    1425             :                              fabric::TASK_READBACK;
    1426             :     }
    1427             :     else
    1428           0 :         _inherit.tasks = _data.tasks;
    1429             : 
    1430         786 :     const Channel* channel = getChannel();
    1431         786 :     if (isDestination() && channel->getView())
    1432          86 :         _inherit.tasks |= fabric::TASK_VIEW;
    1433             :     else
    1434         700 :         _inherit.tasks &= ~fabric::TASK_VIEW;
    1435         786 : }
    1436             : 
    1437         590 : void Compound::_updateInheritStereo()
    1438             : {
    1439         590 :     if (_inherit.iAttributes[IATTR_STEREO_MODE] != fabric::AUTO)
    1440         520 :         return;
    1441             : 
    1442          70 :     const Segment* segment = _inherit.channel->getSegment();
    1443          70 :     const uint32_t eyes = segment ? segment->getEyes() : _inherit.eyes;
    1444          70 :     const bool stereoEyes = (eyes & EYES_STEREO) == EYES_STEREO;
    1445          70 :     if (!stereoEyes)
    1446             :     {
    1447           0 :         _inherit.iAttributes[IATTR_STEREO_MODE] = fabric::PASSIVE;
    1448           0 :         return;
    1449             :     }
    1450             : 
    1451          70 :     const Window* window = _inherit.channel->getWindow();
    1452          70 :     const bool stereoWindow = window->getDrawableConfig().stereo;
    1453             :     const bool usesFBO =
    1454         140 :         window &&
    1455          70 :         window->getIAttribute(WindowSettings::IATTR_HINT_DRAWABLE) ==
    1456          70 :             fabric::FBO;
    1457             : 
    1458          70 :     if (stereoWindow && !usesFBO)
    1459           0 :         _inherit.iAttributes[IATTR_STEREO_MODE] = fabric::QUAD;
    1460             :     else
    1461          70 :         _inherit.iAttributes[IATTR_STEREO_MODE] = fabric::ANAGLYPH;
    1462             : }
    1463             : 
    1464         590 : void Compound::_updateInheritActive(const uint32_t frameNumber)
    1465             : {
    1466             :     const bool phaseActive =
    1467         590 :         ((frameNumber % _inherit.period) == _inherit.phase);
    1468         590 :     const bool channelActive = _inherit.channel->isRunning(); // runtime failure
    1469             : 
    1470        2360 :     for (size_t i = 0; i < fabric::NUM_EYES; ++i)
    1471             :     {
    1472        1770 :         const uint32_t eye = 1 << i;
    1473        1770 :         const bool eyeActive = _inherit.eyes & eye;
    1474             :         const bool destActive =
    1475        1770 :             isDestination() ? _data.active[i] : _inherit.active[i];
    1476             : 
    1477        1770 :         if (destActive && eyeActive && phaseActive && channelActive)
    1478          32 :             _inherit.active[i] = 1;
    1479             :         else
    1480        1738 :             _inherit.active[i] = 0; // deactivate
    1481             :     }
    1482         590 : }
    1483             : 
    1484        2304 : std::ostream& operator<<(std::ostream& os, const Compound& compound)
    1485             : {
    1486        2304 :     os << lunchbox::disableFlush << "compound" << std::endl;
    1487        2304 :     os << "{" << std::endl << lunchbox::indent;
    1488             : 
    1489        2304 :     const std::string& name = compound.getName();
    1490        2304 :     if (!name.empty())
    1491         104 :         os << "name     \"" << name << "\"" << std::endl;
    1492             : 
    1493        2304 :     const Channel* channel = compound.getChannel();
    1494        2304 :     if (channel)
    1495             :     {
    1496        2140 :         Compound* parent = compound.getParent();
    1497        2140 :         if (!parent || parent->getChannel() != channel)
    1498             :         {
    1499        1516 :             const std::string& channelName = channel->getName();
    1500        1516 :             const Config* config = compound.getConfig();
    1501        1516 :             LBASSERT(config);
    1502             : 
    1503        3032 :             if (!channelName.empty() &&
    1504        1516 :                 config->find<Channel>(channelName) == channel)
    1505             :             {
    1506         878 :                 os << "channel  \"" << channelName << "\"" << std::endl;
    1507             :             }
    1508             :             else
    1509             :             {
    1510         638 :                 const Segment* segment = channel->getSegment();
    1511         638 :                 const View* view = channel->getView();
    1512             : 
    1513         638 :                 if (view && segment)
    1514             :                 {
    1515         638 :                     os << "channel  ( ";
    1516             : 
    1517         638 :                     const Canvas* canvas = segment->getCanvas();
    1518         638 :                     const std::string& canvasName = canvas->getName();
    1519         654 :                     if (!canvasName.empty() &&
    1520          16 :                         config->find<Canvas>(canvasName) == canvas)
    1521             :                     {
    1522          16 :                         os << "canvas \"" << canvasName << "\"  ";
    1523             :                     }
    1524             :                     else
    1525         622 :                         os << canvas->getPath() << "  ";
    1526             : 
    1527         638 :                     const std::string& segmentName = segment->getName();
    1528         638 :                     if (!segmentName.empty() &&
    1529           0 :                         canvas->findSegment(segmentName) == segment)
    1530             :                     {
    1531           0 :                         os << "segment \"" << segmentName << "\"   ";
    1532             :                     }
    1533             :                     else
    1534         638 :                         os << "segment " << segment->getPath().segmentIndex
    1535         638 :                            << "   ";
    1536             : 
    1537         638 :                     const Layout* layout = view->getLayout();
    1538         638 :                     const std::string& layoutName = layout->getName();
    1539         986 :                     if (!layoutName.empty() &&
    1540         348 :                         config->find<Layout>(layoutName) == layout)
    1541             :                     {
    1542         348 :                         os << "layout \"" << layoutName << "\"  ";
    1543             :                     }
    1544             :                     else
    1545         290 :                         os << layout->getPath() << "  ";
    1546             : 
    1547         638 :                     const std::string& viewName = view->getName();
    1548         640 :                     if (!viewName.empty() &&
    1549           2 :                         config->find<View>(viewName) == view)
    1550             :                     {
    1551           2 :                         os << "view \"" << viewName << '\"';
    1552             :                     }
    1553             :                     else
    1554         636 :                         os << "view " << view->getPath().viewIndex;
    1555             : 
    1556         638 :                     os << " )" << std::endl;
    1557             :                 }
    1558             :                 else
    1559           0 :                     os << "channel  ( " << channel->getPath() << " )"
    1560           0 :                        << std::endl;
    1561             :             }
    1562             :         }
    1563             :     }
    1564             : 
    1565        2304 :     const uint32_t tasks = compound.getTasks();
    1566        2304 :     if (tasks != fabric::TASK_DEFAULT)
    1567             :     {
    1568         184 :         os << "task     [";
    1569         184 :         if (tasks & fabric::TASK_CLEAR)
    1570          88 :             os << " CLEAR";
    1571         184 :         if (compound.isLeaf() && (tasks & fabric::TASK_DRAW))
    1572           4 :             os << " DRAW";
    1573         184 :         if (tasks & fabric::TASK_ASSEMBLE)
    1574         116 :             os << " ASSEMBLE";
    1575         184 :         if (tasks & fabric::TASK_READBACK)
    1576          72 :             os << " READBACK";
    1577         184 :         os << " ]" << std::endl;
    1578             :     }
    1579             : 
    1580        2304 :     const fabric::Frame::Buffer buffers = compound.getBuffers();
    1581        2304 :     if (buffers != Frame::Buffer::undefined)
    1582             :     {
    1583         118 :         os << "buffers  [";
    1584         118 :         if (buffers & Frame::Buffer::color)
    1585         118 :             os << " COLOR";
    1586         118 :         if (buffers & Frame::Buffer::depth)
    1587         118 :             os << " DEPTH";
    1588         118 :         os << " ]" << std::endl;
    1589             :     }
    1590             : 
    1591        2304 :     const Viewport& vp = compound.getViewport();
    1592        2304 :     if (vp.isValid() && vp != Viewport::FULL)
    1593         140 :         os << "viewport " << vp << std::endl;
    1594             : 
    1595        2304 :     const Range& range = compound.getRange();
    1596        2304 :     if (range.isValid() && range != Range::ALL)
    1597         370 :         os << range << std::endl;
    1598             : 
    1599        2304 :     const Pixel& pixel = compound.getPixel();
    1600        2304 :     if (pixel.isValid() && pixel != Pixel::ALL)
    1601          46 :         os << pixel << std::endl;
    1602             : 
    1603        2304 :     const SubPixel& subPixel = compound.getSubPixel();
    1604        2304 :     if (subPixel.isValid() && subPixel != SubPixel::ALL)
    1605          30 :         os << subPixel << std::endl;
    1606             : 
    1607        2304 :     const Zoom& zoom = compound.getZoom();
    1608        2304 :     if (zoom.isValid() && zoom != Zoom::NONE)
    1609           2 :         os << zoom << std::endl;
    1610             : 
    1611        2304 :     const uint32_t eye = compound.getEyes();
    1612        2304 :     if (eye)
    1613             :     {
    1614         196 :         os << "eye      [ ";
    1615         196 :         if (eye & fabric::EYE_CYCLOP)
    1616          94 :             os << "CYCLOP ";
    1617         196 :         if (eye & fabric::EYE_LEFT)
    1618          96 :             os << "LEFT ";
    1619         196 :         if (eye & fabric::EYE_RIGHT)
    1620          98 :             os << "RIGHT ";
    1621         196 :         os << "]" << std::endl;
    1622             :     }
    1623             : 
    1624        2304 :     const uint32_t period = compound.getPeriod();
    1625        2304 :     const uint32_t phase = compound.getPhase();
    1626        2304 :     if (period != LB_UNDEFINED_UINT32)
    1627          34 :         os << "period " << period << "  ";
    1628             : 
    1629        2304 :     if (phase != LB_UNDEFINED_UINT32)
    1630          34 :         os << "phase " << phase;
    1631             : 
    1632        2304 :     if (period != LB_UNDEFINED_UINT32 || phase != LB_UNDEFINED_UINT32)
    1633          34 :         os << std::endl;
    1634             : 
    1635             :     // attributes
    1636        2304 :     bool attrPrinted = false;
    1637             : 
    1638       13824 :     for (Compound::IAttribute i = static_cast<Compound::IAttribute>(0);
    1639       13824 :          i < Compound::IATTR_ALL;
    1640       11520 :          i = static_cast<Compound::IAttribute>(uint32_t(i) + 1))
    1641             :     {
    1642       11520 :         const int value = compound.getIAttribute(i);
    1643       11520 :         if (value == Global::instance()->getCompoundIAttribute(i))
    1644       11506 :             continue;
    1645             : 
    1646          14 :         if (!attrPrinted)
    1647             :         {
    1648          10 :             os << std::endl << "attributes" << std::endl;
    1649          10 :             os << "{" << std::endl << lunchbox::indent;
    1650          10 :             attrPrinted = true;
    1651             :         }
    1652             : 
    1653             :         os << (i == Compound::IATTR_STEREO_MODE
    1654             :                    ? "stereo_mode                "
    1655             :                    : i == Compound::IATTR_STEREO_ANAGLYPH_LEFT_MASK
    1656          12 :                          ? "stereo_anaglyph_left_mask  "
    1657             :                          : i == Compound::IATTR_STEREO_ANAGLYPH_RIGHT_MASK
    1658           4 :                                ? "stereo_anaglyph_right_mask "
    1659          22 :                                : "ERROR ");
    1660             : 
    1661          14 :         switch (i)
    1662             :         {
    1663             :         case Compound::IATTR_STEREO_MODE:
    1664           6 :             os << static_cast<fabric::IAttribute>(value) << std::endl;
    1665           6 :             break;
    1666             : 
    1667             :         case Compound::IATTR_STEREO_ANAGLYPH_LEFT_MASK:
    1668             :         case Compound::IATTR_STEREO_ANAGLYPH_RIGHT_MASK:
    1669           8 :             os << ColorMask(value) << std::endl;
    1670           8 :             break;
    1671             : 
    1672             :         default:
    1673           0 :             LBASSERTINFO(0, "unimplemented");
    1674             :         }
    1675             :     }
    1676             : 
    1677        2304 :     if (attrPrinted)
    1678          10 :         os << lunchbox::exdent << "}" << std::endl << std::endl;
    1679             : 
    1680        2304 :     switch (compound.getFrustumType())
    1681             :     {
    1682             :     case Frustum::TYPE_WALL:
    1683          28 :         os << compound.getWall() << std::endl;
    1684          28 :         break;
    1685             :     case Frustum::TYPE_PROJECTION:
    1686           0 :         os << compound.getProjection() << std::endl;
    1687           0 :         break;
    1688             :     default:
    1689        2276 :         break;
    1690             :     }
    1691             : 
    1692        2304 :     const Equalizers& equalizers = compound.getEqualizers();
    1693        2478 :     for (EqualizersCIter i = equalizers.begin(); i != equalizers.end(); ++i)
    1694         174 :         os << *i;
    1695             : 
    1696        2304 :     const TileQueues& outputQueues = compound.getOutputTileQueues();
    1697        2304 :     for (TileQueuesCIter i = outputQueues.begin(); i != outputQueues.end(); ++i)
    1698           0 :         os << "output" << *i;
    1699             : 
    1700        2304 :     const TileQueues& inputQueues = compound.getInputTileQueues();
    1701        2304 :     for (TileQueuesCIter i = inputQueues.begin(); i != inputQueues.end(); ++i)
    1702           0 :         os << "input" << *i;
    1703             : 
    1704        2304 :     if (compound.getSwapBarrier())
    1705         232 :         os << *compound.getSwapBarrier();
    1706             : 
    1707        2304 :     const Compounds& children = compound.getChildren();
    1708        2304 :     if (!children.empty())
    1709             :     {
    1710         752 :         os << std::endl;
    1711        2700 :         for (CompoundsCIter i = children.begin(); i != children.end(); ++i)
    1712        1948 :             os << **i;
    1713             :     }
    1714             : 
    1715        2304 :     const Frames& inputFrames = compound.getInputFrames();
    1716        3756 :     for (FramesCIter i = inputFrames.begin(); i != inputFrames.end(); ++i)
    1717        1452 :         os << "input" << **i << std::endl;
    1718             : 
    1719        2304 :     const Frames& outputFrames = compound.getOutputFrames();
    1720        3722 :     for (FramesCIter i = outputFrames.begin(); i != outputFrames.end(); ++i)
    1721        1418 :         os << "output" << **i << std::endl;
    1722             : 
    1723        2304 :     return os << lunchbox::exdent << "}" << std::endl << lunchbox::enableFlush;
    1724             : }
    1725             : }
    1726          60 : }

Generated by: LCOV version 1.11