Line data Source code
1 :
2 : /* Copyright (c) 2009-2013, Stefan Eilemann <eile@equalizergraphics.com>
3 : *
4 : * This library is free software; you can redistribute it and/or modify it under
5 : * the terms of the GNU Lesser General Public License version 2.1 as published
6 : * by the Free Software Foundation.
7 : *
8 : * This library is distributed in the hope that it will be useful, but WITHOUT
9 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10 : * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 : * details.
12 : *
13 : * You should have received a copy of the GNU Lesser General Public License
14 : * along with this library; if not, write to the Free Software Foundation, Inc.,
15 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 : */
17 :
18 : #include "viewEqualizer.h"
19 :
20 : #include "../compound.h"
21 : #include "../compoundVisitor.h"
22 : #include "../config.h"
23 : #include "../log.h"
24 : #include "../pipe.h"
25 :
26 : #include <eq/fabric/statistic.h>
27 :
28 : #include <set>
29 :
30 : #define MIN_USAGE .1f // 10%
31 :
32 : namespace eq
33 : {
34 : namespace server
35 : {
36 32 : ViewEqualizer::ViewEqualizer()
37 32 : : _nPipes(0)
38 : {
39 32 : LBINFO << "New view equalizer @" << (void*)this << std::endl;
40 32 : }
41 :
42 0 : ViewEqualizer::ViewEqualizer(const ViewEqualizer& from)
43 : : Equalizer(from)
44 0 : , _nPipes(0)
45 : {
46 0 : }
47 :
48 96 : ViewEqualizer::~ViewEqualizer()
49 : {
50 32 : attach(0);
51 32 : LBINFO << "Delete view equalizer @" << (void*)this << std::endl;
52 64 : }
53 :
54 0 : ViewEqualizer::Listener::Listener()
55 : {
56 0 : }
57 :
58 0 : ViewEqualizer::Listener::~Listener()
59 : {
60 0 : }
61 :
62 96 : void ViewEqualizer::attach(Compound* compound)
63 : {
64 96 : for (Listeners::iterator i = _listeners.begin(); i != _listeners.end(); ++i)
65 0 : (*i).clear();
66 :
67 96 : _listeners.clear();
68 96 : Equalizer::attach(compound);
69 96 : }
70 :
71 0 : void ViewEqualizer::notifyUpdatePre(Compound* compound LB_UNUSED,
72 : const uint32_t frameNumber)
73 : {
74 0 : LBASSERT(compound == getCompound());
75 :
76 0 : _updateListeners();
77 0 : _updateResources();
78 0 : _update(frameNumber);
79 0 : }
80 :
81 : namespace
82 : {
83 0 : class SelfAssigner : public CompoundVisitor
84 : {
85 : public:
86 0 : SelfAssigner(const Pipe* self, float& nResources,
87 : lunchbox::PtrHash<Pipe*, float>& pipeUsage)
88 0 : : _self(self)
89 : , _nResources(nResources)
90 : , _pipeUsage(pipeUsage)
91 0 : , _numChannels(0)
92 : {
93 0 : }
94 :
95 0 : virtual VisitorResult visitLeaf(Compound* compound)
96 : {
97 0 : if (!compound->isActive())
98 0 : return TRAVERSE_CONTINUE;
99 :
100 0 : Pipe* pipe = compound->getPipe();
101 0 : LBASSERT(pipe);
102 :
103 0 : if (pipe != _self)
104 0 : return TRAVERSE_CONTINUE;
105 :
106 0 : if (_pipeUsage.find(pipe) == _pipeUsage.end())
107 0 : _pipeUsage[pipe] = 0.0f;
108 :
109 0 : float& pipeUsage = _pipeUsage[pipe];
110 0 : if (pipeUsage >= 1.0f)
111 : {
112 0 : compound->setUsage(0.f);
113 0 : return TRAVERSE_TERMINATE;
114 : }
115 :
116 0 : if (pipeUsage > 0.0f) // pipe already partly used
117 : {
118 0 : LBASSERT(pipeUsage < 1.0f);
119 :
120 0 : float use = 1.0f - pipeUsage;
121 0 : use = LB_MAX(use, MIN_USAGE);
122 :
123 0 : compound->setUsage(use);
124 0 : _nResources -= use;
125 0 : pipeUsage = 1.0f; // Don't use more than twice
126 0 : LBLOG(LOG_LB1) << " Use "
127 0 : << static_cast<unsigned>(use * 100.f + .5f)
128 0 : << "% of " << pipe->getName() << " task "
129 0 : << compound->getTaskID() << ", "
130 0 : << _nResources * 100.f << "% left" << std::endl;
131 : }
132 : else
133 : {
134 0 : LBASSERT(pipeUsage == 0.0f);
135 :
136 0 : float use = LB_MIN(1.0f, _nResources);
137 :
138 0 : compound->setUsage(use);
139 0 : _nResources -= use;
140 0 : pipeUsage = use;
141 0 : LBLOG(LOG_LB1) << " Use "
142 0 : << static_cast<unsigned>(use * 100.f + .5f)
143 0 : << "% of " << pipe->getName() << " task "
144 0 : << compound->getTaskID() << ", "
145 0 : << _nResources * 100.f << "% left" << std::endl;
146 : }
147 0 : ++_numChannels;
148 :
149 0 : return TRAVERSE_TERMINATE;
150 : }
151 :
152 0 : uint32_t getNumChannels() const { return _numChannels; }
153 : private:
154 : const Pipe* const _self;
155 : float& _nResources;
156 : lunchbox::PtrHash<Pipe*, float>& _pipeUsage;
157 : uint32_t _numChannels;
158 : };
159 :
160 0 : class PreviousAssigner : public CompoundVisitor
161 : {
162 : public:
163 0 : PreviousAssigner(const Pipe* self, float& nResources,
164 : lunchbox::PtrHash<Pipe*, float>& pipeUsage)
165 0 : : _self(self)
166 : , _nResources(nResources)
167 : , _pipeUsage(pipeUsage)
168 0 : , _numChannels(0)
169 : {
170 0 : }
171 :
172 0 : virtual VisitorResult visitLeaf(Compound* compound)
173 : {
174 0 : if (!compound->isActive())
175 0 : return TRAVERSE_CONTINUE;
176 :
177 0 : Pipe* pipe = compound->getPipe();
178 0 : LBASSERT(pipe);
179 :
180 0 : if (compound->getUsage() == 0.0f || // not previously used
181 0 : pipe == _self) // already assigned above
182 : {
183 0 : return TRAVERSE_CONTINUE;
184 : }
185 :
186 0 : compound->setUsage(0.0f); // reset to unused
187 0 : if (_nResources <= MIN_USAGE) // done
188 0 : return TRAVERSE_CONTINUE;
189 :
190 0 : if (_pipeUsage.find(pipe) == _pipeUsage.end())
191 0 : _pipeUsage[pipe] = 0.0f;
192 :
193 0 : float& pipeUsage = _pipeUsage[pipe];
194 0 : if (pipeUsage > 0.0f) // pipe already partly used
195 0 : return TRAVERSE_CONTINUE;
196 :
197 0 : float use = LB_MIN(1.0f, _nResources);
198 0 : if (use + MIN_USAGE > 1.0f)
199 0 : use = 1.0f;
200 :
201 0 : pipeUsage = use;
202 0 : compound->setUsage(use);
203 0 : _nResources -= use;
204 0 : ++_numChannels;
205 :
206 0 : LBLOG(LOG_LB1) << " Use " << static_cast<unsigned>(use * 100.f + .5f)
207 0 : << "% of " << pipe->getName() << " task "
208 0 : << compound->getTaskID() << ", " << _nResources * 100.f
209 0 : << "% left" << std::endl;
210 0 : return TRAVERSE_CONTINUE;
211 : }
212 :
213 0 : uint32_t getNumChannels() const { return _numChannels; }
214 : private:
215 : const Pipe* const _self;
216 : float& _nResources;
217 : lunchbox::PtrHash<Pipe*, float>& _pipeUsage;
218 : uint32_t _numChannels;
219 : };
220 :
221 0 : class NewAssigner : public CompoundVisitor
222 : {
223 : public:
224 0 : NewAssigner(float& nResources, lunchbox::PtrHash<Pipe*, float>& pipeUsage)
225 0 : : _nResources(nResources)
226 : , _pipeUsage(pipeUsage)
227 : , _numChannels(0)
228 0 : , _fallback(0)
229 : {
230 0 : }
231 :
232 0 : virtual VisitorResult visitLeaf(Compound* compound)
233 : {
234 0 : if (!compound->isActive())
235 0 : return TRAVERSE_CONTINUE;
236 :
237 0 : if (!_fallback)
238 0 : _fallback = compound;
239 :
240 0 : if (compound->getUsage() != 0.0f) // already used
241 0 : return TRAVERSE_CONTINUE;
242 :
243 0 : Pipe* pipe = compound->getPipe();
244 0 : LBASSERT(pipe);
245 :
246 0 : if (_pipeUsage.find(pipe) == _pipeUsage.end())
247 0 : _pipeUsage[pipe] = 0.0f;
248 :
249 0 : float& pipeUsage = _pipeUsage[pipe];
250 0 : if (pipeUsage >= 1.0f)
251 0 : return TRAVERSE_CONTINUE;
252 :
253 0 : if (pipeUsage > 0.0f) // pipe already partly used
254 : {
255 0 : LBASSERT(pipeUsage < 1.0f);
256 :
257 0 : float use = 1.0f - pipeUsage;
258 0 : use = LB_MAX(use, MIN_USAGE);
259 :
260 0 : compound->setUsage(use);
261 0 : _nResources -= use;
262 0 : pipeUsage = 1.0f; // Don't use more than twice
263 0 : LBLOG(LOG_LB1) << " Use "
264 0 : << static_cast<unsigned>(use * 100.f + .5f)
265 0 : << "% of " << pipe->getName() << " task "
266 0 : << compound->getTaskID() << ", "
267 0 : << _nResources * 100.f << "% left" << std::endl;
268 : }
269 : else
270 : {
271 0 : LBASSERT(pipeUsage == 0.0f);
272 :
273 0 : float use = LB_MIN(1.0f, _nResources);
274 :
275 0 : compound->setUsage(use);
276 0 : _nResources -= use;
277 0 : pipeUsage = use;
278 0 : LBLOG(LOG_LB1) << " Use "
279 0 : << static_cast<unsigned>(use * 100.f + .5f)
280 0 : << "% of " << pipe->getName() << " task "
281 0 : << compound->getTaskID() << ", "
282 0 : << _nResources * 100.f << "% left" << std::endl;
283 : }
284 0 : ++_numChannels;
285 :
286 0 : if (_nResources <= MIN_USAGE)
287 0 : return TRAVERSE_TERMINATE; // done
288 0 : return TRAVERSE_CONTINUE;
289 : }
290 :
291 0 : uint32_t getNumChannels() const { return _numChannels; }
292 0 : Compound* getFallback() { return _fallback; }
293 : private:
294 : float& _nResources;
295 : lunchbox::PtrHash<Pipe*, float>& _pipeUsage;
296 : uint32_t _numChannels;
297 : Compound* _fallback;
298 : };
299 : }
300 :
301 0 : void ViewEqualizer::_update(const uint32_t frameNumber)
302 : {
303 0 : const uint32_t frame = _findInputFrameNumber();
304 0 : LBLOG(LOG_LB1) << "Using data from frame " << frame << std::endl;
305 :
306 : //----- Gather data for frame
307 0 : Loads loads;
308 0 : int64_t totalTime(0);
309 :
310 0 : for (Listeners::iterator i = _listeners.begin(); i != _listeners.end(); ++i)
311 : {
312 0 : Listener& listener = *i;
313 0 : const Listener::Load& load = listener.useLoad(frame);
314 :
315 0 : totalTime += load.time;
316 0 : loads.push_back(load);
317 : }
318 :
319 0 : const Compound* compound = getCompound();
320 :
321 0 : if (isFrozen() || !compound->isActive() || _nPipes == 0)
322 : // always execute code above to not leak memory
323 0 : return;
324 :
325 0 : if (totalTime == 0) // no data
326 0 : totalTime = 1;
327 :
328 0 : const float resourceTime(static_cast<float>(totalTime) /
329 0 : static_cast<float>(_nPipes));
330 0 : LBLOG(LOG_LB1) << resourceTime << "ms/resource" << std::endl;
331 :
332 : //----- Assign new resource usage
333 0 : const Compounds& children = compound->getChildren();
334 0 : const size_t size(_listeners.size());
335 0 : LBASSERT(children.size() == size);
336 0 : lunchbox::PtrHash<Pipe*, float> pipeUsage;
337 0 : float* leftOvers = static_cast<float*>(alloca(size * sizeof(float)));
338 :
339 : // use self
340 0 : for (size_t i = 0; i < size; ++i)
341 : {
342 0 : Listener::Load& load = loads[i];
343 0 : LBASSERT(load.missing == 0);
344 :
345 0 : Compound* child = children[i];
346 0 : if (!child->isActive())
347 0 : continue;
348 :
349 0 : float segmentResources(load.time / resourceTime);
350 :
351 0 : LBLOG(LOG_LB1) << "----- balance step 1 for view " << i << " ("
352 0 : << child->getChannel()->getName() << " "
353 0 : << child->getChannel()->getSerial() << ") using "
354 0 : << segmentResources << " resources" << std::endl;
355 0 : SelfAssigner assigner(child->getPipe(), segmentResources, pipeUsage);
356 :
357 0 : child->accept(assigner);
358 0 : load.missing = assigner.getNumChannels();
359 0 : leftOvers[i] = segmentResources;
360 : }
361 :
362 : // use previous' frames resources
363 0 : for (size_t i = 0; i < size; ++i)
364 : {
365 0 : Listener::Load& load = loads[i];
366 0 : Compound* child = children[i];
367 0 : if (!child->isActive())
368 0 : continue;
369 :
370 0 : float& leftOver = leftOvers[i];
371 0 : LBLOG(LOG_LB1) << "----- balance step 2 for view " << i << " ("
372 0 : << child->getChannel()->getName() << " "
373 0 : << child->getChannel()->getSerial() << ") using "
374 0 : << leftOver << " resources" << std::endl;
375 0 : PreviousAssigner assigner(child->getPipe(), leftOver, pipeUsage);
376 :
377 0 : child->accept(assigner);
378 0 : load.missing += assigner.getNumChannels();
379 : }
380 :
381 : // satisfy left-overs
382 0 : for (size_t i = 0; i < size; ++i)
383 : {
384 0 : Listener& listener = _listeners[i];
385 0 : LBASSERTINFO(listener.getNLoads() <= getConfig()->getLatency() + 3,
386 : listener);
387 :
388 0 : float& leftOver = leftOvers[i];
389 0 : Listener::Load& load = loads[i];
390 0 : Compound* child = children[i];
391 :
392 0 : if (!child->isActive())
393 0 : continue;
394 :
395 0 : if (leftOver > MIN_USAGE || load.missing == 0)
396 : {
397 0 : LBLOG(LOG_LB1) << "----- balance step 3 for view " << i << " ("
398 0 : << child->getChannel()->getName() << ") using "
399 0 : << leftOver << " resources" << std::endl;
400 :
401 0 : NewAssigner assigner(leftOver, pipeUsage);
402 0 : child->accept(assigner);
403 0 : load.missing += assigner.getNumChannels();
404 :
405 0 : if (load.missing == 0) // assign at least one resource
406 : {
407 0 : Compound* fallback = assigner.getFallback();
408 0 : LBASSERT(fallback);
409 0 : LBASSERT(leftOver > 0);
410 :
411 0 : fallback->setUsage(leftOver);
412 0 : load.missing = 1;
413 0 : LBLOG(LOG_LB1)
414 0 : << " Use " << static_cast<unsigned>(leftOver * 100.f + .5f)
415 0 : << "% of " << fallback->getPipe()->getName() << " task "
416 0 : << fallback->getTaskID() << std::endl;
417 : }
418 : }
419 :
420 0 : listener.newLoad(frameNumber, load.missing);
421 : }
422 : }
423 :
424 0 : uint32_t ViewEqualizer::_findInputFrameNumber() const
425 : {
426 0 : LBASSERT(!_listeners.empty());
427 :
428 0 : uint32_t frame = std::numeric_limits<uint32_t>::max();
429 0 : const Compound* compound = getCompound();
430 0 : const Compounds& children = compound->getChildren();
431 0 : const size_t nChildren = children.size();
432 0 : LBASSERT(nChildren == _listeners.size());
433 :
434 0 : bool change = true;
435 0 : while (change)
436 : {
437 0 : change = false;
438 0 : for (size_t i = 0; i < nChildren; ++i)
439 : {
440 0 : const Compound* child = children[i];
441 0 : if (!child->isActive())
442 0 : continue;
443 :
444 0 : const Listener& listener = _listeners[i];
445 0 : const uint32_t youngest = listener.findYoungestLoad(frame);
446 0 : if (frame > youngest)
447 : {
448 0 : change = true;
449 0 : frame = youngest;
450 : }
451 : }
452 : }
453 :
454 0 : return frame;
455 : }
456 :
457 0 : void ViewEqualizer::_updateListeners()
458 : {
459 0 : if (!_listeners.empty())
460 : {
461 0 : LBASSERT(getCompound()->getChildren().size() == _listeners.size());
462 0 : return;
463 : }
464 :
465 0 : Compound* compound = getCompound();
466 0 : const Compounds& children = compound->getChildren();
467 0 : const size_t nChildren = children.size();
468 :
469 0 : _listeners.resize(nChildren);
470 0 : for (size_t i = 0; i < nChildren; ++i)
471 : {
472 0 : LBLOG(LOG_LB1) << lunchbox::disableFlush << "Tasks for view " << i
473 0 : << ": ";
474 0 : Listener& listener = _listeners[i];
475 0 : listener.update(children[i]);
476 0 : LBLOG(LOG_LB1) << std::endl << lunchbox::enableFlush;
477 : }
478 : }
479 :
480 : namespace
481 : {
482 0 : class PipeCounter : public CompoundVisitor
483 : {
484 : public:
485 0 : virtual VisitorResult visitPre(const Compound* compound)
486 : {
487 0 : return compound->isActive() ? TRAVERSE_CONTINUE : TRAVERSE_PRUNE;
488 : }
489 :
490 0 : virtual VisitorResult visitLeaf(const Compound* compound)
491 : {
492 0 : if (!compound->isActive())
493 0 : return TRAVERSE_PRUNE;
494 :
495 0 : const Pipe* pipe = compound->getPipe();
496 0 : LBASSERT(pipe);
497 0 : _pipes.insert(pipe);
498 0 : return TRAVERSE_CONTINUE;
499 : }
500 :
501 0 : size_t getNPipes() const { return _pipes.size(); }
502 : private:
503 : std::set<const Pipe*> _pipes;
504 : };
505 : }
506 :
507 0 : void ViewEqualizer::_updateResources()
508 : {
509 0 : PipeCounter counter;
510 0 : const Compound* compound = getCompound();
511 0 : compound->accept(counter);
512 0 : _nPipes = counter.getNPipes();
513 0 : }
514 :
515 : //---------------------------------------------------------------------------
516 : // Per-child listener implementation
517 : //---------------------------------------------------------------------------
518 : namespace
519 : {
520 0 : class LoadSubscriber : public CompoundVisitor
521 : {
522 : public:
523 0 : LoadSubscriber(ChannelListener* listener,
524 : lunchbox::PtrHash<Channel*, uint32_t>& taskIDs)
525 0 : : _listener(listener)
526 0 : , _taskIDs(taskIDs)
527 : {
528 0 : }
529 :
530 0 : virtual VisitorResult visitLeaf(Compound* compound)
531 : {
532 0 : Channel* channel = compound->getChannel();
533 0 : LBASSERT(channel);
534 :
535 0 : if (_taskIDs.find(channel) == _taskIDs.end())
536 : {
537 0 : channel->addListener(_listener);
538 0 : _taskIDs[channel] = compound->getTaskID();
539 0 : LBLOG(LOG_LB1) << _taskIDs[channel] << ' ';
540 : }
541 : else
542 : {
543 0 : LBASSERTINFO(0, "View equalizer does not support using channel "
544 : << channel->getName()
545 : << " multiple times in one branch");
546 : }
547 0 : return TRAVERSE_CONTINUE;
548 : }
549 :
550 : private:
551 : ChannelListener* const _listener;
552 : lunchbox::PtrHash<Channel*, uint32_t>& _taskIDs;
553 : };
554 : }
555 :
556 0 : void ViewEqualizer::Listener::update(Compound* compound)
557 : {
558 0 : LBASSERT(_taskIDs.empty());
559 0 : LoadSubscriber subscriber(this, _taskIDs);
560 0 : compound->accept(subscriber);
561 0 : }
562 :
563 0 : void ViewEqualizer::Listener::clear()
564 : {
565 0 : for (TaskIDHash::const_iterator i = _taskIDs.begin(); i != _taskIDs.end();
566 : ++i)
567 : {
568 0 : i->first->removeListener(this);
569 : }
570 0 : _taskIDs.clear();
571 0 : }
572 :
573 20 : ViewEqualizer::Listener::Load ViewEqualizer::Listener::Load::NONE(0, 0, 1);
574 20 : ViewEqualizer::Listener::Load::Load(const uint32_t frame_,
575 : const uint32_t missing_,
576 20 : const int64_t time_)
577 : : frame(frame_)
578 : , missing(missing_)
579 : , nResources(missing_)
580 20 : , time(time_)
581 : {
582 20 : }
583 :
584 0 : bool ViewEqualizer::Listener::Load::operator==(const Load& rhs) const
585 : {
586 0 : return (frame == rhs.frame && missing == rhs.missing && time == rhs.time);
587 : }
588 :
589 0 : void ViewEqualizer::Listener::notifyLoadData(Channel* channel,
590 : const uint32_t frameNumber,
591 : const Statistics& statistics,
592 : const Viewport& /*region*/)
593 : {
594 0 : Load& load = _getLoad(frameNumber);
595 0 : if (load == Load::NONE)
596 0 : return;
597 :
598 0 : LBASSERT(_taskIDs.find(channel) != _taskIDs.end());
599 0 : const uint32_t taskID = _taskIDs[channel];
600 :
601 : // gather relevant load data
602 0 : int64_t startTime = std::numeric_limits<int64_t>::max();
603 0 : int64_t endTime = 0;
604 0 : bool loadSet = false;
605 0 : int64_t transmitTime = 0;
606 0 : for (size_t i = 0; i < statistics.size() && !loadSet; ++i)
607 : {
608 0 : const Statistic& data = statistics[i];
609 0 : if (data.task != taskID) // data from another compound
610 0 : continue;
611 :
612 0 : switch (data.type)
613 : {
614 : case Statistic::CHANNEL_CLEAR:
615 : case Statistic::CHANNEL_DRAW:
616 : case Statistic::CHANNEL_READBACK:
617 0 : startTime = LB_MIN(startTime, data.startTime);
618 0 : endTime = LB_MAX(endTime, data.endTime);
619 0 : break;
620 :
621 : case Statistic::CHANNEL_ASYNC_READBACK:
622 : case Statistic::CHANNEL_FRAME_TRANSMIT:
623 0 : transmitTime += data.startTime - data.endTime;
624 0 : break;
625 : case Statistic::CHANNEL_FRAME_WAIT_SENDTOKEN:
626 0 : transmitTime -= data.endTime - data.startTime;
627 0 : break;
628 :
629 : // assemble blocks on input frames, stop using subsequent data
630 : case Statistic::CHANNEL_ASSEMBLE:
631 0 : loadSet = true;
632 0 : break;
633 :
634 : default:
635 0 : break;
636 : }
637 : }
638 :
639 0 : if (startTime == std::numeric_limits<int64_t>::max())
640 0 : return;
641 :
642 0 : LBASSERTINFO(load.missing > 0, load << " for " << channel->getName() << " "
643 : << channel->getSerial());
644 :
645 0 : const int64_t time = LB_MAX(endTime - startTime, transmitTime);
646 0 : load.time += time;
647 0 : --load.missing;
648 :
649 0 : if (load.missing == 0)
650 : {
651 0 : const float rTime = float(load.time) / float(load.nResources);
652 0 : load.time = int64_t(rTime * sqrtf(float(load.nResources)));
653 : }
654 :
655 0 : LBLOG(LOG_LB1) << "Task " << taskID << ", added time " << time << " to "
656 0 : << load << " from " << channel->getName() << " "
657 0 : << channel->getSerial() << std::endl;
658 : }
659 :
660 0 : uint32_t ViewEqualizer::Listener::findYoungestLoad(const uint32_t frame) const
661 : {
662 0 : for (LoadDeque::const_iterator i = _loads.begin(); i != _loads.end(); ++i)
663 : {
664 0 : const Load& load = *i;
665 0 : if (load.missing == 0 && load.frame <= frame)
666 0 : return load.frame;
667 : }
668 0 : return 0;
669 : }
670 :
671 0 : const ViewEqualizer::Listener::Load& ViewEqualizer::Listener::useLoad(
672 : const uint32_t frame)
673 : {
674 0 : for (LoadDeque::iterator i = _loads.begin(); i != _loads.end(); ++i)
675 : {
676 0 : Load& load = *i;
677 0 : if (load.frame == frame)
678 : {
679 0 : LBASSERT(load.missing == 0);
680 0 : if (load.time == 0)
681 0 : load.time = 1;
682 :
683 0 : ++i;
684 0 : _loads.erase(i, _loads.end());
685 0 : return load;
686 : }
687 : }
688 :
689 0 : return Load::NONE;
690 : }
691 :
692 0 : ViewEqualizer::Listener::Load& ViewEqualizer::Listener::_getLoad(
693 : const uint32_t frame)
694 : {
695 0 : for (LoadDeque::iterator i = _loads.begin(); i != _loads.end(); ++i)
696 : {
697 0 : const Load& load = *i;
698 0 : if (load.frame == frame)
699 0 : return *i;
700 : }
701 :
702 0 : return Load::NONE;
703 : }
704 :
705 0 : void ViewEqualizer::Listener::newLoad(const uint32_t frameNumber,
706 : const uint32_t nChannels)
707 : {
708 0 : LBASSERT(nChannels > 0);
709 0 : _loads.push_front(Load(frameNumber, nChannels, 0));
710 0 : }
711 :
712 16 : std::ostream& operator<<(std::ostream& os, const ViewEqualizer* equalizer)
713 : {
714 16 : if (equalizer)
715 16 : os << "view_equalizer {}" << std::endl;
716 16 : return os;
717 : }
718 :
719 0 : std::ostream& operator<<(std::ostream& os,
720 : const ViewEqualizer::Listener& listener)
721 : {
722 0 : os << lunchbox::disableFlush << "Listener" << std::endl << lunchbox::indent;
723 0 : for (ViewEqualizer::Listener::LoadDeque::const_iterator i =
724 0 : listener._loads.begin();
725 0 : i != listener._loads.end(); ++i)
726 : {
727 0 : os << *i << std::endl;
728 : }
729 0 : os << lunchbox::exdent << lunchbox::enableFlush;
730 0 : return os;
731 : }
732 :
733 0 : std::ostream& operator<<(std::ostream& os,
734 : const ViewEqualizer::Listener::Load& load)
735 : {
736 0 : os << "frame " << load.frame << " missing " << load.missing << " t "
737 0 : << load.time;
738 0 : return os;
739 : }
740 : }
741 60 : }
|