Line data Source code
1 :
2 : /* Copyright (c) 2006-2014, Stefan Eilemann <eile@equalizergraphics.com>
3 : * 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 "frameData.h"
21 :
22 : #include "nodeStatistics.h"
23 : #include "channelStatistics.h"
24 : #include "exception.h"
25 : #include "image.h"
26 : #include "log.h"
27 : #include "pixelData.h"
28 : #include "roiFinder.h"
29 :
30 : #include <eq/fabric/drawableConfig.h>
31 : #include <eq/util/objectManager.h>
32 : #include <co/commandFunc.h>
33 : #include <co/connectionDescription.h>
34 : #include <co/dataIStream.h>
35 : #include <co/dataOStream.h>
36 : #include <lunchbox/monitor.h>
37 : #include <lunchbox/scopedMutex.h>
38 : #include <lunchbox/plugins/compressor.h>
39 :
40 : #include <boost/foreach.hpp>
41 :
42 : #include <algorithm>
43 :
44 : namespace eq
45 : {
46 : typedef lunchbox::Monitor< uint64_t > Monitor;
47 : typedef std::vector< FrameData::Listener* > Listeners;
48 :
49 : namespace detail
50 : {
51 3 : class FrameData
52 : {
53 : public:
54 3 : FrameData()
55 3 : : version( co::VERSION_NONE.low( ))
56 : , useAlpha( true )
57 : , colorQuality( 1.f )
58 : , depthQuality( 1.f )
59 : , colorCompressor( EQ_COMPRESSOR_AUTO )
60 6 : , depthCompressor( EQ_COMPRESSOR_AUTO )
61 3 : {}
62 :
63 : eq::FrameData::Data data;
64 :
65 : Images images;
66 : Images imageCache;
67 : lunchbox::Lock imageCacheLock;
68 :
69 : ROIFinder roiFinder;
70 :
71 : Images pendingImages;
72 :
73 : uint64_t version; //!< The current version
74 :
75 : /** Data ready monitor for output->input synchronization. */
76 : Monitor readyVersion;
77 :
78 : /** External monitors for readiness synchronization. */
79 : lunchbox::Lockable< Listeners, lunchbox::SpinLock > listeners;
80 :
81 : bool useAlpha;
82 : float colorQuality;
83 : float depthQuality;
84 :
85 : uint32_t colorCompressor;
86 : uint32_t depthCompressor;
87 : };
88 : }
89 :
90 : typedef co::CommandFunc<FrameData> CmdFunc;
91 :
92 3 : FrameData::FrameData()
93 3 : : _impl( new detail::FrameData )
94 3 : {}
95 :
96 9 : FrameData::~FrameData()
97 : {
98 3 : clear();
99 :
100 8 : BOOST_FOREACH( Image* image, _impl->imageCache )
101 : {
102 5 : LBWARN << "Unflushed image in FrameData destructor" << std::endl;
103 5 : delete image;
104 : }
105 3 : _impl->imageCache.clear();
106 :
107 3 : delete _impl;
108 6 : }
109 :
110 :
111 6 : Frame::Type FrameData::getType() const
112 : {
113 6 : return _impl->data.frameType;
114 : }
115 :
116 0 : void FrameData::setType( const Frame::Type type )
117 : {
118 0 : _impl->data.frameType = type;
119 0 : }
120 :
121 6 : uint32_t FrameData::getBuffers() const
122 : {
123 6 : return _impl->data.buffers;
124 : }
125 :
126 2 : void FrameData::setBuffers( const uint32_t buffers )
127 : {
128 2 : _impl->data.buffers = buffers;
129 2 : }
130 :
131 0 : const Range& FrameData::getRange() const
132 : {
133 0 : return _impl->data.range;
134 : }
135 :
136 0 : void FrameData::setRange( const Range& range )
137 : {
138 0 : _impl->data.range = range;
139 0 : }
140 :
141 46 : const Pixel& FrameData::getPixel() const
142 : {
143 46 : return _impl->data.pixel;
144 : }
145 :
146 23 : const SubPixel& FrameData::getSubPixel() const
147 : {
148 23 : return _impl->data.subpixel;
149 : }
150 :
151 0 : uint32_t FrameData::getPeriod() const
152 : {
153 0 : return _impl->data.period;
154 : }
155 :
156 0 : uint32_t FrameData::getPhase() const
157 : {
158 0 : return _impl->data.phase;
159 : }
160 :
161 55 : const Images& FrameData::getImages() const
162 : {
163 55 : return _impl->images;
164 : }
165 :
166 0 : void FrameData::setPixelViewport( const PixelViewport& pvp )
167 : {
168 0 : _impl->data.pvp = pvp;
169 0 : }
170 :
171 2 : const PixelViewport& FrameData::getPixelViewport() const
172 : {
173 2 : return _impl->data.pvp;
174 : }
175 :
176 0 : void FrameData::setAlphaUsage( const bool useAlpha )
177 : {
178 0 : _impl->useAlpha = useAlpha;
179 0 : }
180 :
181 0 : void FrameData::setZoom( const Zoom& zoom )
182 : {
183 0 : _impl->data.zoom = zoom;
184 0 : }
185 :
186 23 : const Zoom& FrameData::getZoom() const
187 : {
188 23 : return _impl->data.zoom;
189 : }
190 :
191 2 : bool FrameData::isReady() const
192 : {
193 2 : return _impl->readyVersion.get() >= _impl->version;
194 : }
195 :
196 0 : void FrameData::disableBuffer( const Frame::Buffer buffer )
197 : {
198 0 : _impl->data.buffers &= ~buffer;
199 0 : }
200 :
201 0 : const FrameData::Data& FrameData::getData() const
202 : {
203 0 : return _impl->data;
204 : }
205 :
206 0 : void FrameData::setQuality( Frame::Buffer buffer, float quality )
207 : {
208 0 : if( buffer != Frame::BUFFER_COLOR )
209 : {
210 0 : LBASSERT( buffer == Frame::BUFFER_DEPTH );
211 0 : _impl->depthQuality = quality;
212 0 : return;
213 : }
214 :
215 0 : _impl->colorQuality = quality;
216 : }
217 :
218 0 : void FrameData::useCompressor( const Frame::Buffer buffer, const uint32_t name )
219 : {
220 0 : if( buffer != Frame::BUFFER_COLOR )
221 : {
222 0 : LBASSERT( buffer == Frame::BUFFER_DEPTH );
223 0 : _impl->depthCompressor = name;
224 0 : return;
225 : }
226 :
227 0 : _impl->colorCompressor = name;
228 : }
229 :
230 0 : void FrameData::getInstanceData( co::DataOStream& os )
231 : {
232 0 : LBUNREACHABLE;
233 0 : _impl->data.serialize( os );
234 0 : }
235 :
236 2 : void FrameData::applyInstanceData( co::DataIStream& is )
237 : {
238 2 : clear();
239 2 : _impl->data.deserialize( is );
240 2 : LBLOG( LOG_ASSEMBLY ) << "applied " << this << std::endl;
241 2 : }
242 :
243 18 : void FrameData::Data::serialize( co::DataOStream& os ) const
244 : {
245 18 : os << pvp << frameType << buffers << period << phase << range
246 36 : << pixel << subpixel << zoom;
247 18 : }
248 :
249 2 : void FrameData::Data::deserialize( co::DataIStream& is )
250 : {
251 2 : is >> pvp >> frameType >> buffers >> period >> phase >> range
252 4 : >> pixel >> subpixel >> zoom;
253 2 : }
254 :
255 6 : void FrameData::clear()
256 : {
257 6 : _impl->imageCacheLock.set();
258 : _impl->imageCache.insert( _impl->imageCache.end(), _impl->images.begin(),
259 6 : _impl->images.end( ));
260 6 : _impl->imageCacheLock.unset();
261 6 : _impl->images.clear();
262 6 : }
263 :
264 0 : void FrameData::flush()
265 : {
266 0 : clear();
267 :
268 0 : for( ImagesCIter i = _impl->imageCache.begin();
269 0 : i != _impl->imageCache.end(); ++i )
270 : {
271 0 : Image* image = *i;
272 0 : image->flush();
273 0 : delete image;
274 : }
275 :
276 0 : _impl->imageCache.clear();
277 0 : }
278 :
279 4 : void FrameData::deleteGLObjects( util::ObjectManager& om )
280 : {
281 8 : BOOST_FOREACH( Image* image, _impl->images )
282 4 : image->deleteGLObjects( om );
283 4 : BOOST_FOREACH( Image* image, _impl->imageCache )
284 0 : image->deleteGLObjects( om );
285 4 : }
286 :
287 2 : void FrameData::resetPlugins()
288 : {
289 4 : BOOST_FOREACH( Image* image, _impl->images )
290 2 : image->resetPlugins();
291 2 : BOOST_FOREACH( Image* image, _impl->imageCache )
292 0 : image->resetPlugins();
293 2 : }
294 :
295 8 : Image* FrameData::newImage( const eq::Frame::Type type,
296 : const DrawableConfig& config )
297 : {
298 8 : Image* image = _allocImage( type, config, true /* set quality */ );
299 8 : _impl->images.push_back( image );
300 8 : return image;
301 : }
302 :
303 8 : Image* FrameData::_allocImage( const eq::Frame::Type type,
304 : const DrawableConfig& config,
305 : const bool setQuality_ )
306 : {
307 : Image* image;
308 8 : _impl->imageCacheLock.set();
309 :
310 8 : if( _impl->imageCache.empty( ))
311 : {
312 5 : _impl->imageCacheLock.unset();
313 5 : image = new Image;
314 : }
315 : else
316 : {
317 3 : image = _impl->imageCache.back();
318 3 : _impl->imageCache.pop_back();
319 3 : _impl->imageCacheLock.unset();
320 :
321 3 : image->reset();
322 : }
323 :
324 8 : image->setAlphaUsage( _impl->useAlpha );
325 8 : image->setStorageType( type );
326 8 : if( setQuality_ )
327 : {
328 8 : image->setQuality( Frame::BUFFER_COLOR, _impl->colorQuality );
329 8 : image->setQuality( Frame::BUFFER_DEPTH, _impl->depthQuality );
330 : }
331 :
332 8 : image->useCompressor( Frame::BUFFER_COLOR, _impl->colorCompressor );
333 8 : image->useCompressor( Frame::BUFFER_DEPTH, _impl->depthCompressor );
334 :
335 : image->setInternalFormat( Frame::BUFFER_DEPTH,
336 8 : EQ_COMPRESSOR_DATATYPE_DEPTH );
337 8 : switch( config.colorBits )
338 : {
339 : case 16:
340 : image->setInternalFormat( Frame::BUFFER_COLOR,
341 0 : EQ_COMPRESSOR_DATATYPE_RGBA16F );
342 0 : break;
343 : case 32:
344 : image->setInternalFormat( Frame::BUFFER_COLOR,
345 0 : EQ_COMPRESSOR_DATATYPE_RGBA32F );
346 0 : break;
347 : case 10:
348 : image->setInternalFormat( Frame::BUFFER_COLOR,
349 0 : EQ_COMPRESSOR_DATATYPE_RGB10_A2 );
350 0 : break;
351 : default:
352 : image->setInternalFormat( Frame::BUFFER_COLOR,
353 8 : EQ_COMPRESSOR_DATATYPE_RGBA );
354 : }
355 :
356 8 : return image;
357 : }
358 :
359 : #ifndef EQ_2_0_API
360 0 : void FrameData::readback( const Frame& frame,
361 : util::ObjectManager& glObjects,
362 : const DrawableConfig& config )
363 : {
364 : const Images& images = startReadback( frame, glObjects, config,
365 0 : PixelViewports( 1, getPixelViewport( )));
366 :
367 0 : for( ImagesCIter i = images.begin(); i != images.end(); ++i )
368 0 : (*i)->finishReadback( frame.getZoom(), glObjects.glewGetContext( ));
369 0 : }
370 : #endif
371 :
372 2 : Images FrameData::startReadback( const Frame& frame,
373 : util::ObjectManager& glObjects,
374 : const DrawableConfig& config,
375 : const PixelViewports& regions )
376 : {
377 2 : if( _impl->data.buffers == Frame::BUFFER_NONE )
378 0 : return Images();
379 :
380 2 : const Zoom& zoom = frame.getZoom();
381 2 : if( !zoom.isValid( ))
382 : {
383 0 : LBWARN << "Invalid zoom factor, skipping frame" << std::endl;
384 0 : return Images();
385 : }
386 :
387 2 : const eq::PixelViewport& framePVP = getPixelViewport();
388 2 : const PixelViewport absPVP = framePVP + frame.getOffset();
389 2 : if( !absPVP.isValid( ))
390 0 : return Images();
391 :
392 2 : Images images;
393 :
394 : // readback the whole screen when using textures
395 2 : if( getType() == eq::Frame::TYPE_TEXTURE )
396 : {
397 0 : Image* image = newImage( getType(), config );
398 0 : if( image->startReadback( getBuffers(), absPVP, zoom, glObjects ))
399 0 : images.push_back( image );
400 0 : image->setOffset( 0, 0 );
401 0 : return images;
402 : }
403 : //else read only required regions
404 :
405 : #if 0
406 : // TODO: issue #85: move automatic ROI detection to eq::Channel
407 : PixelViewports regions;
408 : if( _impl->data.buffers & Frame::BUFFER_DEPTH && zoom == Zoom::NONE )
409 : regions = _impl->roiFinder->findRegions( _impl->data.buffers, absPVP,
410 : zoom, frame.getAssemblyStage(),
411 : frame.getFrameID(), glObjects);
412 : else
413 : regions.push_back( absPVP );
414 : #endif
415 :
416 2 : LBASSERT( getType() == eq::Frame::TYPE_MEMORY );
417 2 : const eq::Pixel& pixel = getPixel();
418 :
419 4 : for( uint32_t i = 0; i < regions.size(); ++i )
420 : {
421 2 : PixelViewport pvp = regions[ i ] + frame.getOffset();
422 2 : pvp.intersect( absPVP );
423 2 : if( !pvp.hasArea( ))
424 0 : continue;
425 :
426 2 : Image* image = newImage( getType(), config );
427 2 : if( image->startReadback( getBuffers(), pvp, zoom, glObjects ))
428 2 : images.push_back( image );
429 :
430 2 : pvp -= frame.getOffset();
431 2 : pvp.apply( zoom );
432 2 : image->setOffset( (pvp.x - framePVP.x) * pixel.w,
433 4 : (pvp.y - framePVP.y) * pixel.h );
434 : }
435 2 : return images;
436 : }
437 :
438 10 : void FrameData::setVersion( const uint64_t version )
439 : {
440 10 : LBASSERTINFO( _impl->version <= version, _impl->version << " > "
441 : << version );
442 10 : _impl->version = version;
443 10 : LBLOG( LOG_ASSEMBLY ) << "New v" << version << std::endl;
444 10 : }
445 :
446 21 : void FrameData::waitReady( const uint32_t timeout ) const
447 : {
448 21 : if( !_impl->readyVersion.timedWaitGE( _impl->version, timeout ))
449 0 : throw Exception( Exception::TIMEOUT_INPUTFRAME );
450 21 : }
451 :
452 2 : void FrameData::setReady()
453 : {
454 2 : _setReady( _impl->version );
455 2 : }
456 :
457 0 : void FrameData::setReady( const co::ObjectVersion& frameData,
458 : const FrameData::Data& data )
459 : {
460 0 : clear();
461 0 : LBASSERT( frameData.version.high() == 0 );
462 0 : LBASSERT( _impl->readyVersion < frameData.version.low( ));
463 0 : LBASSERT( _impl->readyVersion == 0 ||
464 : _impl->readyVersion + 1 == frameData.version.low( ));
465 0 : LBASSERT( _impl->version == frameData.version.low( ));
466 :
467 0 : _impl->images.swap( _impl->pendingImages );
468 0 : _impl->data = data;
469 0 : _setReady( frameData.version.low());
470 :
471 0 : LBLOG( LOG_ASSEMBLY ) << this << " applied v"
472 0 : << frameData.version.low() << std::endl;
473 0 : }
474 :
475 2 : void FrameData::_setReady( const uint64_t version )
476 : {
477 2 : LBASSERTINFO( _impl->readyVersion <= version,
478 : "v" << _impl->version << " ready " << _impl->readyVersion
479 : << " new " << version );
480 :
481 2 : lunchbox::ScopedMutex< lunchbox::SpinLock > mutex( _impl->listeners );
482 2 : if( _impl->readyVersion >= version )
483 2 : return;
484 :
485 2 : _impl->readyVersion = version;
486 2 : LBLOG( LOG_ASSEMBLY ) << "set ready " << this << ", "
487 0 : << _impl->listeners->size() << " monitoring"
488 2 : << std::endl;
489 :
490 4 : BOOST_FOREACH( Listener* listener, _impl->listeners.data )
491 4 : ++(*listener);
492 : }
493 :
494 2 : void FrameData::addListener( Listener& listener )
495 : {
496 2 : lunchbox::ScopedFastWrite mutex( _impl->listeners );
497 :
498 2 : _impl->listeners->push_back( &listener );
499 2 : if( _impl->readyVersion >= _impl->version )
500 0 : ++listener;
501 2 : }
502 :
503 2 : void FrameData::removeListener( Listener& listener )
504 : {
505 2 : lunchbox::ScopedFastWrite mutex( _impl->listeners );
506 :
507 2 : Listeners::iterator i = lunchbox::find( _impl->listeners.data, &listener );
508 2 : LBASSERT( i != _impl->listeners->end( ));
509 2 : _impl->listeners->erase( i );
510 2 : }
511 :
512 0 : bool FrameData::addImage( const co::ObjectVersion& frameDataVersion,
513 : const PixelViewport& pvp, const Zoom& zoom,
514 : const uint32_t buffers_, const bool useAlpha,
515 : uint8_t* data )
516 : {
517 0 : LBASSERT( _impl->readyVersion < frameDataVersion.version.low( ));
518 0 : if( _impl->readyVersion >= frameDataVersion.version.low( ))
519 0 : return false;
520 :
521 : Image* image = _allocImage( Frame::TYPE_MEMORY, DrawableConfig(),
522 0 : false /* set quality */ );
523 :
524 0 : image->setPixelViewport( pvp );
525 0 : image->setAlphaUsage( useAlpha );
526 :
527 0 : Frame::Buffer buffers[] = { Frame::BUFFER_COLOR, Frame::BUFFER_DEPTH };
528 0 : for( unsigned i = 0; i < 2; ++i )
529 : {
530 0 : const Frame::Buffer buffer = buffers[i];
531 :
532 0 : if( buffers_ & buffer )
533 : {
534 0 : PixelData pixelData;
535 0 : const ImageHeader* header = reinterpret_cast<ImageHeader*>( data );
536 0 : data += sizeof( ImageHeader );
537 :
538 0 : pixelData.internalFormat = header->internalFormat;
539 0 : pixelData.externalFormat = header->externalFormat;
540 0 : pixelData.pixelSize = header->pixelSize;
541 0 : pixelData.pvp = header->pvp;
542 0 : pixelData.compressorFlags = header->compressorFlags;
543 :
544 0 : const uint32_t compressor = header->compressorName;
545 0 : if( compressor > EQ_COMPRESSOR_NONE )
546 : {
547 0 : lunchbox::CompressorChunks chunks;
548 0 : const uint32_t nChunks = header->nChunks;
549 0 : chunks.reserve( nChunks );
550 :
551 0 : for( uint32_t j = 0; j < nChunks; ++j )
552 : {
553 0 : const uint64_t size = *reinterpret_cast< uint64_t*>( data );
554 0 : data += sizeof( uint64_t );
555 :
556 0 : chunks.push_back( lunchbox::CompressorChunk( data, size ));
557 0 : data += size;
558 : }
559 0 : pixelData.compressedData =
560 0 : lunchbox::CompressorResult( compressor, chunks );
561 : }
562 : else
563 : {
564 0 : const uint64_t size = *reinterpret_cast< uint64_t*>( data );
565 0 : data += sizeof( uint64_t );
566 :
567 0 : pixelData.pixels = data;
568 0 : data += size;
569 0 : LBASSERT( size == pixelData.pvp.getArea()*pixelData.pixelSize );
570 : }
571 :
572 0 : image->setZoom( zoom );
573 0 : image->setQuality( buffer, header->quality );
574 0 : image->setPixelData( buffer, pixelData );
575 : }
576 : }
577 :
578 0 : _impl->pendingImages.push_back( image );
579 0 : return true;
580 : }
581 :
582 0 : std::ostream& operator << ( std::ostream& os, const FrameData& data )
583 : {
584 0 : return os << "frame data id " << data.getID() << "." << data.getInstanceID()
585 0 : << " v" << data.getVersion() << ' ' << data.getImages().size()
586 0 : << " images, ready " << ( data.isReady() ? 'y' :'n' ) << " "
587 0 : << data.getZoom();
588 : }
589 :
590 36 : }
|