LCOV - code coverage report
Current view: top level - eq - compositor.cpp (source / functions) Hit Total Coverage
Test: Equalizer Lines: 301 736 40.9 %
Date: 2017-12-16 05:07:20 Functions: 31 50 62.0 %

          Line data    Source code
       1             : 
       2             : /* Copyright (c) 2007-2017, Stefan Eilemann <eile@equalizergraphics.com>
       3             :  *                          Daniel Nachbaur <danielnachbaur@gmail.com>
       4             :  *                          Cedric Stalder <cedric.stalder@gmail.com>
       5             :  *
       6             :  * This library is free software; you can redistribute it and/or modify it under
       7             :  * the terms of the GNU Lesser General Public License version 2.1 as published
       8             :  * by the Free Software Foundation.
       9             :  *
      10             :  * This library is distributed in the hope that it will be useful, but WITHOUT
      11             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      12             :  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
      13             :  * details.
      14             :  *
      15             :  * You should have received a copy of the GNU Lesser General Public License
      16             :  * along with this library; if not, write to the Free Software Foundation, Inc.,
      17             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             :  */
      19             : 
      20             : #include <lunchbox/perThread.h>
      21             : 
      22             : #include "channel.h"
      23             : #include "channelStatistics.h"
      24             : #include "client.h"
      25             : #include "compositor.h"
      26             : #include "config.h"
      27             : #include "exception.h"
      28             : #include "frameData.h"
      29             : #include "gl.h"
      30             : #include "image.h"
      31             : #include "imageOp.h"
      32             : #include "log.h"
      33             : #include "pixelData.h"
      34             : #include "server.h"
      35             : #include "window.h"
      36             : #include "windowSystem.h"
      37             : 
      38             : #include <eq/util/accum.h>
      39             : #include <eq/util/objectManager.h>
      40             : #include <eq/util/shader.h>
      41             : 
      42             : #include <co/global.h>
      43             : #include <lunchbox/debug.h>
      44             : #include <lunchbox/monitor.h>
      45             : #include <lunchbox/os.h>
      46             : #include <pression/plugins/compressor.h>
      47             : 
      48             : using lunchbox::Monitor;
      49             : 
      50             : namespace eq
      51             : {
      52             : #define glewGetContext channel->glewGetContext
      53             : 
      54             : namespace
      55             : {
      56             : // use to address one shader and program per shared context set
      57             : static const char seed = 42;
      58             : static const char* shaderDBKey = &seed;
      59          10 : static const char* colorDBKey = shaderDBKey + 1;
      60          10 : static const char* depthDBKey = shaderDBKey + 2;
      61             : 
      62             : // Image used for CPU-based assembly
      63          10 : static lunchbox::PerThread<Image> _resultImage;
      64             : 
      65             : struct CPUAssemblyFormat
      66             : {
      67           0 :     CPUAssemblyFormat(const bool blend_)
      68           0 :         : colorInt(0)
      69             :         , colorExt(0)
      70             :         , depthInt(0)
      71             :         , depthExt(0)
      72           0 :         , blend(blend_)
      73             :     {
      74           0 :     }
      75             : 
      76             :     uint32_t colorInt;
      77             :     uint32_t colorExt;
      78             :     uint32_t depthInt;
      79             :     uint32_t depthExt;
      80             :     const bool blend;
      81             : };
      82             : 
      83           0 : bool _useCPUAssembly(const Image* image, CPUAssemblyFormat& format)
      84             : {
      85           0 :     const bool hasColor = image->hasPixelData(Frame::Buffer::color);
      86           0 :     const bool hasDepth = image->hasPixelData(Frame::Buffer::depth);
      87             : 
      88           0 :     if ( // Not an alpha-blending compositing
      89           0 :         (!format.blend || !hasColor || !image->hasAlpha()) &&
      90             :         // and not a depth-sorting compositing
      91           0 :         (!hasColor || !hasDepth))
      92             :     {
      93           0 :         return false;
      94             :     }
      95             : 
      96           0 :     if (format.colorInt == 0)
      97           0 :         format.colorInt = image->getInternalFormat(Frame::Buffer::color);
      98           0 :     if (format.colorExt == 0)
      99           0 :         format.colorExt = image->getExternalFormat(Frame::Buffer::color);
     100             : 
     101           0 :     if (format.colorInt != image->getInternalFormat(Frame::Buffer::color) ||
     102           0 :         format.colorExt != image->getExternalFormat(Frame::Buffer::color))
     103             :     {
     104           0 :         return false;
     105             :     }
     106             : 
     107           0 :     switch (format.colorExt)
     108             :     {
     109             :     case EQ_COMPRESSOR_DATATYPE_RGB10_A2:
     110             :     case EQ_COMPRESSOR_DATATYPE_BGR10_A2:
     111           0 :         if (!hasDepth)
     112             :             // blending of RGB10A2 not implemented
     113           0 :             return false;
     114           0 :         break;
     115             : 
     116             :     case EQ_COMPRESSOR_DATATYPE_RGBA:
     117             :     case EQ_COMPRESSOR_DATATYPE_BGRA:
     118           0 :         break;
     119             : 
     120             :     default:
     121           0 :         return false;
     122             :     }
     123             : 
     124           0 :     if (!hasDepth)
     125           0 :         return true;
     126             : 
     127           0 :     if (format.depthInt == 0)
     128           0 :         format.depthInt = image->getInternalFormat(Frame::Buffer::depth);
     129           0 :     if (format.depthExt == 0)
     130           0 :         format.depthExt = image->getExternalFormat(Frame::Buffer::depth);
     131             : 
     132           0 :     if (format.depthInt != image->getInternalFormat(Frame::Buffer::depth) ||
     133           0 :         format.depthExt != image->getExternalFormat(Frame::Buffer::depth))
     134             :     {
     135           0 :         return false;
     136             :     }
     137             : 
     138           0 :     return format.depthExt == EQ_COMPRESSOR_DATATYPE_DEPTH_UNSIGNED_INT;
     139             : }
     140             : 
     141           1 : bool _useCPUAssembly(const Frames& frames, Channel* channel,
     142             :                      const bool blend = false)
     143             : {
     144             :     // It doesn't make sense to use CPU-assembly for only one frame
     145           1 :     if (frames.size() < 2)
     146           1 :         return false;
     147             : 
     148             :     // Test that the input frames have color and depth buffers or that
     149             :     // alpha-blended assembly is used with multiple RGBA buffers. We assume then
     150             :     // that we will have at least one image per frame so most likely it's worth
     151             :     // to wait for the images and to do a CPU-based assembly. Also test early
     152             :     // for unsupported decomposition modes
     153             :     const Frame::Buffer desiredBuffers =
     154           0 :         blend ? Frame::Buffer::color
     155           0 :               : Frame::Buffer::color | Frame::Buffer::depth;
     156           0 :     for (const Frame* frame : frames)
     157             :     {
     158           0 :         const RenderContext& context = frame->getFrameData()->getContext();
     159             : 
     160           0 :         if (frame->getBuffers() != desiredBuffers ||
     161           0 :             context.pixel != Pixel::ALL || context.subPixel != SubPixel::ALL ||
     162           0 :             frame->getFrameData()->getZoom() != Zoom::NONE ||
     163           0 :             frame->getZoom() != Zoom::NONE) // Not supported by CPU compositor
     164             :         {
     165           0 :             return false;
     166             :         }
     167             :     }
     168             : 
     169             :     // Wait for all images to be ready and test if our assumption was correct,
     170             :     // that there are enough images to make a CPU-based assembly worthwhile and
     171             :     // all other preconditions for our CPU-based assembly code are true.
     172           0 :     size_t nImages = 0;
     173           0 :     const uint32_t timeout = channel->getConfig()->getTimeout();
     174           0 :     CPUAssemblyFormat format(blend);
     175             : 
     176           0 :     for (const Frame* frame : frames)
     177             :     {
     178             :         {
     179             :             ChannelStatistics event(Statistic::CHANNEL_FRAME_WAIT_READY,
     180           0 :                                     channel);
     181           0 :             frame->waitReady(timeout);
     182             :         }
     183             : 
     184           0 :         const Images& images = frame->getImages();
     185           0 :         for (const Image* image : images)
     186             :         {
     187           0 :             if (!_useCPUAssembly(image, format))
     188           0 :                 return false;
     189           0 :             ++nImages;
     190             :         }
     191             :     }
     192           0 :     return (nImages > 1);
     193             : }
     194             : 
     195           0 : bool _useCPUAssembly(const ImageOps& ops, const bool blend)
     196             : {
     197           0 :     CPUAssemblyFormat format(blend);
     198           0 :     size_t nImages = 0;
     199             : 
     200           0 :     for (const ImageOp& op : ops)
     201             :     {
     202           0 :         if (!_useCPUAssembly(op.image, format))
     203           0 :             return false;
     204           0 :         ++nImages;
     205             :     }
     206           0 :     return (nImages > 1);
     207             : }
     208             : 
     209           0 : uint32_t _assembleCPUImage(const Image* image, Channel* channel)
     210             : {
     211           0 :     if (!image)
     212           0 :         return 0;
     213             : 
     214             :     // assemble result on dest channel
     215           0 :     ImageOp operation;
     216           0 :     operation.image = image;
     217           0 :     operation.buffers = Frame::Buffer::color | Frame::Buffer::depth;
     218           0 :     Compositor::assembleImage(operation, channel);
     219             : 
     220             : #if 0
     221             :     static uint32_t counter = 0;
     222             :     std::ostringstream stringstream;
     223             :     stringstream << "Image_" << ++counter;
     224             :     image->writeImages( stringstream.str( ));
     225             : #endif
     226             : 
     227           0 :     return 1;
     228             : }
     229             : 
     230          84 : void _collectOutputData(const PixelData& pixelData, uint32_t& internalFormat,
     231             :                         uint32_t& pixelSize, uint32_t& externalFormat)
     232             : {
     233          84 :     LBASSERT(internalFormat == GL_NONE ||
     234             :              internalFormat == pixelData.internalFormat);
     235          84 :     LBASSERT(externalFormat == GL_NONE ||
     236             :              externalFormat == pixelData.externalFormat);
     237          84 :     LBASSERT(pixelSize == GL_NONE || pixelSize == pixelData.pixelSize);
     238          84 :     internalFormat = pixelData.internalFormat;
     239          84 :     pixelSize = pixelData.pixelSize;
     240          84 :     externalFormat = pixelData.externalFormat;
     241          84 : }
     242             : 
     243           9 : bool _collectOutputData(const ImageOps& ops, PixelViewport& destPVP,
     244             :                         uint32_t& colorInt, uint32_t& colorPixelSize,
     245             :                         uint32_t& colorExt, uint32_t& depthInt,
     246             :                         uint32_t& depthPixelSize, uint32_t& depthExt)
     247             : {
     248          72 :     for (const ImageOp& op : ops)
     249             :     {
     250          63 :         const RenderContext& context = op.image->getContext();
     251         252 :         if (context.pixel != Pixel::ALL || context.subPixel != SubPixel::ALL ||
     252         189 :             op.zoom != Zoom::NONE ||
     253          63 :             op.image->getStorageType() != Frame::TYPE_MEMORY)
     254             :         {
     255           0 :             return false;
     256             :         }
     257             : 
     258          63 :         if (!op.image->hasPixelData(Frame::Buffer::color))
     259           0 :             continue;
     260             : 
     261          63 :         destPVP.merge(op.image->getPixelViewport() + op.offset);
     262             : 
     263          63 :         _collectOutputData(op.image->getPixelData(Frame::Buffer::color),
     264          63 :                            colorInt, colorPixelSize, colorExt);
     265             : 
     266          63 :         if (op.image->hasPixelData(Frame::Buffer::depth))
     267             :         {
     268          21 :             _collectOutputData(op.image->getPixelData(Frame::Buffer::depth),
     269          21 :                                depthInt, depthPixelSize, depthExt);
     270             :         }
     271             :     }
     272             : 
     273           9 :     if (!destPVP.hasArea())
     274           0 :         LBWARN << "Nothing to assemble: " << destPVP << std::endl;
     275           9 :     return destPVP.hasArea();
     276             : }
     277             : 
     278          21 : void _mergeDBImage(void* destColor, void* destDepth,
     279             :                    const PixelViewport& destPVP, const Image* image,
     280             :                    const Vector2i& offset)
     281             : {
     282          21 :     LBASSERT(destColor && destDepth);
     283             : 
     284          21 :     LBVERB << "CPU-DB assembly" << std::endl;
     285             : 
     286          21 :     uint32_t* destC = reinterpret_cast<uint32_t*>(destColor);
     287          21 :     uint32_t* destD = reinterpret_cast<uint32_t*>(destDepth);
     288             : 
     289          21 :     const PixelViewport& pvp = image->getPixelViewport();
     290             : 
     291          21 :     const int32_t destX = offset.x() + pvp.x - destPVP.x;
     292          21 :     const int32_t destY = offset.y() + pvp.y - destPVP.y;
     293             : 
     294             :     const uint32_t* color = reinterpret_cast<const uint32_t*>(
     295          21 :         image->getPixelPointer(Frame::Buffer::color));
     296             :     const uint32_t* depth = reinterpret_cast<const uint32_t*>(
     297          21 :         image->getPixelPointer(Frame::Buffer::depth));
     298             : 
     299         131 : #pragma omp parallel for
     300         110 :     for (int32_t y = 0; y < pvp.h; ++y)
     301             :     {
     302       21333 :         const uint32_t skip = (destY + y) * destPVP.w + destX;
     303       21333 :         uint32_t* destColorIt = destC + skip;
     304       21333 :         uint32_t* destDepthIt = destD + skip;
     305       21333 :         const uint32_t* colorIt = color + y * pvp.w;
     306       21333 :         const uint32_t* depthIt = depth + y * pvp.w;
     307             : 
     308     7164817 :         for (int32_t x = 0; x < pvp.w; ++x)
     309             :         {
     310     7143484 :             if (*destDepthIt > *depthIt)
     311             :             {
     312     1751206 :                 *destColorIt = *colorIt;
     313     1751206 :                 *destDepthIt = *depthIt;
     314             :             }
     315             : 
     316     7143484 :             ++destColorIt;
     317     7143484 :             ++destDepthIt;
     318     7143484 :             ++colorIt;
     319     7143484 :             ++depthIt;
     320             :         }
     321             :     }
     322          21 : }
     323             : 
     324          21 : void _merge2DImage(void* destColor, void* destDepth,
     325             :                    const eq::PixelViewport& destPVP, const Image* image,
     326             :                    const Vector2i& offset)
     327             : {
     328             :     // This is mostly copy&paste code from _mergeDBImage :-/
     329          21 :     LBVERB << "CPU-2D assembly" << std::endl;
     330             : 
     331          21 :     uint8_t* destC = reinterpret_cast<uint8_t*>(destColor);
     332          21 :     uint8_t* destD = reinterpret_cast<uint8_t*>(destDepth);
     333             : 
     334          21 :     const PixelViewport& pvp = image->getPixelViewport();
     335          21 :     const int32_t destX = offset.x() + pvp.x - destPVP.x;
     336          21 :     const int32_t destY = offset.y() + pvp.y - destPVP.y;
     337             : 
     338          21 :     LBASSERT(image->hasPixelData(Frame::Buffer::color));
     339             : 
     340          21 :     const uint8_t* color = image->getPixelPointer(Frame::Buffer::color);
     341          21 :     const size_t pixelSize = image->getPixelSize(Frame::Buffer::color);
     342          21 :     const size_t rowLength = pvp.w * pixelSize;
     343             : 
     344         127 : #pragma omp parallel for
     345         106 :     for (int32_t y = 0; y < pvp.h; ++y)
     346             :     {
     347       16564 :         const size_t skip = ((destY + y) * destPVP.w + destX) * pixelSize;
     348       16564 :         memcpy(destC + skip, color + y * pvp.w * pixelSize, rowLength);
     349             :         // clear depth, for depth-assembly into existing FB
     350       16564 :         if (destD)
     351           0 :             lunchbox::setZero(destD + skip, rowLength);
     352             :     }
     353          21 : }
     354             : 
     355          21 : void _blendImage(void* dest, const eq::PixelViewport& destPVP,
     356             :                  const Image* image, const Vector2i& offset)
     357             : {
     358          21 :     LBVERB << "CPU-Blend assembly" << std::endl;
     359             : 
     360          21 :     int32_t* destColor = reinterpret_cast<int32_t*>(dest);
     361             : 
     362          21 :     const PixelViewport& pvp = image->getPixelViewport();
     363          21 :     const int32_t destX = offset.x() + pvp.x - destPVP.x;
     364          21 :     const int32_t destY = offset.y() + pvp.y - destPVP.y;
     365             : 
     366          21 :     LBASSERT(image->getPixelSize(Frame::Buffer::color) == 4);
     367          21 :     LBASSERT(image->hasPixelData(Frame::Buffer::color));
     368          21 :     LBASSERT(image->hasAlpha());
     369             : 
     370             :     const int32_t* color = reinterpret_cast<const int32_t*>(
     371          21 :         image->getPixelPointer(Frame::Buffer::color));
     372             : 
     373             :     // Blending of two slices, none of which is on final image (i.e. result
     374             :     // could be blended on to something else) should be performed with:
     375             :     // glBlendFuncSeparate( GL_ONE, GL_SRC_ALPHA, GL_ZERO, GL_SRC_ALPHA )
     376             :     // which means:
     377             :     // dstColor = 1*srcColor + srcAlpha*dstColor
     378             :     // dstAlpha = 0*srcAlpha + srcAlpha*dstAlpha
     379             :     // because we accumulate light which is go through (= 1-Alpha) and we
     380             :     // already have colors as Alpha*Color
     381             : 
     382          21 :     int32_t* destColorStart = destColor + destY * destPVP.w + destX;
     383          21 :     const uint32_t step = sizeof(int32_t);
     384             : 
     385         130 : #pragma omp parallel for
     386         109 :     for (int32_t y = 0; y < pvp.h; ++y)
     387             :     {
     388             :         const unsigned char* src =
     389       25109 :             reinterpret_cast<const uint8_t*>(color + pvp.w * y);
     390             :         unsigned char* dst =
     391       25109 :             reinterpret_cast<uint8_t*>(destColorStart + destPVP.w * y);
     392             : 
     393    11371317 :         for (int32_t x = 0; x < pvp.w; ++x)
     394             :         {
     395    11346208 :             dst[0] = LB_MIN(src[0] + (src[3] * dst[0] >> 8), 255);
     396    11346208 :             dst[1] = LB_MIN(src[1] + (src[3] * dst[1] >> 8), 255);
     397    11346208 :             dst[2] = LB_MIN(src[2] + (src[3] * dst[2] >> 8), 255);
     398    11346208 :             dst[3] = src[3] * dst[3] >> 8;
     399             : 
     400    11346208 :             src += step;
     401    11346208 :             dst += step;
     402             :         }
     403             :     }
     404          21 : }
     405             : 
     406           9 : void _mergeImages(const ImageOps& ops, const bool blend, void* colorBuffer,
     407             :                   void* depthBuffer, const PixelViewport& destPVP)
     408             : {
     409          72 :     for (const ImageOp& op : ops)
     410             :     {
     411          63 :         if (!op.image->hasPixelData(Frame::Buffer::color))
     412           0 :             continue;
     413             : 
     414          63 :         if (op.image->hasPixelData(Frame::Buffer::depth))
     415          21 :             _mergeDBImage(colorBuffer, depthBuffer, destPVP, op.image,
     416          21 :                           op.offset);
     417          42 :         else if (blend && op.image->hasAlpha())
     418          21 :             _blendImage(colorBuffer, destPVP, op.image, op.offset);
     419             :         else
     420          21 :             _merge2DImage(colorBuffer, depthBuffer, destPVP, op.image,
     421          21 :                           op.offset);
     422             :     }
     423           9 : }
     424             : 
     425           0 : Vector4f _getCoords(const ImageOp& op, const PixelViewport& pvp)
     426             : {
     427           0 :     const Pixel& pixel = op.image->getContext().pixel;
     428           0 :     return Vector4f(op.offset.x() + pvp.x * pixel.w + pixel.x,
     429           0 :                     op.offset.x() + pvp.getXEnd() * pixel.w * op.zoom.x() +
     430           0 :                         pixel.x,
     431           0 :                     op.offset.y() + pvp.y * pixel.h + pixel.y,
     432           0 :                     op.offset.y() + pvp.getYEnd() * pixel.h * op.zoom.y() +
     433           0 :                         pixel.y);
     434             : }
     435             : 
     436           1 : bool _setupDrawPixels(const ImageOp& op, const Frame::Buffer which,
     437             :                       Channel* channel)
     438             : {
     439           1 :     const PixelViewport& pvp = op.image->getPixelViewport();
     440           1 :     const util::Texture* texture = 0;
     441           1 :     if (op.image->getStorageType() == Frame::TYPE_MEMORY)
     442             :     {
     443           1 :         LBASSERT(op.image->hasPixelData(which));
     444           1 :         util::ObjectManager& objects = channel->getObjectManager();
     445             : 
     446             :         const bool coreProfile =
     447           1 :             channel->getWindow()->getIAttribute(
     448           1 :                 WindowSettings::IATTR_HINT_CORE_PROFILE) == ON;
     449           1 :         if (op.zoom == Zoom::NONE && !coreProfile)
     450             :         {
     451           1 :             op.image->upload(which, 0, op.offset, objects);
     452           1 :             return false;
     453             :         }
     454             :         util::Texture* ncTexture =
     455           0 :             objects.obtainEqTexture(which == Frame::Buffer::color ? colorDBKey
     456             :                                                                   : depthDBKey,
     457           0 :                                     GL_TEXTURE_RECTANGLE_ARB);
     458           0 :         texture = ncTexture;
     459             : 
     460           0 :         const Vector2i offset(-pvp.x, -pvp.y); // will be applied with quad
     461           0 :         op.image->upload(which, ncTexture, offset, objects);
     462             :     }
     463             :     else // texture image
     464             :     {
     465           0 :         LBASSERT(op.image->hasTextureData(which));
     466           0 :         texture = &op.image->getTexture(which);
     467             :     }
     468             : 
     469           0 :     EQ_GL_CALL(glActiveTexture(GL_TEXTURE0));
     470           0 :     texture->bind();
     471           0 :     texture->applyZoomFilter(op.zoomFilter);
     472           0 :     texture->applyWrap();
     473             : 
     474           0 :     if (which == Frame::Buffer::color)
     475           0 :         EQ_GL_CALL(glDepthMask(false))
     476             :     else
     477             :     {
     478           0 :         LBASSERT(which == Frame::Buffer::depth)
     479           0 :         EQ_GL_CALL(glColorMask(false, false, false, false));
     480             :     }
     481           0 :     return true;
     482             : }
     483             : 
     484           0 : void _drawPixelsFF(const ImageOp& op, const Frame::Buffer which,
     485             :                    Channel* channel)
     486             : {
     487           0 :     const PixelViewport& pvp = op.image->getPixelViewport();
     488           0 :     LBLOG(LOG_ASSEMBLY) << "_drawPixelsFF " << pvp << " offset " << op.offset
     489           0 :                         << std::endl;
     490             : 
     491           0 :     if (!_setupDrawPixels(op, which, channel))
     492           0 :         return;
     493             : 
     494           0 :     const Vector4f& coords = _getCoords(op, pvp);
     495             : 
     496           0 :     EQ_GL_CALL(glDisable(GL_LIGHTING));
     497           0 :     EQ_GL_CALL(glEnable(GL_TEXTURE_RECTANGLE_ARB));
     498             : 
     499           0 :     EQ_GL_CALL(glColor3f(1.0f, 1.0f, 1.0f));
     500             : 
     501           0 :     glBegin(GL_QUADS);
     502           0 :     glTexCoord2f(0.0f, 0.0f);
     503           0 :     glVertex3f(coords[0], coords[2], 0.0f);
     504             : 
     505           0 :     glTexCoord2f(float(pvp.w), 0.0f);
     506           0 :     glVertex3f(coords[1], coords[2], 0.0f);
     507             : 
     508           0 :     glTexCoord2f(float(pvp.w), float(pvp.h));
     509           0 :     glVertex3f(coords[1], coords[3], 0.0f);
     510             : 
     511           0 :     glTexCoord2f(0.0f, float(pvp.h));
     512           0 :     glVertex3f(coords[0], coords[3], 0.0f);
     513           0 :     glEnd();
     514             : 
     515             :     // restore state
     516           0 :     EQ_GL_CALL(glDisable(GL_TEXTURE_RECTANGLE_ARB));
     517             : 
     518           0 :     if (which == Frame::Buffer::color)
     519           0 :         EQ_GL_CALL(glDepthMask(true))
     520             :     else
     521             :     {
     522           0 :         const ColorMask& colorMask = channel->getDrawBufferMask();
     523           0 :         EQ_GL_CALL(
     524             :             glColorMask(colorMask.red, colorMask.green, colorMask.blue, true));
     525             :     }
     526             : }
     527             : 
     528             : template <typename T>
     529           0 : void _drawTexturedQuad(const T* key, const ImageOp& op,
     530             :                        const PixelViewport& pvp, const bool withDepth,
     531             :                        Channel* channel)
     532             : {
     533           0 :     util::ObjectManager& om = channel->getObjectManager();
     534           0 :     GLuint program = om.getProgram(key);
     535           0 :     GLuint vertexArray = om.getVertexArray(key);
     536           0 :     GLuint vertexBuffer = om.getBuffer(key);
     537           0 :     GLuint uvBuffer = om.getBuffer(key + 1);
     538           0 :     if (program == util::ObjectManager::INVALID)
     539             :     {
     540           0 :         vertexBuffer = om.newBuffer(key);
     541           0 :         uvBuffer = om.newBuffer(key + 1);
     542           0 :         vertexArray = om.newVertexArray(key);
     543           0 :         program = om.newProgram(key);
     544             : 
     545             :         const char* vertexShaderGLSL = {
     546             :             "#version 330 core\n"
     547             :             "layout(location = 0) in vec3 vert;\n"
     548             :             "layout(location = 1) in vec2 vertTexCoord;\n"
     549             :             "uniform mat4 proj;\n"
     550             :             "out vec2 fragTexCoord;\n"
     551             :             "void main() {\n"
     552             :             "    fragTexCoord = vertTexCoord;\n"
     553             :             "    gl_Position = proj * vec4(vert, 1);\n"
     554           0 :             "}\n"};
     555             : 
     556             :         const char* fragmentShaderGLSL = {
     557             :             withDepth
     558             :                 ? "#version 330 core\n"
     559             :                   "#extension GL_ARB_texture_rectangle : enable\n"
     560             :                   "uniform sampler2DRect color;\n"
     561             :                   "uniform sampler2DRect depth;\n"
     562             :                   "in vec2 fragTexCoord;\n"
     563             :                   "out vec4 finalColor;\n"
     564             :                   "void main() {\n"
     565             :                   "    finalColor = texture2DRect(color, fragTexCoord);\n"
     566             :                   "    gl_FragDepth = texture2DRect(depth, fragTexCoord).x;\n"
     567             :                   "}\n"
     568             :                 : // no depth
     569             :                 "#version 330 core\n"
     570             :                 "#extension GL_ARB_texture_rectangle : enable\n"
     571             :                 "uniform sampler2DRect color;\n"
     572             :                 "in vec2 fragTexCoord;\n"
     573             :                 "out vec4 finalColor;\n"
     574             :                 "void main() {\n"
     575             :                 "    finalColor = texture2DRect(color, fragTexCoord);\n"
     576           0 :                 "}\n"};
     577             : 
     578           0 :         LBCHECK(util::shader::linkProgram(glewGetContext(), program,
     579             :                                           vertexShaderGLSL,
     580             :                                           fragmentShaderGLSL));
     581             : 
     582           0 :         EQ_GL_CALL(glUseProgram(program));
     583             : 
     584           0 :         GLint colorParam = glGetUniformLocation(program, "color");
     585           0 :         EQ_GL_CALL(glUniform1i(colorParam, 0));
     586           0 :         if (withDepth)
     587             :         {
     588           0 :             const GLint depthParam = glGetUniformLocation(program, "depth");
     589           0 :             EQ_GL_CALL(glUniform1i(depthParam, 1));
     590             :         }
     591             :     }
     592             : 
     593           0 :     const Vector4f& coords = _getCoords(op, pvp);
     594             :     const GLfloat vertices[] = {coords[0], coords[2], 0.0f,      coords[1],
     595             :                                 coords[2], 0.0f,      coords[0], coords[3],
     596           0 :                                 0.0f,      coords[1], coords[3], 0.0f};
     597             : 
     598           0 :     const GLfloat uvs[] = {0.0f, 0.0f,         float(pvp.w), 0.0f,
     599           0 :                            0.0f, float(pvp.h), float(pvp.w), float(pvp.h)};
     600             :     const eq::Matrix4f& proj =
     601           0 :         eq::Frustumf(channel->getPixelViewport().x,
     602           0 :                      channel->getPixelViewport().getXEnd(),
     603           0 :                      channel->getPixelViewport().y,
     604           0 :                      channel->getPixelViewport().getYEnd(), -1.0f, 1.0f)
     605           0 :             .computeOrthoMatrix();
     606           0 :     if (withDepth)
     607           0 :         EQ_GL_CALL(glEnable(GL_DEPTH_TEST));
     608             : 
     609           0 :     EQ_GL_CALL(glBindVertexArray(vertexArray));
     610           0 :     EQ_GL_CALL(glUseProgram(program));
     611             : 
     612           0 :     const GLuint projection = glGetUniformLocation(program, "proj");
     613           0 :     EQ_GL_CALL(glUniformMatrix4fv(projection, 1, GL_FALSE, proj.data()));
     614             : 
     615           0 :     EQ_GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer));
     616           0 :     EQ_GL_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
     617             :                             GL_DYNAMIC_DRAW));
     618           0 :     EQ_GL_CALL(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0));
     619             : 
     620           0 :     EQ_GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, uvBuffer));
     621           0 :     EQ_GL_CALL(
     622             :         glBufferData(GL_ARRAY_BUFFER, sizeof(uvs), uvs, GL_DYNAMIC_DRAW));
     623           0 :     EQ_GL_CALL(glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0));
     624           0 :     EQ_GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, 0));
     625             : 
     626           0 :     EQ_GL_CALL(glEnableVertexAttribArray(0));
     627           0 :     EQ_GL_CALL(glEnableVertexAttribArray(1));
     628           0 :     EQ_GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
     629           0 :     EQ_GL_CALL(glDisableVertexAttribArray(0));
     630           0 :     EQ_GL_CALL(glDisableVertexAttribArray(1));
     631             : 
     632           0 :     EQ_GL_CALL(glBindVertexArray(0));
     633           0 :     EQ_GL_CALL(glUseProgram(0));
     634             : 
     635           0 :     if (withDepth)
     636           0 :         EQ_GL_CALL(glDisable(GL_DEPTH_TEST));
     637           0 : }
     638             : 
     639           1 : void _drawPixelsGLSL(const ImageOp& op, const Frame::Buffer which,
     640             :                      Channel* channel)
     641             : {
     642           1 :     const PixelViewport& pvp = op.image->getPixelViewport();
     643           1 :     LBLOG(LOG_ASSEMBLY) << "_drawPixelsGLSL " << pvp << " offset " << op.offset
     644           1 :                         << std::endl;
     645             : 
     646           1 :     if (!_setupDrawPixels(op, which, channel))
     647           1 :         return;
     648             : 
     649           0 :     _drawTexturedQuad(channel, op, pvp, false, channel);
     650             : 
     651             :     // restore state
     652           0 :     if (which == Frame::Buffer::color)
     653           0 :         EQ_GL_CALL(glDepthMask(true))
     654             :     else
     655             :     {
     656           0 :         const ColorMask& colorMask = channel->getDrawBufferMask();
     657           0 :         EQ_GL_CALL(
     658             :             glColorMask(colorMask.red, colorMask.green, colorMask.blue, true));
     659             :     }
     660             : }
     661             : 
     662           0 : util::Accum* _obtainAccum(Channel* channel)
     663             : {
     664           0 :     const PixelViewport& pvp = channel->getPixelViewport();
     665             : 
     666           0 :     LBASSERT(pvp.isValid());
     667             : 
     668           0 :     util::ObjectManager& objects = channel->getObjectManager();
     669           0 :     util::Accum* accum = objects.getEqAccum(channel);
     670           0 :     if (!accum)
     671             :     {
     672           0 :         accum = objects.newEqAccum(channel);
     673           0 :         if (!accum->init(pvp, channel->getWindow()->getColorFormat()))
     674             :         {
     675           0 :             LBERROR << "Accumulation initialization failed." << std::endl;
     676             :         }
     677             :     }
     678             :     else
     679           0 :         accum->resize(pvp.w, pvp.h);
     680             : 
     681           0 :     accum->clear();
     682           0 :     return accum;
     683             : }
     684             : }
     685             : 
     686           1 : uint32_t Compositor::assembleFrames(const Frames& frames, Channel* channel,
     687             :                                     util::Accum* accum)
     688             : {
     689           1 :     if (frames.empty())
     690           0 :         return 0;
     691             : 
     692           1 :     if (_useCPUAssembly(frames, channel))
     693           0 :         return assembleFramesCPU(frames, channel);
     694             : 
     695             :     // else
     696           1 :     return assembleFramesUnsorted(frames, channel, accum);
     697             : }
     698             : 
     699           0 : uint32_t Compositor::blendImages(const ImageOps& ops, Channel* channel,
     700             :                                  util::Accum* accum)
     701             : {
     702           0 :     if (ops.empty())
     703           0 :         return 0;
     704             : 
     705           0 :     if (isSubPixelDecomposition(ops))
     706             :     {
     707             :         const bool coreProfile =
     708           0 :             channel->getWindow()->getIAttribute(
     709           0 :                 WindowSettings::IATTR_HINT_CORE_PROFILE) == ON;
     710           0 :         if (coreProfile)
     711             :         {
     712           0 :             LBERROR << "No support for sub pixel assembly for OpenGL core"
     713           0 :                        "profile, skipping assemble"
     714           0 :                     << std::endl;
     715           0 :             return 0;
     716             :         }
     717             : 
     718           0 :         if (!accum)
     719             :         {
     720           0 :             accum = _obtainAccum(channel);
     721           0 :             accum->clear();
     722             : 
     723           0 :             const SubPixel& subPixel = ops.front().image->getContext().subPixel;
     724           0 :             accum->setTotalSteps(subPixel.size);
     725             :         }
     726             : 
     727           0 :         uint32_t count = 0;
     728           0 :         ImageOps opsLeft = ops;
     729           0 :         while (!opsLeft.empty())
     730             :         {
     731           0 :             ImageOps current = extractOneSubPixel(opsLeft);
     732           0 :             const uint32_t subCount = blendImages(current, channel, accum);
     733           0 :             LBASSERT(subCount < 2);
     734             : 
     735           0 :             if (subCount > 0)
     736           0 :                 accum->accum();
     737           0 :             count += subCount;
     738             :         }
     739           0 :         if (count > 0)
     740           0 :             accum->display();
     741           0 :         return count;
     742             :     }
     743             : 
     744           0 :     uint32_t count = 1;
     745           0 :     if (_useCPUAssembly(ops, true))
     746           0 :         count = assembleImagesCPU(ops, channel, true);
     747             :     else
     748           0 :         for (const ImageOp& op : ops)
     749           0 :             assembleImage(op, channel);
     750           0 :     return count;
     751             : }
     752             : 
     753           0 : uint32_t Compositor::blendFrames(const Frames& frames, Channel* channel,
     754             :                                  util::Accum* accum)
     755             : {
     756           0 :     ImageOps ops;
     757           0 :     for (const Frame* frame : frames)
     758             :     {
     759             :         {
     760           0 :             const uint32_t timeout = channel->getConfig()->getTimeout();
     761             :             ChannelStatistics event(Statistic::CHANNEL_FRAME_WAIT_READY,
     762           0 :                                     channel);
     763           0 :             frame->waitReady(timeout);
     764             :         }
     765             : 
     766           0 :         for (const Image* image : frame->getImages())
     767           0 :             ops.push_back(ImageOp(frame, image));
     768             :     }
     769             : 
     770           0 :     return blendImages(ops, channel, accum);
     771             : }
     772             : 
     773           1 : bool Compositor::isSubPixelDecomposition(const Frames& frames)
     774             : {
     775           1 :     if (frames.empty())
     776           0 :         return false;
     777             : 
     778             :     const SubPixel& subpixel =
     779           1 :         frames.front()->getFrameData()->getContext().subPixel;
     780           2 :     for (const Frame* frame : frames)
     781           1 :         if (subpixel != frame->getFrameData()->getContext().subPixel)
     782           0 :             return true;
     783           1 :     return false;
     784             : }
     785             : 
     786           0 : bool Compositor::isSubPixelDecomposition(const ImageOps& ops)
     787             : {
     788           0 :     if (ops.empty())
     789           0 :         return false;
     790             : 
     791           0 :     const SubPixel& subPixel = ops.front().image->getContext().subPixel;
     792           0 :     for (const ImageOp& op : ops)
     793           0 :         if (op.image->getContext().subPixel != subPixel)
     794           0 :             return true;
     795           0 :     return false;
     796             : }
     797             : 
     798           0 : Frames Compositor::extractOneSubPixel(Frames& frames)
     799             : {
     800           0 :     Frames current;
     801             : 
     802             :     const SubPixel& subPixel =
     803           0 :         frames.back()->getFrameData()->getContext().subPixel;
     804           0 :     current.push_back(frames.back());
     805           0 :     frames.pop_back();
     806             : 
     807           0 :     for (Frames::iterator i = frames.begin(); i != frames.end();)
     808             :     {
     809           0 :         Frame* frame = *i;
     810             : 
     811           0 :         if (frame->getFrameData()->getContext().subPixel == subPixel)
     812             :         {
     813           0 :             current.push_back(frame);
     814           0 :             i = frames.erase(i);
     815             :         }
     816             :         else
     817           0 :             ++i;
     818             :     }
     819             : 
     820           0 :     return current;
     821             : }
     822             : 
     823           0 : ImageOps Compositor::extractOneSubPixel(ImageOps& ops)
     824             : {
     825           0 :     ImageOps current;
     826             : 
     827           0 :     const SubPixel& subPixel = ops.back().image->getContext().subPixel;
     828           0 :     current.push_back(ops.back());
     829           0 :     ops.pop_back();
     830             : 
     831           0 :     for (ImageOps::iterator i = ops.begin(); i != ops.end();)
     832             :     {
     833           0 :         ImageOp& op = *i;
     834             : 
     835           0 :         if (op.image->getContext().subPixel == subPixel)
     836             :         {
     837           0 :             current.push_back(op);
     838           0 :             i = ops.erase(i);
     839             :         }
     840             :         else
     841           0 :             ++i;
     842             :     }
     843             : 
     844           0 :     return current;
     845             : }
     846             : 
     847           1 : uint32_t Compositor::assembleFramesUnsorted(const Frames& frames,
     848             :                                             Channel* channel,
     849             :                                             util::Accum* accum)
     850             : {
     851           1 :     if (frames.empty())
     852           0 :         return 0;
     853             : 
     854           1 :     LBVERB << "Unsorted GPU assembly" << std::endl;
     855           1 :     if (isSubPixelDecomposition(frames))
     856             :     {
     857             :         const bool coreProfile =
     858           0 :             channel->getWindow()->getIAttribute(
     859           0 :                 WindowSettings::IATTR_HINT_CORE_PROFILE) == ON;
     860           0 :         if (coreProfile)
     861             :         {
     862           0 :             LBERROR << "No support for sub pixel assembly for OpenGL core"
     863           0 :                        "profile, skipping assemble"
     864           0 :                     << std::endl;
     865           0 :             return 0;
     866             :         }
     867             : 
     868           0 :         uint32_t count = 0;
     869             : 
     870           0 :         if (!accum)
     871             :         {
     872           0 :             accum = _obtainAccum(channel);
     873           0 :             accum->clear();
     874             : 
     875             :             const SubPixel& subPixel =
     876           0 :                 frames.back()->getFrameData()->getContext().subPixel;
     877           0 :             accum->setTotalSteps(subPixel.size);
     878             :         }
     879             : 
     880           0 :         Frames framesLeft = frames;
     881           0 :         while (!framesLeft.empty())
     882             :         {
     883             :             // get the frames with the same subpixel compound
     884           0 :             Frames current = extractOneSubPixel(framesLeft);
     885             : 
     886             :             // use assembleFrames to potentially benefit from CPU assembly
     887           0 :             const uint32_t subCount = assembleFrames(current, channel, accum);
     888           0 :             LBASSERT(subCount < 2)
     889           0 :             if (subCount > 0)
     890           0 :                 accum->accum();
     891           0 :             count += subCount;
     892             :         }
     893           0 :         if (count > 1)
     894           0 :             accum->display();
     895           0 :         return count;
     896             :     }
     897             : 
     898             :     // This is an optimized assembly version. The frames are not assembled in
     899             :     // the saved order, but in the order they become available, which is faster
     900             :     // because less time is spent waiting on frame availability.
     901             :     //
     902             :     // The ready frames are counted in a monitor. Whenever a frame becomes
     903             :     // available, it increments the monitor which causes this code to wake up
     904             :     // and assemble it.
     905             : 
     906           1 :     uint32_t count = 0;
     907             : 
     908             :     // wait and assemble frames
     909           1 :     WaitHandle* handle = startWaitFrames(frames, channel);
     910           2 :     for (Frame* frame = waitFrame(handle); frame; frame = waitFrame(handle))
     911             :     {
     912           1 :         if (frame->getImages().empty())
     913           0 :             continue;
     914             : 
     915           1 :         count = 1;
     916           1 :         assembleFrame(frame, channel);
     917             :     }
     918             : 
     919           1 :     return count;
     920             : }
     921             : 
     922             : class Compositor::WaitHandle
     923             : {
     924             : public:
     925           1 :     WaitHandle(const Frames& frames, Channel* ch)
     926           1 :         : left(frames)
     927             :         , channel(ch)
     928           1 :         , processed(0)
     929             :     {
     930           1 :     }
     931           1 :     ~WaitHandle()
     932           1 :     {
     933             :         // de-register the monitor on eventual left-overs on error/exception
     934           1 :         for (FramesCIter i = left.begin(); i != left.end(); ++i)
     935           0 :             (*i)->removeListener(monitor);
     936           1 :         left.clear();
     937           1 :     }
     938             : 
     939             :     lunchbox::Monitor<uint32_t> monitor;
     940             :     Frames left;
     941             :     Channel* const channel;
     942             :     uint32_t processed;
     943             : };
     944             : 
     945           1 : Compositor::WaitHandle* Compositor::startWaitFrames(const Frames& frames,
     946             :                                                     Channel* channel)
     947             : {
     948           1 :     WaitHandle* handle = new WaitHandle(frames, channel);
     949           2 :     for (FramesCIter i = frames.begin(); i != frames.end(); ++i)
     950           1 :         (*i)->addListener(handle->monitor);
     951             : 
     952           1 :     return handle;
     953             : }
     954             : 
     955           2 : Frame* Compositor::waitFrame(WaitHandle* handle)
     956             : {
     957           2 :     if (handle->left.empty())
     958             :     {
     959           1 :         delete handle;
     960           1 :         return 0;
     961             :     }
     962             : 
     963             :     ChannelStatistics event(Statistic::CHANNEL_FRAME_WAIT_READY,
     964           2 :                             handle->channel);
     965           1 :     Config* config = handle->channel->getConfig();
     966           1 :     const uint32_t timeout = config->getTimeout();
     967             : 
     968           1 :     ++handle->processed;
     969           1 :     if (timeout == LB_TIMEOUT_INDEFINITE)
     970           1 :         handle->monitor.waitGE(handle->processed);
     971             :     else
     972             :     {
     973           0 :         const int64_t time = config->getTime() + timeout;
     974           0 :         const int64_t aliveTimeout = co::Global::getKeepaliveTimeout();
     975             : 
     976           0 :         while (!handle->monitor.timedWaitGE(handle->processed, aliveTimeout))
     977             :         {
     978             :             // pings timed out nodes
     979           0 :             const bool pinged = config->getLocalNode()->pingIdleNodes();
     980             : 
     981           0 :             if (config->getTime() >= time || !pinged)
     982             :             {
     983           0 :                 delete handle;
     984           0 :                 throw Exception(Exception::TIMEOUT_INPUTFRAME);
     985             :             }
     986             :         }
     987             :     }
     988             : 
     989           1 :     for (FramesIter i = handle->left.begin(); i != handle->left.end(); ++i)
     990             :     {
     991           1 :         Frame* frame = *i;
     992           1 :         if (!frame->isReady())
     993           0 :             continue;
     994             : 
     995           1 :         frame->removeListener(handle->monitor);
     996           1 :         handle->left.erase(i);
     997           1 :         return frame;
     998             :     }
     999             : 
    1000           0 :     LBASSERTINFO(false, "Unreachable code");
    1001           0 :     delete handle;
    1002           0 :     return 0;
    1003             : }
    1004             : 
    1005           0 : uint32_t Compositor::assembleFramesCPU(const Frames& frames, Channel* channel,
    1006             :                                        const bool blend)
    1007             : {
    1008           0 :     if (frames.empty())
    1009           0 :         return 0;
    1010             : 
    1011             :     // Assembles images from DB and 2D compounds using the CPU and then
    1012             :     // assembles the result image. Does not support Pixel or Eye compounds.
    1013           0 :     LBVERB << "Sorted CPU assembly" << std::endl;
    1014             : 
    1015             :     const Image* result =
    1016           0 :         mergeFramesCPU(frames, blend, channel->getConfig()->getTimeout());
    1017           0 :     return _assembleCPUImage(result, channel);
    1018             : }
    1019             : 
    1020           0 : uint32_t Compositor::assembleImagesCPU(const ImageOps& images, Channel* channel,
    1021             :                                        const bool blend)
    1022             : {
    1023           0 :     if (images.empty())
    1024           0 :         return 0;
    1025             : 
    1026             :     // Assembles images from DB and 2D compounds using the CPU and then
    1027             :     // assembles the result image. Does not support Pixel or Eye compounds.
    1028           0 :     LBVERB << "Sorted CPU assembly" << std::endl;
    1029             : 
    1030           0 :     const Image* result = mergeImagesCPU(images, blend);
    1031           0 :     return _assembleCPUImage(result, channel);
    1032             : }
    1033             : 
    1034           9 : const Image* Compositor::mergeFramesCPU(const Frames& frames, const bool blend,
    1035             :                                         const uint32_t timeout)
    1036             : {
    1037          18 :     ImageOps ops;
    1038          30 :     for (const Frame* frame : frames)
    1039             :     {
    1040          21 :         frame->waitReady(timeout);
    1041          84 :         for (const Image* image : frame->getImages())
    1042             :         {
    1043          63 :             ImageOp op(frame, image);
    1044          63 :             op.offset = frame->getOffset();
    1045          63 :             ops.emplace_back(op);
    1046             :         }
    1047             :     }
    1048          18 :     return mergeImagesCPU(ops, blend);
    1049             : }
    1050             : 
    1051           9 : const Image* Compositor::mergeImagesCPU(const ImageOps& ops, const bool blend)
    1052             : {
    1053           9 :     LBVERB << "Sorted CPU assembly" << std::endl;
    1054             : 
    1055             :     // Collect input image information and check preconditions
    1056           9 :     PixelViewport destPVP;
    1057           9 :     uint32_t colorInt = 0;
    1058           9 :     uint32_t colorExt = 0;
    1059           9 :     uint32_t colorPixelSize = 0;
    1060           9 :     uint32_t depthInt = 0;
    1061           9 :     uint32_t depthExt = 0;
    1062           9 :     uint32_t depthPixelSize = 0;
    1063             : 
    1064           9 :     if (!_collectOutputData(ops, destPVP, colorInt, colorPixelSize, colorExt,
    1065             :                             depthInt, depthPixelSize, depthExt))
    1066             :     {
    1067           0 :         return 0;
    1068             :     }
    1069             : 
    1070             :     // prepare output image
    1071           9 :     if (!_resultImage)
    1072           1 :         _resultImage = new Image;
    1073           9 :     Image* result = _resultImage.get();
    1074             : 
    1075             :     // pre-condition check for current _merge implementations
    1076           9 :     LBASSERT(colorInt != 0);
    1077             : 
    1078           9 :     result->setPixelViewport(destPVP);
    1079             : 
    1080          18 :     PixelData colorPixels;
    1081           9 :     colorPixels.internalFormat = colorInt;
    1082           9 :     colorPixels.externalFormat = colorExt;
    1083           9 :     colorPixels.pixelSize = colorPixelSize;
    1084           9 :     colorPixels.pvp = destPVP;
    1085           9 :     result->setPixelData(Frame::Buffer::color, colorPixels);
    1086             : 
    1087           9 :     void* destDepth = 0;
    1088           9 :     if (depthInt != 0) // at least one depth assembly
    1089             :     {
    1090           3 :         LBASSERT(depthExt == EQ_COMPRESSOR_DATATYPE_DEPTH_UNSIGNED_INT);
    1091           6 :         PixelData depthPixels;
    1092           3 :         depthPixels.internalFormat = depthInt;
    1093           3 :         depthPixels.externalFormat = depthExt;
    1094           3 :         depthPixels.pixelSize = depthPixelSize;
    1095           3 :         depthPixels.pvp = destPVP;
    1096           3 :         result->setPixelData(Frame::Buffer::depth, depthPixels);
    1097           3 :         destDepth = result->getPixelPointer(Frame::Buffer::depth);
    1098             :     }
    1099             : 
    1100             :     // assembly
    1101           9 :     _mergeImages(ops, blend, result->getPixelPointer(Frame::Buffer::color),
    1102           9 :                  destDepth, destPVP);
    1103           9 :     return result;
    1104             : }
    1105             : 
    1106           1 : void Compositor::assembleFrame(const Frame* frame, Channel* channel)
    1107             : {
    1108           1 :     const Images& images = frame->getImages();
    1109           1 :     if (images.empty())
    1110           0 :         LBINFO << "No images to assemble" << std::endl;
    1111             : 
    1112           2 :     for (Image* image : images)
    1113             :     {
    1114           1 :         ImageOp op(frame, image);
    1115           1 :         op.offset = frame->getOffset();
    1116           1 :         assembleImage(op, channel);
    1117             :     }
    1118           1 : }
    1119             : 
    1120           1 : void Compositor::assembleImage(const ImageOp& op, Channel* channel)
    1121             : {
    1122           1 :     const bool coreProfile = channel->getWindow()->getIAttribute(
    1123           1 :                                  WindowSettings::IATTR_HINT_CORE_PROFILE) == ON;
    1124           1 :     if (coreProfile && op.image->getContext().pixel != Pixel::ALL)
    1125             :     {
    1126           0 :         LBERROR << "No support for pixel assembly for OpenGL core profile,"
    1127           0 :                    "skipping image"
    1128           0 :                 << std::endl;
    1129           0 :         return;
    1130             :     }
    1131             : 
    1132           1 :     ImageOp operation = op;
    1133           1 :     operation.buffers = Frame::Buffer::none;
    1134             : 
    1135           1 :     const Frame::Buffer buffer[] = {Frame::Buffer::color, Frame::Buffer::depth};
    1136           3 :     for (unsigned i = 0; i < 2; ++i)
    1137             :     {
    1138           2 :         if ((op.buffers & buffer[i]) && (op.image->hasPixelData(buffer[i]) ||
    1139           0 :                                          op.image->hasTextureData(buffer[i])))
    1140             :         {
    1141           1 :             operation.buffers |= buffer[i];
    1142             :         }
    1143             :     }
    1144             : 
    1145           1 :     if (operation.buffers == Frame::Buffer::none)
    1146             :     {
    1147           0 :         LBWARN << "No image attachment buffers to assemble" << std::endl;
    1148           0 :         return;
    1149             :     }
    1150             : 
    1151           1 :     setupStencilBuffer(operation, channel);
    1152             : 
    1153           1 :     if (operation.buffers == Frame::Buffer::color)
    1154           1 :         assembleImage2D(operation, channel);
    1155           0 :     else if (operation.buffers == (Frame::Buffer::color | Frame::Buffer::depth))
    1156           0 :         assembleImageDB(operation, channel);
    1157             :     else
    1158           0 :         LBWARN << "Don't know how to assemble using buffers "
    1159           0 :                << operation.buffers << std::endl;
    1160             : 
    1161           1 :     clearStencilBuffer(operation);
    1162             : }
    1163             : 
    1164           1 : void Compositor::setupStencilBuffer(const ImageOp& op, const Channel* channel)
    1165             : {
    1166           1 :     const Pixel& pixel = op.image->getContext().pixel;
    1167           1 :     if (pixel == Pixel::ALL)
    1168           1 :         return;
    1169             : 
    1170             :     // mark stencil buffer where pixel shall not pass
    1171             :     // TODO: OPT!
    1172           0 :     EQ_GL_CALL(glClear(GL_STENCIL_BUFFER_BIT));
    1173           0 :     EQ_GL_CALL(glEnable(GL_STENCIL_TEST));
    1174           0 :     EQ_GL_CALL(glEnable(GL_DEPTH_TEST));
    1175             : 
    1176           0 :     EQ_GL_CALL(glStencilFunc(GL_ALWAYS, 1, 1));
    1177           0 :     EQ_GL_CALL(glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE));
    1178             : 
    1179           0 :     EQ_GL_CALL(glLineWidth(1.0f));
    1180           0 :     EQ_GL_CALL(glDepthMask(false));
    1181           0 :     EQ_GL_CALL(glColorMask(false, false, false, false));
    1182             : 
    1183           0 :     const PixelViewport& pvp = op.image->getPixelViewport();
    1184             : 
    1185           0 :     EQ_GL_CALL(glPixelZoom(float(pixel.w), float(pixel.h)));
    1186             : 
    1187           0 :     if (pixel.w > 1)
    1188             :     {
    1189           0 :         const float width = float(pvp.w * pixel.w);
    1190           0 :         const float step = float(pixel.w);
    1191             : 
    1192             :         const float startX =
    1193           0 :             float(op.offset.x() + pvp.x) + 0.5f - float(pixel.w);
    1194           0 :         const float endX = startX + width + pixel.w + step;
    1195           0 :         const float startY = float(op.offset.y() + pvp.y + pixel.y);
    1196           0 :         const float endY = float(startY + pvp.h * pixel.h);
    1197             : 
    1198           0 :         glBegin(GL_QUADS);
    1199           0 :         for (float x = startX + pixel.x + 1.0f; x < endX; x += step)
    1200             :         {
    1201           0 :             glVertex3f(x - step, startY, 0.0f);
    1202           0 :             glVertex3f(x - 1.0f, startY, 0.0f);
    1203           0 :             glVertex3f(x - 1.0f, endY, 0.0f);
    1204           0 :             glVertex3f(x - step, endY, 0.0f);
    1205             :         }
    1206           0 :         glEnd();
    1207             :     }
    1208           0 :     if (pixel.h > 1)
    1209             :     {
    1210           0 :         const float height = float(pvp.h * pixel.h);
    1211           0 :         const float step = float(pixel.h);
    1212           0 :         const float startX = float(op.offset.x() + pvp.x + pixel.x);
    1213           0 :         const float endX = float(startX + pvp.w * pixel.w);
    1214             :         const float startY =
    1215           0 :             float(op.offset.y() + pvp.y) + 0.5f - float(pixel.h);
    1216           0 :         const float endY = startY + height + pixel.h + step;
    1217             : 
    1218           0 :         glBegin(GL_QUADS);
    1219           0 :         for (float y = startY + pixel.y; y < endY; y += step)
    1220             :         {
    1221           0 :             glVertex3f(startX, y - step, 0.0f);
    1222           0 :             glVertex3f(endX, y - step, 0.0f);
    1223           0 :             glVertex3f(endX, y - 1.0f, 0.0f);
    1224           0 :             glVertex3f(startX, y - 1.0f, 0.0f);
    1225             :         }
    1226           0 :         glEnd();
    1227             :     }
    1228             : 
    1229           0 :     EQ_GL_CALL(glDisable(GL_DEPTH_TEST));
    1230           0 :     EQ_GL_CALL(glStencilFunc(GL_EQUAL, 0, 1));
    1231           0 :     EQ_GL_CALL(glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
    1232             : 
    1233           0 :     const ColorMask& colorMask = channel->getDrawBufferMask();
    1234           0 :     EQ_GL_CALL(
    1235             :         glColorMask(colorMask.red, colorMask.green, colorMask.blue, true));
    1236           0 :     EQ_GL_CALL(glDepthMask(true));
    1237             : }
    1238             : 
    1239           1 : void Compositor::clearStencilBuffer(const ImageOp& op)
    1240             : {
    1241           1 :     if (op.image->getContext().pixel == Pixel::ALL)
    1242           1 :         return;
    1243             : 
    1244           0 :     EQ_GL_CALL(glPixelZoom(1.f, 1.f));
    1245           0 :     EQ_GL_CALL(glDisable(GL_STENCIL_TEST));
    1246             : }
    1247             : 
    1248           1 : void Compositor::assembleImage2D(const ImageOp& op, Channel* channel)
    1249             : {
    1250           1 :     if (GLEW_VERSION_3_3)
    1251           1 :         _drawPixelsGLSL(op, Frame::Buffer::color, channel);
    1252             :     else
    1253           0 :         _drawPixelsFF(op, Frame::Buffer::color, channel);
    1254           1 :     declareRegion(op, channel);
    1255             : #if 0
    1256             :     static lunchbox::a_int32_t counter;
    1257             :     std::ostringstream stringstream;
    1258             :     stringstream << "Image_" << ++counter;
    1259             :     op.image->writeImages( stringstream.str( ));
    1260             : #endif
    1261           1 : }
    1262             : 
    1263           0 : void Compositor::assembleImageDB(const ImageOp& op, Channel* channel)
    1264             : {
    1265           0 :     if (GLEW_VERSION_3_3)
    1266           0 :         assembleImageDB_GLSL(op, channel);
    1267             :     else
    1268           0 :         assembleImageDB_FF(op, channel);
    1269           0 : }
    1270             : 
    1271           0 : void Compositor::assembleImageDB_FF(const ImageOp& op, Channel* channel)
    1272             : {
    1273           0 :     const PixelViewport& pvp = op.image->getPixelViewport();
    1274           0 :     LBLOG(LOG_ASSEMBLY) << "assembleImageDB_FF " << pvp << std::endl;
    1275             : 
    1276             :     // Z-Based sort-last assembly
    1277           0 :     EQ_GL_CALL(glRasterPos2i(op.offset.x() + pvp.x, op.offset.y() + pvp.y));
    1278           0 :     EQ_GL_CALL(glEnable(GL_STENCIL_TEST));
    1279             : 
    1280             :     // test who is in front and mark in stencil buffer
    1281           0 :     EQ_GL_CALL(glEnable(GL_DEPTH_TEST));
    1282             : 
    1283           0 :     const bool pixelComposite = (op.image->getContext().pixel != Pixel::ALL);
    1284           0 :     if (pixelComposite)
    1285             :     { // keep already marked stencil values
    1286           0 :         EQ_GL_CALL(glStencilFunc(GL_EQUAL, 1, 1));
    1287           0 :         EQ_GL_CALL(glStencilOp(GL_KEEP, GL_ZERO, GL_REPLACE));
    1288             :     }
    1289             :     else
    1290             :     {
    1291           0 :         EQ_GL_CALL(glStencilFunc(GL_ALWAYS, 1, 1));
    1292           0 :         EQ_GL_CALL(glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE));
    1293             :     }
    1294             : 
    1295           0 :     _drawPixelsFF(op, Frame::Buffer::depth, channel);
    1296             : 
    1297           0 :     EQ_GL_CALL(glDisable(GL_DEPTH_TEST));
    1298             : 
    1299             :     // draw front-most, visible pixels using stencil mask
    1300           0 :     EQ_GL_CALL(glStencilFunc(GL_EQUAL, 1, 1));
    1301           0 :     EQ_GL_CALL(glStencilOp(GL_KEEP, GL_ZERO, GL_ZERO));
    1302             : 
    1303           0 :     _drawPixelsFF(op, Frame::Buffer::color, channel);
    1304             : 
    1305           0 :     EQ_GL_CALL(glDisable(GL_STENCIL_TEST));
    1306           0 :     declareRegion(op, channel);
    1307           0 : }
    1308             : 
    1309           0 : void Compositor::assembleImageDB_GLSL(const ImageOp& op, Channel* channel)
    1310             : {
    1311           0 :     const PixelViewport& pvp = op.image->getPixelViewport();
    1312           0 :     LBLOG(LOG_ASSEMBLY) << "assembleImageDB_GLSL " << pvp << std::endl;
    1313             : 
    1314           0 :     util::ObjectManager& om = channel->getObjectManager();
    1315             :     const bool useImageTexture =
    1316           0 :         op.image->getStorageType() == Frame::TYPE_TEXTURE;
    1317             : 
    1318           0 :     const util::Texture* textureColor = 0;
    1319           0 :     const util::Texture* textureDepth = 0;
    1320           0 :     if (useImageTexture)
    1321             :     {
    1322           0 :         textureColor = &op.image->getTexture(Frame::Buffer::color);
    1323           0 :         textureDepth = &op.image->getTexture(Frame::Buffer::depth);
    1324             :     }
    1325             :     else
    1326             :     {
    1327             :         util::Texture* ncTextureColor =
    1328           0 :             om.obtainEqTexture(colorDBKey, GL_TEXTURE_RECTANGLE_ARB);
    1329             :         util::Texture* ncTextureDepth =
    1330           0 :             om.obtainEqTexture(depthDBKey, GL_TEXTURE_RECTANGLE_ARB);
    1331           0 :         const Vector2i offset(-pvp.x, -pvp.y); // will be applied with quad
    1332             : 
    1333           0 :         op.image->upload(Frame::Buffer::color, ncTextureColor, offset, om);
    1334           0 :         op.image->upload(Frame::Buffer::depth, ncTextureDepth, offset, om);
    1335             : 
    1336           0 :         textureColor = ncTextureColor;
    1337           0 :         textureDepth = ncTextureDepth;
    1338             :     }
    1339             : 
    1340           0 :     EQ_GL_CALL(glActiveTexture(GL_TEXTURE1));
    1341           0 :     textureDepth->bind();
    1342           0 :     textureDepth->applyZoomFilter(op.zoomFilter);
    1343             : 
    1344           0 :     EQ_GL_CALL(glActiveTexture(GL_TEXTURE0));
    1345           0 :     textureColor->bind();
    1346           0 :     textureColor->applyZoomFilter(op.zoomFilter);
    1347             : 
    1348           0 :     _drawTexturedQuad(shaderDBKey, op, pvp, true, channel);
    1349             : 
    1350           0 :     declareRegion(op, channel);
    1351           0 : }
    1352             : 
    1353           1 : void Compositor::declareRegion(const ImageOp& op, Channel* channel)
    1354             : {
    1355           1 :     if (!channel)
    1356           0 :         return;
    1357             : 
    1358           1 :     const eq::PixelViewport area = op.image->getPixelViewport() + op.offset;
    1359           1 :     channel->declareRegion(area);
    1360             : }
    1361             : 
    1362             : #undef glewGetContext
    1363             : #define glewGetContext() glCtx
    1364             : 
    1365           2 : void Compositor::setupAssemblyState(const PixelViewport& pvp,
    1366             :                                     const GLEWContext* glCtx)
    1367             : {
    1368           2 :     EQ_GL_ERROR("before setupAssemblyState");
    1369             :     glPushAttrib(GL_ENABLE_BIT | GL_STENCIL_BUFFER_BIT | GL_LINE_BIT |
    1370           2 :                  GL_PIXEL_MODE_BIT | GL_POLYGON_BIT | GL_TEXTURE_BIT);
    1371             : 
    1372           2 :     glDisable(GL_DEPTH_TEST);
    1373           2 :     glDisable(GL_BLEND);
    1374           2 :     glDisable(GL_ALPHA_TEST);
    1375           2 :     glDisable(GL_STENCIL_TEST);
    1376           2 :     glDisable(GL_TEXTURE_1D);
    1377           2 :     glDisable(GL_TEXTURE_2D);
    1378           2 :     if (GLEW_VERSION_1_2)
    1379           2 :         glDisable(GL_TEXTURE_3D);
    1380           2 :     glDisable(GL_FOG);
    1381           2 :     glDisable(GL_CLIP_PLANE0);
    1382           2 :     glDisable(GL_CLIP_PLANE1);
    1383           2 :     glDisable(GL_CLIP_PLANE2);
    1384           2 :     glDisable(GL_CLIP_PLANE3);
    1385           2 :     glDisable(GL_CLIP_PLANE4);
    1386           2 :     glDisable(GL_CLIP_PLANE5);
    1387             : 
    1388           2 :     glPolygonMode(GL_FRONT, GL_FILL);
    1389           2 :     if (GLEW_VERSION_1_3)
    1390           2 :         glActiveTexture(GL_TEXTURE0);
    1391             : 
    1392           2 :     glMatrixMode(GL_TEXTURE);
    1393           2 :     glPushMatrix();
    1394           2 :     glLoadIdentity();
    1395             : 
    1396           2 :     glMatrixMode(GL_PROJECTION);
    1397           2 :     glPushMatrix();
    1398           2 :     glLoadIdentity();
    1399           2 :     if (pvp.hasArea())
    1400           2 :         glOrtho(pvp.x, pvp.getXEnd(), pvp.y, pvp.getYEnd(), -1.0f, 1.0f);
    1401             : 
    1402           2 :     glMatrixMode(GL_MODELVIEW);
    1403           2 :     glPushMatrix();
    1404           2 :     glLoadIdentity();
    1405           2 :     EQ_GL_ERROR("after  setupAssemblyState");
    1406           2 : }
    1407             : 
    1408           2 : void Compositor::resetAssemblyState()
    1409             : {
    1410           2 :     EQ_GL_CALL(glMatrixMode(GL_TEXTURE));
    1411           2 :     EQ_GL_CALL(glPopMatrix());
    1412           2 :     EQ_GL_CALL(glMatrixMode(GL_PROJECTION));
    1413           2 :     EQ_GL_CALL(glPopMatrix());
    1414           2 :     EQ_GL_CALL(glMatrixMode(GL_MODELVIEW));
    1415           2 :     EQ_GL_CALL(glPopMatrix());
    1416           2 :     EQ_GL_CALL(glPopAttrib());
    1417           2 : }
    1418          30 : }

Generated by: LCOV version 1.11