LCOV - code coverage report
Current view: top level - eq/fabric - window.ipp (source / functions) Hit Total Coverage
Test: Equalizer Lines: 219 287 76.3 %
Date: 2017-12-16 05:07:20 Functions: 47 112 42.0 %

          Line data    Source code
       1             : 
       2             : /* Copyright (c) 2010-2017, Stefan Eilemann <eile@equalizergraphics.com>
       3             :  *                          Cedric Stalder <cedric.stalder@gmail.com>
       4             :  *                          Daniel Nachbaur <danielnachbaur@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 "window.h"
      21             : 
      22             : #include "channel.h"
      23             : #include "commands.h"
      24             : #include "elementVisitor.h"
      25             : #include "leafVisitor.h"
      26             : #include "log.h"
      27             : #include "task.h"
      28             : 
      29             : #include <co/dataIStream.h>
      30             : #include <co/dataOStream.h>
      31             : #include <co/objectICommand.h>
      32             : 
      33             : namespace eq
      34             : {
      35             : namespace fabric
      36             : {
      37             : namespace
      38             : {
      39             : #define MAKE_WINDOW_ATTR_STRING(attr) (std::string("EQ_WINDOW_") + #attr)
      40         100 : std::string _iAttributeStrings[] = {
      41         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_HINT_CORE_PROFILE),
      42         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_HINT_OPENGL_MAJOR),
      43         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_HINT_OPENGL_MINOR),
      44         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_HINT_STEREO),
      45         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_HINT_DOUBLEBUFFER),
      46         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_HINT_FULLSCREEN),
      47         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_HINT_DECORATION),
      48         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_HINT_SWAPSYNC),
      49         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_HINT_DRAWABLE),
      50         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_HINT_STATISTICS),
      51         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_HINT_SCREENSAVER),
      52         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_HINT_GRAB_POINTER),
      53         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_HINT_WIDTH),
      54         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_HINT_HEIGHT),
      55         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_PLANES_COLOR),
      56         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_PLANES_ALPHA),
      57         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_PLANES_DEPTH),
      58         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_PLANES_STENCIL),
      59         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_PLANES_ACCUM),
      60         100 :     MAKE_WINDOW_ATTR_STRING(IATTR_PLANES_ACCUM_ALPHA),
      61        2050 :     MAKE_WINDOW_ATTR_STRING(IATTR_PLANES_SAMPLES)};
      62             : }
      63             : 
      64             : template <class P, class W, class C, class Settings>
      65        1496 : Window<P, W, C, Settings>::Window(P* parent)
      66        1496 :     : _pipe(parent)
      67             : {
      68        1496 :     LBASSERT(parent);
      69        1496 :     parent->_addWindow(static_cast<W*>(this));
      70        1496 :     LBLOG(LOG_INIT) << "New " << lunchbox::className(this) << std::endl;
      71        1496 : }
      72             : 
      73             : template <class P, class W, class C, class Settings>
      74        2992 : Window<P, W, C, Settings>::BackupData::BackupData()
      75        2992 :     : fixedVP(true)
      76             : {
      77        2992 : }
      78             : 
      79             : template <class P, class W, class C, class Settings>
      80        1494 : Window<P, W, C, Settings>::~Window()
      81             : {
      82        1494 :     LBLOG(LOG_INIT) << "Delete " << lunchbox::className(this) << std::endl;
      83        7234 :     while (!_channels.empty())
      84             :     {
      85        2870 :         C* channel = _channels.back();
      86             : 
      87        2870 :         LBASSERT(channel->getWindow() == this);
      88        2870 :         _removeChannel(channel);
      89        2870 :         delete channel;
      90             :     }
      91        1494 :     _pipe->_removeWindow(static_cast<W*>(this));
      92        2988 : }
      93             : 
      94             : template <class P, class W, class C, class Settings>
      95        1492 : void Window<P, W, C, Settings>::init()
      96             : {
      97        1492 :     notifyViewportChanged();
      98        1492 :     unsetDirty(DIRTY_VIEWPORT);
      99        1492 : }
     100             : 
     101             : template <class P, class W, class C, class Settings>
     102           6 : void Window<P, W, C, Settings>::attach(const uint128_t& id,
     103             :                                        const uint32_t instanceID)
     104             : {
     105           6 :     Object::attach(id, instanceID);
     106           6 :     co::CommandQueue* queue = _pipe->getMainThreadQueue();
     107           6 :     LBASSERT(queue);
     108             : 
     109           6 :     registerCommand(CMD_WINDOW_NEW_CHANNEL,
     110             :                     CmdFunc(this, &Window<P, W, C, Settings>::_cmdNewChannel),
     111             :                     queue);
     112           6 :     registerCommand(CMD_WINDOW_NEW_CHANNEL_REPLY,
     113             :                     CmdFunc(this,
     114             :                             &Window<P, W, C, Settings>::_cmdNewChannelReply),
     115             :                     0);
     116           6 : }
     117             : 
     118             : template <class P, class W, class C, class Settings>
     119           4 : void Window<P, W, C, Settings>::backup()
     120             : {
     121           4 :     Object::backup();
     122           4 :     _backup = _data;
     123           4 : }
     124             : 
     125             : template <class P, class W, class C, class Settings>
     126           0 : void Window<P, W, C, Settings>::restore()
     127             : {
     128           0 :     _data = _backup;
     129           0 :     _data.drawableConfig = DrawableConfig();
     130             : 
     131           0 :     Object::restore();
     132           0 :     notifyViewportChanged();
     133           0 :     setDirty(DIRTY_VIEWPORT);
     134           0 : }
     135             : 
     136             : template <class P, class W, class C, class Settings>
     137          19 : uint128_t Window<P, W, C, Settings>::commit(const uint32_t incarnation)
     138             : {
     139          19 :     if (Serializable::isDirty(DIRTY_CHANNELS))
     140          16 :         commitChildren<C>(_channels, CMD_WINDOW_NEW_CHANNEL, incarnation);
     141          19 :     return Object::commit(incarnation);
     142             : }
     143             : 
     144             : template <class P, class W, class C, class Settings>
     145          18 : void Window<P, W, C, Settings>::serialize(co::DataOStream& os,
     146             :                                           const uint64_t dirtyBits)
     147             : {
     148          18 :     Object::serialize(os, dirtyBits);
     149          18 :     if (dirtyBits & DIRTY_SETTINGS)
     150          10 :         _data.windowSettings.serialize(os);
     151          18 :     if (dirtyBits & DIRTY_CHANNELS && isMaster())
     152             :     {
     153          14 :         os << _mapNodeObjects();
     154          14 :         os.serializeChildren(_channels);
     155             :     }
     156          18 :     if (dirtyBits & DIRTY_VIEWPORT)
     157           7 :         os << _data.vp << _data.pvp << _data.fixedVP;
     158          18 :     if (dirtyBits & DIRTY_DRAWABLECONFIG)
     159          10 :         os << _data.drawableConfig;
     160          18 : }
     161             : 
     162             : template <class P, class W, class C, class Settings>
     163          13 : void Window<P, W, C, Settings>::deserialize(co::DataIStream& is,
     164             :                                             const uint64_t dirtyBits)
     165             : {
     166          13 :     Object::deserialize(is, dirtyBits);
     167          13 :     if (dirtyBits & DIRTY_SETTINGS)
     168           8 :         _data.windowSettings.deserialize(is);
     169          13 :     if (dirtyBits & DIRTY_CHANNELS)
     170             :     {
     171           9 :         if (isMaster())
     172           4 :             syncChildren(_channels);
     173             :         else
     174             :         {
     175           5 :             const bool useChildren = is.read<bool>();
     176           5 :             if (useChildren && _mapNodeObjects())
     177             :             {
     178           0 :                 Channels result;
     179           0 :                 is.deserializeChildren(this, _channels, result);
     180           0 :                 _channels.swap(result);
     181           0 :                 LBASSERT(_channels.size() == result.size());
     182             :             }
     183             :             else // consume unused ObjectVersions
     184           5 :                 is.read<co::ObjectVersions>();
     185             :         }
     186             :     }
     187          13 :     if (dirtyBits & DIRTY_VIEWPORT)
     188             :     {
     189             :         // Ignore data from master (server) if we have local changes
     190           5 :         if (!Serializable::isDirty(DIRTY_VIEWPORT) || isMaster())
     191             :         {
     192           5 :             is >> _data.vp >> _data.pvp >> _data.fixedVP;
     193           5 :             notifyViewportChanged();
     194             :         }
     195             :         else // consume unused data
     196           0 :             is.getRemainingBuffer(sizeof(_data.vp) + sizeof(_data.pvp) +
     197             :                                   sizeof(_data.fixedVP));
     198             :     }
     199             : 
     200          13 :     if (dirtyBits & DIRTY_DRAWABLECONFIG)
     201           8 :         is >> _data.drawableConfig;
     202          13 : }
     203             : 
     204             : template <class P, class W, class C, class Settings>
     205       54869 : void Window<P, W, C, Settings>::setDirty(const uint64_t dirtyBits)
     206             : {
     207       54869 :     Object::setDirty(dirtyBits);
     208       54869 :     _pipe->setDirty(P::DIRTY_WINDOWS);
     209       54869 : }
     210             : 
     211             : template <class P, class W, class C, class Settings>
     212           6 : void Window<P, W, C, Settings>::notifyDetach()
     213             : {
     214           6 :     Object::notifyDetach();
     215          12 :     co::LocalNodePtr node = getLocalNode();
     216             : 
     217           6 :     if (isMaster())
     218             :     {
     219          66 :         for (typename Channels::const_iterator i = _channels.begin();
     220          44 :              i != _channels.end(); ++i)
     221             :         {
     222          18 :             node->releaseObject(*i);
     223             :         }
     224             :     }
     225             :     else
     226             :     {
     227           2 :         while (!_channels.empty())
     228             :         {
     229           0 :             C* channel = _channels.back();
     230           0 :             LBASSERT(channel->isAttached());
     231             : 
     232           0 :             node->releaseObject(channel);
     233           0 :             _removeChannel(channel);
     234           0 :             _pipe->getServer()->getNodeFactory()->releaseChannel(channel);
     235             :         }
     236             :     }
     237           6 : }
     238             : 
     239             : template <class P, class W, class C, class Settings>
     240           0 : void Window<P, W, C, Settings>::create(C** channel)
     241             : {
     242           0 :     *channel = _pipe->getServer()->getNodeFactory()->createChannel(
     243             :         static_cast<W*>(this));
     244           0 :     (*channel)->init(); // not in ctor, virtual method
     245           0 : }
     246             : 
     247             : template <class P, class W, class C, class Settings>
     248           0 : void Window<P, W, C, Settings>::release(C* channel)
     249             : {
     250           0 :     _pipe->getServer()->getNodeFactory()->releaseChannel(channel);
     251           0 : }
     252             : 
     253             : template <class P, class W, class C, class Settings>
     254        2876 : void Window<P, W, C, Settings>::_addChannel(C* channel)
     255             : {
     256        2876 :     LBASSERT(channel->getWindow() == this);
     257        2876 :     _channels.push_back(channel);
     258        2876 :     setDirty(DIRTY_CHANNELS);
     259        2876 : }
     260             : 
     261             : template <class P, class W, class C, class Settings>
     262        5742 : bool Window<P, W, C, Settings>::_removeChannel(C* channel)
     263             : {
     264        5742 :     typename Channels::iterator i = lunchbox::find(_channels, channel);
     265        5742 :     if (i == _channels.end())
     266        2870 :         return false;
     267             : 
     268        2872 :     _channels.erase(i);
     269        2872 :     setDirty(DIRTY_CHANNELS);
     270        2872 :     if (!isMaster())
     271        2872 :         postRemove(channel);
     272        2872 :     return true;
     273             : }
     274             : 
     275             : template <class P, class W, class C, class Settings>
     276           2 : C* Window<P, W, C, Settings>::_findChannel(const uint128_t& id)
     277             : {
     278           6 :     for (typename Channels::const_iterator i = _channels.begin();
     279           4 :          i != _channels.end(); ++i)
     280             :     {
     281           2 :         C* channel = *i;
     282           2 :         if (channel->getID() == id)
     283           2 :             return channel;
     284             :     }
     285           0 :     return 0;
     286             : }
     287             : 
     288             : template <class P, class W, class C, class Settings>
     289       31700 : void Window<P, W, C, Settings>::setIAttribute(
     290             :     const WindowSettings::IAttribute attr, const int32_t value)
     291             : {
     292       31700 :     if (_data.windowSettings.setIAttribute(attr, value))
     293       17438 :         setDirty(DIRTY_SETTINGS);
     294       31700 : }
     295             : 
     296             : template <class P, class W, class C, class Settings>
     297       15880 : int32_t Window<P, W, C, Settings>::getIAttribute(
     298             :     const WindowSettings::IAttribute attr) const
     299             : {
     300       15880 :     return _data.windowSettings.getIAttribute(attr);
     301             : }
     302             : 
     303             : template <class P, class W, class C, class Settings>
     304       12966 : const std::string& Window<P, W, C, Settings>::getIAttributeString(
     305             :     const WindowSettings::IAttribute attr)
     306             : {
     307       12966 :     return _iAttributeStrings[attr];
     308             : }
     309             : 
     310             : template <class P, class W, class C, class Settings>
     311           0 : void Window<P, W, C, Settings>::setSettings(const Settings& settings)
     312             : {
     313           0 :     _data.windowSettings = settings;
     314           0 :     setDirty(DIRTY_SETTINGS);
     315           0 : }
     316             : 
     317             : template <class P, class W, class C, class Settings>
     318           3 : const Settings& Window<P, W, C, Settings>::getSettings() const
     319             : {
     320           3 :     return _data.windowSettings;
     321             : }
     322             : 
     323             : template <class P, class W, class C, class Settings>
     324           0 : WindowPath Window<P, W, C, Settings>::getPath() const
     325             : {
     326           0 :     const P* pipe = getPipe();
     327           0 :     LBASSERT(pipe);
     328           0 :     WindowPath path(pipe->getPath());
     329             : 
     330           0 :     const typename std::vector<W*>& windows = pipe->getWindows();
     331             :     typename std::vector<W*>::const_iterator i =
     332           0 :         std::find(windows.begin(), windows.end(), this);
     333           0 :     LBASSERT(i != windows.end());
     334           0 :     path.windowIndex = std::distance(windows.begin(), i);
     335           0 :     return path;
     336             : }
     337             : 
     338             : namespace
     339             : {
     340             : template <class W, class V>
     341       38046 : VisitorResult _accept(W* window, V& visitor)
     342             : {
     343       38046 :     VisitorResult result = visitor.visitPre(window);
     344       38046 :     if (result != TRAVERSE_CONTINUE)
     345           0 :         return result;
     346             : 
     347       38046 :     const typename W::Channels& channels = window->getChannels();
     348      438102 :     for (typename W::Channels::const_iterator i = channels.begin();
     349      292068 :          i != channels.end(); ++i)
     350             :     {
     351      113400 :         switch ((*i)->accept(visitor))
     352             :         {
     353             :         case TRAVERSE_TERMINATE:
     354        5412 :             return TRAVERSE_TERMINATE;
     355             : 
     356             :         case TRAVERSE_PRUNE:
     357           0 :             result = TRAVERSE_PRUNE;
     358           0 :             break;
     359             : 
     360             :         case TRAVERSE_CONTINUE:
     361             :         default:
     362      107988 :             break;
     363             :         }
     364             :     }
     365             : 
     366       32634 :     switch (visitor.visitPost(window))
     367             :     {
     368             :     case TRAVERSE_TERMINATE:
     369           0 :         return TRAVERSE_TERMINATE;
     370             : 
     371             :     case TRAVERSE_PRUNE:
     372           0 :         return TRAVERSE_PRUNE;
     373             : 
     374             :     case TRAVERSE_CONTINUE:
     375             :     default:
     376       32634 :         break;
     377             :     }
     378             : 
     379       32634 :     return result;
     380             : }
     381             : }
     382             : 
     383             : template <class P, class W, class C, class Settings>
     384       38046 : VisitorResult Window<P, W, C, Settings>::accept(Visitor& visitor)
     385             : {
     386       38046 :     return _accept(static_cast<W*>(this), visitor);
     387             : }
     388             : 
     389             : template <class P, class W, class C, class Settings>
     390           0 : VisitorResult Window<P, W, C, Settings>::accept(Visitor& visitor) const
     391             : {
     392           0 :     return _accept(static_cast<const W*>(this), visitor);
     393             : }
     394             : 
     395             : template <class P, class W, class C, class Settings>
     396        4838 : const PixelViewport& Window<P, W, C, Settings>::getPixelViewport() const
     397             : {
     398        4838 :     return _data.windowSettings.getPixelViewport();
     399             : }
     400             : 
     401             : template <class P, class W, class C, class Settings>
     402         544 : void Window<P, W, C, Settings>::setName(const std::string& name)
     403             : {
     404         544 :     Object::setName(name);
     405         544 :     if (_data.windowSettings.getName() == name)
     406           0 :         return;
     407         544 :     _data.windowSettings.setName(name);
     408         544 :     setDirty(DIRTY_SETTINGS);
     409             : }
     410             : 
     411             : template <class P, class W, class C, class Settings>
     412         696 : void Window<P, W, C, Settings>::setPixelViewport(const PixelViewport& pvp)
     413             : {
     414         696 :     LBASSERTINFO(pvp.isValid(), pvp);
     415         696 :     if (!pvp.isValid())
     416           0 :         return;
     417             : 
     418         696 :     _data.fixedVP = false;
     419             : 
     420         696 :     if (pvp == _data.pvp && _data.vp.hasArea())
     421           2 :         return;
     422             : 
     423         694 :     _data.pvp = pvp;
     424         694 :     _data.vp.invalidate();
     425         694 :     _data.windowSettings.setPixelViewport(pvp);
     426             : 
     427         694 :     notifyViewportChanged();
     428         694 :     setDirty(DIRTY_VIEWPORT);
     429         694 :     setDirty(DIRTY_SETTINGS);
     430             : }
     431             : 
     432             : template <class P, class W, class C, class Settings>
     433         602 : void Window<P, W, C, Settings>::setViewport(const Viewport& vp)
     434             : {
     435         602 :     if (!vp.hasArea())
     436           0 :         return;
     437             : 
     438         602 :     _data.fixedVP = true;
     439             : 
     440         602 :     if (vp == _data.vp && _data.pvp.hasArea())
     441           0 :         return;
     442         602 :     _data.vp = vp;
     443         602 :     _data.pvp.invalidate();
     444         602 :     _data.windowSettings.setPixelViewport(PixelViewport());
     445             : 
     446         602 :     setDirty(DIRTY_VIEWPORT);
     447         602 :     setDirty(DIRTY_SETTINGS);
     448         602 :     notifyViewportChanged();
     449             : }
     450             : 
     451             : template <class P, class W, class C, class Settings>
     452        2793 : void Window<P, W, C, Settings>::notifyViewportChanged()
     453             : {
     454        2793 :     const PixelViewport pipePVP = _pipe->getPixelViewport();
     455             : 
     456        2793 :     if (_data.fixedVP) // update pixel viewport
     457             :     {
     458        2095 :         const PixelViewport oldPVP = _data.pvp;
     459        2095 :         _data.pvp = pipePVP;
     460        2095 :         _data.pvp.apply(_data.vp);
     461        2095 :         if (oldPVP != _data.pvp)
     462             :         {
     463         407 :             _data.windowSettings.setPixelViewport(_data.pvp);
     464         407 :             setDirty(DIRTY_VIEWPORT);
     465         407 :             setDirty(DIRTY_SETTINGS);
     466             :         }
     467             :     }
     468             :     else // update viewport
     469             :     {
     470         698 :         const Viewport oldVP = _data.vp;
     471         698 :         _data.vp = _data.pvp / pipePVP;
     472         698 :         if (oldVP != _data.vp)
     473         694 :             setDirty(DIRTY_VIEWPORT);
     474             :     }
     475             : 
     476        2796 :     for (C* channel : _channels)
     477           3 :         channel->notifyViewportChanged();
     478             : 
     479        2793 :     LBVERB << getName() << " viewport update: " << _data.vp << ":" << _data.pvp
     480           0 :            << std::endl;
     481        2793 : }
     482             : 
     483             : template <class P, class W, class C, class Settings>
     484           2 : void Window<P, W, C, Settings>::_setDrawableConfig(
     485             :     const DrawableConfig& drawableConfig)
     486             : {
     487           2 :     _data.drawableConfig = drawableConfig;
     488           2 :     setDirty(DIRTY_DRAWABLECONFIG);
     489           2 : }
     490             : 
     491             : //----------------------------------------------------------------------
     492             : // ICommand handlers
     493             : //----------------------------------------------------------------------
     494             : template <class P, class W, class C, class Settings>
     495           0 : bool Window<P, W, C, Settings>::_cmdNewChannel(co::ICommand& cmd)
     496             : {
     497           0 :     co::ObjectICommand command(cmd);
     498             : 
     499           0 :     C* channel = 0;
     500           0 :     create(&channel);
     501           0 :     LBASSERT(channel);
     502             : 
     503           0 :     getLocalNode()->registerObject(channel);
     504           0 :     LBASSERT(channel->isAttached());
     505             : 
     506           0 :     send(command.getRemoteNode(), CMD_WINDOW_NEW_CHANNEL_REPLY)
     507           0 :         << command.read<uint32_t>() << channel->getID();
     508           0 :     LBASSERT(channel->isAttached());
     509             : 
     510           0 :     return true;
     511             : }
     512             : 
     513             : template <class P, class W, class C, class Settings>
     514           0 : bool Window<P, W, C, Settings>::_cmdNewChannelReply(co::ICommand& cmd)
     515             : {
     516           0 :     co::ObjectICommand command(cmd);
     517             : 
     518           0 :     const uint32_t requestID = command.read<uint32_t>();
     519           0 :     const uint128_t& result = command.read<uint128_t>();
     520             : 
     521           0 :     getLocalNode()->serveRequest(requestID, result);
     522             : 
     523           0 :     return true;
     524             : }
     525             : 
     526             : template <class P, class W, class C, class Settings>
     527         752 : std::ostream& operator<<(std::ostream& os,
     528             :                          const Window<P, W, C, Settings>& window)
     529             : {
     530         752 :     os << lunchbox::disableFlush << lunchbox::disableHeader << "window"
     531             :        << std::endl;
     532         752 :     os << "{" << std::endl << lunchbox::indent;
     533             : 
     534         752 :     const std::string& name = window.getName();
     535         752 :     if (!name.empty())
     536         278 :         os << "name     \"" << name << "\"" << std::endl;
     537             : 
     538         752 :     const Viewport& vp = window.getViewport();
     539         752 :     const PixelViewport& pvp = window.getPixelViewport();
     540         752 :     if (vp.hasArea() && window.hasFixedViewport())
     541             :     {
     542         402 :         if (pvp.hasArea())
     543           0 :             os << "viewport " << pvp << std::endl;
     544         402 :         os << "viewport " << vp << std::endl;
     545             :     }
     546         350 :     else if (pvp.hasArea() && !window.hasFixedViewport())
     547             :     {
     548         350 :         if (vp.hasArea())
     549           4 :             os << "viewport " << vp << std::endl;
     550         350 :         os << "viewport " << pvp << std::endl;
     551             :     }
     552             : 
     553         752 :     window.output(os);
     554             : 
     555         752 :     const typename W::Channels& channels = window.getChannels();
     556        6642 :     for (typename W::Channels::const_iterator i = channels.begin();
     557        4428 :          i != channels.end(); ++i)
     558             :     {
     559        1462 :         os << **i;
     560             :     }
     561             : 
     562        1504 :     os << lunchbox::exdent << "}" << std::endl
     563         752 :        << lunchbox::enableHeader << lunchbox::enableFlush;
     564         752 :     return os;
     565             : }
     566             : }
     567             : }

Generated by: LCOV version 1.11