Line data Source code
1 :
2 : /* Copyright (c) 2006-2017, Stefan Eilemann <eile@equalizergraphics.com>
3 : * Daniel Nachbaur <danielnachbaur@gmail.com>
4 : * Cedric Stalder <cedric.stalder@gmail.com>
5 : * Enrique <egparedes@ifi.uzh.ch>
6 : *
7 : * This library is free software; you can redistribute it and/or modify it under
8 : * the terms of the GNU Lesser General Public License version 2.1 as published
9 : * by the Free Software Foundation.
10 : *
11 : * This library is distributed in the hope that it will be useful, but WITHOUT
12 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 : * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14 : * details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public License
17 : * along with this library; if not, write to the Free Software Foundation, Inc.,
18 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 : */
20 :
21 : #include "frameData.h"
22 :
23 : #include "channelStatistics.h"
24 : #include "exception.h"
25 : #include "image.h"
26 : #include "log.h"
27 : #include "nodeStatistics.h"
28 : #include "pixelData.h"
29 : #include "roiFinder.h"
30 :
31 : #include <co/commandFunc.h>
32 : #include <co/connectionDescription.h>
33 : #include <co/dataIStream.h>
34 : #include <co/dataOStream.h>
35 : #include <eq/fabric/drawableConfig.h>
36 : #include <eq/fabric/frameData.h>
37 : #include <eq/util/objectManager.h>
38 : #include <lunchbox/monitor.h>
39 : #include <lunchbox/scopedMutex.h>
40 : #include <pression/plugins/compressor.h>
41 :
42 : #include <boost/foreach.hpp>
43 :
44 : #include <algorithm>
45 :
46 : namespace eq
47 : {
48 : typedef lunchbox::Monitor<uint64_t> Monitor;
49 : typedef std::vector<FrameData::Listener*> Listeners;
50 :
51 : namespace detail
52 : {
53 2 : class FrameData
54 : {
55 : public:
56 2 : FrameData()
57 4 : : version(co::VERSION_NONE.low())
58 : , useAlpha(true)
59 : , colorQuality(1.f)
60 : , depthQuality(1.f)
61 : , colorCompressor(EQ_COMPRESSOR_AUTO)
62 4 : , depthCompressor(EQ_COMPRESSOR_AUTO)
63 : {
64 2 : }
65 :
66 : Images images;
67 : Images imageCache;
68 : std::mutex imageCacheLock;
69 :
70 : ROIFinder roiFinder;
71 :
72 : Images pendingImages;
73 :
74 : uint64_t version; //!< The current version
75 :
76 : /** Data ready monitor for output->input synchronization. */
77 : Monitor readyVersion;
78 :
79 : /** External monitors for readiness synchronization. */
80 : lunchbox::Lockable<Listeners, lunchbox::SpinLock> listeners;
81 :
82 : bool useAlpha;
83 : float colorQuality;
84 : float depthQuality;
85 :
86 : uint32_t colorCompressor;
87 : uint32_t depthCompressor;
88 : };
89 : }
90 :
91 : typedef co::CommandFunc<FrameData> CmdFunc;
92 :
93 2 : FrameData::FrameData()
94 2 : : _impl(new detail::FrameData)
95 : {
96 2 : }
97 :
98 6 : FrameData::~FrameData()
99 : {
100 2 : clear();
101 :
102 6 : for (Image* image : _impl->imageCache)
103 : {
104 12 : LBLOG(LOG_BUG) << "Unflushed image in FrameData destructor"
105 12 : << std::endl;
106 4 : delete image;
107 : }
108 2 : _impl->imageCache.clear();
109 :
110 2 : delete _impl;
111 4 : }
112 :
113 28 : const Images& FrameData::getImages() const
114 : {
115 28 : return _impl->images;
116 : }
117 :
118 0 : void FrameData::setAlphaUsage(const bool useAlpha)
119 : {
120 0 : _impl->useAlpha = useAlpha;
121 0 : }
122 :
123 1 : bool FrameData::isReady() const
124 : {
125 1 : return _impl->readyVersion.get() >= _impl->version;
126 : }
127 :
128 0 : void FrameData::setQuality(Frame::Buffer buffer, float quality)
129 : {
130 0 : if (buffer != Frame::Buffer::color)
131 : {
132 0 : LBASSERT(buffer == Frame::Buffer::depth);
133 0 : _impl->depthQuality = quality;
134 0 : return;
135 : }
136 :
137 0 : _impl->colorQuality = quality;
138 : }
139 :
140 0 : void FrameData::useCompressor(const Frame::Buffer buffer, const uint32_t name)
141 : {
142 0 : if (buffer != Frame::Buffer::color)
143 : {
144 0 : LBASSERT(buffer == Frame::Buffer::depth);
145 0 : _impl->depthCompressor = name;
146 0 : return;
147 : }
148 :
149 0 : _impl->colorCompressor = name;
150 : }
151 :
152 0 : void FrameData::getInstanceData(co::DataOStream& os)
153 : {
154 0 : LBUNREACHABLE;
155 0 : serialize(os);
156 0 : }
157 :
158 1 : void FrameData::applyInstanceData(co::DataIStream& is)
159 : {
160 1 : clear();
161 1 : deserialize(is);
162 1 : LBLOG(LOG_ASSEMBLY) << "applied " << this << std::endl;
163 1 : }
164 :
165 4 : void FrameData::clear()
166 : {
167 4 : _impl->imageCacheLock.lock();
168 16 : _impl->imageCache.insert(_impl->imageCache.end(), _impl->images.begin(),
169 16 : _impl->images.end());
170 4 : _impl->imageCacheLock.unlock();
171 4 : _impl->images.clear();
172 4 : }
173 :
174 0 : void FrameData::flush()
175 : {
176 0 : clear();
177 :
178 0 : for (ImagesCIter i = _impl->imageCache.begin();
179 0 : i != _impl->imageCache.end(); ++i)
180 : {
181 0 : Image* image = *i;
182 0 : image->flush();
183 0 : delete image;
184 : }
185 :
186 0 : _impl->imageCache.clear();
187 0 : }
188 :
189 2 : void FrameData::deleteGLObjects(util::ObjectManager& om)
190 : {
191 4 : for (Image* image : _impl->images)
192 2 : image->deleteGLObjects(om);
193 2 : for (Image* image : _impl->imageCache)
194 0 : image->deleteGLObjects(om);
195 2 : }
196 :
197 1 : void FrameData::resetPlugins()
198 : {
199 2 : BOOST_FOREACH (Image* image, _impl->images)
200 1 : image->resetPlugins();
201 1 : BOOST_FOREACH (Image* image, _impl->imageCache)
202 0 : image->resetPlugins();
203 1 : }
204 :
205 7 : Image* FrameData::newImage(const eq::Frame::Type type,
206 : const DrawableConfig& config)
207 : {
208 7 : Image* image = _allocImage(type, config, true /* set quality */);
209 7 : _impl->images.push_back(image);
210 7 : return image;
211 : }
212 :
213 7 : Image* FrameData::_allocImage(const eq::Frame::Type type,
214 : const DrawableConfig& config,
215 : const bool setQuality_)
216 : {
217 : Image* image;
218 7 : _impl->imageCacheLock.lock();
219 :
220 7 : if (_impl->imageCache.empty())
221 : {
222 4 : _impl->imageCacheLock.unlock();
223 4 : image = new Image;
224 : }
225 : else
226 : {
227 3 : image = _impl->imageCache.back();
228 3 : _impl->imageCache.pop_back();
229 3 : _impl->imageCacheLock.unlock();
230 :
231 3 : image->reset();
232 : }
233 :
234 7 : image->setAlphaUsage(_impl->useAlpha);
235 7 : image->setStorageType(type);
236 7 : if (setQuality_)
237 : {
238 7 : image->setQuality(Frame::Buffer::color, _impl->colorQuality);
239 7 : image->setQuality(Frame::Buffer::depth, _impl->depthQuality);
240 : }
241 :
242 7 : image->useCompressor(Frame::Buffer::color, _impl->colorCompressor);
243 7 : image->useCompressor(Frame::Buffer::depth, _impl->depthCompressor);
244 :
245 : image->setInternalFormat(Frame::Buffer::depth,
246 7 : EQ_COMPRESSOR_DATATYPE_DEPTH);
247 7 : switch (config.colorBits)
248 : {
249 : case 16:
250 : image->setInternalFormat(Frame::Buffer::color,
251 0 : EQ_COMPRESSOR_DATATYPE_RGBA16F);
252 0 : break;
253 : case 32:
254 : image->setInternalFormat(Frame::Buffer::color,
255 0 : EQ_COMPRESSOR_DATATYPE_RGBA32F);
256 0 : break;
257 : case 10:
258 : image->setInternalFormat(Frame::Buffer::color,
259 0 : EQ_COMPRESSOR_DATATYPE_RGB10_A2);
260 0 : break;
261 : default:
262 : image->setInternalFormat(Frame::Buffer::color,
263 7 : EQ_COMPRESSOR_DATATYPE_RGBA);
264 : }
265 :
266 7 : return image;
267 : }
268 :
269 1 : Images FrameData::startReadback(const Frame& frame,
270 : util::ObjectManager& glObjects,
271 : const DrawableConfig& config,
272 : const PixelViewports& regions,
273 : const RenderContext& context)
274 : {
275 1 : if (_buffers == Frame::Buffer::none)
276 0 : return Images();
277 :
278 1 : const Zoom& frameZoom = frame.getZoom();
279 1 : if (!frameZoom.isValid())
280 : {
281 0 : LBWARN << "Invalid zoom factor, skipping frame" << std::endl;
282 0 : return Images();
283 : }
284 :
285 1 : const eq::PixelViewport& framePVP = getPixelViewport();
286 1 : const PixelViewport absPVP = framePVP + frame.getOffset();
287 1 : if (!absPVP.isValid())
288 0 : return Images();
289 :
290 2 : Images images;
291 :
292 : // readback the whole screen when using textures
293 1 : if (getType() == eq::Frame::TYPE_TEXTURE)
294 : {
295 0 : Image* image = newImage(getType(), config);
296 0 : if (image->startReadback(getBuffers(), absPVP, context, frameZoom,
297 : glObjects))
298 : {
299 0 : images.push_back(image);
300 : }
301 0 : image->setOffset(0, 0);
302 0 : return images;
303 : }
304 : // else read only required regions
305 :
306 : #if 0
307 : // TODO: issue #85: move automatic ROI detection to eq::Channel
308 : PixelViewports regions;
309 : if( buffers & Frame::Buffer::DEPTH && frameZoom == Zoom::NONE )
310 : regions = _impl->roiFinder->findRegions( buffers, absPVP, frameZoom,
311 : frame.getAssemblyStage(),
312 : frame.getFrameID(), glObjects);
313 : else
314 : regions.push_back( absPVP );
315 : #endif
316 :
317 1 : LBASSERT(getType() == eq::Frame::TYPE_MEMORY);
318 :
319 2 : for (uint32_t i = 0; i < regions.size(); ++i)
320 : {
321 1 : PixelViewport pvp = regions[i] + frame.getOffset();
322 1 : pvp.intersect(absPVP);
323 1 : if (!pvp.hasArea())
324 0 : continue;
325 :
326 1 : Image* image = newImage(getType(), config);
327 1 : if (image->startReadback(getBuffers(), pvp, context, frameZoom,
328 : glObjects))
329 1 : images.push_back(image);
330 :
331 1 : pvp -= frame.getOffset();
332 1 : pvp.apply(frameZoom);
333 1 : image->setOffset((pvp.x - framePVP.x) * context.pixel.w,
334 2 : (pvp.y - framePVP.y) * context.pixel.h);
335 : }
336 1 : return images;
337 : }
338 :
339 5 : void FrameData::setVersion(const uint64_t version)
340 : {
341 5 : LBASSERTINFO(_impl->version <= version, _impl->version << " > " << version);
342 5 : _impl->version = version;
343 5 : LBLOG(LOG_ASSEMBLY) << "New v" << version << std::endl;
344 5 : }
345 :
346 21 : void FrameData::waitReady(const uint32_t timeout) const
347 : {
348 21 : if (!_impl->readyVersion.timedWaitGE(_impl->version, timeout))
349 0 : throw Exception(Exception::TIMEOUT_INPUTFRAME);
350 21 : }
351 :
352 1 : void FrameData::setReady()
353 : {
354 1 : _setReady(_impl->version);
355 1 : }
356 :
357 0 : void FrameData::setReady(const co::ObjectVersion& frameData,
358 : const fabric::FrameData& data)
359 : {
360 0 : clear();
361 0 : LBASSERT(frameData.version.high() == 0);
362 0 : LBASSERT(_impl->readyVersion < frameData.version.low());
363 0 : LBASSERT(_impl->readyVersion == 0 ||
364 : _impl->readyVersion + 1 == frameData.version.low());
365 0 : LBASSERT(_impl->version == frameData.version.low());
366 :
367 0 : _impl->images.swap(_impl->pendingImages);
368 0 : fabric::FrameData::operator=(data);
369 0 : _setReady(frameData.version.low());
370 :
371 0 : LBLOG(LOG_ASSEMBLY) << this << " applied v" << frameData.version.low()
372 0 : << std::endl;
373 0 : }
374 :
375 1 : void FrameData::_setReady(const uint64_t version)
376 : {
377 1 : LBASSERTINFO(_impl->readyVersion <= version,
378 : "v" << _impl->version << " ready " << _impl->readyVersion
379 : << " new " << version);
380 :
381 2 : lunchbox::ScopedFastWrite mutex(_impl->listeners);
382 1 : if (_impl->readyVersion >= version)
383 0 : return;
384 :
385 1 : _impl->readyVersion = version;
386 1 : LBLOG(LOG_ASSEMBLY) << "set ready " << this << ", "
387 0 : << _impl->listeners->size() << " monitoring"
388 1 : << std::endl;
389 :
390 2 : BOOST_FOREACH (Listener* listener, _impl->listeners.data)
391 1 : ++(*listener);
392 : }
393 :
394 1 : void FrameData::addListener(Listener& listener)
395 : {
396 2 : lunchbox::ScopedFastWrite mutex(_impl->listeners);
397 :
398 1 : _impl->listeners->push_back(&listener);
399 1 : if (_impl->readyVersion >= _impl->version)
400 0 : ++listener;
401 1 : }
402 :
403 1 : void FrameData::removeListener(Listener& listener)
404 : {
405 2 : lunchbox::ScopedFastWrite mutex(_impl->listeners);
406 :
407 1 : Listeners::iterator i = lunchbox::find(_impl->listeners.data, &listener);
408 1 : LBASSERT(i != _impl->listeners->end());
409 1 : _impl->listeners->erase(i);
410 1 : }
411 :
412 0 : bool FrameData::addImage(const co::ObjectVersion& frameDataVersion,
413 : const PixelViewport& pvp, const Zoom& zoom,
414 : const RenderContext& context,
415 : const Frame::Buffer buffers_, const bool useAlpha,
416 : uint8_t* data)
417 : {
418 0 : LBASSERT(_impl->readyVersion < frameDataVersion.version.low());
419 0 : if (_impl->readyVersion >= frameDataVersion.version.low())
420 0 : return false;
421 :
422 0 : Image* image = _allocImage(Frame::TYPE_MEMORY, DrawableConfig(),
423 0 : false /* set quality */);
424 :
425 0 : image->setPixelViewport(pvp);
426 0 : image->setAlphaUsage(useAlpha);
427 :
428 0 : Frame::Buffer buffers[] = {Frame::Buffer::color, Frame::Buffer::depth};
429 0 : for (unsigned i = 0; i < 2; ++i)
430 : {
431 0 : const Frame::Buffer buffer = buffers[i];
432 :
433 0 : if (buffers_ & buffer)
434 : {
435 0 : PixelData pixelData;
436 0 : const ImageHeader* header = reinterpret_cast<ImageHeader*>(data);
437 0 : data += sizeof(ImageHeader);
438 :
439 0 : pixelData.internalFormat = header->internalFormat;
440 0 : pixelData.externalFormat = header->externalFormat;
441 0 : pixelData.pixelSize = header->pixelSize;
442 0 : pixelData.pvp = header->pvp;
443 0 : pixelData.compressorFlags = header->compressorFlags;
444 :
445 0 : const uint32_t compressor = header->compressorName;
446 0 : if (compressor > EQ_COMPRESSOR_NONE)
447 : {
448 0 : pression::CompressorChunks chunks;
449 0 : const uint32_t nChunks = header->nChunks;
450 0 : chunks.reserve(nChunks);
451 :
452 0 : for (uint32_t j = 0; j < nChunks; ++j)
453 : {
454 0 : const uint64_t size = *reinterpret_cast<uint64_t*>(data);
455 0 : data += sizeof(uint64_t);
456 :
457 0 : chunks.push_back(pression::CompressorChunk(data, size));
458 0 : data += size;
459 : }
460 : pixelData.compressedData =
461 0 : pression::CompressorResult(compressor, chunks);
462 : }
463 : else
464 : {
465 0 : const uint64_t size = *reinterpret_cast<uint64_t*>(data);
466 0 : data += sizeof(uint64_t);
467 :
468 0 : pixelData.pixels = data;
469 0 : data += size;
470 0 : LBASSERT(size == pixelData.pvp.getArea() * pixelData.pixelSize);
471 : }
472 :
473 0 : image->setZoom(zoom);
474 0 : image->setContext(context);
475 0 : image->setQuality(buffer, header->quality);
476 0 : image->setPixelData(buffer, pixelData);
477 : }
478 : }
479 :
480 0 : _impl->pendingImages.push_back(image);
481 0 : return true;
482 : }
483 :
484 0 : std::ostream& operator<<(std::ostream& os, const FrameData& data)
485 : {
486 0 : return os << "frame data id " << data.getID() << "." << data.getInstanceID()
487 0 : << " v" << data.getVersion() << ' ' << data.getImages().size()
488 0 : << " images, ready " << (data.isReady() ? 'y' : 'n') << " "
489 0 : << data.getZoom();
490 : }
491 30 : }
|