LCOV - code coverage report
Current view: top level - eq - channel.cpp (source / functions) Hit Total Coverage
Test: Equalizer Lines: 635 1113 57.1 %
Date: 2017-12-16 05:07:20 Functions: 83 119 69.7 %

          Line data    Source code
       1             : /* Copyright (c) 2005-2017, Stefan Eilemann <eile@equalizergraphics.com>
       2             :  *                          Cedric Stalder <cedric.stalder@gmail.com>
       3             :  *                          Daniel Nachbaur <danielnachbaur@gmail.com>
       4             :  *                          Julio Delgado Mangas <julio.delgadomangas@epfl.ch>
       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 "channel.h"
      21             : 
      22             : // must be included before any header defining Bool
      23             : #ifdef EQ_QT_USED
      24             : #include "qt/window.h"
      25             : #include <QThread>
      26             : #endif
      27             : 
      28             : #include "channelStatistics.h"
      29             : #include "client.h"
      30             : #include "compositor.h"
      31             : #include "config.h"
      32             : #include "detail/fileFrameWriter.h"
      33             : #include "error.h"
      34             : #include "frame.h"
      35             : #include "frameData.h"
      36             : #include "gl.h"
      37             : #include "global.h"
      38             : #include "image.h"
      39             : #include "jitter.h"
      40             : #include "log.h"
      41             : #include "node.h"
      42             : #include "nodeFactory.h"
      43             : #include "pipe.h"
      44             : #include "pixelData.h"
      45             : #include "server.h"
      46             : #include "systemWindow.h"
      47             : #include "view.h"
      48             : #include "window.h"
      49             : 
      50             : #include <eq/fabric/commands.h>
      51             : #include <eq/fabric/frameData.h>
      52             : #include <eq/fabric/keyEvent.h>
      53             : #include <eq/fabric/pointerEvent.h>
      54             : #include <eq/fabric/sizeEvent.h>
      55             : #include <eq/fabric/task.h>
      56             : #include <eq/fabric/tile.h>
      57             : #include <eq/util/accum.h>
      58             : #include <eq/util/objectManager.h>
      59             : 
      60             : #include <co/connectionDescription.h>
      61             : #include <co/exception.h>
      62             : #include <co/objectICommand.h>
      63             : #include <co/queueSlave.h>
      64             : #include <co/sendToken.h>
      65             : #include <lunchbox/rng.h>
      66             : #include <lunchbox/scopedMutex.h>
      67             : #include <pression/plugins/compressor.h>
      68             : 
      69             : #ifdef EQUALIZER_USE_GLSTATS
      70             : #include "detail/statsRenderer.h"
      71             : #include <GLStats/GLStats.h>
      72             : #endif
      73             : 
      74             : #include <bitset>
      75             : #include <set>
      76             : 
      77             : #include "detail/channel.ipp"
      78             : 
      79             : #ifdef EQUALIZER_USE_DEFLECT
      80             : #include "deflect/proxy.h"
      81             : #endif
      82             : 
      83             : namespace eq
      84             : {
      85             : /** @cond IGNORE */
      86             : typedef fabric::Channel<Window, Channel> Super;
      87             : typedef co::CommandFunc<Channel> CmdFunc;
      88             : using detail::STATE_STOPPED;
      89             : using detail::STATE_INITIALIZING;
      90             : using detail::STATE_RUNNING;
      91             : using detail::STATE_FAILED;
      92             : /** @endcond */
      93             : 
      94           2 : Channel::Channel(Window* parent)
      95             :     : Super(parent)
      96           2 :     , _impl(new detail::Channel)
      97             : {
      98           2 : }
      99             : 
     100           4 : Channel::~Channel()
     101             : {
     102           2 :     delete _impl;
     103           2 : }
     104             : 
     105           2 : void Channel::attach(const uint128_t& id, const uint32_t instanceID)
     106             : {
     107           2 :     Super::attach(id, instanceID);
     108           2 :     co::CommandQueue* queue = getPipeThreadQueue();
     109           2 :     co::CommandQueue* commandQ = getCommandThreadQueue();
     110           2 :     co::CommandQueue* tmitQ = getNode()->getTransmitterQueue();
     111           2 :     co::CommandQueue* transferQ = getPipe()->getTransferThreadQueue();
     112             : 
     113           2 :     registerCommand(fabric::CMD_CHANNEL_CONFIG_INIT,
     114           4 :                     CmdFunc(this, &Channel::_cmdConfigInit), queue);
     115           2 :     registerCommand(fabric::CMD_CHANNEL_CONFIG_EXIT,
     116           4 :                     CmdFunc(this, &Channel::_cmdConfigExit), queue);
     117           2 :     registerCommand(fabric::CMD_CHANNEL_FRAME_START,
     118           4 :                     CmdFunc(this, &Channel::_cmdFrameStart), queue);
     119           2 :     registerCommand(fabric::CMD_CHANNEL_FRAME_FINISH,
     120           4 :                     CmdFunc(this, &Channel::_cmdFrameFinish), queue);
     121           2 :     registerCommand(fabric::CMD_CHANNEL_FRAME_CLEAR,
     122           4 :                     CmdFunc(this, &Channel::_cmdFrameClear), queue);
     123           2 :     registerCommand(fabric::CMD_CHANNEL_FRAME_DRAW,
     124           4 :                     CmdFunc(this, &Channel::_cmdFrameDraw), queue);
     125           2 :     registerCommand(fabric::CMD_CHANNEL_FRAME_DRAW_FINISH,
     126           4 :                     CmdFunc(this, &Channel::_cmdFrameDrawFinish), queue);
     127           2 :     registerCommand(fabric::CMD_CHANNEL_FRAME_ASSEMBLE,
     128           4 :                     CmdFunc(this, &Channel::_cmdFrameAssemble), queue);
     129           2 :     registerCommand(fabric::CMD_CHANNEL_FRAME_READBACK,
     130           4 :                     CmdFunc(this, &Channel::_cmdFrameReadback), queue);
     131           2 :     registerCommand(fabric::CMD_CHANNEL_FRAME_TRANSMIT_IMAGE,
     132           4 :                     CmdFunc(this, &Channel::_cmdFrameTransmitImage), tmitQ);
     133           2 :     registerCommand(fabric::CMD_CHANNEL_FRAME_SET_READY,
     134           4 :                     CmdFunc(this, &Channel::_cmdFrameSetReady), transferQ);
     135           2 :     registerCommand(fabric::CMD_CHANNEL_FRAME_SET_READY_NODE,
     136           4 :                     CmdFunc(this, &Channel::_cmdFrameSetReadyNode), tmitQ);
     137           2 :     registerCommand(fabric::CMD_CHANNEL_FRAME_VIEW_START,
     138           4 :                     CmdFunc(this, &Channel::_cmdFrameViewStart), queue);
     139           2 :     registerCommand(fabric::CMD_CHANNEL_FRAME_VIEW_FINISH,
     140           4 :                     CmdFunc(this, &Channel::_cmdFrameViewFinish), queue);
     141           2 :     registerCommand(fabric::CMD_CHANNEL_STOP_FRAME,
     142           4 :                     CmdFunc(this, &Channel::_cmdStopFrame), commandQ);
     143           2 :     registerCommand(fabric::CMD_CHANNEL_FRAME_TILES,
     144           4 :                     CmdFunc(this, &Channel::_cmdFrameTiles), queue);
     145           2 :     registerCommand(fabric::CMD_CHANNEL_FINISH_READBACK,
     146           4 :                     CmdFunc(this, &Channel::_cmdFinishReadback), transferQ);
     147           2 :     registerCommand(fabric::CMD_CHANNEL_DELETE_TRANSFER_WINDOW,
     148           4 :                     CmdFunc(this, &Channel::_cmdDeleteTransferWindow),
     149           2 :                     transferQ);
     150           2 : }
     151             : 
     152           2 : co::CommandQueue* Channel::getPipeThreadQueue()
     153             : {
     154           2 :     return getWindow()->getPipeThreadQueue();
     155             : }
     156             : 
     157           2 : co::CommandQueue* Channel::getCommandThreadQueue()
     158             : {
     159           2 :     return getWindow()->getCommandThreadQueue();
     160             : }
     161             : 
     162          12 : uint32_t Channel::getCurrentFrame() const
     163             : {
     164          12 :     return getPipe()->getCurrentFrame();
     165             : }
     166             : 
     167          21 : Pipe* Channel::getPipe()
     168             : {
     169          21 :     Window* window = getWindow();
     170          21 :     LBASSERT(window);
     171          21 :     return (window ? window->getPipe() : 0);
     172             : }
     173             : 
     174          13 : const Pipe* Channel::getPipe() const
     175             : {
     176          13 :     const Window* window = getWindow();
     177          13 :     LBASSERT(window);
     178          13 :     return (window ? window->getPipe() : 0);
     179             : }
     180             : 
     181           5 : Node* Channel::getNode()
     182             : {
     183           5 :     Window* window = getWindow();
     184           5 :     LBASSERT(window);
     185           5 :     return (window ? window->getNode() : 0);
     186             : }
     187           0 : const Node* Channel::getNode() const
     188             : {
     189           0 :     const Window* window = getWindow();
     190           0 :     LBASSERT(window);
     191           0 :     return (window ? window->getNode() : 0);
     192             : }
     193             : 
     194          39 : Config* Channel::getConfig()
     195             : {
     196          39 :     Window* window = getWindow();
     197          39 :     LBASSERT(window);
     198          39 :     return (window ? window->getConfig() : 0);
     199             : }
     200           0 : const Config* Channel::getConfig() const
     201             : {
     202           0 :     const Window* window = getWindow();
     203           0 :     LBASSERT(window);
     204           0 :     return (window ? window->getConfig() : 0);
     205             : }
     206             : 
     207           2 : ServerPtr Channel::getServer()
     208             : {
     209           2 :     Window* window = getWindow();
     210           2 :     LBASSERT(window);
     211           2 :     return (window ? window->getServer() : 0);
     212             : }
     213             : 
     214           2 : util::ObjectManager& Channel::getObjectManager()
     215             : {
     216           2 :     Window* window = getWindow();
     217           2 :     LBASSERT(window);
     218           2 :     return window->getObjectManager();
     219             : }
     220             : 
     221           3 : const DrawableConfig& Channel::getDrawableConfig() const
     222             : {
     223           3 :     const Window* window = getWindow();
     224           3 :     LBASSERT(window);
     225           3 :     return window->getDrawableConfig();
     226             : }
     227             : 
     228           5 : const GLEWContext* Channel::glewGetContext() const
     229             : {
     230           5 :     const Window* window = getWindow();
     231           5 :     LBASSERT(window);
     232           5 :     return window->glewGetContext();
     233             : }
     234             : 
     235           2 : bool Channel::configExit()
     236             : {
     237             : #ifdef EQUALIZER_USE_DEFLECT
     238           2 :     delete _impl->_deflectProxy;
     239           2 :     _impl->_deflectProxy = 0;
     240             : #endif
     241           2 :     _impl->framebufferImage.flush();
     242           2 :     return true;
     243             : }
     244             : 
     245           2 : bool Channel::configInit(const uint128_t&)
     246             : {
     247             : #ifdef EQUALIZER_USE_DEFLECT
     248           2 :     if (getView())
     249             :     {
     250           1 :         LBASSERT(!_impl->_deflectProxy);
     251             :         try
     252             :         {
     253             :             // Try to create Deflect proxy from env vars or
     254             :             // config values, silently ignore failure
     255           2 :             _impl->_deflectProxy = new deflect::Proxy(*this);
     256             :         }
     257           1 :         catch (...)
     258             :         {
     259             :         }
     260             :     }
     261             : #endif
     262           2 :     return true;
     263             : }
     264             : 
     265           8 : void Channel::notifyViewportChanged()
     266             : {
     267           8 :     const PixelViewport oldPVP = getPixelViewport();
     268           8 :     Super::notifyViewportChanged();
     269           8 :     const PixelViewport& newPVP = getPixelViewport();
     270             : 
     271           8 :     if (newPVP == oldPVP)
     272           5 :         return;
     273             : 
     274           3 :     SizeEvent event;
     275           3 :     event.x = newPVP.x;
     276           3 :     event.y = newPVP.y;
     277           3 :     event.w = newPVP.w;
     278           3 :     event.h = newPVP.h;
     279             : 
     280           3 :     processEvent(EVENT_CHANNEL_RESIZE, event);
     281             : }
     282             : 
     283           0 : void Channel::notifyStopFrame(const uint32_t)
     284             : {
     285           0 : }
     286             : 
     287          11 : void Channel::addStatistic(Statistic& event)
     288             : {
     289             :     {
     290          11 :         const uint32_t frameNumber = event.frameNumber;
     291          11 :         const size_t index = frameNumber % _impl->statistics->size();
     292          11 :         LBASSERT(index < _impl->statistics->size());
     293          11 :         LBASSERTINFO(_impl->statistics.data[index].used > 0, frameNumber);
     294             : 
     295          22 :         lunchbox::ScopedFastWrite mutex(_impl->statistics);
     296          11 :         Statistics& statistics = _impl->statistics.data[index].data;
     297          11 :         statistics.push_back(event);
     298             :     }
     299          11 :     processEvent(event);
     300          11 : }
     301             : 
     302             : //---------------------------------------------------------------------------
     303             : // operations
     304             : //---------------------------------------------------------------------------
     305           2 : bool Channel::waitFrameFinished(const uint32_t frame,
     306             :                                 const uint32_t timeout) const
     307             : {
     308           2 :     return _impl->finishedFrame.timedWaitGE(frame, timeout);
     309             : }
     310             : 
     311           3 : void Channel::frameClear(const uint128_t&)
     312             : {
     313           3 :     resetRegions();
     314           3 :     EQ_GL_CALL(applyBuffer());
     315           3 :     EQ_GL_CALL(applyViewport());
     316             : 
     317             : #ifndef NDEBUG
     318           3 :     if (getenv("EQ_TAINT_CHANNELS"))
     319             :     {
     320           0 :         const Vector3ub color = getUniqueColor();
     321           0 :         EQ_GL_CALL(glClearColor(color.r() / 255.f, color.g() / 255.f,
     322             :                                 color.b() / 255.f, 0.f));
     323             :     }
     324             : #endif // NDEBUG
     325             : 
     326           3 :     EQ_GL_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
     327           3 : }
     328             : 
     329           0 : void Channel::frameDraw(const uint128_t&)
     330             : {
     331           0 :     EQ_GL_CALL(applyBuffer());
     332           0 :     EQ_GL_CALL(applyViewport());
     333             : 
     334           0 :     const bool coreProfile = getWindow()->getIAttribute(
     335           0 :                                  WindowSettings::IATTR_HINT_CORE_PROFILE) == ON;
     336           0 :     if (coreProfile)
     337           0 :         return;
     338             : 
     339           0 :     EQ_GL_CALL(glMatrixMode(GL_PROJECTION));
     340           0 :     EQ_GL_CALL(glLoadIdentity());
     341           0 :     EQ_GL_CALL(applyFrustum());
     342             : 
     343           0 :     EQ_GL_CALL(glMatrixMode(GL_MODELVIEW));
     344           0 :     EQ_GL_CALL(glLoadIdentity());
     345           0 :     EQ_GL_CALL(applyHeadTransform());
     346             : }
     347             : 
     348           1 : void Channel::frameAssemble(const uint128_t&, const Frames& frames)
     349             : {
     350           1 :     EQ_GL_CALL(applyBuffer());
     351           1 :     EQ_GL_CALL(applyViewport());
     352           1 :     EQ_GL_CALL(setupAssemblyState());
     353             :     try
     354             :     {
     355           1 :         Compositor::assembleFrames(frames, this, 0);
     356             :     }
     357           0 :     catch (const co::Exception& e)
     358             :     {
     359           0 :         LBWARN << e.what() << std::endl;
     360             :     }
     361           1 :     EQ_GL_CALL(resetAssemblyState());
     362           1 : }
     363             : 
     364           1 : void Channel::frameReadback(const uint128_t&, const Frames& frames)
     365             : {
     366           1 :     const PixelViewport& region = getRegion();
     367           1 :     if (!region.hasArea())
     368           0 :         return;
     369             : 
     370           1 :     EQ_GL_CALL(applyBuffer());
     371           1 :     EQ_GL_CALL(applyViewport());
     372           1 :     EQ_GL_CALL(setupAssemblyState());
     373             : 
     374           1 :     util::ObjectManager& glObjects = getObjectManager();
     375           1 :     const DrawableConfig& drawable = getDrawableConfig();
     376             : 
     377           2 :     for (Frame* frame : frames)
     378           2 :         frame->startReadback(glObjects, drawable, PixelViewports(1, region),
     379           2 :                              getContext());
     380             : 
     381           1 :     EQ_GL_CALL(resetAssemblyState());
     382             : }
     383             : 
     384           2 : void Channel::startFrame(const uint32_t)
     385             : { /* nop */
     386           2 : }
     387           2 : void Channel::releaseFrame(const uint32_t)
     388             : { /* nop */
     389           2 : }
     390           2 : void Channel::releaseFrameLocal(const uint32_t)
     391             : { /* nop */
     392           2 : }
     393             : 
     394           2 : void Channel::frameStart(const uint128_t&, const uint32_t frameNumber)
     395             : {
     396           2 :     resetRegions();
     397           2 :     startFrame(frameNumber);
     398           2 : }
     399             : 
     400           2 : void Channel::frameFinish(const uint128_t&, const uint32_t frameNumber)
     401             : {
     402           2 :     _impl->frameFinish();
     403           2 :     releaseFrame(frameNumber);
     404           2 : }
     405             : 
     406           2 : void Channel::frameDrawFinish(const uint128_t&, const uint32_t frameNumber)
     407             : {
     408           2 :     releaseFrameLocal(frameNumber);
     409           2 : }
     410             : 
     411           1 : void Channel::frameViewStart(const uint128_t&)
     412             : { /* nop */
     413           1 : }
     414             : 
     415           1 : void Channel::frameViewFinish(const uint128_t& frameID)
     416             : {
     417           1 :     frameDrawOverlay(frameID);
     418           1 :     _impl->frameViewFinish(*this);
     419           1 : }
     420             : 
     421           1 : void Channel::frameDrawOverlay(const uint128_t&)
     422             : { /* nop */
     423           1 : }
     424             : 
     425           2 : void Channel::setupAssemblyState()
     426             : {
     427           2 :     EQ_GL_CALL(bindFrameBuffer());
     428           2 :     const PixelViewport& pvp = getPixelViewport();
     429           2 :     const bool coreProfile = getWindow()->getIAttribute(
     430           2 :                                  WindowSettings::IATTR_HINT_CORE_PROFILE) == ON;
     431           2 :     if (!coreProfile)
     432           2 :         Compositor::setupAssemblyState(pvp, glewGetContext());
     433           2 : }
     434             : 
     435           2 : void Channel::resetAssemblyState()
     436             : {
     437           2 :     EQ_GL_CALL(bindFrameBuffer());
     438           2 :     const bool coreProfile = getWindow()->getIAttribute(
     439           2 :                                  WindowSettings::IATTR_HINT_CORE_PROFILE) == ON;
     440           2 :     if (!coreProfile)
     441           2 :         Compositor::resetAssemblyState();
     442           2 : }
     443             : 
     444           9 : void Channel::_overrideContext(RenderContext& context)
     445             : {
     446           9 :     overrideContext(context);
     447           9 :     Window* window = getWindow();
     448           9 :     window->_addRenderContext(context);
     449           9 : }
     450             : 
     451           0 : Frustumf Channel::getScreenFrustum() const
     452             : {
     453           0 :     const Pixel& pixel = getPixel();
     454           0 :     PixelViewport pvp(getPixelViewport());
     455           0 :     const Viewport& vp(getViewport());
     456             : 
     457           0 :     pvp.x = static_cast<int32_t>(pvp.w / vp.w * vp.x);
     458           0 :     pvp.y = static_cast<int32_t>(pvp.h / vp.h * vp.y);
     459           0 :     pvp.unapply(pixel);
     460             : 
     461           0 :     return eq::Frustumf(static_cast<float>(pvp.x),
     462           0 :                         static_cast<float>(pvp.getXEnd()),
     463           0 :                         static_cast<float>(pvp.y),
     464           0 :                         static_cast<float>(pvp.getYEnd()), -1.f, 1.f);
     465             : }
     466             : 
     467           5 : View* Channel::getView()
     468             : {
     469           5 :     LB_TS_THREAD(_pipeThread);
     470           5 :     Pipe* pipe = getPipe();
     471           5 :     return pipe->getView(getContext().view);
     472             : }
     473             : 
     474           1 : const View* Channel::getView() const
     475             : {
     476           1 :     LB_TS_THREAD(_pipeThread);
     477           1 :     const Pipe* pipe = getPipe();
     478           1 :     return pipe->getView(getContext().view);
     479             : }
     480             : 
     481           0 : co::QueueSlave* Channel::_getQueue(const uint128_t& queueID)
     482             : {
     483           0 :     LB_TS_THREAD(_pipeThread);
     484           0 :     Pipe* pipe = getPipe();
     485           0 :     return pipe->getQueue(queueID);
     486             : }
     487             : 
     488           0 : View* Channel::getNativeView()
     489             : {
     490           0 :     LB_TS_THREAD(_pipeThread);
     491           0 :     Pipe* pipe = getPipe();
     492           0 :     return pipe->getView(getNativeContext().view);
     493             : }
     494             : 
     495           0 : const View* Channel::getNativeView() const
     496             : {
     497           0 :     LB_TS_THREAD(_pipeThread);
     498           0 :     const Pipe* pipe = getPipe();
     499           0 :     return pipe->getView(getNativeContext().view);
     500             : }
     501             : 
     502           2 : void Channel::changeLatency(const uint32_t latency)
     503             : {
     504             : #ifndef NDEBUG
     505           6 :     for (detail::Channel::StatisticsRBCIter i = _impl->statistics->begin();
     506           4 :          i != _impl->statistics->end(); ++i)
     507             :     {
     508           0 :         LBASSERT((*i).used == 0);
     509             :     }
     510             : #endif // NDEBUG
     511           2 :     _impl->statistics->resize(latency + 1);
     512           2 : }
     513             : 
     514           0 : void Channel::addResultImageListener(ResultImageListener* listener)
     515             : {
     516           0 :     _impl->addResultImageListener(listener);
     517           0 : }
     518             : 
     519           0 : void Channel::removeResultImageListener(ResultImageListener* listener)
     520             : {
     521           0 :     _impl->removeResultImageListener(listener);
     522           0 : }
     523             : 
     524           0 : std::string Channel::getDumpImageFileName() const
     525             : {
     526           0 :     std::stringstream name;
     527           0 :     name << getCurrentFrame() << ".rgb";
     528           0 :     return name.str();
     529             : }
     530             : 
     531             : //---------------------------------------------------------------------------
     532             : // apply convenience methods
     533             : //---------------------------------------------------------------------------
     534           5 : void Channel::applyBuffer()
     535             : {
     536           5 :     LB_TS_THREAD(_pipeThread);
     537           5 :     const Window* window = getWindow();
     538           5 :     if (!window->getSystemWindow()->getFrameBufferObject())
     539             :     {
     540           3 :         EQ_GL_CALL(glReadBuffer(getReadBuffer()));
     541           3 :         EQ_GL_CALL(glDrawBuffer(getDrawBuffer()));
     542             :     }
     543             : 
     544           5 :     applyColorMask();
     545           5 : }
     546             : 
     547          11 : void Channel::bindFrameBuffer()
     548             : {
     549          11 :     LB_TS_THREAD(_pipeThread);
     550          11 :     const Window* window = getWindow();
     551          11 :     if (!window->getSystemWindow())
     552           0 :         return;
     553             : 
     554          11 :     if (_impl->_updateFrameBuffer)
     555             :     {
     556           5 :         window->updateFrameBuffer();
     557           5 :         _impl->_updateFrameBuffer = false;
     558             :     }
     559          11 :     window->bindFrameBuffer();
     560             : }
     561             : 
     562           5 : void Channel::bindDrawFrameBuffer()
     563             : {
     564           5 :     LB_TS_THREAD(_pipeThread);
     565           5 :     const Window* window = getWindow();
     566           5 :     if (!window->getSystemWindow())
     567           0 :         return;
     568             : 
     569           5 :     window->bindDrawFrameBuffer();
     570           5 :     _impl->_updateFrameBuffer = true;
     571             : }
     572             : 
     573           5 : void Channel::applyColorMask() const
     574             : {
     575           5 :     LB_TS_THREAD(_pipeThread);
     576           5 :     const ColorMask& colorMask = getDrawBufferMask();
     577           5 :     EQ_GL_CALL(
     578             :         glColorMask(colorMask.red, colorMask.green, colorMask.blue, true));
     579           5 : }
     580             : 
     581           5 : void Channel::applyViewport() const
     582             : {
     583           5 :     LB_TS_THREAD(_pipeThread);
     584           5 :     const PixelViewport& pvp = getPixelViewport();
     585             : 
     586           5 :     if (!pvp.hasArea())
     587             :     {
     588           0 :         LBERROR << "Can't apply viewport " << pvp << std::endl;
     589           0 :         return;
     590             :     }
     591             : 
     592           5 :     EQ_GL_CALL(glViewport(pvp.x, pvp.y, pvp.w, pvp.h));
     593           5 :     EQ_GL_CALL(glScissor(pvp.x, pvp.y, pvp.w, pvp.h));
     594             : }
     595             : 
     596           0 : void Channel::applyFrustum() const
     597             : {
     598           0 :     LB_TS_THREAD(_pipeThread);
     599           0 :     if (useOrtho())
     600           0 :         applyOrtho();
     601             :     else
     602           0 :         applyPerspective();
     603           0 : }
     604             : 
     605           0 : void Channel::applyPerspective() const
     606             : {
     607           0 :     LB_TS_THREAD(_pipeThread);
     608           0 :     Frustumf frustum = getPerspective();
     609           0 :     const Vector2f jitter = getJitter();
     610             : 
     611           0 :     frustum.jitter(jitter);
     612           0 :     EQ_GL_CALL(glFrustum(frustum.left(), frustum.right(), frustum.bottom(),
     613             :                          frustum.top(), frustum.nearPlane(),
     614             :                          frustum.farPlane()));
     615           0 : }
     616             : 
     617           0 : void Channel::applyOrtho() const
     618             : {
     619           0 :     LB_TS_THREAD(_pipeThread);
     620           0 :     Frustumf ortho = getOrtho();
     621           0 :     const Vector2f jitter = getJitter();
     622             : 
     623           0 :     ortho.jitter(jitter);
     624           0 :     EQ_GL_CALL(glOrtho(ortho.left(), ortho.right(), ortho.bottom(), ortho.top(),
     625             :                        ortho.nearPlane(), ortho.farPlane()));
     626           0 : }
     627             : 
     628           0 : void Channel::applyScreenFrustum() const
     629             : {
     630           0 :     LB_TS_THREAD(_pipeThread);
     631           0 :     const Frustumf frustum = getScreenFrustum();
     632           0 :     EQ_GL_CALL(glOrtho(frustum.left(), frustum.right(), frustum.bottom(),
     633             :                        frustum.top(), frustum.nearPlane(), frustum.farPlane()));
     634           0 : }
     635             : 
     636           0 : void Channel::applyHeadTransform() const
     637             : {
     638           0 :     LB_TS_THREAD(_pipeThread);
     639           0 :     if (useOrtho())
     640           0 :         applyOrthoTransform();
     641             :     else
     642           0 :         applyPerspectiveTransform();
     643           0 : }
     644             : 
     645           0 : void Channel::applyPerspectiveTransform() const
     646             : {
     647           0 :     LB_TS_THREAD(_pipeThread);
     648           0 :     const Matrix4f& xfm = getPerspectiveTransform();
     649           0 :     EQ_GL_CALL(glMultMatrixf(xfm.array));
     650           0 : }
     651             : 
     652           0 : void Channel::applyOrthoTransform() const
     653             : {
     654           0 :     LB_TS_THREAD(_pipeThread);
     655           0 :     const Matrix4f& xfm = getOrthoTransform();
     656           0 :     EQ_GL_CALL(glMultMatrixf(xfm.array));
     657           0 : }
     658             : 
     659           0 : void Channel::applyOverlayState()
     660             : {
     661           0 :     applyBuffer();
     662           0 :     applyViewport();
     663           0 :     setupAssemblyState();
     664             : 
     665           0 :     glMatrixMode(GL_PROJECTION);
     666           0 :     glLoadIdentity();
     667           0 :     applyScreenFrustum();
     668             : 
     669           0 :     EQ_GL_CALL(glLogicOp(GL_XOR));
     670           0 :     EQ_GL_CALL(glEnable(GL_COLOR_LOGIC_OP));
     671           0 :     EQ_GL_CALL(glDisable(GL_DEPTH_TEST));
     672           0 :     EQ_GL_CALL(glDisable(GL_LIGHTING));
     673           0 :     EQ_GL_CALL(glCullFace(GL_BACK));
     674             : 
     675           0 :     EQ_GL_CALL(glColor3f(1.f, 1.f, 1.f));
     676           0 : }
     677             : 
     678           0 : void Channel::resetOverlayState()
     679             : {
     680           0 :     EQ_GL_CALL(glDisable(GL_COLOR_LOGIC_OP));
     681           0 :     EQ_GL_CALL(glEnable(GL_DEPTH_TEST));
     682           0 :     EQ_GL_CALL(glEnable(GL_LIGHTING));
     683           0 :     resetAssemblyState();
     684           0 : }
     685             : 
     686             : namespace
     687             : {
     688           0 : static Vector2f* _lookupJitterTable(const uint32_t size)
     689             : {
     690           0 :     switch (size)
     691             :     {
     692             :     case 2:
     693           0 :         return Jitter::j2;
     694             :     case 3:
     695           0 :         return Jitter::j3;
     696             :     case 4:
     697           0 :         return Jitter::j4;
     698             :     case 8:
     699           0 :         return Jitter::j8;
     700             :     case 15:
     701           0 :         return Jitter::j15;
     702             :     case 24:
     703           0 :         return Jitter::j24;
     704             :     case 66:
     705           0 :         return Jitter::j66;
     706             :     default:
     707           0 :         break;
     708             :     }
     709           0 :     return 0;
     710             : }
     711             : }
     712             : 
     713           0 : Vector2f Channel::getJitter() const
     714             : {
     715           0 :     const SubPixel& subpixel = getSubPixel();
     716           0 :     if (subpixel == SubPixel::ALL)
     717           0 :         return Vector2f();
     718             : 
     719             :     // Compute a pixel size
     720           0 :     const PixelViewport& pvp = getPixelViewport();
     721           0 :     const float pvp_w = static_cast<float>(pvp.w);
     722           0 :     const float pvp_h = static_cast<float>(pvp.h);
     723             : 
     724           0 :     const Frustumf& frustum = getFrustum();
     725           0 :     const float frustum_w = frustum.getWidth();
     726           0 :     const float frustum_h = frustum.getHeight();
     727             : 
     728           0 :     const float pixel_w = frustum_w / pvp_w;
     729           0 :     const float pixel_h = frustum_h / pvp_h;
     730             : 
     731           0 :     const Vector2f pixelSize(pixel_w, pixel_h);
     732             : 
     733           0 :     Vector2f* table = _lookupJitterTable(subpixel.size);
     734           0 :     Vector2f jitter;
     735           0 :     if (!table)
     736             :     {
     737           0 :         static lunchbox::RNG rng;
     738           0 :         jitter.x() = rng.get<float>();
     739           0 :         jitter.y() = rng.get<float>();
     740             :     }
     741             :     else
     742           0 :         jitter = table[subpixel.index];
     743             : 
     744           0 :     const Pixel& pixel = getPixel();
     745           0 :     jitter.x() /= static_cast<float>(pixel.w);
     746           0 :     jitter.y() /= static_cast<float>(pixel.h);
     747             : 
     748           0 :     return jitter * pixelSize;
     749             : }
     750             : 
     751           2 : bool Channel::isStopped() const
     752             : {
     753           2 :     return _impl->state == STATE_STOPPED;
     754             : }
     755             : 
     756           0 : const Vector3ub& Channel::getUniqueColor() const
     757             : {
     758           0 :     return _impl->color;
     759             : }
     760             : 
     761           5 : void Channel::resetRegions()
     762             : {
     763           5 :     _impl->regions.clear();
     764           5 : }
     765             : 
     766           0 : void Channel::declareRegion(const eq::Viewport& vp)
     767             : {
     768           0 :     eq::PixelViewport region = getPixelViewport();
     769           0 :     region.x = 0;
     770           0 :     region.y = 0;
     771             : 
     772           0 :     region.apply(vp);
     773           0 :     declareRegion(region);
     774           0 : }
     775             : 
     776             : namespace
     777             : {
     778             : #ifndef NDEBUG
     779           3 : bool _hasOverlap(PixelViewports& regions)
     780             : {
     781           3 :     if (regions.size() < 2)
     782           2 :         return false;
     783             : 
     784           2 :     for (size_t i = 0; i < regions.size() - 1; ++i)
     785           2 :         for (size_t j = i + 1; j < regions.size(); ++j)
     786             :         {
     787           1 :             PixelViewport pv = regions[j];
     788           1 :             pv.intersect(regions[i]);
     789           1 :             if (pv.hasArea())
     790           0 :                 return true;
     791             :         }
     792           1 :     return false;
     793             : }
     794             : #endif
     795             : 
     796             : /** Remove overlapping regions by merging them */
     797           3 : bool _removeOverlap(PixelViewports& regions)
     798             : {
     799           3 :     if (regions.size() < 2)
     800           2 :         return false;
     801             : 
     802           2 :     for (size_t i = 0; i < regions.size() - 1; ++i)
     803           2 :         for (size_t j = i + 1; j < regions.size(); ++j)
     804             :         {
     805           1 :             PixelViewport pvp = regions[i];
     806           1 :             if (!pvp.hasArea())
     807             :             {
     808           0 :                 std::swap(regions[i], regions.back());
     809           0 :                 regions.pop_back();
     810           0 :                 return true;
     811             :             }
     812             : 
     813           1 :             pvp.intersect(regions[j]);
     814           1 :             if (pvp.hasArea())
     815             :             {
     816           0 :                 regions[i].merge(regions[j]);
     817           0 :                 std::swap(regions[j], regions.back());
     818           0 :                 regions.pop_back();
     819           0 :                 return true;
     820             :             }
     821             :         }
     822           1 :     return false;
     823             : }
     824             : }
     825             : 
     826           3 : void Channel::declareRegion(const PixelViewport& region)
     827             : {
     828           3 :     PixelViewports& regions = _impl->regions;
     829           3 :     PixelViewport clippedRegion = region;
     830           3 :     PixelViewport pvp = getPixelViewport();
     831           3 :     pvp.x = 0;
     832           3 :     pvp.y = 0;
     833             : 
     834           3 :     clippedRegion.intersect(pvp);
     835           3 :     if (clippedRegion.hasArea())
     836             :     {
     837           3 :         regions.push_back(clippedRegion);
     838             : #ifndef NDEBUG
     839           3 :         const PixelViewport pvpBefore = getRegion();
     840             : #endif
     841           3 :         while (_removeOverlap(regions))
     842             :         { /* nop */
     843             :         }
     844             : 
     845             : #ifndef NDEBUG
     846           3 :         LBASSERT(!_hasOverlap(regions));
     847           3 :         LBASSERT(pvpBefore == getRegion());
     848             : #endif
     849           3 :         return;
     850             :     }
     851             : 
     852           0 :     if (regions.empty()) // set on first declaration of empty ROI
     853           0 :         regions.push_back(PixelViewport(0, 0, 0, 0));
     854             : }
     855             : 
     856          11 : PixelViewport Channel::getRegion() const
     857             : {
     858          11 :     PixelViewport region;
     859          22 :     for (const PixelViewport& pvp : _impl->regions)
     860          11 :         region.merge(pvp);
     861             : 
     862          11 :     return region;
     863             : }
     864             : 
     865           0 : const PixelViewports& Channel::getRegions() const
     866             : {
     867           0 :     return _impl->regions;
     868             : }
     869             : 
     870           0 : EventOCommand Channel::sendError(const uint32_t error)
     871             : {
     872           0 :     return getConfig()->sendError(EVENT_CHANNEL_ERROR, Error(error, getID()));
     873             : }
     874             : 
     875           3 : bool Channel::processEvent(const EventType type, SizeEvent& event)
     876             : {
     877           3 :     Config* config = getConfig();
     878           3 :     updateEvent(event, config->getTime());
     879             : 
     880           3 :     if (type == EVENT_CHANNEL_RESIZE)
     881             :     {
     882           3 :         const uint128_t& viewID = getNativeContext().view.identifier;
     883           3 :         if (viewID == 0)
     884           3 :             return true;
     885             : 
     886             :         // transform to view event, which is meaningful for the config
     887           0 :         event.originator = viewID;
     888             : 
     889           0 :         event.dw = event.w / float(_impl->initialSize.x());
     890           0 :         event.dh = event.h / float(_impl->initialSize.y());
     891           0 :         config->sendEvent(EVENT_VIEW_RESIZE) << event;
     892           0 :         return true;
     893             :     }
     894             : 
     895           0 :     config->sendEvent(type) << event;
     896           0 :     return true;
     897             : }
     898             : 
     899           0 : bool Channel::processEvent(const EventType type, PointerEvent& event)
     900             : {
     901           0 :     Config* config = getConfig();
     902           0 :     updateEvent(event, config->getTime());
     903           0 :     config->sendEvent(type) << event;
     904           0 :     return true;
     905             : }
     906             : 
     907           0 : bool Channel::processEvent(const EventType type, KeyEvent& event)
     908             : {
     909           0 :     Config* config = getConfig();
     910           0 :     updateEvent(event, config->getTime());
     911           0 :     config->sendEvent(type) << event;
     912           0 :     return true;
     913             : }
     914             : 
     915          11 : bool Channel::processEvent(Statistic& event)
     916             : {
     917          11 :     Config* config = getConfig();
     918          11 :     updateEvent(event, config->getTime());
     919          11 :     config->sendEvent(EVENT_STATISTIC) << event;
     920          11 :     return true;
     921             : }
     922             : 
     923           0 : void Channel::drawStatistics()
     924             : {
     925           0 :     const PixelViewport& pvp = getPixelViewport();
     926           0 :     LBASSERT(pvp.hasArea());
     927           0 :     Window* window = getWindow();
     928             :     const bool coreProfile =
     929           0 :         window->getIAttribute(WindowSettings::IATTR_HINT_CORE_PROFILE) == ON;
     930           0 :     if (!pvp.hasArea() || coreProfile)
     931           0 :         return;
     932             : 
     933             :     //----- setup
     934           0 :     applyOverlayState();
     935             : 
     936           0 :     EQ_GL_CALL(glDisable(GL_COLOR_LOGIC_OP));
     937           0 :     EQ_GL_CALL(glEnable(GL_BLEND));
     938           0 :     EQ_GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
     939             : 
     940             : #ifdef EQUALIZER_USE_GLSTATS
     941           0 :     const util::BitmapFont* font = window->getSmallFont();
     942           0 :     const Config* config = getConfig();
     943           0 :     const GLStats::Data& data = config->getStatistics();
     944           0 :     detail::StatsRenderer renderer(font);
     945           0 :     const Viewport& vp = getViewport();
     946           0 :     const uint32_t width = uint32_t(pvp.w / vp.w);
     947           0 :     const uint32_t height = uint32_t(pvp.h / vp.h);
     948             : 
     949           0 :     renderer.setViewport(width, height);
     950           0 :     renderer.draw(data);
     951             : #endif
     952             : 
     953           0 :     EQ_GL_CALL(glEnable(GL_COLOR_LOGIC_OP));
     954           0 :     EQ_GL_CALL(glColor3f(1.f, 1.f, 1.f));
     955           0 :     window->drawFPS();
     956             : 
     957           0 :     EQ_GL_CALL(glDisable(GL_BLEND));
     958           0 :     resetOverlayState();
     959             : }
     960             : 
     961           0 : void Channel::outlineViewport()
     962             : {
     963           0 :     const bool coreProfile = getWindow()->getIAttribute(
     964           0 :                                  WindowSettings::IATTR_HINT_CORE_PROFILE) == ON;
     965           0 :     if (coreProfile)
     966           0 :         return;
     967             : 
     968           0 :     applyOverlayState();
     969             : 
     970           0 :     const eq::PixelViewport& region = getRegion();
     971           0 :     glColor3f(.5f, .5f, .5f);
     972           0 :     glBegin(GL_LINE_LOOP);
     973             :     {
     974           0 :         glVertex3f(region.x + .5f, region.y + .5f, 0.f);
     975           0 :         glVertex3f(region.getXEnd() - .5f, region.y + .5f, 0.f);
     976           0 :         glVertex3f(region.getXEnd() - .5f, region.getYEnd() - .5f, 0.f);
     977           0 :         glVertex3f(region.x + .5f, region.getYEnd() - .5f, 0.f);
     978             :     }
     979           0 :     glEnd();
     980             : 
     981           0 :     const PixelViewport& pvp = getPixelViewport();
     982           0 :     glColor3f(1.0f, 1.0f, 1.0f);
     983           0 :     glBegin(GL_LINE_LOOP);
     984             :     {
     985           0 :         glVertex3f(pvp.x + .5f, pvp.y + .5f, 0.f);
     986           0 :         glVertex3f(pvp.getXEnd() - .5f, pvp.y + .5f, 0.f);
     987           0 :         glVertex3f(pvp.getXEnd() - .5f, pvp.getYEnd() - .5f, 0.f);
     988           0 :         glVertex3f(pvp.x + .5f, pvp.getYEnd() - .5f, 0.f);
     989             :     }
     990           0 :     glEnd();
     991             : 
     992           0 :     resetOverlayState();
     993             : }
     994             : 
     995             : namespace detail
     996             : {
     997           1 : struct RBStat
     998             : {
     999           1 :     explicit RBStat(eq::Channel* channel)
    1000           1 :         : event(Statistic::CHANNEL_READBACK, channel)
    1001             :         , uncompressed(0)
    1002           1 :         , compressed(0)
    1003             :     {
    1004           1 :         event.statistic.plugins[0] = EQ_COMPRESSOR_NONE;
    1005           1 :         event.statistic.plugins[1] = EQ_COMPRESSOR_NONE;
    1006           1 :         LBASSERT(event.statistic.frameNumber > 0);
    1007           1 :     }
    1008             : 
    1009             :     lunchbox::SpinLock lock;
    1010             :     ChannelStatistics event;
    1011             :     size_t uncompressed;
    1012             :     size_t compressed;
    1013             : 
    1014           2 :     void ref(void*) { ++_refCount; }
    1015           2 :     bool unref(void*)
    1016             :     {
    1017           2 :         if (--_refCount > 0)
    1018           1 :             return false;
    1019             : 
    1020           1 :         if (uncompressed > 0 && compressed > 0)
    1021             :         {
    1022           1 :             event.statistic.ratio = float(compressed) / float(uncompressed);
    1023             :         }
    1024             :         else
    1025           0 :             event.statistic.ratio = 1.0f;
    1026           1 :         delete this;
    1027           1 :         return true;
    1028             :     }
    1029             : 
    1030             :     int32_t getRefCount() const { return _refCount; }
    1031             : private:
    1032             :     a_int32_t _refCount;
    1033             : };
    1034             : }
    1035             : 
    1036             : typedef lunchbox::RefPtr<detail::RBStat> RBStatPtr;
    1037             : 
    1038           0 : void Channel::_frameTiles(RenderContext& context, const bool isLocal,
    1039             :                           const uint128_t& queueID, const uint32_t tasks,
    1040             :                           const co::ObjectVersions& frameIDs)
    1041             : {
    1042           0 :     _overrideContext(context);
    1043           0 :     frameTilesStart(context.frameID);
    1044             : 
    1045           0 :     RBStatPtr stat;
    1046           0 :     Frames frames;
    1047           0 :     if (tasks & fabric::TASK_READBACK)
    1048             :     {
    1049           0 :         frames = _getFrames(frameIDs, true);
    1050           0 :         stat = new detail::RBStat(this);
    1051             :     }
    1052             : 
    1053           0 :     int64_t startTime = getConfig()->getTime();
    1054           0 :     int64_t clearTime = 0;
    1055           0 :     int64_t drawTime = 0;
    1056           0 :     int64_t readbackTime = 0;
    1057           0 :     bool hasAsyncReadback = false;
    1058           0 :     const uint32_t timeout = getConfig()->getTimeout();
    1059             : 
    1060           0 :     co::QueueSlave* queue = _getQueue(queueID);
    1061           0 :     LBASSERT(queue);
    1062             :     for (;;)
    1063             :     {
    1064           0 :         co::ObjectICommand tileCmd = queue->pop(timeout);
    1065           0 :         if (!tileCmd.isValid())
    1066           0 :             break;
    1067             : 
    1068           0 :         const Tile& tile = tileCmd.read<Tile>();
    1069           0 :         context.apply(tile, isLocal);
    1070           0 :         _overrideContext(context);
    1071             : 
    1072           0 :         if (tasks & fabric::TASK_CLEAR)
    1073             :         {
    1074           0 :             const int64_t time = getConfig()->getTime();
    1075           0 :             frameClear(context.frameID);
    1076           0 :             clearTime += getConfig()->getTime() - time;
    1077             :         }
    1078             : 
    1079           0 :         if (tasks & fabric::TASK_DRAW)
    1080             :         {
    1081           0 :             const int64_t time = getConfig()->getTime();
    1082           0 :             frameDraw(context.frameID);
    1083           0 :             drawTime += getConfig()->getTime() - time;
    1084             :             // Set to full region if application has declared nothing
    1085           0 :             if (!getRegion().isValid())
    1086           0 :                 declareRegion(getPixelViewport());
    1087             :         }
    1088             : 
    1089           0 :         if (tasks & fabric::TASK_READBACK)
    1090             :         {
    1091           0 :             const int64_t time = getConfig()->getTime();
    1092           0 :             const size_t nFrames = frames.size();
    1093             : 
    1094           0 :             std::vector<size_t> nImages(nFrames, 0);
    1095           0 :             for (size_t i = 0; i < nFrames; ++i)
    1096             :             {
    1097           0 :                 nImages[i] = frames[i]->getImages().size();
    1098           0 :                 frames[i]->getFrameData()->setPixelViewport(getPixelViewport());
    1099             :             }
    1100             : 
    1101           0 :             frameReadback(context.frameID, frames);
    1102           0 :             readbackTime += getConfig()->getTime() - time;
    1103             : 
    1104           0 :             for (size_t i = 0; i < nFrames; ++i)
    1105             :             {
    1106           0 :                 const Frame* frame = frames[i];
    1107           0 :                 const Images& images = frame->getImages();
    1108           0 :                 for (size_t j = nImages[i]; j < images.size(); ++j)
    1109             :                 {
    1110           0 :                     Image* image = images[j];
    1111           0 :                     const PixelViewport& pvp = image->getPixelViewport();
    1112           0 :                     image->setOffset(pvp.x + tile.pvp.x, pvp.y + tile.pvp.y);
    1113             :                 }
    1114             :             }
    1115             : 
    1116           0 :             if (_asyncFinishReadback(nImages, frames))
    1117           0 :                 hasAsyncReadback = true;
    1118             :         }
    1119           0 :     }
    1120             : 
    1121           0 :     if (tasks & fabric::TASK_CLEAR)
    1122             :     {
    1123           0 :         ChannelStatistics event(Statistic::CHANNEL_CLEAR, this);
    1124           0 :         event.statistic.startTime = startTime;
    1125           0 :         startTime += clearTime;
    1126           0 :         event.statistic.endTime = startTime;
    1127             :     }
    1128             : 
    1129           0 :     if (tasks & fabric::TASK_DRAW)
    1130             :     {
    1131           0 :         ChannelStatistics event(Statistic::CHANNEL_DRAW, this);
    1132           0 :         event.statistic.startTime = startTime;
    1133           0 :         startTime += drawTime;
    1134           0 :         event.statistic.endTime = startTime;
    1135             :     }
    1136             : 
    1137           0 :     if (tasks & fabric::TASK_READBACK)
    1138             :     {
    1139           0 :         stat->event.statistic.startTime = startTime;
    1140           0 :         startTime += readbackTime;
    1141           0 :         stat->event.statistic.endTime = startTime;
    1142             : 
    1143           0 :         _setReady(hasAsyncReadback, stat.get(), frames);
    1144             :     }
    1145             : 
    1146           0 :     frameTilesFinish(context.frameID);
    1147           0 :     resetContext();
    1148           0 : }
    1149             : 
    1150           3 : void Channel::_refFrame(const uint32_t frameNumber)
    1151             : {
    1152           3 :     const size_t index = frameNumber % _impl->statistics->size();
    1153           3 :     detail::Channel::FrameStatistics& stats = _impl->statistics.data[index];
    1154           3 :     LBASSERTINFO(stats.used > 0, frameNumber);
    1155           3 :     ++stats.used;
    1156           3 : }
    1157             : 
    1158           5 : void Channel::_unrefFrame(const uint32_t frameNumber)
    1159             : {
    1160           5 :     const size_t index = frameNumber % _impl->statistics->size();
    1161           5 :     detail::Channel::FrameStatistics& stats = _impl->statistics.data[index];
    1162           5 :     if (--stats.used != 0) // Frame still in use
    1163           3 :         return;
    1164             : 
    1165           4 :     send(getServer(), fabric::CMD_CHANNEL_FRAME_FINISH_REPLY)
    1166           4 :         << stats.region << frameNumber << stats.data;
    1167             : 
    1168           2 :     stats.data.clear();
    1169           2 :     stats.region = Viewport::FULL;
    1170           2 :     _impl->finishedFrame = frameNumber;
    1171             : }
    1172             : 
    1173           2 : Frames Channel::_getFrames(const co::ObjectVersions& frameIDs,
    1174             :                            const bool isOutput)
    1175             : {
    1176           2 :     LB_TS_THREAD(_pipeThread);
    1177             : 
    1178           2 :     Frames frames;
    1179           4 :     for (size_t i = 0; i < frameIDs.size(); ++i)
    1180             :     {
    1181           2 :         Pipe* pipe = getPipe();
    1182           2 :         Frame* frame = pipe->getFrame(frameIDs[i], getEye(), isOutput);
    1183           2 :         LBASSERTINFO(lunchbox::find(frames, frame) == frames.end(),
    1184             :                      "frame " << i << " " << frameIDs[i]);
    1185             : 
    1186           2 :         frames.push_back(frame);
    1187             :     }
    1188             : 
    1189           2 :     return frames;
    1190             : }
    1191             : 
    1192             : //---------------------------------------------------------------------------
    1193             : // Asynchronous image readback, compression and transmission
    1194             : //---------------------------------------------------------------------------
    1195           1 : void Channel::_frameReadback(const uint128_t& frameID,
    1196             :                              const co::ObjectVersions& frameIDs)
    1197             : {
    1198           1 :     LB_TS_THREAD(_pipeThread);
    1199             : 
    1200           2 :     RBStatPtr stat = new detail::RBStat(this);
    1201           2 :     const Frames& frames = _getFrames(frameIDs, true);
    1202             : 
    1203           2 :     std::vector<size_t> nImages(frames.size(), 0);
    1204           2 :     for (size_t i = 0; i < frames.size(); ++i)
    1205           1 :         nImages[i] = frames[i]->getImages().size();
    1206             : 
    1207           1 :     frameReadback(frameID, frames);
    1208           1 :     LBASSERT(stat->event.statistic.frameNumber > 0);
    1209           1 :     const bool async = _asyncFinishReadback(nImages, frames);
    1210           1 :     _setReady(async, stat.get(), frames);
    1211           1 : }
    1212             : 
    1213           1 : bool Channel::_asyncFinishReadback(const std::vector<size_t>& imagePos,
    1214             :                                    const Frames& frames)
    1215             : {
    1216           1 :     LB_TS_THREAD(_pipeThread);
    1217             : 
    1218           1 :     bool hasAsyncReadback = false;
    1219           1 :     LBASSERT(frames.size() == imagePos.size());
    1220             : 
    1221           2 :     for (size_t i = 0; i < frames.size(); ++i)
    1222             :     {
    1223           1 :         Frame* frame = frames[i];
    1224           2 :         FrameDataPtr frameData = frame->getFrameData();
    1225           1 :         const uint32_t frameNumber = getCurrentFrame();
    1226             : 
    1227           1 :         if (frameData->getBuffers() == Frame::Buffer::none)
    1228           0 :             continue;
    1229             : 
    1230           1 :         const Images& images = frameData->getImages();
    1231           1 :         const size_t nImages = images.size();
    1232           1 :         const Eye eye = getEye();
    1233           1 :         const std::vector<uint128_t>& nodes = frame->getInputNodes(eye);
    1234           1 :         const co::NodeIDs& netNodes = frame->getInputNetNodes(eye);
    1235             : 
    1236           2 :         for (uint64_t j = imagePos[i]; j < nImages; ++j)
    1237             :         {
    1238           1 :             if (images[j]->hasAsyncReadback()) // finish async readback
    1239             :             {
    1240           1 :                 _createTransferWindow();
    1241             : 
    1242           1 :                 hasAsyncReadback = true;
    1243           1 :                 _refFrame(frameNumber);
    1244             : 
    1245           2 :                 send(getLocalNode(), fabric::CMD_CHANNEL_FINISH_READBACK)
    1246           3 :                     << co::ObjectVersion(frameData) << j << frameNumber
    1247           3 :                     << getTaskID() << nodes << netNodes;
    1248             :             }
    1249             :             else // transmit images asynchronously
    1250           0 :                 _asyncTransmit(frameData, frameNumber, j, nodes, netNodes,
    1251           0 :                                getTaskID());
    1252             :         }
    1253             :     }
    1254           1 :     return hasAsyncReadback;
    1255             : }
    1256             : 
    1257           1 : void Channel::_finishReadback(const co::ObjectVersion& frameDataVersion,
    1258             :                               const uint64_t imageIndex,
    1259             :                               const uint32_t frameNumber, const uint32_t taskID,
    1260             :                               const std::vector<uint128_t>& nodes,
    1261             :                               const co::NodeIDs& netNodes)
    1262             : {
    1263           1 :     LBLOG(LOG_TASKS | LOG_ASSEMBLY) << "Finish readback" << std::endl;
    1264             : 
    1265           1 :     const Window* window = getWindow();
    1266           1 :     const SystemWindow* transferWindow = window->getTransferWindow();
    1267           1 :     LBASSERT(transferWindow);
    1268           1 :     transferWindow->makeCurrent();
    1269             : 
    1270           2 :     FrameDataPtr frameData = getNode()->getFrameData(frameDataVersion);
    1271           1 :     LBASSERT(frameData);
    1272             : 
    1273           1 :     const Images& images = frameData->getImages();
    1274           1 :     LBASSERT(images.size() > imageIndex);
    1275             : 
    1276           1 :     Image* image = images[imageIndex];
    1277           1 :     LBASSERT(image->hasAsyncReadback());
    1278             : 
    1279           1 :     const GLEWContext* glewContext = window->getTransferGlewContext();
    1280           1 :     image->finishReadback(glewContext);
    1281           1 :     LBASSERT(!image->hasAsyncReadback());
    1282             : 
    1283             :     // schedule async image tranmission
    1284           1 :     _asyncTransmit(frameData, frameNumber, imageIndex, nodes, netNodes, taskID);
    1285           1 : }
    1286             : 
    1287           1 : void Channel::_asyncTransmit(FrameDataPtr frame, const uint32_t frameNumber,
    1288             :                              const uint64_t image,
    1289             :                              const std::vector<uint128_t>& nodes,
    1290             :                              const co::NodeIDs& netNodes, const uint32_t taskID)
    1291             : {
    1292           1 :     LBASSERT(nodes.size() == netNodes.size());
    1293           1 :     co::NodeIDs::const_iterator j = netNodes.begin();
    1294           3 :     for (std::vector<uint128_t>::const_iterator i = nodes.begin();
    1295           2 :          i != nodes.end(); ++i, ++j)
    1296             :     {
    1297           0 :         _refFrame(frameNumber);
    1298             : 
    1299           0 :         LBLOG(LOG_TASKS | LOG_ASSEMBLY) << "Start transmit frame data " << frame
    1300           0 :                                         << " receiver " << *i << " on " << *j
    1301           0 :                                         << std::endl;
    1302           0 :         send(getLocalNode(), fabric::CMD_CHANNEL_FRAME_TRANSMIT_IMAGE)
    1303           0 :             << co::ObjectVersion(frame) << *i << *j << image << frameNumber
    1304           0 :             << taskID;
    1305             :     }
    1306           1 : }
    1307             : 
    1308           0 : void Channel::_transmitImage(const co::ObjectVersion& frameDataVersion,
    1309             :                              const uint128_t& nodeID,
    1310             :                              const co::NodeID& netNodeID,
    1311             :                              const uint64_t imageIndex,
    1312             :                              const uint32_t frameNumber, const uint32_t taskID)
    1313             : {
    1314           0 :     LBLOG(LOG_TASKS | LOG_ASSEMBLY) << "Transmit" << std::endl;
    1315           0 :     FrameDataPtr frameData = getNode()->getFrameData(frameDataVersion);
    1316           0 :     LBASSERT(frameData);
    1317             : 
    1318           0 :     if (frameData->getBuffers() == Frame::Buffer::none)
    1319             :     {
    1320           0 :         LBWARN << "No buffers for frame data" << std::endl;
    1321           0 :         return;
    1322             :     }
    1323             : 
    1324             :     ChannelStatistics transmitEvent(Statistic::CHANNEL_FRAME_TRANSMIT, this,
    1325           0 :                                     frameNumber);
    1326           0 :     transmitEvent.statistic.task = taskID;
    1327             : 
    1328           0 :     const Images& images = frameData->getImages();
    1329           0 :     Image* image = images[imageIndex];
    1330           0 :     LBASSERT(images.size() > imageIndex);
    1331             : 
    1332           0 :     if (image->getStorageType() == Frame::TYPE_TEXTURE)
    1333             :     {
    1334           0 :         LBWARN << "Can't transmit image of type TEXTURE" << std::endl;
    1335           0 :         LBUNIMPLEMENTED;
    1336           0 :         return;
    1337             :     }
    1338             : 
    1339           0 :     co::LocalNodePtr localNode = getLocalNode();
    1340           0 :     co::NodePtr toNode = localNode->connect(netNodeID);
    1341           0 :     if (!toNode || !toNode->isReachable())
    1342             :     {
    1343           0 :         LBWARN << "Can't connect node " << netNodeID << " to send output frame"
    1344           0 :                << std::endl;
    1345           0 :         return;
    1346             :     }
    1347             : 
    1348           0 :     co::ConnectionPtr connection = toNode->getConnection();
    1349             :     co::ConstConnectionDescriptionPtr description =
    1350           0 :         connection->getDescription();
    1351             : 
    1352             :     // use compression on links up to 2 GBit/s
    1353           0 :     const bool useCompression = (description->bandwidth <= 262144);
    1354             : 
    1355           0 :     std::vector<const PixelData*> pixelDatas;
    1356           0 :     std::vector<float> qualities;
    1357             : 
    1358           0 :     Frame::Buffer commandBuffers = Frame::Buffer::none;
    1359           0 :     uint64_t imageDataSize = 0;
    1360             :     {
    1361           0 :         uint64_t rawSize(0);
    1362             :         ChannelStatistics compressEvent(Statistic::CHANNEL_FRAME_COMPRESS, this,
    1363             :                                         frameNumber,
    1364           0 :                                         useCompression ? AUTO : OFF);
    1365           0 :         compressEvent.statistic.task = taskID;
    1366           0 :         compressEvent.statistic.ratio = 1.0f;
    1367           0 :         compressEvent.statistic.plugins[0] = EQ_COMPRESSOR_NONE;
    1368           0 :         compressEvent.statistic.plugins[1] = EQ_COMPRESSOR_NONE;
    1369             : 
    1370             :         // Prepare image pixel data
    1371           0 :         Frame::Buffer buffers[] = {Frame::Buffer::color, Frame::Buffer::depth};
    1372             : 
    1373             :         // for each image attachment
    1374           0 :         for (unsigned j = 0; j < 2; ++j)
    1375             :         {
    1376           0 :             Frame::Buffer buffer = buffers[j];
    1377           0 :             if (image->hasPixelData(buffer))
    1378             :             {
    1379             :                 // format, type, nChunks, compressor name
    1380           0 :                 imageDataSize += sizeof(FrameData::ImageHeader);
    1381             : 
    1382             :                 const PixelData& data = useCompression
    1383           0 :                                             ? image->compressPixelData(buffer)
    1384           0 :                                             : image->getPixelData(buffer);
    1385           0 :                 pixelDatas.push_back(&data);
    1386           0 :                 qualities.push_back(image->getQuality(buffer));
    1387             : 
    1388           0 :                 if (data.compressedData.isCompressed())
    1389             :                 {
    1390           0 :                     imageDataSize +=
    1391           0 :                         data.compressedData.getSize() +
    1392           0 :                         data.compressedData.chunks.size() * sizeof(uint64_t);
    1393           0 :                     compressEvent.statistic.plugins[j] =
    1394           0 :                         data.compressedData.compressor;
    1395             :                 }
    1396             :                 else
    1397           0 :                     imageDataSize +=
    1398           0 :                         sizeof(uint64_t) + image->getPixelDataSize(buffer);
    1399             : 
    1400           0 :                 commandBuffers |= buffer;
    1401           0 :                 rawSize += image->getPixelDataSize(buffer);
    1402             :             }
    1403             :         }
    1404             : 
    1405           0 :         if (rawSize > 0)
    1406           0 :             compressEvent.statistic.ratio =
    1407           0 :                 float(imageDataSize) / float(rawSize);
    1408             :     }
    1409             : 
    1410           0 :     if (pixelDatas.empty())
    1411           0 :         return;
    1412             : 
    1413             :     // send image pixel data command
    1414           0 :     co::LocalNode::SendToken token;
    1415           0 :     if (getIAttribute(IATTR_HINT_SENDTOKEN) == ON)
    1416             :     {
    1417             :         ChannelStatistics waitEvent(Statistic::CHANNEL_FRAME_WAIT_SENDTOKEN,
    1418           0 :                                     this, frameNumber);
    1419           0 :         waitEvent.statistic.task = taskID;
    1420           0 :         token = getLocalNode()->acquireSendToken(toNode);
    1421             :     }
    1422           0 :     LBASSERT(image->getPixelViewport().isValid());
    1423             : 
    1424           0 :     co::ObjectOCommand command(co::Connections(1, connection),
    1425             :                                fabric::CMD_NODE_FRAMEDATA_TRANSMIT,
    1426           0 :                                co::COMMANDTYPE_OBJECT, nodeID, CO_INSTANCE_ALL);
    1427           0 :     command << frameDataVersion << image->getPixelViewport() << image->getZoom()
    1428           0 :             << image->getContext() << commandBuffers << frameNumber
    1429           0 :             << image->getAlphaUsage();
    1430           0 :     command.sendHeader(imageDataSize);
    1431             : 
    1432             : #ifndef NDEBUG
    1433           0 :     size_t sentBytes = 0;
    1434             : #endif
    1435             : 
    1436           0 :     for (uint32_t j = 0; j < pixelDatas.size(); ++j)
    1437             :     {
    1438             : #ifndef NDEBUG
    1439           0 :         sentBytes += sizeof(FrameData::ImageHeader);
    1440             : #endif
    1441           0 :         const PixelData* data = pixelDatas[j];
    1442           0 :         const bool isCompressed = data->compressedData.isCompressed();
    1443             :         const uint32_t nChunks =
    1444           0 :             isCompressed ? uint32_t(data->compressedData.chunks.size()) : 1;
    1445             : 
    1446             :         const FrameData::ImageHeader header = {
    1447           0 :             data->internalFormat,
    1448           0 :             data->externalFormat,
    1449           0 :             data->pixelSize,
    1450             :             data->pvp,
    1451           0 :             isCompressed ? data->compressedData.compressor : EQ_COMPRESSOR_NONE,
    1452           0 :             data->compressorFlags,
    1453             :             nChunks,
    1454           0 :             qualities[j]};
    1455             : 
    1456           0 :         connection->send(&header, sizeof(header), true);
    1457             : 
    1458           0 :         if (isCompressed)
    1459             :         {
    1460           0 :             for (const auto& chunk : data->compressedData.chunks)
    1461             :             {
    1462           0 :                 const uint64_t dataSize = chunk.getNumBytes();
    1463             : 
    1464           0 :                 connection->send(&dataSize, sizeof(dataSize), true);
    1465           0 :                 if (dataSize > 0)
    1466           0 :                     connection->send(chunk.data, dataSize, true);
    1467             : #ifndef NDEBUG
    1468           0 :                 sentBytes += sizeof(dataSize) + dataSize;
    1469             : #endif
    1470             :             }
    1471             :         }
    1472             :         else
    1473             :         {
    1474           0 :             const uint64_t dataSize = data->pvp.getArea() * data->pixelSize;
    1475           0 :             connection->send(&dataSize, sizeof(dataSize), true);
    1476           0 :             connection->send(data->pixels, dataSize, true);
    1477             : #ifndef NDEBUG
    1478           0 :             sentBytes += sizeof(dataSize) + dataSize;
    1479             : #endif
    1480             :         }
    1481             :     }
    1482             : #ifndef NDEBUG
    1483           0 :     LBASSERTINFO(sentBytes == imageDataSize, sentBytes << " != "
    1484             :                                                        << imageDataSize);
    1485             : #endif
    1486             : }
    1487             : 
    1488           1 : void Channel::_setReady(const bool async, detail::RBStat* stat,
    1489             :                         const Frames& frames)
    1490             : {
    1491           2 :     for (FramesCIter i = frames.begin(); i != frames.end(); ++i)
    1492             :     {
    1493           1 :         Frame* frame = *i;
    1494           1 :         const Eye eye = getEye();
    1495           1 :         const std::vector<uint128_t>& nodes = frame->getInputNodes(eye);
    1496           1 :         const co::NodeIDs& netNodes = frame->getInputNetNodes(eye);
    1497             : 
    1498           1 :         if (async)
    1499           1 :             _asyncSetReady(frame->getFrameData(), stat, nodes, netNodes);
    1500             :         else
    1501           0 :             _setReady(frame->getFrameData(), stat, nodes, netNodes);
    1502             :     }
    1503           1 : }
    1504             : 
    1505           1 : void Channel::_asyncSetReady(const FrameDataPtr frame, detail::RBStat* stat,
    1506             :                              const std::vector<uint128_t>& nodes,
    1507             :                              const co::NodeIDs& netNodes)
    1508             : {
    1509           1 :     LBASSERT(stat->event.statistic.frameNumber > 0);
    1510             : 
    1511           1 :     stat->event.statistic.type = Statistic::CHANNEL_ASYNC_READBACK;
    1512             : 
    1513           1 :     _refFrame(stat->event.statistic.frameNumber);
    1514           1 :     stat->ref(0);
    1515             : 
    1516           2 :     send(getLocalNode(), fabric::CMD_CHANNEL_FRAME_SET_READY)
    1517           3 :         << co::ObjectVersion(frame) << stat << nodes << netNodes;
    1518           1 : }
    1519             : 
    1520           1 : void Channel::_setReady(FrameDataPtr frame, detail::RBStat* stat,
    1521             :                         const std::vector<uint128_t>& nodes,
    1522             :                         const co::NodeIDs& netNodes)
    1523             : {
    1524           2 :     LBLOG(LOG_TASKS | LOG_ASSEMBLY) << "Set ready " << co::ObjectVersion(frame)
    1525           3 :                                     << std::endl;
    1526           1 :     frame->setReady();
    1527             : 
    1528           1 :     const uint32_t frameNumber = stat->event.statistic.frameNumber;
    1529           1 :     _refFrame(frameNumber);
    1530             : 
    1531           2 :     send(getLocalNode(), fabric::CMD_CHANNEL_FRAME_SET_READY_NODE)
    1532           3 :         << co::ObjectVersion(frame) << nodes << netNodes << frameNumber;
    1533             : 
    1534           1 :     const DrawableConfig& dc = getDrawableConfig();
    1535           1 :     const size_t colorBytes = (3 * dc.colorBits + dc.alphaBits) / 8;
    1536             : 
    1537             :     {
    1538           2 :         lunchbox::ScopedFastWrite mutex(stat->lock);
    1539           1 :         const Images& images = frame->getImages();
    1540           2 :         for (ImagesCIter i = images.begin(); i != images.end(); ++i)
    1541             :         {
    1542           1 :             const Image* image = *i;
    1543           1 :             if (image->hasPixelData(Frame::Buffer::color))
    1544             :             {
    1545           1 :                 stat->uncompressed +=
    1546           1 :                     colorBytes * image->getPixelViewport().getArea();
    1547           1 :                 stat->compressed +=
    1548           1 :                     image->getPixelDataSize(Frame::Buffer::color);
    1549           1 :                 stat->event.statistic.plugins[0] =
    1550           1 :                     image->getDownloaderName(Frame::Buffer::color);
    1551             :             }
    1552           1 :             if (image->hasPixelData(Frame::Buffer::depth))
    1553             :             {
    1554           0 :                 stat->uncompressed += 4 * image->getPixelViewport().getArea();
    1555           0 :                 stat->compressed +=
    1556           0 :                     image->getPixelDataSize(Frame::Buffer::depth);
    1557           0 :                 stat->event.statistic.plugins[1] =
    1558           0 :                     image->getDownloaderName(Frame::Buffer::depth);
    1559             :             }
    1560             :         }
    1561             :     }
    1562           1 : }
    1563             : 
    1564           1 : void Channel::_createTransferWindow()
    1565             : {
    1566           1 :     if (getWindow()->getTransferWindow())
    1567           0 :         return;
    1568             : 
    1569           1 :     Pipe* pipe = getPipe();
    1570           1 :     Window* window = getWindow();
    1571           1 :     LBCHECK(pipe->startTransferThread());
    1572           1 :     LBCHECK(window->createTransferWindow());
    1573             : 
    1574             : #ifdef EQ_QT_USED
    1575             :     // transfer window creation must happen in pipe thread (#177), but the
    1576             :     // context is used in the transfer thread and Qt requires moving the object
    1577             :     // to that thread.
    1578             :     qt::Window* qtWindow =
    1579           1 :         dynamic_cast<qt::Window*>(window->getTransferWindow());
    1580           1 :     QThread* qThread = pipe->getTransferQThread();
    1581             : 
    1582           1 :     if (qtWindow && qThread)
    1583           0 :         qtWindow->moveContextToThread(qThread);
    1584             : #endif
    1585             : }
    1586             : 
    1587           2 : void Channel::_deleteTransferWindow()
    1588             : {
    1589           2 :     if (!getPipe()->hasTransferThread())
    1590           1 :         return;
    1591             : 
    1592             :     // #510: Need to schedule deletion in transfer thread since qt::Window was
    1593             :     // potentially moved to this thread
    1594           2 :     co::LocalNodePtr localNode = getLocalNode();
    1595           2 :     const lunchbox::Request<void>& request = localNode->registerRequest<void>();
    1596           1 :     send(localNode, fabric::CMD_CHANNEL_DELETE_TRANSFER_WINDOW) << request;
    1597             : }
    1598             : 
    1599             : //---------------------------------------------------------------------------
    1600             : // command handlers
    1601             : //---------------------------------------------------------------------------
    1602           2 : bool Channel::_cmdConfigInit(co::ICommand& cmd)
    1603             : {
    1604           4 :     co::ObjectICommand command(cmd);
    1605             : 
    1606           2 :     LBLOG(LOG_INIT) << "TASK channel config init " << command << std::endl;
    1607             : 
    1608           2 :     const Config* config = getConfig();
    1609           2 :     changeLatency(config->getLatency());
    1610             : 
    1611           2 :     bool result = false;
    1612           2 :     const Window* window = getWindow();
    1613           2 :     if (window->isRunning())
    1614             :     {
    1615           2 :         _impl->state = STATE_INITIALIZING;
    1616             : 
    1617           2 :         const PixelViewport& pvp = getPixelViewport();
    1618           2 :         LBASSERT(pvp.hasArea());
    1619           2 :         _impl->initialSize.x() = pvp.w;
    1620           2 :         _impl->initialSize.y() = pvp.h;
    1621           2 :         _impl->finishedFrame = window->getCurrentFrame();
    1622             : 
    1623           2 :         result = configInit(command.read<uint128_t>());
    1624             : 
    1625           2 :         if (result)
    1626           2 :             _impl->state = STATE_RUNNING;
    1627             :     }
    1628             :     else
    1629           0 :         sendError(ERROR_CHANNEL_WINDOW_NOTRUNNING);
    1630             : 
    1631           2 :     LBLOG(LOG_INIT) << "TASK channel config init reply " << result << std::endl;
    1632           2 :     commit();
    1633           4 :     send(command.getRemoteNode(), fabric::CMD_CHANNEL_CONFIG_INIT_REPLY)
    1634           2 :         << result;
    1635           4 :     return true;
    1636             : }
    1637             : 
    1638           2 : bool Channel::_cmdConfigExit(co::ICommand& cmd)
    1639             : {
    1640           2 :     LBLOG(LOG_INIT) << "Exit channel " << co::ObjectICommand(cmd) << std::endl;
    1641             : 
    1642           2 :     if (_impl->state != STATE_STOPPED)
    1643           2 :         _impl->state = configExit() ? STATE_STOPPED : STATE_FAILED;
    1644             : 
    1645           2 :     _deleteTransferWindow();
    1646           4 :     getWindow()->send(getLocalNode(), fabric::CMD_WINDOW_DESTROY_CHANNEL)
    1647           4 :         << getID();
    1648           2 :     return true;
    1649             : }
    1650             : 
    1651           2 : bool Channel::_cmdFrameStart(co::ICommand& cmd)
    1652             : {
    1653           4 :     co::ObjectICommand command(cmd);
    1654             : 
    1655           4 :     RenderContext context = command.read<RenderContext>();
    1656           2 :     const uint128_t& version = command.read<uint128_t>();
    1657           2 :     const uint32_t frameNumber = command.read<uint32_t>();
    1658             : 
    1659           2 :     LBVERB << "handle channel frame start " << command << " " << context
    1660           2 :            << " frame " << frameNumber << std::endl;
    1661             : 
    1662             :     //_grabFrame( frameNumber ); single-threaded
    1663           2 :     sync(version);
    1664             : 
    1665           2 :     overrideContext(context);
    1666           2 :     bindFrameBuffer();
    1667           2 :     frameStart(context.frameID, frameNumber);
    1668             : 
    1669           2 :     const size_t index = frameNumber % _impl->statistics->size();
    1670           2 :     detail::Channel::FrameStatistics& statistic = _impl->statistics.data[index];
    1671           2 :     LBASSERTINFO(statistic.used == 0, "Frame " << frameNumber << " used "
    1672             :                                                << statistic.used);
    1673           2 :     LBASSERT(statistic.data.empty());
    1674           2 :     statistic.used = 1;
    1675             : 
    1676           2 :     resetContext();
    1677           4 :     return true;
    1678             : }
    1679             : 
    1680           2 : bool Channel::_cmdFrameFinish(co::ICommand& cmd)
    1681             : {
    1682           4 :     co::ObjectICommand command(cmd);
    1683             : 
    1684           4 :     RenderContext context = command.read<RenderContext>();
    1685           2 :     const uint32_t frameNumber = command.read<uint32_t>();
    1686             : 
    1687           2 :     LBLOG(LOG_TASKS) << "TASK frame finish " << getName() << " " << command
    1688           2 :                      << " " << context << std::endl;
    1689             : 
    1690           2 :     overrideContext(context);
    1691           2 :     frameFinish(context.frameID, frameNumber);
    1692           2 :     resetContext();
    1693             : 
    1694           2 :     _unrefFrame(frameNumber);
    1695           4 :     return true;
    1696             : }
    1697             : 
    1698           3 : bool Channel::_cmdFrameClear(co::ICommand& cmd)
    1699             : {
    1700           3 :     LBASSERT(_impl->state == STATE_RUNNING);
    1701             : 
    1702           6 :     co::ObjectICommand command(cmd);
    1703           6 :     RenderContext context = command.read<RenderContext>();
    1704             : 
    1705           3 :     LBLOG(LOG_TASKS) << "TASK clear " << getName() << " " << command << " "
    1706           3 :                      << context << std::endl;
    1707             : 
    1708           3 :     bindDrawFrameBuffer();
    1709           3 :     _overrideContext(context);
    1710           6 :     ChannelStatistics event(Statistic::CHANNEL_CLEAR, this);
    1711           3 :     frameClear(context.frameID);
    1712           3 :     resetContext();
    1713           3 :     bindFrameBuffer();
    1714             : 
    1715           6 :     return true;
    1716             : }
    1717             : 
    1718           2 : bool Channel::_cmdFrameDraw(co::ICommand& cmd)
    1719             : {
    1720           4 :     co::ObjectICommand command(cmd);
    1721           4 :     RenderContext context = command.read<RenderContext>();
    1722           2 :     const bool finish = command.read<bool>();
    1723             : 
    1724           2 :     LBLOG(LOG_TASKS) << "TASK draw " << getName() << " " << command << " "
    1725           2 :                      << context << std::endl;
    1726             : 
    1727           2 :     bindDrawFrameBuffer();
    1728           2 :     _overrideContext(context);
    1729           2 :     const uint32_t frameNumber = getCurrentFrame();
    1730             :     ChannelStatistics event(Statistic::CHANNEL_DRAW, this, frameNumber,
    1731           4 :                             finish ? NICEST : AUTO);
    1732             : 
    1733           2 :     frameDraw(context.frameID);
    1734             :     // Set to full region if application has declared nothing
    1735           2 :     if (!getRegion().isValid())
    1736           2 :         declareRegion(getPixelViewport());
    1737           2 :     const size_t index = frameNumber % _impl->statistics->size();
    1738           2 :     _impl->statistics.data[index].region = getRegion() / getPixelViewport();
    1739             : 
    1740           2 :     resetContext();
    1741           2 :     bindFrameBuffer();
    1742             : 
    1743           4 :     return true;
    1744             : }
    1745             : 
    1746           2 : bool Channel::_cmdFrameDrawFinish(co::ICommand& cmd)
    1747             : {
    1748           4 :     co::ObjectICommand command(cmd);
    1749           2 :     const uint128_t& frameID = command.read<uint128_t>();
    1750           2 :     const uint32_t frameNumber = command.read<uint32_t>();
    1751             : 
    1752           2 :     LBLOG(LOG_TASKS) << "TASK draw finish " << getName() << " " << command
    1753           0 :                      << " frame " << frameNumber << " id " << frameID
    1754           2 :                      << std::endl;
    1755             : 
    1756           4 :     ChannelStatistics event(Statistic::CHANNEL_DRAW_FINISH, this);
    1757           2 :     frameDrawFinish(frameID, frameNumber);
    1758             : 
    1759           4 :     return true;
    1760             : }
    1761             : 
    1762           1 : bool Channel::_cmdFrameAssemble(co::ICommand& cmd)
    1763             : {
    1764           2 :     co::ObjectICommand command(cmd);
    1765           2 :     RenderContext context = command.read<RenderContext>();
    1766           2 :     const co::ObjectVersions& frameIDs = command.read<co::ObjectVersions>();
    1767             : 
    1768           1 :     LBLOG(LOG_TASKS | LOG_ASSEMBLY) << "TASK assemble " << getName() << " "
    1769           0 :                                     << command << " " << context << " nFrames "
    1770           1 :                                     << frameIDs.size() << std::endl;
    1771             : 
    1772           1 :     _overrideContext(context);
    1773             : 
    1774           2 :     ChannelStatistics event(Statistic::CHANNEL_ASSEMBLE, this);
    1775           2 :     const Frames& frames = _getFrames(frameIDs, false);
    1776           1 :     frameAssemble(context.frameID, frames);
    1777             : 
    1778           1 :     resetContext();
    1779           2 :     return true;
    1780             : }
    1781             : 
    1782           1 : bool Channel::_cmdFrameReadback(co::ICommand& cmd)
    1783             : {
    1784           2 :     co::ObjectICommand command(cmd);
    1785           2 :     RenderContext context = command.read<RenderContext>();
    1786           2 :     const co::ObjectVersions& frames = command.read<co::ObjectVersions>();
    1787           1 :     LBLOG(LOG_TASKS | LOG_ASSEMBLY) << "TASK readback " << getName() << " "
    1788           0 :                                     << command << " " << context << " nFrames "
    1789           1 :                                     << frames.size() << std::endl;
    1790             : 
    1791           1 :     _overrideContext(context);
    1792           1 :     _frameReadback(context.frameID, frames);
    1793           1 :     resetContext();
    1794           2 :     return true;
    1795             : }
    1796             : 
    1797           1 : bool Channel::_cmdFinishReadback(co::ICommand& cmd)
    1798             : {
    1799           2 :     co::ObjectICommand command(cmd);
    1800             : 
    1801           1 :     LBLOG(LOG_TASKS | LOG_ASSEMBLY) << "Finish readback " << command
    1802           1 :                                     << std::endl;
    1803             : 
    1804           1 :     const co::ObjectVersion& frameData = command.read<co::ObjectVersion>();
    1805           1 :     const uint64_t imageIndex = command.read<uint64_t>();
    1806           1 :     const uint32_t frameNumber = command.read<uint32_t>();
    1807           1 :     const uint32_t taskID = command.read<uint32_t>();
    1808             :     const std::vector<uint128_t>& nodes =
    1809           2 :         command.read<std::vector<uint128_t>>();
    1810           2 :     const co::NodeIDs& netNodes = command.read<co::NodeIDs>();
    1811             : 
    1812             :     _finishReadback(frameData, imageIndex, frameNumber, taskID, nodes,
    1813           1 :                     netNodes);
    1814           1 :     _unrefFrame(frameNumber);
    1815           2 :     return true;
    1816             : }
    1817             : 
    1818           1 : bool Channel::_cmdFrameSetReady(co::ICommand& cmd)
    1819             : {
    1820           2 :     co::ObjectICommand command(cmd);
    1821             : 
    1822             :     const co::ObjectVersion frameDataVersion =
    1823           1 :         command.read<co::ObjectVersion>();
    1824           1 :     detail::RBStat* stat = command.read<detail::RBStat*>();
    1825             :     const std::vector<uint128_t>& nodes =
    1826           2 :         command.read<std::vector<uint128_t>>();
    1827           2 :     const co::NodeIDs& netNodes = command.read<co::NodeIDs>();
    1828             : 
    1829           1 :     LBASSERT(stat->event.statistic.frameNumber > 0);
    1830             : 
    1831           2 :     FrameDataPtr frameData = getNode()->getFrameData(frameDataVersion);
    1832           1 :     _setReady(frameData, stat, nodes, netNodes);
    1833             : 
    1834           1 :     const uint32_t frame = stat->event.statistic.frameNumber;
    1835           1 :     stat->unref(0);
    1836           1 :     _unrefFrame(frame);
    1837           2 :     return true;
    1838             : }
    1839             : 
    1840           0 : bool Channel::_cmdFrameTransmitImage(co::ICommand& cmd)
    1841             : {
    1842           0 :     co::ObjectICommand command(cmd);
    1843           0 :     const co::ObjectVersion& frameData = command.read<co::ObjectVersion>();
    1844           0 :     const uint128_t& nodeID = command.read<uint128_t>();
    1845           0 :     const co::NodeID& netNodeID = command.read<co::NodeID>();
    1846           0 :     const uint64_t imageIndex = command.read<uint64_t>();
    1847           0 :     const uint32_t frameNumber = command.read<uint32_t>();
    1848           0 :     const uint32_t taskID = command.read<uint32_t>();
    1849             : 
    1850           0 :     LBLOG(LOG_TASKS | LOG_ASSEMBLY) << "Transmit " << command << " frame data "
    1851           0 :                                     << frameData << " receiver " << nodeID
    1852           0 :                                     << " on " << netNodeID << std::endl;
    1853             : 
    1854             :     _transmitImage(frameData, nodeID, netNodeID, imageIndex, frameNumber,
    1855           0 :                    taskID);
    1856           0 :     _unrefFrame(frameNumber);
    1857           0 :     return true;
    1858             : }
    1859             : 
    1860           1 : bool Channel::_cmdFrameSetReadyNode(co::ICommand& cmd)
    1861             : {
    1862           2 :     co::ObjectICommand command(cmd);
    1863             : 
    1864             :     const co::ObjectVersion& frameDataVersion =
    1865           1 :         command.read<co::ObjectVersion>();
    1866             :     const std::vector<uint128_t>& nodes =
    1867           2 :         command.read<std::vector<uint128_t>>();
    1868           2 :     const co::NodeIDs& netNodes = command.read<co::NodeIDs>();
    1869           1 :     const uint32_t frameNumber = command.read<uint32_t>();
    1870             : 
    1871           2 :     co::LocalNodePtr localNode = getLocalNode();
    1872           2 :     const FrameDataPtr frameData = getNode()->getFrameData(frameDataVersion);
    1873             : 
    1874           1 :     co::NodeIDs::const_iterator j = netNodes.begin();
    1875           3 :     for (std::vector<uint128_t>::const_iterator i = nodes.begin();
    1876           2 :          i != nodes.end(); ++i, ++j)
    1877             :     {
    1878           0 :         co::NodePtr toNode = localNode->connect(*j);
    1879           0 :         if (!toNode)
    1880             :         {
    1881           0 :             LBERROR << "Can't connect to " << *j << " to signal ready of frame "
    1882           0 :                     << frameNumber << std::endl;
    1883           0 :             continue;
    1884             :         }
    1885           0 :         co::ObjectOCommand os(co::Connections(1, toNode->getConnection()),
    1886             :                               fabric::CMD_NODE_FRAMEDATA_READY,
    1887           0 :                               co::COMMANDTYPE_OBJECT, *i, CO_INSTANCE_ALL);
    1888           0 :         os << frameDataVersion;
    1889           0 :         frameData->serialize(os);
    1890             :     }
    1891             : 
    1892           1 :     _unrefFrame(frameNumber);
    1893           2 :     return true;
    1894             : }
    1895             : 
    1896           1 : bool Channel::_cmdFrameViewStart(co::ICommand& cmd)
    1897             : {
    1898           2 :     co::ObjectICommand command(cmd);
    1899           2 :     RenderContext context = command.read<RenderContext>();
    1900             : 
    1901           1 :     LBLOG(LOG_TASKS) << "TASK view start " << getName() << " " << command << " "
    1902           1 :                      << context << std::endl;
    1903             : 
    1904           1 :     _overrideContext(context);
    1905           1 :     frameViewStart(context.frameID);
    1906           1 :     resetContext();
    1907             : 
    1908           2 :     return true;
    1909             : }
    1910             : 
    1911           1 : bool Channel::_cmdFrameViewFinish(co::ICommand& cmd)
    1912             : {
    1913           2 :     co::ObjectICommand command(cmd);
    1914           2 :     RenderContext context = command.read<RenderContext>();
    1915             : 
    1916           1 :     LBLOG(LOG_TASKS) << "TASK view finish " << getName() << " " << command
    1917           1 :                      << " " << context << std::endl;
    1918             : 
    1919           1 :     _overrideContext(context);
    1920             :     {
    1921           2 :         ChannelStatistics event(Statistic::CHANNEL_VIEW_FINISH, this);
    1922           1 :         frameViewFinish(context.frameID);
    1923             :     }
    1924           1 :     resetContext();
    1925             : 
    1926           2 :     return true;
    1927             : }
    1928             : 
    1929           0 : bool Channel::_cmdStopFrame(co::ICommand& cmd)
    1930             : {
    1931           0 :     co::ObjectICommand command(cmd);
    1932             : 
    1933           0 :     LBLOG(LOG_TASKS) << "TASK channel stop frame " << getName() << " "
    1934           0 :                      << command << std::endl;
    1935             : 
    1936           0 :     notifyStopFrame(command.read<uint32_t>());
    1937           0 :     return true;
    1938             : }
    1939             : 
    1940           0 : bool Channel::_cmdFrameTiles(co::ICommand& cmd)
    1941             : {
    1942           0 :     co::ObjectICommand command(cmd);
    1943           0 :     RenderContext context = command.read<RenderContext>();
    1944           0 :     const bool isLocal = command.read<bool>();
    1945           0 :     const uint128_t& queueID = command.read<uint128_t>();
    1946           0 :     const uint32_t tasks = command.read<uint32_t>();
    1947           0 :     const co::ObjectVersions& frames = command.read<co::ObjectVersions>();
    1948             : 
    1949           0 :     LBLOG(LOG_TASKS) << "TASK channel frame tiles " << getName() << " "
    1950           0 :                      << command << " " << context << std::endl;
    1951             : 
    1952           0 :     _frameTiles(context, isLocal, queueID, tasks, frames);
    1953           0 :     return true;
    1954             : }
    1955             : 
    1956           1 : bool Channel::_cmdDeleteTransferWindow(co::ICommand& cmd)
    1957             : {
    1958           2 :     co::ObjectICommand command(cmd);
    1959           1 :     LBLOG(LOG_INIT) << "Delete transfer window " << command << std::endl;
    1960             : 
    1961           1 :     getWindow()->deleteTransferWindow();
    1962           1 :     getLocalNode()->serveRequest(command.read<uint32_t>());
    1963           2 :     return true;
    1964             : }
    1965             : }
    1966             : 
    1967             : #include <eq/fabric/channel.ipp>
    1968             : template class eq::fabric::Channel<eq::Window, eq::Channel>;
    1969             : 
    1970             : /** @cond IGNORE */
    1971             : template EQFABRIC_API std::ostream& eq::fabric::operator<<(std::ostream&,
    1972          30 :                                                            const eq::Super&);
    1973             : /** @endcond */

Generated by: LCOV version 1.11