LCOV - code coverage report
Current view: top level - eq/client - compositor.cpp (source / functions) Hit Total Coverage
Test: lcov2.info Lines: 305 720 42.4 %
Date: 2014-06-18 Functions: 29 38 76.3 %

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

Generated by: LCOV version 1.10