LCOV - code coverage report
Current view: top level - eq - compositor.cpp (source / functions) Hit Total Coverage
Test: Equalizer Lines: 144 725 19.9 %
Date: 2016-07-30 05:04:55 Functions: 10 47 21.3 %

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

Generated by: LCOV version 1.11