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 : }
|