• Main Page
  • Related Pages
  • Namespaces
  • Classes
  • Files

compositor.cpp

00001 
00002 /* Copyright (c) 2007-2008, Stefan Eilemann <[email protected]> 
00003    All rights reserved. */
00004 
00005 #include <pthread.h>
00006 #include <eq/base/perThread.h>
00007 
00008 #include "compositor.h"
00009 
00010 #include "channel.h"
00011 #include "channelStatistics.h"
00012 #include "frame.h"
00013 #include "log.h"
00014 #include "image.h"
00015 #include "windowSystem.h"
00016 
00017 #include <eq/base/debug.h>
00018 
00019 #include <eq/base/executionListener.h>
00020 #include <eq/base/monitor.h>
00021 
00022 #ifdef EQ_USE_PARACOMP
00023 #  include <pcapi.h>
00024 #endif
00025 
00026 #ifdef WIN32
00027 #  define bzero( ptr, size ) memset( ptr, 0, size );
00028 #endif
00029 
00030 using eq::base::Monitor;
00031 using namespace std;
00032 
00033 namespace eq
00034 {
00035 
00036 #define glewGetContext op.channel->glewGetContext
00037 
00038 namespace
00039 {
00040 // use to address one shader and program per shared context set
00041 static const char glslKey = 42;
00042 
00043 class ResultImage : public Image
00044 {
00045 public:
00046     virtual ~ResultImage() {}
00047 
00048     void notifyPerThreadDelete() { delete this; }
00049 };
00050 
00051 
00052 // Image used for CPU-based assembly
00053 static base::PerThread< ResultImage > _resultImage;
00054 
00055 
00056 static bool _useCPUAssembly( const FrameVector& frames, Channel* channel, 
00057                              const bool blendAlpha = false )
00058 {
00059     // It doesn't make sense to use CPU-assembly for only one frame
00060     if( frames.size() < 2 )
00061         return false;
00062 
00063     // Test that at least two input frames have color and depth buffers or that
00064     // alpha-blended assembly is used with multiple RGBA buffers. We assume then
00065     // that we will have at least one image per frame so most likely it's worth
00066     // to wait for the images and to do a CPU-based assembly.
00067     const uint32_t desiredBuffers = blendAlpha ? Frame::BUFFER_COLOR :
00068                                     Frame::BUFFER_COLOR | Frame::BUFFER_DEPTH;
00069     size_t nFrames = 0;
00070     for( FrameVector::const_iterator i = frames.begin();
00071          i != frames.end(); ++i )
00072     {
00073         const Frame* frame = *i;
00074         if( frame->getPixel() != Pixel::ALL ) // Not supported yet on the CPU
00075             return false;
00076 
00077         if( frame->getBuffers() == desiredBuffers )
00078             ++nFrames;
00079     }
00080     if( nFrames < 2 )
00081         return false;
00082 
00083     // Now wait for all images to be ready and test if our assumption was
00084     // correct, that there are enough images to make a CPU-based assembly
00085     // worthwhile and all other preconditions for our current CPU-based assembly
00086     // code are true.
00087     size_t   nImages     = 0;
00088     uint32_t colorFormat = 0;
00089     uint32_t colorType   = 0;
00090     uint32_t depthFormat = 0;
00091     uint32_t depthType   = 0;
00092 
00093     for( FrameVector::const_iterator i = frames.begin();
00094          i != frames.end(); ++i )
00095     {
00096         const Frame* frame = *i;
00097         {
00098             ChannelStatistics event( Statistic::CHANNEL_WAIT_FRAME, channel );
00099             frame->waitReady();
00100         }
00101 
00102         const vector< Image* >& images = frame->getImages();        
00103         for( vector< Image* >::const_iterator j = images.begin(); 
00104              j != images.end(); ++j )
00105         {
00106             const Image* image = *j;
00107 
00108             const bool hasColor = image->hasPixelData( Frame::BUFFER_COLOR );
00109             const bool hasDepth = image->hasPixelData( Frame::BUFFER_DEPTH );
00110             
00111             if(( blendAlpha && hasColor && image->hasAlpha( )) ||
00112                ( hasColor && hasDepth ))
00113             {
00114                 if( colorFormat == 0 )
00115                 {
00116                     colorFormat = image->getFormat( Frame::BUFFER_COLOR );
00117                     colorType   = image->getType(   Frame::BUFFER_COLOR );
00118                 }
00119 
00120                 if( colorFormat != image->getFormat( Frame::BUFFER_COLOR ) ||
00121                     colorType   != image->getType(   Frame::BUFFER_COLOR ))
00122 
00123                     return false;
00124 
00125                 if( image->hasPixelData( Frame::BUFFER_DEPTH ))
00126                 {
00127                     if( depthFormat == 0 )
00128                     {
00129                         depthFormat = image->getFormat( Frame::BUFFER_DEPTH );
00130                         depthType   = image->getType(   Frame::BUFFER_DEPTH );
00131                     }
00132 
00133                     if( depthFormat != image->getFormat(Frame::BUFFER_DEPTH ) ||
00134                         depthType   != image->getType(  Frame::BUFFER_DEPTH ))
00135 
00136                         return false;
00137                 }
00138 
00139                 ++nImages;
00140             }
00141         }
00142 
00143         if( nImages > 1 ) // early-out to reduce wait time
00144             return true;
00145     }
00146 
00147     return false;
00148 }
00149 }
00150 
00151 void Compositor::assembleFrames( const FrameVector& frames,
00152                                  Channel* channel )
00153 {
00154     if( frames.empty( ))
00155         return;
00156 
00157     if( _useCPUAssembly( frames, channel ))
00158         assembleFramesCPU( frames, channel );
00159     else
00160         assembleFramesUnsorted( frames, channel );
00161 }
00162 
00163 void Compositor::assembleFramesSorted( const FrameVector& frames,
00164                                        Channel* channel, const bool blendAlpha )
00165 {
00166     if( frames.empty( ))
00167         return;
00168 
00169     if( blendAlpha )
00170     {
00171         glEnable( GL_BLEND );
00172         glBlendFunc( GL_ONE, GL_SRC_ALPHA );
00173     }
00174 
00175     if( _useCPUAssembly( frames, channel, blendAlpha ))
00176         assembleFramesCPU( frames, channel, blendAlpha );
00177     else
00178     {
00179         for( FrameVector::const_iterator i = frames.begin();
00180              i != frames.end(); ++i )
00181         {
00182             Frame* frame = *i;
00183             {
00184                 ChannelStatistics event( Statistic::CHANNEL_WAIT_FRAME,
00185                                          channel );
00186                 frame->waitReady( );
00187             }
00188             assembleFrame( frame, channel );
00189         }
00190     }
00191 
00192     if( blendAlpha )
00193         glDisable( GL_BLEND );
00194 }
00195 
00196 void Compositor::assembleFramesUnsorted( const FrameVector& frames, 
00197                                          Channel* channel )
00198 {
00199     if( frames.empty( ))
00200         return;
00201 
00202     EQVERB << "Unsorted GPU assembly" << endl;
00203     // This is an optimized assembly version. The frames are not assembled in
00204     // the saved order, but in the order they become available, which is faster
00205     // because less time is spent waiting on frame availability.
00206     //
00207     // The ready frames are counted in a monitor. Whenever a frame becomes
00208     // available, it increments the monitor which causes this code to wake up
00209     // and assemble it.
00210 
00211     // register monitor with all input frames
00212     Monitor<uint32_t> monitor;
00213     for( FrameVector::const_iterator i = frames.begin();
00214          i != frames.end(); ++i )
00215     {
00216         Frame* frame = *i;
00217         frame->addListener( monitor );
00218     }
00219 
00220     uint32_t    nUsedFrames  = 0;
00221     FrameVector unusedFrames = frames;
00222 
00223     // wait and assemble frames
00224     while( !unusedFrames.empty( ))
00225     {
00226         {
00227             ChannelStatistics event( Statistic::CHANNEL_WAIT_FRAME, channel );
00228             monitor.waitGE( ++nUsedFrames );
00229         }
00230 
00231         for( FrameVector::iterator i = unusedFrames.begin();
00232              i != unusedFrames.end(); ++i )
00233         {
00234             Frame* frame = *i;
00235             if( !frame->isReady( ))
00236                 continue;
00237 
00238             assembleFrame( frame, channel );
00239             unusedFrames.erase( i );
00240             break;
00241         }
00242     }
00243 
00244     // de-register the monitor
00245     for( FrameVector::const_iterator i = frames.begin(); i != frames.end();
00246          ++i )
00247     {
00248         Frame* frame = *i;
00249         // syncAssembleFrame( frame );
00250         frame->removeListener( monitor );
00251     }
00252 }
00253 
00254 void Compositor::assembleFramesCPU( const FrameVector& frames,
00255                                     Channel* channel, const bool blendAlpha )
00256 {
00257     if( frames.empty( ))
00258         return;
00259 
00260     EQVERB << "Sorted CPU assembly" << endl;
00261     // Assembles images from DB and 2D compounds using the CPU and then
00262     // assembles the result image. Does not yet support Pixel or Eye
00263     // compounds.
00264 
00265     const Image* result = assembleFramesCPU( frames, blendAlpha );
00266     if( !result )
00267         return;
00268 
00269     // assemble result on dest channel
00270     ImageOp operation;
00271     operation.channel = channel;
00272     operation.buffers = Frame::BUFFER_COLOR | Frame::BUFFER_DEPTH;
00273     assembleImage( result, operation );
00274 
00275 #if 0
00276     static uint32_t counter = 0;
00277     ostringstream stringstream;
00278     stringstream << "Image_" << ++counter;
00279     result->writeImages( stringstream.str( ));
00280 #endif
00281 }
00282 
00283 const Image* Compositor::assembleFramesCPU( const FrameVector& frames,
00284                                             const bool blendAlpha )
00285 {
00286     EQVERB << "Sorted CPU assembly" << endl;
00287 
00288     // collect and categorize input images
00289     vector< FrameImage > imagesDB;
00290     vector< FrameImage > images2D;
00291     vector< FrameImage > imagesBlend;
00292     PixelViewport        resultPVP;
00293 
00294     int colorFormat = GL_BGRA;
00295     int colorType   = GL_UNSIGNED_BYTE;
00296     int depthFormat = GL_DEPTH_COMPONENT;
00297     int depthType   = GL_FLOAT;
00298 
00299     for( FrameVector::const_iterator i = frames.begin(); i != frames.end(); ++i)
00300     {
00301         Frame* frame = *i;
00302         frame->waitReady();
00303 
00304         EQASSERTINFO( frame->getPixel() == Pixel::ALL,
00305                       "CPU-based pixel recomposition not implemented" );
00306 
00307         const vector< Image* >& images = frame->getImages();        
00308         for( vector< Image* >::const_iterator j = images.begin(); 
00309              j != images.end(); ++j )
00310         {
00311             const Image* image = *j;
00312 
00313             if( !image->hasPixelData( Frame::BUFFER_COLOR ))
00314                 continue;
00315 
00316             resultPVP.merge( image->getPixelViewport() + frame->getOffset( ));
00317             
00318             colorFormat = image->getFormat( Frame::BUFFER_COLOR );
00319             colorType   = image->getType( Frame::BUFFER_COLOR );
00320 
00321             if( image->hasPixelData( Frame::BUFFER_DEPTH ))
00322             {
00323                 imagesDB.push_back( FrameImage( frame, image ));
00324                 depthFormat = image->getFormat( Frame::BUFFER_DEPTH );
00325                 depthType   = image->getType( Frame::BUFFER_DEPTH );
00326             }
00327             else if( blendAlpha && image->hasAlpha( ))
00328                 imagesBlend.push_back( FrameImage( frame, image ));
00329             else
00330                 images2D.push_back( FrameImage( frame, image ));
00331         }
00332     }
00333 
00334     if( !resultPVP.hasArea( ))
00335     {
00336         EQWARN << "Nothing to assemble: " << resultPVP << endl;
00337         return 0;
00338     }
00339 
00340     // prepare output image
00341     Image* result = _resultImage.get();
00342     if( !result )
00343     {
00344         _resultImage = new ResultImage;
00345         result       = _resultImage.get();;
00346     }
00347 
00348     result->setFormat( Frame::BUFFER_COLOR, colorFormat );
00349     result->setType(   Frame::BUFFER_COLOR, colorType );
00350     result->setFormat( Frame::BUFFER_DEPTH, depthFormat );
00351     result->setType(   Frame::BUFFER_DEPTH, depthType );
00352     result->setPixelViewport( resultPVP );
00353     result->clearPixelData( Frame::BUFFER_COLOR );
00354     if( !imagesDB.empty( ))
00355         result->clearPixelData( Frame::BUFFER_DEPTH );
00356 
00357     // assembly
00358     _assembleDBImages( result, imagesDB );
00359     _assemble2DImages( result, images2D );
00360     _assembleBlendImages( result, imagesBlend );
00361 
00362     return result;
00363 }
00364 
00365 
00366 void Compositor::_assembleDBImages( Image* result, 
00367                                     const std::vector< FrameImage >& images )
00368 {
00369     if( images.empty( ))
00370         return;
00371 
00372     EQVERB << "CPU-DB assembly of " << images.size() << " images" << endl;
00373 
00374     EQASSERT( result->getFormat( Frame::BUFFER_DEPTH ) == GL_DEPTH_COMPONENT);
00375     EQASSERT( result->getType( Frame::BUFFER_DEPTH )   == GL_FLOAT );
00376 
00377     const eq::PixelViewport resultPVP = result->getPixelViewport();
00378     uint32_t* destColor = reinterpret_cast< uint32_t* >
00379         ( result->getPixelPointer( Frame::BUFFER_COLOR ));
00380     float*    destDepth = reinterpret_cast< float* >
00381         ( result->getPixelPointer( Frame::BUFFER_DEPTH ));
00382 
00383     for( vector< FrameImage >::const_iterator i = images.begin();
00384          i != images.end(); ++i )
00385     {
00386         const Frame*          frame  = i->first;
00387         const Image*          image  = i->second;
00388         const vmml::Vector2i& offset = frame->getOffset();
00389         const PixelViewport&  pvp    = image->getPixelViewport();
00390 
00391         EQASSERT( image->getDepth( Frame::BUFFER_COLOR ) == 4 );
00392         EQASSERT( image->getDepth( Frame::BUFFER_DEPTH ) == 4 );
00393         EQASSERT( image->hasPixelData( Frame::BUFFER_COLOR ));
00394         EQASSERT( image->hasPixelData( Frame::BUFFER_DEPTH ));
00395         EQASSERTINFO( image->getFormat( Frame::BUFFER_COLOR ) ==
00396                       result->getFormat( Frame::BUFFER_COLOR ),
00397                       image->getFormat( Frame::BUFFER_COLOR ) << " != "
00398                       << result->getFormat( Frame::BUFFER_COLOR ));
00399         EQASSERT( image->getType( Frame::BUFFER_COLOR ) ==
00400                   result->getType( Frame::BUFFER_COLOR ));
00401         EQASSERT( image->getFormat( Frame::BUFFER_DEPTH ) ==
00402                   result->getFormat( Frame::BUFFER_DEPTH ));
00403         EQASSERT( image->getType( Frame::BUFFER_DEPTH ) ==
00404                   result->getType( Frame::BUFFER_DEPTH ));
00405 
00406 #ifdef EQ_USE_PARACOMP_DEPTH
00407         if( pvp == resultPVP && offset == vmml::Vector2i::ZERO )
00408         {
00409             // Use Paracomp to composite
00410             if( !_assembleImage_PC( PC_COMP_DEPTH, result, image ))
00411                 EQWARN << "Paracomp compositing failed, using fallback" << endl;
00412             else
00413                 continue; // Go to next input image
00414         }
00415 #endif
00416 
00417         const int32_t         destX  = offset.x + pvp.x - resultPVP.x;
00418         const int32_t         destY  = offset.y + pvp.y - resultPVP.y;
00419 
00420         const uint32_t* color = reinterpret_cast< const uint32_t* >
00421             ( image->getPixelPointer( Frame::BUFFER_COLOR ));
00422         const float*   depth = reinterpret_cast< const float* >
00423             ( image->getPixelPointer( Frame::BUFFER_DEPTH ));
00424 
00425 #  pragma omp parallel for
00426         for( int32_t y = 0; y < pvp.h; ++y )
00427         {
00428             const uint32_t skip =  (destY + y) * resultPVP.w + destX;
00429             EQASSERT( skip * sizeof( int32_t ) <= 
00430                       result->getPixelDataSize( Frame::BUFFER_COLOR ));
00431             EQASSERT( skip * sizeof( float ) <= 
00432                       result->getPixelDataSize( Frame::BUFFER_DEPTH ));
00433 
00434             uint32_t*   destColorIt = destColor + skip;
00435             float*      destDepthIt = destDepth + skip;
00436             const uint32_t* colorIt = color + y * pvp.w;
00437             const float*    depthIt = depth + y * pvp.w;
00438 
00439             for( int32_t x = 0; x < pvp.w; ++x )
00440             {
00441                 if( *destDepthIt > *depthIt )
00442                 {
00443                     *destColorIt = *colorIt;
00444                     *destDepthIt = *depthIt;
00445                 }
00446 
00447                 ++destColorIt;
00448                 ++destDepthIt;
00449                 ++colorIt;
00450                 ++depthIt;
00451             }
00452         }
00453     }
00454 }
00455 
00456 void Compositor::_assemble2DImages( Image* result, 
00457                                     const std::vector< FrameImage >& images )
00458 {
00459     // This is mostly copy&paste code from _assembleDBImages :-/
00460     if( images.empty( ))
00461         return;
00462 
00463     EQVERB << "CPU-2D assembly of " << images.size() << " images" << endl;
00464 
00465     const eq::PixelViewport resultPVP = result->getPixelViewport();
00466     uint8_t* destColor = result->getPixelPointer( Frame::BUFFER_COLOR );
00467     uint8_t* destDepth = result->hasPixelData( Frame::BUFFER_DEPTH ) ?
00468         reinterpret_cast< uint8_t* >(result->getPixelPointer( Frame::BUFFER_DEPTH))
00469         : 0;
00470 
00471     for( vector< FrameImage >::const_iterator i = images.begin();
00472          i != images.end(); ++i )
00473     {
00474         const Frame*          frame  = i->first;
00475         const Image*          image  = i->second;
00476         const vmml::Vector2i& offset = frame->getOffset();
00477         const PixelViewport&  pvp    = image->getPixelViewport();
00478         const int32_t         destX  = offset.x + pvp.x - resultPVP.x;
00479         const int32_t         destY  = offset.y + pvp.y - resultPVP.y;
00480 
00481         EQASSERT( image->hasPixelData( Frame::BUFFER_COLOR ));
00482         EQASSERT( image->getFormat( Frame::BUFFER_COLOR ) ==
00483                   result->getFormat( Frame::BUFFER_COLOR ));
00484         EQASSERT( image->getType( Frame::BUFFER_COLOR ) ==
00485                   result->getType( Frame::BUFFER_COLOR ));
00486 
00487         const uint8_t*   color = image->getPixelPointer( Frame::BUFFER_COLOR );
00488         const size_t pixelSize = image->getDepth( Frame::BUFFER_COLOR );
00489         const size_t rowLength = pvp.w * pixelSize;
00490 
00491 #  pragma omp parallel for
00492         for( int32_t y = 0; y < pvp.h; ++y )
00493         {
00494             const size_t skip = ( (destY + y) * resultPVP.w + destX ) *
00495                                     pixelSize;
00496             EQASSERT( skip + rowLength <= 
00497                       result->getPixelDataSize( Frame::BUFFER_COLOR ));
00498 
00499             memcpy( destColor + skip, color + y * pvp.w * pixelSize, rowLength);
00500             if( destDepth )
00501             {
00502                 EQASSERT( pixelSize == image->getDepth( Frame::BUFFER_DEPTH ));
00503                 bzero( destDepth + skip, rowLength );
00504             }
00505         }
00506     }
00507 }
00508 
00509 
00510 void Compositor::_assembleBlendImages( Image* result, 
00511                                        const std::vector< FrameImage >& images )
00512 {
00513     if( images.empty( ))
00514         return;
00515 
00516     EQVERB << "CPU-Blend assembly of " << images.size() <<" images"<< endl;
00517 
00518     const eq::PixelViewport resultPVP = result->getPixelViewport();
00519     int32_t* destColor = reinterpret_cast< int32_t* >
00520         ( result->getPixelPointer( Frame::BUFFER_COLOR ));
00521 
00522     for( vector< FrameImage >::const_iterator i = images.begin();
00523          i != images.end(); ++i )
00524     {
00525         const Frame*          frame  = i->first;
00526         const Image*          image  = i->second;
00527         const vmml::Vector2i& offset = frame->getOffset();
00528         const PixelViewport&  pvp    = image->getPixelViewport();
00529         const int32_t         destX  = offset.x + pvp.x - resultPVP.x;
00530         const int32_t         destY  = offset.y + pvp.y - resultPVP.y;
00531 
00532         EQASSERT( image->getDepth( Frame::BUFFER_COLOR ) == 4 );
00533         EQASSERT( image->hasPixelData( Frame::BUFFER_COLOR ));
00534         EQASSERT( image->hasAlpha( ));
00535         EQASSERT( image->getFormat( Frame::BUFFER_COLOR ) ==
00536                   result->getFormat( Frame::BUFFER_COLOR ));
00537         EQASSERT( image->getType( Frame::BUFFER_COLOR ) ==
00538                   result->getType( Frame::BUFFER_COLOR ));
00539 
00540 #ifdef EQ_USE_PARACOMP_BLEND
00541         if( pvp == resultPVP && offset == vmml::Vector2i::ZERO )
00542         { 
00543             // Use Paracomp to composite
00544             if( !_assembleImage_PC( PC_COMP_ALPHA_SORT2_HP,result, image ))
00545                 EQWARN << "Paracomp compositing failed, using fallback" << endl;
00546             else
00547                 continue; // Go to next input image
00548         }
00549 #endif
00550 
00551         const int32_t* color = reinterpret_cast< const int32_t* >
00552                                 ( image->getPixelPointer( Frame::BUFFER_COLOR ));
00553 
00554         // Check if we have enought space
00555         EQASSERT( ((destY+pvp.h-1)*resultPVP.w+destX+pvp.w)*sizeof(uint32_t) <=
00556                   result->getPixelDataSize( Frame::BUFFER_COLOR ));
00557 
00558 
00559         // Blending of two slices, none of which is on final image (i.e. result
00560         // could be blended on to something else) should be performed with:
00561         // glBlendFuncSeparate( GL_ONE, GL_SRC_ALPHA, GL_ZERO, GL_SRC_ALPHA )
00562         // which means:
00563         // dstColor = 1*srcColor + srcAlpha*dstColor
00564         // dstAlpha = 0*srcAlpha + srcAlpha*dstAlpha
00565         // because we accumulate light which is go through (= 1-Alpha) and we
00566         // already have colors as Alpha*Color
00567 
00568         const int32_t* colorIt     = color;
00569         int32_t*       destColorIt = destColor + destY*resultPVP.w + destX;
00570         const uint32_t step        = sizeof( int32_t );
00571 
00572 #  pragma omp parallel for
00573         for( int32_t y = 0; y < pvp.h; ++y )
00574         {
00575             const unsigned char* src =
00576                 reinterpret_cast< const unsigned char* >( colorIt );
00577             unsigned char*       dst =
00578                 reinterpret_cast< unsigned char* >( destColorIt );
00579 
00580             for( int32_t x = 0; x < pvp.w; ++x )
00581             {
00582                 dst[0] = EQ_MIN( src[0] + (src[3]*dst[0] >> 8), 255 );
00583                 dst[1] = EQ_MIN( src[1] + (src[3]*dst[1] >> 8), 255 );
00584                 dst[2] = EQ_MIN( src[2] + (src[3]*dst[2] >> 8), 255 );
00585                 dst[3] =                   src[3]*dst[3] >> 8;
00586 
00587                 src += step;
00588                 dst += step;
00589             }
00590             colorIt     += pvp.w;
00591             destColorIt += resultPVP.w;
00592         }
00593     }
00594 }
00595 
00596 #ifdef EQ_USE_PARACOMP
00597 namespace
00598 {
00599 static unsigned glToPCFormat( const unsigned glFormat, const unsigned glType )
00600 {
00601     switch( glFormat )
00602     {
00603         case GL_RGBA:
00604         case GL_RGBA8:
00605             if( glType == GL_UNSIGNED_BYTE )
00606                 return PC_PF_RGBA8;
00607             break;
00608 
00609         case GL_BGRA:
00610             if( glType == GL_UNSIGNED_BYTE )
00611                 return PC_PF_BGRA8;
00612             break;
00613 
00614         case GL_BGR:
00615             if( glType == GL_UNSIGNED_BYTE )
00616                 return PC_PF_BGR8;
00617             break;
00618 
00619         case GL_DEPTH_COMPONENT:
00620             if( glType == GL_FLOAT )
00621                 return PC_PF_Z32F;
00622             break;
00623     }
00624 
00625     return 0;
00626 }
00627 }
00628 #endif
00629 
00630 bool Compositor::_assembleImage_PC( int operation, Image* result, 
00631                                     const Image* source )
00632 {
00633 #ifdef EQ_USE_PARACOMP
00634     const unsigned colorFormat = 
00635         glToPCFormat( result->getFormat( Frame::BUFFER_COLOR ),
00636                       result->getType(   Frame::BUFFER_COLOR ));
00637 
00638     if( colorFormat == 0 )
00639     {
00640         EQWARN << "Format or type of image not supported by Paracomp" << endl;
00641         return false;
00642     }
00643 
00644     PCchannel input[2];
00645     PCchannel output[2];
00646     
00647     input[0].pixelFormat  = colorFormat;
00648     input[0].size         = result->getDepth( Frame::BUFFER_COLOR );
00649     output[0].pixelFormat = colorFormat;
00650     output[0].size        = result->getDepth( Frame::BUFFER_COLOR );
00651 
00652     const PixelViewport& pvp = source->getPixelViewport();
00653     EQASSERT( pvp == result->getPixelViewport( ));
00654 
00655     input[0].xOffset   = 0;
00656     input[0].yOffset   = 0;
00657     input[0].width     = pvp.w;
00658     input[0].height    = pvp.h;
00659     input[0].rowLength = pvp.w * input[0].size;
00660     input[0].address   = source->getPixelPointer( Frame::BUFFER_COLOR );
00661 
00662     output[0].xOffset   = 0;
00663     output[0].yOffset   = 0;
00664     output[0].width     = pvp.w;
00665     output[0].height    = pvp.h;
00666     output[0].rowLength = pvp.w * output[0].size;
00667     output[0].address   = result->getPixelPointer( Frame::BUFFER_COLOR );
00668 
00669     
00670     const bool useDepth = ( operation == PC_COMP_DEPTH );
00671     if( useDepth )
00672     {
00673         const unsigned depthFormat = 
00674             glToPCFormat( result->getFormat( Frame::BUFFER_DEPTH ),
00675                           result->getType(   Frame::BUFFER_DEPTH ));
00676 
00677         if( depthFormat == 0 )
00678         {
00679             EQWARN << "Format or type of image not supported by Paracomp" 
00680                    << endl;
00681             return false;
00682         }
00683 
00684         input[1].pixelFormat  = depthFormat;
00685         input[1].size         = result->getDepth( Frame::BUFFER_DEPTH );
00686         output[1].pixelFormat = depthFormat;
00687         output[1].size        = result->getDepth( Frame::BUFFER_DEPTH );
00688 
00689         input[1].xOffset   = 0;
00690         input[1].yOffset   = 0;
00691         input[1].width     = pvp.w;
00692         input[1].height    = pvp.h;
00693         input[1].rowLength = pvp.w * input[1].size;
00694         input[1].address   = source->getPixelPointer( Frame::BUFFER_DEPTH );
00695 
00696         output[1].xOffset   = 0;
00697         output[1].yOffset   = 0;
00698         output[1].width     = pvp.w;
00699         output[1].height    = pvp.h;
00700         output[1].rowLength = pvp.w * output[1].size;
00701         output[1].address   = result->getPixelPointer( Frame::BUFFER_DEPTH );
00702     }
00703 
00704 
00705     PCchannel* inputImages[2]     = { output, input };
00706     PCchannel* outputImage[1]     = { output };
00707     PCuint     nInputChannels[2]  = { 1, 1 };
00708     PCuint     nOutputChannels[1] = { 1 };
00709 
00710     if( useDepth )
00711     {
00712         nInputChannels[ 0 ]  = 2;
00713         nInputChannels[ 1 ]  = 2;
00714         nOutputChannels[ 0 ] = 2;
00715     }
00716 
00717     const PCerr error = pcCompositeEXT( operation, 
00718                                         2, nInputChannels, inputImages,
00719                                         1, nOutputChannels, outputImage );
00720     if( error != PC_NO_ERROR )
00721     {
00722         EQWARN << "Paracomp compositing failed: " << error << endl;
00723         return false;
00724     }
00725 
00726     EQINFO << "Paracomp compositing successful" << endl;
00727     return true;
00728 #else
00729     return false;
00730 #endif
00731 }
00732 
00733 
00734 void Compositor::assembleFrame( const Frame* frame, Channel* channel )
00735 {
00736     const vector< Image* >& images = frame->getImages();
00737     if( images.empty( ))
00738         EQWARN << "No images to assemble" << endl;
00739 
00740     ImageOp operation;
00741     operation.channel = channel;
00742     operation.buffers = frame->getBuffers();
00743     operation.offset  = frame->getOffset();
00744     operation.pixel   = frame->getPixel();
00745 
00746     for( vector< Image* >::const_iterator i = images.begin(); 
00747          i != images.end(); ++i )
00748     {
00749         const Image* image = *i;
00750         assembleImage( image, operation );
00751     }
00752 }
00753 
00754 void Compositor::assembleImage( const Image* image, const ImageOp& op )
00755 {
00756     ImageOp operation = op;
00757     operation.buffers = Frame::BUFFER_NONE;
00758 
00759     if( op.buffers & Frame::BUFFER_COLOR && 
00760         image->hasPixelData( Frame::BUFFER_COLOR ))
00761 
00762         operation.buffers |= Frame::BUFFER_COLOR;
00763 
00764     if( op.buffers & Frame::BUFFER_DEPTH &&
00765         image->hasPixelData( Frame::BUFFER_DEPTH ))
00766 
00767         operation.buffers |= Frame::BUFFER_DEPTH;
00768 
00769     if( operation.buffers == Frame::BUFFER_NONE )
00770     {
00771         EQWARN << "No image attachment buffers to assemble" << endl;
00772         return;
00773     }
00774 
00775     setupStencilBuffer( image, operation );
00776 
00777     if( operation.buffers == Frame::BUFFER_COLOR )
00778         assembleImage2D( image, operation );
00779     else if( operation.buffers == ( Frame::BUFFER_COLOR | Frame::BUFFER_DEPTH ))
00780         assembleImageDB( image, operation );
00781     else
00782         EQWARN << "Don't know how to assemble using buffers " 
00783                << operation.buffers << endl;
00784 }
00785 
00786 void Compositor::setupStencilBuffer( const Image* image, const ImageOp& op )
00787 {
00788     if( op.pixel == Pixel::ALL )
00789         return;
00790 
00791     // mark stencil buffer where pixel shall not pass
00792     // TODO: OPT!
00793     glClear( GL_STENCIL_BUFFER_BIT );
00794     glEnable( GL_STENCIL_TEST );
00795     glEnable( GL_DEPTH_TEST );
00796 
00797     glStencilFunc( GL_ALWAYS, 1, 1 );
00798     glStencilOp( GL_REPLACE, GL_REPLACE, GL_REPLACE );
00799 
00800     glLineWidth( 1.0f );
00801     glDepthMask( false );
00802     glColorMask( false, false, false, false );
00803     
00804     const PixelViewport& pvp    = image->getPixelViewport();
00805 
00806     glPixelZoom( static_cast< float >( op.pixel.w ),
00807                  static_cast< float >( op.pixel.h ));
00808 
00809     if( op.pixel.w > 1 )
00810     {
00811         const float width  = static_cast< float >( pvp.w * op.pixel.w );
00812         const float step   = static_cast< float >( op.pixel.w );
00813 
00814         const float startX = 
00815             static_cast< float >( op.offset.x + pvp.x ) + 0.5f - 
00816             static_cast< float >( op.pixel.w );
00817         const float endX   = startX + width + op.pixel.w + step;
00818 
00819         const float startY = 
00820             static_cast< float >( op.offset.y + pvp.y + op.pixel.y );
00821         const float endY   = static_cast< float >( startY + pvp.h*op.pixel.h );
00822 
00823         glBegin( GL_QUADS );
00824         for( float x = startX + op.pixel.x + 1.0f ; x < endX; x += step)
00825         {
00826             glVertex3f( x-step, startY, 0.0f );
00827             glVertex3f( x-1.0f, startY, 0.0f );
00828             glVertex3f( x-1.0f, endY, 0.0f );        
00829             glVertex3f( x-step, endY, 0.0f );        
00830         }
00831         glEnd();
00832     }
00833     if( op.pixel.h > 1 )
00834     {
00835         const float height = static_cast< float >( pvp.h * op.pixel.h );
00836         const float step   = static_cast< float >( op.pixel.h );
00837 
00838         const float startX = 
00839             static_cast< float >( op.offset.x + pvp.x + op.pixel.x );
00840         const float endX   = static_cast< float >( startX + pvp.w*op.pixel.w );
00841 
00842         const float startY = 
00843             static_cast< float >( op.offset.y + pvp.y ) + 0.5f - 
00844             static_cast< float >( op.pixel.h );
00845         const float endY   = startY + height + op.pixel.h + step;
00846 
00847         glBegin( GL_QUADS );
00848         for( float y = startY + op.pixel.y; y < endY; y += step)
00849         {
00850             glVertex3f( startX, y-step, 0.0f );
00851             glVertex3f( endX,   y-step, 0.0f );        
00852             glVertex3f( endX,   y-1.0f, 0.0f );        
00853             glVertex3f( startX, y-1.0f, 0.0f );
00854         }
00855         glEnd();
00856     }
00857     
00858     glDisable( GL_DEPTH_TEST );
00859     glStencilFunc( GL_EQUAL, 0, 1 );
00860     glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
00861     
00862     const ColorMask& colorMask = op.channel->getDrawBufferMask();
00863     glColorMask( colorMask.red, colorMask.green, colorMask.blue, true );
00864     glDepthMask( true );
00865 }
00866 
00867 void Compositor::assembleImage2D( const Image* image, const ImageOp& op )
00868 {
00869     const PixelViewport& pvp = image->getPixelViewport();
00870 
00871     EQLOG( LOG_ASSEMBLY ) << "assembleImage2D " << pvp << " offset " 
00872                           << op.offset << endl;
00873     EQASSERT( image->hasPixelData( Frame::BUFFER_COLOR ));
00874 
00875     glRasterPos2i( op.offset.x + pvp.x, op.offset.y + pvp.y );
00876     glDrawPixels( pvp.w, pvp.h, 
00877                   image->getFormat( Frame::BUFFER_COLOR ), 
00878                   image->getType( Frame::BUFFER_COLOR ), 
00879                   image->getPixelPointer( Frame::BUFFER_COLOR ));
00880 }
00881 
00882 void Compositor::assembleImageDB( const Image* image, const ImageOp& op )
00883 {
00884     if( GLEW_VERSION_2_0 )
00885         assembleImageDB_GLSL( image, op );
00886     else
00887         assembleImageDB_FF( image, op );
00888 }
00889 
00890 void Compositor::assembleImageDB_FF( const Image* image, const ImageOp& op )
00891 {
00892     const PixelViewport& pvp = image->getPixelViewport();
00893 
00894     EQLOG( LOG_ASSEMBLY ) << "assembleImageDB, fixed function " << pvp 
00895                           << endl;
00896     EQASSERT( image->hasPixelData( Frame::BUFFER_COLOR ));
00897     EQASSERT( image->hasPixelData( Frame::BUFFER_DEPTH ));
00898 
00899     // Z-Based sort-last assembly
00900     glRasterPos2i( op.offset.x + pvp.x, op.offset.y + pvp.y );
00901     glEnable( GL_STENCIL_TEST );
00902     
00903     // test who is in front and mark in stencil buffer
00904     glEnable( GL_DEPTH_TEST );
00905 
00906     const bool pixelComposite = ( op.pixel != Pixel::ALL );
00907     if( pixelComposite )
00908     {   // keep already marked stencil values
00909         glStencilFunc( GL_EQUAL, 1, 1 );
00910         glStencilOp( GL_KEEP, GL_ZERO, GL_REPLACE );
00911     }
00912     else
00913     {
00914         glStencilFunc( GL_ALWAYS, 1, 1 );
00915         glStencilOp( GL_ZERO, GL_ZERO, GL_REPLACE );
00916     }
00917 
00918     glDrawPixels( pvp.w, pvp.h, image->getFormat( Frame::BUFFER_DEPTH ), 
00919                   image->getType( Frame::BUFFER_DEPTH ), 
00920                   image->getPixelPointer( Frame::BUFFER_DEPTH ));
00921     
00922     glDisable( GL_DEPTH_TEST );
00923 
00924     // draw front-most, visible pixels using stencil mask
00925     glStencilFunc( GL_EQUAL, 1, 1 );
00926     glStencilOp( GL_KEEP, GL_ZERO, GL_ZERO );
00927     
00928     glDrawPixels( pvp.w, pvp.h, image->getFormat( Frame::BUFFER_COLOR ), 
00929                   image->getType( Frame::BUFFER_COLOR ),
00930                   image->getPixelPointer( Frame::BUFFER_COLOR ));
00931 
00932     glDisable( GL_STENCIL_TEST );
00933 }
00934 
00935 void Compositor::assembleImageDB_GLSL( const Image* image, const ImageOp& op )
00936 {
00937     const PixelViewport& pvp = image->getPixelViewport();
00938 
00939     EQLOG( LOG_ASSEMBLY ) << "assembleImageDB, GLSL " << pvp 
00940                           << endl;
00941 
00942     Window*                window  = op.channel->getWindow();
00943     Window::ObjectManager* objects = window->getObjectManager();
00944     
00945     // use per-window textures: using a shared texture across multiple windows
00946     // creates undefined texture contents, since each context has it's own
00947     // command buffer which is flushed unpredictably (at least on Darwin). A
00948     // glFlush at the end of this method would do it too, but the performance
00949     // use case is one window per pipe, hence we'll optimize for that and remove
00950     // the glFlush.
00951     const char*            key     = reinterpret_cast< char* >( window );
00952 
00953     GLuint depthTexture = objects->obtainTexture( key );
00954     GLuint colorTexture = objects->obtainTexture( key+1 );
00955     GLuint program      = objects->getProgram( &glslKey );
00956 
00957     if( program == Window::ObjectManager::FAILED )
00958     {
00959         // Create fragment shader which reads color and depth values from 
00960         // rectangular textures
00961         const GLuint shader = objects->newShader( &glslKey, GL_FRAGMENT_SHADER);
00962         EQASSERT( shader != Window::ObjectManager::FAILED );
00963 
00964         const char* source = "uniform sampler2DRect color; uniform sampler2DRect depth; void main(void){ gl_FragColor = texture2DRect( color, gl_TexCoord[0].st ); gl_FragDepth = texture2DRect( depth, gl_TexCoord[0].st ).x; }";
00965 
00966         EQ_GL_CALL( glShaderSource( shader, 1, &source, 0 ));
00967         EQ_GL_CALL( glCompileShader( shader ));
00968 
00969         GLint status;
00970         glGetShaderiv( shader, GL_COMPILE_STATUS, &status );
00971         if( !status )
00972             EQERROR << "Failed to compile fragment shader for DB compositing" 
00973                     << endl;
00974 
00975         program = objects->newProgram( &glslKey );
00976 
00977         EQ_GL_CALL( glAttachShader( program, shader ));
00978         EQ_GL_CALL( glLinkProgram( program ));
00979 
00980         glGetProgramiv( program, GL_LINK_STATUS, &status );
00981         if( !status )
00982         {
00983             EQWARN << "Failed to link shader program for DB compositing" 
00984                    << endl;
00985             return;
00986         }
00987 
00988         // use fragment shader and setup uniforms
00989         EQ_GL_CALL( glUseProgram( program ));
00990         
00991         const GLint depthParam = glGetUniformLocation( program, "depth" );
00992         glUniform1i( depthParam, 0 );
00993         const GLint colorParam = glGetUniformLocation( program, "color" );
00994         glUniform1i( colorParam, 1 );
00995 
00996         // make sure the other shared context see the program
00997         glFlush();
00998     }
00999     else
01000         // use fragment shader
01001         EQ_GL_CALL( glUseProgram( program ));
01002 
01003     // Enable & download color and depth textures
01004     glEnable( GL_TEXTURE_RECTANGLE_ARB );
01005 
01006     EQ_GL_CALL( glActiveTexture( GL_TEXTURE1 ));
01007     EQ_GL_CALL( glBindTexture( GL_TEXTURE_RECTANGLE_ARB, colorTexture ));
01008     glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
01009                      GL_NEAREST );
01010     glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, 
01011                      GL_NEAREST );
01012 
01013     EQ_GL_CALL( glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, 
01014                               GL_RGBA,
01015                               pvp.w, pvp.h, 0,
01016                               image->getFormat( Frame::BUFFER_COLOR ), 
01017                               image->getType( Frame::BUFFER_COLOR ),
01018                               image->getPixelPointer( Frame::BUFFER_COLOR )));
01019 
01020     EQ_GL_CALL( glActiveTexture( GL_TEXTURE0 ));
01021     EQ_GL_CALL( glBindTexture( GL_TEXTURE_RECTANGLE_ARB, depthTexture ));
01022     glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
01023                      GL_NEAREST );
01024     glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, 
01025                      GL_NEAREST );
01026 
01027     EQ_GL_CALL( glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, 
01028                               GL_DEPTH_COMPONENT32_ARB,
01029                               pvp.w, pvp.h, 0,
01030                               image->getFormat( Frame::BUFFER_DEPTH ), 
01031                               image->getType( Frame::BUFFER_DEPTH ),
01032                               image->getPixelPointer( Frame::BUFFER_DEPTH )));
01033 
01034     // Draw a quad using shader & textures in the right place
01035     glEnable( GL_DEPTH_TEST );
01036     glColor3f( 1.0f, 1.0f, 1.0f );
01037 
01038     const float startX = static_cast< float >
01039         ( op.offset.x + pvp.x * op.pixel.w + op.pixel.x );
01040     const float endX   = static_cast< float >
01041         ( op.offset.x + (pvp.x + pvp.w) * op.pixel.w + op.pixel.x );
01042     const float startY = static_cast< float >
01043         ( op.offset.y + pvp.y * op.pixel.h + op.pixel.y );
01044     const float endY   = static_cast< float >
01045         ( op.offset.y + (pvp.y + pvp.h) * op.pixel.h + op.pixel.y );
01046 
01047     glBegin( GL_TRIANGLE_STRIP );
01048     glMultiTexCoord2f( GL_TEXTURE0, 0.0f, 0.0f );
01049     glMultiTexCoord2f( GL_TEXTURE1, 0.0f, 0.0f );
01050     glVertex3f( startX, startY, 0.0f );
01051 
01052     glMultiTexCoord2f( GL_TEXTURE0, pvp.w, 0.0f );
01053     glMultiTexCoord2f( GL_TEXTURE1, pvp.w, 0.0f );
01054     glVertex3f( endX, startY, 0.0f );
01055 
01056     glMultiTexCoord2f( GL_TEXTURE0, 0.0f, pvp.h );
01057     glMultiTexCoord2f( GL_TEXTURE1, 0.0f, pvp.h );
01058     glVertex3f( startX, endY, 0.0f );
01059 
01060     glMultiTexCoord2f( GL_TEXTURE0, pvp.w, pvp.h );
01061     glMultiTexCoord2f( GL_TEXTURE1, pvp.w, pvp.h );
01062     glVertex3f( endX, endY, 0.0f );
01063 
<a name="l01