Line data Source code
1 :
2 : /* Copyright (c) 2005-2015, Stefan Eilemann <eile@equalizergraphics.com>
3 : * Daniel Nachbaur <danielnachbaur@gmail.com>
4 : * 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 "config.h"
21 :
22 : #include "canvas.h"
23 : #include "channel.h"
24 : #include "client.h"
25 : #include "configEvent.h"
26 : #include "configStatistics.h"
27 : #include "eventICommand.h"
28 : #include "global.h"
29 : #include "layout.h"
30 : #include "log.h"
31 : #include "messagePump.h"
32 : #include "node.h"
33 : #include "nodeFactory.h"
34 : #include "observer.h"
35 : #include "pipe.h"
36 : #include "server.h"
37 : #include "view.h"
38 : #include "window.h"
39 :
40 : #include <eq/fabric/commands.h>
41 : #include <eq/fabric/task.h>
42 :
43 : #include <co/object.h>
44 : #include <co/connectionDescription.h>
45 : #include <co/global.h>
46 :
47 : #include <lunchbox/clock.h>
48 : #include <lunchbox/monitor.h>
49 : #include <lunchbox/scopedMutex.h>
50 : #include <lunchbox/spinLock.h>
51 : #include <pression/plugins/compressor.h>
52 :
53 : #ifdef EQUALIZER_USE_GLSTATS
54 : # include <GLStats/GLStats.h>
55 : #else
56 : namespace GLStats { class Data {} _fakeStats; }
57 : #endif
58 :
59 : #include "exitVisitor.h"
60 : #include "frameVisitor.h"
61 : #include "initVisitor.h"
62 :
63 : #ifdef EQUALIZER_USE_QT5WIDGETS
64 : # include <QApplication>
65 : #endif
66 :
67 : namespace eq
68 : {
69 : namespace
70 : {
71 : /** A proxy object to keep master data within latency. */
72 0 : class LatencyObject : public co::Object
73 : {
74 : public:
75 0 : LatencyObject( const ChangeType type, const uint32_t compressor,
76 : const uint32_t frame )
77 : : frameNumber( frame ), _changeType( type )
78 0 : , _compressor( compressor ) {}
79 :
80 : const uint32_t frameNumber;
81 :
82 : protected:
83 0 : virtual ChangeType getChangeType() const { return _changeType; }
84 0 : virtual void getInstanceData( co::DataOStream& ){ LBDONTCALL }
85 0 : virtual void applyInstanceData( co::DataIStream& ){ LBDONTCALL }
86 0 : virtual uint32_t chooseCompressor() const { return _compressor; }
87 :
88 : private:
89 : const ChangeType _changeType;
90 : const uint32_t _compressor;
91 : };
92 : #ifdef EQUALIZER_USE_GLSTATS
93 : namespace
94 : {
95 : enum
96 : {
97 : THREAD_MAIN,
98 : THREAD_ASYNC1,
99 : THREAD_ASYNC2,
100 : };
101 : }
102 : #endif
103 : }
104 :
105 : namespace detail
106 : {
107 : class Config
108 : {
109 : public:
110 12 : Config()
111 : : eventQueue( co::Global::getCommandQueueLimit( ))
112 : , currentFrame( 0 )
113 : , unlockedFrame( 0 )
114 : , finishedFrame( 0 )
115 12 : , running( false )
116 : {
117 12 : lunchbox::Log::setClock( &clock );
118 12 : }
119 :
120 2 : ~Config()
121 2 : {
122 2 : appNode = 0;
123 2 : lunchbox::Log::setClock( 0 );
124 2 : }
125 :
126 : /** The node running the application thread. */
127 : co::NodePtr appNode;
128 :
129 : /** The receiver->app thread event queue. */
130 : CommandQueue eventQueue;
131 :
132 : /** The last received event to be released. */
133 : co::ICommand lastEvent;
134 :
135 : /** The connections configured by the server for this config. */
136 : co::Connections connections;
137 :
138 : #ifdef EQUALIZER_USE_GLSTATS
139 : /** Global statistics data. */
140 : lunchbox::Lockable< GLStats::Data, lunchbox::SpinLock > statistics;
141 : #endif
142 :
143 : /** The last started frame. */
144 : uint32_t currentFrame;
145 : /** The last locally released frame. */
146 : uint32_t unlockedFrame;
147 : /** The last completed frame. */
148 : lunchbox::Monitor< uint32_t > finishedFrame;
149 :
150 : /** The global clock. */
151 : lunchbox::Clock clock;
152 :
153 : std::deque< int64_t > frameTimes; //!< Start time of last frames
154 :
155 : /** list of the current latency object */
156 : typedef std::vector< LatencyObject* > LatencyObjects;
157 :
158 : /** protected list of the current latency object */
159 : lunchbox::Lockable< LatencyObjects, lunchbox::SpinLock > latencyObjects;
160 :
161 : /** true while the config is initialized and no window has exited. */
162 : bool running;
163 :
164 : /** Errors from last call to update() */
165 : Errors errors;
166 : };
167 : }
168 :
169 : /** @cond IGNORE */
170 : typedef co::CommandFunc<Config> ConfigFunc;
171 : /** @endcond */
172 :
173 12 : Config::Config( ServerPtr server )
174 : : Super( server )
175 12 : , _impl( new detail::Config )
176 12 : {}
177 :
178 6 : Config::~Config()
179 : {
180 2 : LBASSERT( getObservers().empty( ));
181 2 : LBASSERT( getLayouts().empty( ));
182 2 : LBASSERT( getCanvases().empty( ));
183 2 : LBASSERT( getNodes().empty( ));
184 2 : LBASSERT( _impl->latencyObjects->empty() );
185 :
186 2 : _impl->eventQueue.flush();
187 2 : delete _impl;
188 4 : }
189 :
190 2 : void Config::attach( const uint128_t& id, const uint32_t instanceID )
191 : {
192 2 : Super::attach( id, instanceID );
193 :
194 2 : co::CommandQueue* queue = getMainThreadQueue();
195 :
196 : registerCommand( fabric::CMD_CONFIG_CREATE_NODE,
197 2 : ConfigFunc( this, &Config::_cmdCreateNode ), queue );
198 : registerCommand( fabric::CMD_CONFIG_DESTROY_NODE,
199 2 : ConfigFunc( this, &Config::_cmdDestroyNode ), queue );
200 : registerCommand( fabric::CMD_CONFIG_INIT_REPLY,
201 2 : ConfigFunc( this, &Config::_cmdInitReply ), queue );
202 : registerCommand( fabric::CMD_CONFIG_EXIT_REPLY,
203 2 : ConfigFunc( this, &Config::_cmdExitReply ), queue );
204 : registerCommand( fabric::CMD_CONFIG_UPDATE_VERSION,
205 2 : ConfigFunc( this, &Config::_cmdUpdateVersion ), 0 );
206 : registerCommand( fabric::CMD_CONFIG_UPDATE_REPLY,
207 2 : ConfigFunc( this, &Config::_cmdUpdateReply ), queue );
208 : registerCommand( fabric::CMD_CONFIG_RELEASE_FRAME_LOCAL,
209 2 : ConfigFunc( this, &Config::_cmdReleaseFrameLocal ), queue);
210 : registerCommand( fabric::CMD_CONFIG_FRAME_FINISH,
211 2 : ConfigFunc( this, &Config::_cmdFrameFinish ), 0 );
212 : registerCommand( fabric::CMD_CONFIG_EVENT_OLD, ConfigFunc( 0, 0 ),
213 2 : &_impl->eventQueue );
214 : registerCommand( fabric::CMD_CONFIG_EVENT, ConfigFunc( 0, 0 ),
215 2 : &_impl->eventQueue );
216 : registerCommand( fabric::CMD_CONFIG_SYNC_CLOCK,
217 2 : ConfigFunc( this, &Config::_cmdSyncClock ), 0 );
218 : registerCommand( fabric::CMD_CONFIG_SWAP_OBJECT,
219 2 : ConfigFunc( this, &Config::_cmdSwapObject ), 0 );
220 2 : }
221 :
222 2 : void Config::notifyAttached()
223 : {
224 2 : fabric::Object::notifyAttached();
225 2 : LBASSERT( !_impl->appNode )
226 2 : LBASSERT( getAppNodeID().isUUID() )
227 2 : co::LocalNodePtr localNode = getLocalNode();
228 2 : _impl->appNode = localNode->connect( getAppNodeID( ));
229 2 : if( !_impl->appNode )
230 0 : LBWARN << "Connection to application node failed -- misconfigured "
231 0 : << "connections on appNode?" << std::endl;
232 2 : }
233 :
234 2 : void Config::notifyDetach()
235 : {
236 : {
237 2 : ClientPtr client = getClient();
238 4 : lunchbox::ScopedFastWrite mutex( _impl->latencyObjects );
239 4 : while( !_impl->latencyObjects->empty() )
240 : {
241 0 : LatencyObject* latencyObject = _impl->latencyObjects->back();
242 0 : _impl->latencyObjects->pop_back();
243 0 : client->deregisterObject( latencyObject );
244 0 : delete latencyObject;
245 0 : latencyObject = 0;
246 2 : }
247 : }
248 :
249 2 : getClient()->removeListeners( _impl->connections );
250 2 : _impl->connections.clear();
251 2 : _exitMessagePump();
252 2 : Super::notifyDetach();
253 2 : }
254 :
255 8 : co::CommandQueue* Config::getMainThreadQueue()
256 : {
257 8 : return getServer()->getMainThreadQueue();
258 : }
259 :
260 1 : co::CommandQueue* Config::getCommandThreadQueue()
261 : {
262 1 : return getServer()->getCommandThreadQueue();
263 : }
264 :
265 22 : ClientPtr Config::getClient()
266 : {
267 22 : return getServer()->getClient();
268 : }
269 :
270 12 : ConstClientPtr Config::getClient() const
271 : {
272 12 : return getServer()->getClient();
273 : }
274 :
275 3 : co::NodePtr Config::getApplicationNode()
276 : {
277 3 : return _impl->appNode;
278 : }
279 :
280 1 : bool Config::init( const uint128_t& initID )
281 : {
282 1 : LBASSERT( !_impl->running );
283 1 : _impl->currentFrame = 0;
284 1 : _impl->unlockedFrame = 0;
285 1 : _impl->finishedFrame = 0;
286 1 : _impl->frameTimes.clear();
287 :
288 1 : ClientPtr client = getClient();
289 1 : detail::InitVisitor initVisitor( client->getActiveLayouts(),
290 3 : client->getModelUnit( ));
291 1 : if( accept( initVisitor ) == TRAVERSE_TERMINATE )
292 : {
293 0 : LBWARN << "Application-local initialization failed" << std::endl;
294 0 : return false;
295 : }
296 1 : if( initVisitor.needsUpdate( ))
297 0 : update();
298 :
299 2 : co::LocalNodePtr localNode = getLocalNode();
300 2 : lunchbox::Request< bool > request = localNode->registerRequest< bool >();
301 1 : send( getServer(), fabric::CMD_CONFIG_INIT ) << initID << request;
302 :
303 9 : while( !request.isReady( ))
304 : {
305 : #ifdef EQUALIZER_USE_QT5WIDGETS
306 7 : if( QApplication::instance( ))
307 0 : QApplication::instance()->processEvents();
308 : #endif
309 7 : client->processCommand();
310 : }
311 :
312 1 : _impl->running = request.wait();
313 1 : localNode->enableSendOnRegister();
314 :
315 1 : handleEvents();
316 1 : if( !_impl->running )
317 3 : LBWARN << "Config initialization failed" << std::endl
318 3 : << " Consult client log for further information" << std::endl;
319 2 : return _impl->running;
320 : }
321 :
322 0 : bool Config::exit()
323 : {
324 0 : update();
325 0 : finishAllFrames();
326 :
327 0 : co::LocalNodePtr localNode = getLocalNode();
328 0 : localNode->disableSendOnRegister();
329 :
330 0 : lunchbox::Request< bool > request = localNode->registerRequest< bool >();
331 0 : send( getServer(), fabric::CMD_CONFIG_EXIT ) << request;
332 :
333 0 : ClientPtr client = getClient();
334 0 : while( !request.isReady( ))
335 0 : client->processCommand();
336 0 : bool ret = request.wait();
337 :
338 0 : detail::ExitVisitor exitVisitor;
339 0 : if( accept( exitVisitor ) == TRAVERSE_TERMINATE )
340 : {
341 0 : LBWARN << "Application-local de-initialization failed" << std::endl;
342 0 : ret = false;
343 : }
344 0 : _impl->lastEvent.clear();
345 0 : _impl->eventQueue.flush();
346 0 : _impl->running = false;
347 0 : return ret;
348 : }
349 :
350 0 : bool Config::update()
351 : {
352 0 : commit( CO_COMMIT_NEXT );
353 :
354 : // send update req to server
355 0 : ClientPtr client = getClient();
356 :
357 : lunchbox::Request< uint128_t > reqVersion =
358 0 : client->registerRequest< uint128_t >();
359 : lunchbox::Request< uint32_t > reqFinish =
360 0 : client->registerRequest< uint32_t >();
361 0 : lunchbox::Request< bool > request = client->registerRequest< bool >();
362 : send( getServer(), fabric::CMD_CONFIG_UPDATE )
363 0 : << reqVersion << reqFinish << request;
364 :
365 : // wait for new version
366 0 : const uint128_t& version = reqVersion.wait();
367 0 : const uint32_t finishID = reqFinish.wait();
368 :
369 0 : if( finishID == LB_UNDEFINED_UINT32 )
370 : {
371 0 : sync( version );
372 0 : request.unregister();
373 0 : handleEvents();
374 0 : return true;
375 : }
376 :
377 0 : client->disableSendOnRegister();
378 0 : while( _impl->finishedFrame < _impl->currentFrame )
379 0 : client->processCommand();
380 :
381 0 : sync( version );
382 0 : client->ackRequest( getServer(), finishID );
383 :
384 0 : while( !request.isReady( ))
385 0 : client->processCommand();
386 :
387 0 : const bool result = request.wait();
388 0 : client->enableSendOnRegister();
389 : #ifdef EQUALIZER_USE_GLSTATS
390 0 : _impl->statistics->clear();
391 : #endif
392 0 : handleEvents();
393 0 : return result;
394 : }
395 :
396 0 : uint32_t Config::startFrame( const uint128_t& frameID )
397 : {
398 : // Update
399 0 : ConfigStatistics stat( Statistic::CONFIG_START_FRAME, this );
400 0 : detail::FrameVisitor visitor( _impl->currentFrame + 1 );
401 0 : accept( visitor );
402 0 : update();
403 :
404 : // New frame
405 0 : ++_impl->currentFrame;
406 0 : send( getServer(), fabric::CMD_CONFIG_START_FRAME ) << frameID;
407 :
408 0 : LBLOG( LOG_TASKS ) << "---- Started Frame ---- " << _impl->currentFrame
409 0 : << std::endl;
410 0 : stat.event.data.statistic.frameNumber = _impl->currentFrame;
411 0 : return _impl->currentFrame;
412 : }
413 :
414 0 : void Config::_frameStart()
415 : {
416 0 : _impl->frameTimes.push_back( _impl->clock.getTime64( ));
417 0 : while( _impl->frameTimes.size() > getLatency() )
418 : {
419 0 : const int64_t age = _impl->frameTimes.back() -_impl->frameTimes.front();
420 0 : getClient()->expireInstanceData( age );
421 0 : _impl->frameTimes.pop_front();
422 : }
423 0 : }
424 :
425 0 : uint32_t Config::finishFrame()
426 : {
427 0 : ClientPtr client = getClient();
428 0 : const uint32_t latency = getLatency();
429 0 : const uint32_t frameToFinish = (_impl->currentFrame >= latency) ?
430 0 : _impl->currentFrame - latency : 0;
431 :
432 0 : ConfigStatistics stat( Statistic::CONFIG_FINISH_FRAME, this );
433 0 : stat.event.data.statistic.frameNumber = frameToFinish;
434 : {
435 0 : ConfigStatistics waitStat( Statistic::CONFIG_WAIT_FINISH_FRAME, this );
436 0 : waitStat.event.data.statistic.frameNumber = frameToFinish;
437 :
438 : // local draw sync
439 0 : if( _needsLocalSync( ))
440 : {
441 0 : while( _impl->unlockedFrame < _impl->currentFrame )
442 0 : client->processCommand();
443 0 : LBLOG( LOG_TASKS ) << "Local frame sync " << _impl->currentFrame
444 0 : << std::endl;
445 : }
446 :
447 : // local node finish (frame-latency) sync
448 0 : const Nodes& nodes = getNodes();
449 0 : if( !nodes.empty( ))
450 : {
451 0 : LBASSERT( nodes.size() == 1 );
452 0 : const Node* node = nodes.front();
453 :
454 0 : while( node->getFinishedFrame() < frameToFinish )
455 0 : client->processCommand();
456 0 : LBLOG( LOG_TASKS ) << "Local total sync " << frameToFinish
457 0 : << " @ " << _impl->currentFrame << std::endl;
458 : }
459 :
460 : // global sync
461 0 : const uint32_t timeout = getTimeout();
462 0 : if( timeout == LB_TIMEOUT_INDEFINITE )
463 0 : _impl->finishedFrame.waitGE( frameToFinish );
464 : else
465 : {
466 0 : while( !_impl->finishedFrame.timedWaitGE( frameToFinish,
467 0 : timeout ))
468 : {
469 : send( getServer(), fabric::CMD_CONFIG_CHECK_FRAME )
470 0 : << frameToFinish;
471 : }
472 : }
473 0 : LBLOG( LOG_TASKS ) << "Global sync " << frameToFinish << " @ "
474 0 : << _impl->currentFrame << std::endl;
475 : }
476 :
477 0 : handleEvents();
478 0 : _updateStatistics();
479 0 : _releaseObjects();
480 :
481 0 : LBLOG( LOG_TASKS ) << "---- Finished Frame --- " << frameToFinish
482 0 : << " (" << _impl->currentFrame << ')' << std::endl;
483 0 : return frameToFinish;
484 : }
485 :
486 0 : uint32_t Config::finishAllFrames()
487 : {
488 0 : if( _impl->finishedFrame == _impl->currentFrame )
489 0 : return _impl->currentFrame;
490 :
491 0 : LBLOG( LOG_TASKS ) << "-- Finish All Frames --" << std::endl;
492 0 : send( getServer(), fabric::CMD_CONFIG_FINISH_ALL_FRAMES );
493 :
494 0 : ClientPtr client = getClient();
495 0 : const uint32_t timeout = getTimeout();
496 0 : while( _impl->finishedFrame < _impl->currentFrame )
497 0 : client->processCommand( timeout );
498 0 : handleEvents();
499 0 : _updateStatistics();
500 0 : _releaseObjects();
501 0 : LBLOG( LOG_TASKS ) << "-- Finished All Frames --" << std::endl;
502 0 : return _impl->currentFrame;
503 : }
504 :
505 0 : void Config::releaseFrameLocal( const uint32_t frameNumber )
506 : {
507 0 : _impl->unlockedFrame = frameNumber;
508 0 : }
509 :
510 0 : void Config::stopFrames()
511 : {
512 0 : send( getServer(), fabric::CMD_CONFIG_STOP_FRAMES );
513 0 : }
514 :
515 : namespace
516 : {
517 : class ChangeLatencyVisitor : public ConfigVisitor
518 : {
519 : public:
520 0 : explicit ChangeLatencyVisitor( const uint32_t latency )
521 0 : : _latency( latency ) {}
522 :
523 0 : virtual ~ChangeLatencyVisitor() {}
524 :
525 0 : VisitorResult visit( eq::View* view )
526 : {
527 0 : co::Object* userData = view->getUserData();
528 0 : if( userData && userData->isMaster( ))
529 0 : userData->setAutoObsolete( _latency + 1 );
530 0 : return TRAVERSE_CONTINUE;
531 : }
532 :
533 0 : VisitorResult visit( eq::Channel* channel )
534 : {
535 0 : channel->changeLatency( _latency );
536 0 : return TRAVERSE_CONTINUE;
537 : }
538 :
539 : private:
540 : const uint32_t _latency;
541 : };
542 : }
543 :
544 0 : void Config::setLatency( const uint32_t latency )
545 : {
546 0 : if( getLatency() == latency )
547 0 : return;
548 :
549 0 : Super::setLatency( latency );
550 0 : changeLatency( latency );
551 : }
552 :
553 0 : void Config::changeLatency( const uint32_t latency )
554 : {
555 0 : finishAllFrames();
556 0 : commit( CO_COMMIT_NEXT );
557 :
558 : // update views
559 0 : ChangeLatencyVisitor changeLatencyVisitor( latency );
560 0 : accept( changeLatencyVisitor );
561 0 : }
562 :
563 0 : void Config::sendEvent( ConfigEvent& event )
564 : {
565 0 : LBASSERT( event.data.type != Event::STATISTIC ||
566 : event.data.statistic.type != Statistic::NONE );
567 0 : LBASSERT( getAppNodeID() != 0 );
568 0 : LBASSERT( _impl->appNode );
569 :
570 : send( _impl->appNode, fabric::CMD_CONFIG_EVENT_OLD )
571 0 : << event.size << co::Array< void >( &event, event.size );
572 0 : }
573 :
574 : #ifndef EQ_2_0_API
575 0 : const ConfigEvent* Config::nextEvent()
576 : {
577 0 : EventICommand command = getNextEvent( LB_TIMEOUT_INDEFINITE );
578 0 : const ConfigEvent* newEvent = _convertEvent( command );
579 0 : return newEvent ? newEvent : nextEvent();
580 : }
581 :
582 0 : const ConfigEvent* Config::tryNextEvent()
583 : {
584 0 : EventICommand command = getNextEvent( 0 );
585 0 : if( !command.isValid( ))
586 0 : return 0;
587 0 : const ConfigEvent* newEvent = _convertEvent( command );
588 0 : return newEvent ? newEvent : tryNextEvent();
589 : }
590 : #endif
591 :
592 0 : const ConfigEvent* Config::_convertEvent( co::ObjectICommand command )
593 : {
594 0 : LBASSERT( command.isValid( ));
595 :
596 0 : if( command.getCommand() != fabric::CMD_CONFIG_EVENT_OLD )
597 : {
598 0 : _impl->lastEvent.clear();
599 0 : return 0;
600 : }
601 :
602 0 : _impl->lastEvent = command;
603 0 : const uint64_t size = command.read< uint64_t >();
604 : return reinterpret_cast< const ConfigEvent* >
605 0 : ( command.getRemainingBuffer( size ));
606 : }
607 :
608 0 : bool Config::handleEvent( const ConfigEvent* event )
609 : {
610 0 : return _handleEvent( event->data );
611 : }
612 :
613 0 : EventOCommand Config::sendEvent( const uint32_t type )
614 : {
615 0 : LBASSERT( getAppNodeID() != 0 );
616 0 : LBASSERT( _impl->appNode );
617 :
618 0 : EventOCommand cmd( send( _impl->appNode, fabric::CMD_CONFIG_EVENT ));
619 0 : cmd << type;
620 0 : return cmd;
621 : }
622 :
623 3 : EventOCommand Config::sendError( const uint32_t type, const Error& error )
624 : {
625 3 : return Super::sendError( getApplicationNode(), type, error );
626 : }
627 :
628 2 : Errors Config::getErrors()
629 : {
630 2 : Errors errors;
631 2 : errors.swap(_impl->errors );
632 2 : return errors;
633 : }
634 :
635 5 : EventICommand Config::getNextEvent( const uint32_t timeout ) const
636 : {
637 5 : if( timeout == 0 )
638 5 : return _impl->eventQueue.tryPop();
639 0 : return _impl->eventQueue.pop( timeout );
640 : }
641 :
642 4 : bool Config::handleEvent( EventICommand command )
643 : {
644 4 : switch( command.getCommand( ))
645 : {
646 : case fabric::CMD_CONFIG_EVENT_OLD:
647 : {
648 0 : const ConfigEvent* configEvent = _convertEvent( command );
649 0 : LBASSERT( configEvent );
650 0 : if( configEvent )
651 0 : return handleEvent( configEvent );
652 0 : return false;
653 : }
654 :
655 : default:
656 0 : LBUNIMPLEMENTED;
657 : // no break;
658 : case fabric::CMD_CONFIG_EVENT:
659 4 : return _handleNewEvent( command );
660 : }
661 : }
662 :
663 0 : bool Config::checkEvent() const
664 : {
665 0 : return !_impl->eventQueue.isEmpty();
666 : }
667 :
668 5 : void Config::handleEvents()
669 : {
670 : for( ;; )
671 : {
672 5 : EventICommand event = getNextEvent( 0 );
673 5 : if( !event.isValid( ))
674 1 : break;
675 :
676 4 : handleEvent( event );
677 4 : }
678 : #ifdef EQUALIZER_USE_QT5WIDGETS
679 1 : if( QApplication::instance( ))
680 0 : QApplication::instance()->processEvents();
681 : #endif
682 1 : }
683 :
684 4 : bool Config::_handleNewEvent( EventICommand& command )
685 : {
686 4 : switch( command.getEventType( ))
687 : {
688 : case Event::OBSERVER_MOTION:
689 : {
690 0 : const uint128_t& originator = command.read< uint128_t >();
691 0 : LBASSERT( originator != 0 );
692 0 : Observer* observer = find< Observer >( originator );
693 0 : if( observer )
694 0 : return observer->handleEvent( command );
695 0 : break;
696 : }
697 :
698 : case Event::NODE_ERROR:
699 : case Event::PIPE_ERROR:
700 : case Event::WINDOW_ERROR:
701 : case Event::CHANNEL_ERROR:
702 : {
703 3 : const Error& error = command.read< Error >();
704 3 : LBWARN << error;
705 3 : if( error.getCode() < ERROR_CUSTOM )
706 : {
707 8 : while( command.hasData( ))
708 : {
709 2 : const std::string& text = command.read< std::string >();
710 2 : LBWARN << ": " << text;
711 2 : }
712 : }
713 3 : LBWARN << std::endl;
714 3 : _impl->errors.push_back( error );
715 3 : return false;
716 : }
717 : }
718 1 : return false;
719 : }
720 :
721 0 : bool Config::_handleEvent( const Event& event )
722 : {
723 0 : switch( event.type )
724 : {
725 : case Event::EXIT:
726 : case Event::WINDOW_CLOSE:
727 0 : _impl->running = false;
728 0 : return true;
729 :
730 : case Event::KEY_PRESS:
731 0 : if( event.keyPress.key == KC_ESCAPE )
732 : {
733 0 : _impl->running = false;
734 0 : return true;
735 : }
736 0 : break;
737 :
738 : case Event::WINDOW_POINTER_BUTTON_PRESS:
739 : case Event::CHANNEL_POINTER_BUTTON_PRESS:
740 0 : if( event.pointerButtonPress.buttons ==
741 : ( PTR_BUTTON1 | PTR_BUTTON2 | PTR_BUTTON3 ))
742 : {
743 0 : _impl->running = false;
744 0 : return true;
745 : }
746 0 : break;
747 :
748 : case Event::STATISTIC:
749 0 : LBLOG( LOG_STATS ) << event << std::endl;
750 0 : addStatistic( event.serial, event.statistic );
751 0 : break;
752 :
753 : case Event::VIEW_RESIZE:
754 : {
755 0 : LBASSERT( event.originator != 0 );
756 0 : View* view = find< View >( event.originator );
757 0 : if( view )
758 0 : return view->handleEvent( event );
759 0 : break;
760 : }
761 : }
762 :
763 0 : return false;
764 : }
765 :
766 0 : void Config::addStatistic( const uint32_t originator LB_UNUSED,
767 : const Statistic& stat LB_UNUSED )
768 : {
769 : #ifdef EQUALIZER_USE_GLSTATS
770 0 : const uint32_t frame = stat.frameNumber;
771 0 : LBASSERT( stat.type != Statistic::NONE );
772 :
773 : // Not a frame-related stat event OR no event-type set
774 0 : if( frame == 0 || stat.type == Statistic::NONE )
775 0 : return;
776 :
777 0 : lunchbox::ScopedFastWrite mutex( _impl->statistics );
778 0 : GLStats::Item item;
779 0 : item.entity = originator;
780 0 : item.type = stat.type;
781 0 : item.frame = stat.frameNumber;
782 0 : item.start = stat.startTime;
783 0 : item.end = stat.endTime;
784 :
785 0 : GLStats::Entity entity;
786 0 : entity.name = stat.resourceName;
787 :
788 0 : GLStats::Type type;
789 0 : const Vector3f& color = Statistic::getColor( stat.type );
790 :
791 0 : type.color[0] = color[0];
792 0 : type.color[1] = color[1];
793 0 : type.color[2] = color[2];
794 0 : type.name = Statistic::getName( stat.type );
795 :
796 0 : switch( stat.type )
797 : {
798 : case Statistic::CHANNEL_FRAME_COMPRESS:
799 : case Statistic::CHANNEL_FRAME_WAIT_SENDTOKEN:
800 0 : type.subgroup = "transmit";
801 0 : item.thread = THREAD_ASYNC2;
802 : // no break;
803 : case Statistic::CHANNEL_FRAME_WAIT_READY:
804 0 : type.group = "channel";
805 0 : item.layer = 1;
806 0 : break;
807 : case Statistic::CHANNEL_CLEAR:
808 : case Statistic::CHANNEL_DRAW:
809 : case Statistic::CHANNEL_DRAW_FINISH:
810 : case Statistic::CHANNEL_ASSEMBLE:
811 : case Statistic::CHANNEL_READBACK:
812 : case Statistic::CHANNEL_VIEW_FINISH:
813 0 : type.group = "channel";
814 0 : break;
815 : case Statistic::CHANNEL_ASYNC_READBACK:
816 0 : type.group = "channel";
817 0 : type.subgroup = "transfer";
818 0 : item.thread = THREAD_ASYNC1;
819 0 : break;
820 : case Statistic::CHANNEL_FRAME_TRANSMIT:
821 0 : type.group = "channel";
822 0 : type.subgroup = "transmit";
823 0 : item.thread = THREAD_ASYNC2;
824 0 : break;
825 :
826 : case Statistic::WINDOW_FINISH:
827 : case Statistic::WINDOW_THROTTLE_FRAMERATE:
828 : case Statistic::WINDOW_SWAP_BARRIER:
829 : case Statistic::WINDOW_SWAP:
830 0 : type.group = "window";
831 0 : break;
832 : case Statistic::NODE_FRAME_DECOMPRESS:
833 0 : type.group = "node";
834 0 : break;
835 :
836 : case Statistic::CONFIG_WAIT_FINISH_FRAME:
837 0 : item.layer = 1;
838 : // no break;
839 : case Statistic::CONFIG_START_FRAME:
840 : case Statistic::CONFIG_FINISH_FRAME:
841 0 : type.group = "config";
842 0 : break;
843 :
844 : case Statistic::PIPE_IDLE:
845 : {
846 0 : const std::string& string = _impl->statistics->getText();
847 0 : const float idle = stat.idleTime * 100ll / stat.totalTime;
848 0 : std::stringstream text;
849 0 : if( string.empty( ))
850 0 : text << "Idle: " << stat.resourceName << ' ' << idle << "%";
851 : else
852 : {
853 0 : const size_t pos = string.find( stat.resourceName );
854 :
855 0 : if( pos == std::string::npos ) // append new pipe
856 0 : text << string << ", " << stat.resourceName << ' '
857 0 : << idle << "%";
858 : else // replace existing text
859 : {
860 0 : const std::string& left = string.substr( pos + 1 );
861 :
862 0 : text << string.substr( 0, pos ) << stat.resourceName << ' '
863 0 : << idle << left.substr( left.find( '%' ));
864 : }
865 : }
866 0 : _impl->statistics->setText( text.str( ));
867 : }
868 : // no break;
869 :
870 : case Statistic::WINDOW_FPS:
871 : case Statistic::NONE:
872 : case Statistic::ALL:
873 0 : return;
874 : }
875 0 : switch( stat.type )
876 : {
877 : case Statistic::CHANNEL_FRAME_COMPRESS:
878 : case Statistic::CHANNEL_ASYNC_READBACK:
879 : case Statistic::CHANNEL_READBACK:
880 : {
881 0 : std::stringstream text;
882 0 : text << unsigned( 100.f * stat.ratio ) << '%';
883 :
884 0 : if( stat.plugins[ 0 ] > EQ_COMPRESSOR_NONE )
885 0 : text << " 0x" << std::hex << stat.plugins[0] << std::dec;
886 0 : if( stat.plugins[ 1 ] > EQ_COMPRESSOR_NONE &&
887 0 : stat.plugins[ 0 ] != stat.plugins[ 1 ] )
888 : {
889 0 : text << " 0x" << std::hex << stat.plugins[1] << std::dec;
890 : }
891 0 : item.text = text.str();
892 0 : break;
893 : }
894 : default:
895 0 : break;
896 : }
897 :
898 0 : _impl->statistics->setType( stat.type, type );
899 0 : _impl->statistics->setEntity( originator, entity );
900 0 : _impl->statistics->addItem( item );
901 : #endif
902 : }
903 :
904 0 : bool Config::_needsLocalSync() const
905 : {
906 0 : const Nodes& nodes = getNodes();
907 0 : if( nodes.empty( ))
908 0 : return true; // server sends unlock command - process it
909 :
910 0 : LBASSERT( nodes.size() == 1 );
911 0 : const Node* node = nodes.front();
912 0 : switch( node->getIAttribute( Node::IATTR_THREAD_MODEL ))
913 : {
914 : case ASYNC:
915 0 : return false;
916 :
917 : case DRAW_SYNC:
918 0 : if( !(node->getTasks() & fabric::TASK_DRAW) )
919 0 : return false;
920 0 : break;
921 :
922 : case LOCAL_SYNC:
923 0 : if( node->getTasks() == fabric::TASK_NONE )
924 0 : return false;
925 0 : break;
926 :
927 : default:
928 0 : LBUNIMPLEMENTED;
929 : }
930 :
931 0 : return true;
932 : }
933 :
934 0 : void Config::_updateStatistics()
935 : {
936 : #ifdef EQUALIZER_USE_GLSTATS
937 : // keep statistics for three frames
938 0 : lunchbox::ScopedFastWrite mutex( _impl->statistics );
939 0 : _impl->statistics->obsolete( 2 /* frames to keep */ );
940 : #endif
941 0 : }
942 :
943 0 : GLStats::Data Config::getStatistics() const
944 : {
945 : #ifdef EQUALIZER_USE_GLSTATS
946 0 : lunchbox::ScopedFastRead mutex( _impl->statistics );
947 0 : return _impl->statistics.data;
948 : #else
949 : return GLStats::_fakeStats;
950 : #endif
951 : }
952 :
953 0 : uint32_t Config::getCurrentFrame() const
954 : {
955 0 : return _impl->currentFrame;
956 : }
957 :
958 0 : uint32_t Config::getFinishedFrame() const
959 : {
960 0 : return _impl->finishedFrame.get();
961 : }
962 :
963 1 : bool Config::isRunning() const
964 : {
965 1 : return _impl->running;
966 : }
967 :
968 0 : void Config::stopRunning()
969 : {
970 0 : _impl->running = false;
971 0 : }
972 :
973 0 : int64_t Config::getTime() const
974 : {
975 0 : return _impl->clock.getTime64();
976 : }
977 :
978 12 : bool Config::mapViewObjects() const
979 : {
980 : // only on application node...
981 12 : return ( getClient()->getNodeID() == getAppNodeID( ));
982 : }
983 :
984 1 : void Config::setupMessagePump( Pipe* pipe )
985 : {
986 1 : const bool isThreaded = pipe->isThreaded();
987 1 : const WindowSystem windowSystem = pipe->getWindowSystem();
988 :
989 1 : if( isThreaded && !windowSystem.hasMainThreadEvents( ))
990 2 : return;
991 :
992 : // called from pipe threads - but only during init
993 0 : static lunchbox::Lock _lock;
994 0 : lunchbox::ScopedWrite mutex( _lock );
995 :
996 0 : if( _impl->eventQueue.getMessagePump( )) // Already done
997 0 : return;
998 :
999 0 : MessagePump* pump = pipe->createMessagePump();
1000 0 : _impl->eventQueue.setMessagePump( pump );
1001 :
1002 0 : ClientPtr client = getClient();
1003 0 : CommandQueue* queue = LBSAFECAST( CommandQueue*,
1004 : client->getMainThreadQueue( ));
1005 0 : LBASSERT( queue );
1006 0 : LBASSERT( !queue->getMessagePump( ));
1007 :
1008 0 : queue->setMessagePump( pump );
1009 : }
1010 :
1011 2 : void Config::_exitMessagePump()
1012 : {
1013 2 : MessagePump* pump = _impl->eventQueue.getMessagePump();
1014 2 : _impl->eventQueue.setMessagePump( 0 );
1015 :
1016 2 : ClientPtr client = getClient();
1017 2 : CommandQueue* queue = LBSAFECAST( CommandQueue*,
1018 : client->getMainThreadQueue( ));
1019 2 : LBASSERT( queue );
1020 2 : LBASSERT( queue->getMessagePump() == pump );
1021 :
1022 2 : queue->setMessagePump( 0 );
1023 2 : delete pump;
1024 2 : }
1025 :
1026 0 : MessagePump* Config::getMessagePump()
1027 : {
1028 0 : ClientPtr client = getClient();
1029 0 : CommandQueue* queue = LBSAFECAST( CommandQueue*,
1030 : client->getMainThreadQueue( ));
1031 0 : if( queue )
1032 0 : return queue->getMessagePump();
1033 0 : return 0;
1034 : }
1035 :
1036 2 : void Config::setupServerConnections( const std::string& connectionData )
1037 : {
1038 2 : std::string data = connectionData;
1039 4 : co::ConnectionDescriptions descriptions;
1040 2 : LBCHECK( co::deserialize( data, descriptions ));
1041 2 : LBASSERTINFO( data.empty(), data << " left from " << connectionData );
1042 :
1043 6 : for( co::ConnectionDescriptionsCIter i = descriptions.begin();
1044 4 : i != descriptions.end(); ++i )
1045 : {
1046 0 : co::ConnectionPtr connection = getClient()->addListener( *i );
1047 0 : if( connection )
1048 0 : _impl->connections.push_back( connection );
1049 2 : }
1050 2 : }
1051 :
1052 0 : bool Config::registerObject( co::Object* object )
1053 : {
1054 0 : if( !getClient()->registerObject( object ))
1055 0 : return false;
1056 0 : object->setAutoObsolete( getLatency() + 1 );
1057 0 : return true;
1058 : }
1059 :
1060 0 : void Config::deregisterObject( co::Object* object )
1061 : {
1062 0 : LBASSERT( object )
1063 0 : LBASSERT( object->isMaster( ));
1064 :
1065 0 : if( !object->isAttached( )) // not registered
1066 0 : return;
1067 :
1068 0 : const uint32_t latency = getLatency();
1069 0 : ClientPtr client = getClient();
1070 0 : if( latency == 0 || !_impl->running || !object->isBuffered( )) // OPT
1071 : {
1072 0 : client->deregisterObject( object );
1073 0 : return;
1074 : }
1075 :
1076 : // Keep a distributed object latency frames.
1077 : // Replaces the object with a dummy proxy object using the
1078 : // existing master change manager.
1079 : const lunchbox::Request< void > request =
1080 0 : getLocalNode()->registerRequest< void >();
1081 0 : send( client, fabric::CMD_CONFIG_SWAP_OBJECT ) << request << object;
1082 : }
1083 :
1084 4 : bool Config::mapObject( co::Object* object, const uint128_t& id,
1085 : const uint128_t& version )
1086 : {
1087 4 : return mapObjectSync( mapObjectNB( object, id, version ));
1088 : }
1089 :
1090 4 : uint32_t Config::mapObjectNB( co::Object* object, const uint128_t& id,
1091 : const uint128_t& version )
1092 : {
1093 4 : return getClient()->mapObjectNB( object, id, version );
1094 : }
1095 :
1096 0 : uint32_t Config::mapObjectNB( co::Object* object, const uint128_t& id,
1097 : const uint128_t& version, co::NodePtr master )
1098 : {
1099 0 : return getClient()->mapObjectNB( object, id, version, master );
1100 : }
1101 :
1102 4 : bool Config::mapObjectSync( const uint32_t requestID )
1103 : {
1104 4 : return getClient()->mapObjectSync( requestID );
1105 : }
1106 :
1107 4 : void Config::unmapObject( co::Object* object )
1108 : {
1109 4 : getClient()->unmapObject( object );
1110 4 : }
1111 :
1112 0 : f_bool_t Config::syncObject( co::Object* object, const uint128_t& id,
1113 : co::NodePtr master, const uint32_t instanceID )
1114 : {
1115 0 : return getClient()->syncObject( object, id, master, instanceID );
1116 : }
1117 :
1118 0 : void Config::_releaseObjects()
1119 : {
1120 0 : ClientPtr client = getClient();
1121 :
1122 0 : lunchbox::ScopedFastWrite mutex( _impl->latencyObjects );
1123 0 : while( !_impl->latencyObjects->empty() )
1124 : {
1125 0 : LatencyObject* latencyObject = _impl->latencyObjects->front();
1126 0 : if( latencyObject->frameNumber > _impl->currentFrame )
1127 0 : break;
1128 :
1129 0 : client->deregisterObject( latencyObject );
1130 0 : _impl->latencyObjects->erase( _impl->latencyObjects->begin() );
1131 :
1132 0 : delete latencyObject;
1133 0 : }
1134 0 : }
1135 :
1136 : //---------------------------------------------------------------------------
1137 : // command handlers
1138 : //---------------------------------------------------------------------------
1139 1 : bool Config::_cmdCreateNode( co::ICommand& cmd )
1140 : {
1141 1 : co::ObjectICommand command( cmd );
1142 :
1143 1 : LBVERB << "Handle create node " << command << std::endl;
1144 :
1145 1 : Node* node = Global::getNodeFactory()->createNode( this );
1146 1 : LBCHECK( mapObject( node, command.read< uint128_t >( )));
1147 1 : return true;
1148 : }
1149 :
1150 1 : bool Config::_cmdDestroyNode( co::ICommand& cmd )
1151 : {
1152 1 : co::ObjectICommand command( cmd );
1153 :
1154 1 : LBVERB << "Handle destroy node " << command << std::endl;
1155 :
1156 1 : Node* node = _findNode( command.read< uint128_t >( ));
1157 1 : LBASSERT( node );
1158 1 : if( !node )
1159 0 : return true;
1160 :
1161 1 : const bool isStopped = node->isStopped();
1162 :
1163 1 : LBASSERT( node->getPipes().empty( ));
1164 1 : unmapObject( node );
1165 1 : node->send( getServer(), fabric::CMD_NODE_CONFIG_EXIT_REPLY ) << isStopped;
1166 1 : Global::getNodeFactory()->releaseNode( node );
1167 :
1168 1 : return true;
1169 : }
1170 :
1171 1 : bool Config::_cmdInitReply( co::ICommand& cmd )
1172 : {
1173 1 : co::ObjectICommand command( cmd );
1174 :
1175 1 : LBVERB << "handle init reply " << command << std::endl;
1176 :
1177 1 : const uint128_t& version = command.read< uint128_t >();
1178 1 : const uint32_t requestID = command.read< uint32_t >();
1179 1 : const bool result = command.read< bool >();
1180 :
1181 1 : sync( version );
1182 1 : getLocalNode()->serveRequest( requestID, result );
1183 1 : return true;
1184 : }
1185 :
1186 0 : bool Config::_cmdExitReply( co::ICommand& cmd )
1187 : {
1188 0 : co::ObjectICommand command( cmd );
1189 :
1190 0 : LBVERB << "handle exit reply " << command << std::endl;
1191 :
1192 0 : const uint32_t requestID = command.read< uint32_t >();
1193 0 : const bool result = command.read< bool >();
1194 :
1195 0 : _exitMessagePump();
1196 0 : getLocalNode()->serveRequest( requestID, result );
1197 0 : return true;
1198 : }
1199 :
1200 0 : bool Config::_cmdUpdateVersion( co::ICommand& cmd )
1201 : {
1202 0 : co::ObjectICommand command( cmd );
1203 0 : const uint128_t& version = command.read< uint128_t >();
1204 0 : const uint32_t versionID = command.read< uint32_t >();
1205 0 : const uint32_t finishID = command.read< uint32_t >();
1206 0 : const uint32_t requestID = command.read< uint32_t >();
1207 :
1208 0 : getClient()->serveRequest( versionID, version );
1209 0 : getClient()->serveRequest( finishID, requestID );
1210 0 : return true;
1211 : }
1212 :
1213 0 : bool Config::_cmdUpdateReply( co::ICommand& cmd )
1214 : {
1215 0 : co::ObjectICommand command( cmd );
1216 0 : const uint128_t& version = command.read< uint128_t >();
1217 0 : const uint32_t requestID = command.read< uint32_t >();
1218 0 : const bool result = command.read< bool >();
1219 :
1220 0 : sync( version );
1221 0 : getClient()->serveRequest( requestID, result );
1222 0 : return true;
1223 : }
1224 :
1225 0 : bool Config::_cmdReleaseFrameLocal( co::ICommand& cmd )
1226 : {
1227 0 : co::ObjectICommand command( cmd );
1228 :
1229 0 : _frameStart(); // never happened from node
1230 0 : releaseFrameLocal( command.read< uint32_t >( ));
1231 0 : return true;
1232 : }
1233 :
1234 0 : bool Config::_cmdFrameFinish( co::ICommand& cmd )
1235 : {
1236 0 : co::ObjectICommand command( cmd );
1237 :
1238 0 : _impl->finishedFrame = command.read< uint32_t >();
1239 :
1240 0 : LBLOG( LOG_TASKS ) << "frame finish " << command
1241 0 : << " frame " << _impl->finishedFrame << std::endl;
1242 :
1243 0 : if( _impl->unlockedFrame < _impl->finishedFrame.get( ))
1244 : {
1245 0 : LBWARN << "Finished frame " << _impl->unlockedFrame
1246 0 : << " was not locally unlocked, enforcing unlock" << std::endl;
1247 0 : _impl->unlockedFrame = _impl->finishedFrame.get();
1248 : }
1249 :
1250 0 : co::ICommand empty;
1251 0 : getMainThreadQueue()->push( empty ); // wakeup signal
1252 0 : return true;
1253 : }
1254 :
1255 0 : bool Config::_cmdSyncClock( co::ICommand& cmd )
1256 : {
1257 0 : co::ObjectICommand command( cmd );
1258 0 : const int64_t time = command.read< int64_t >();
1259 :
1260 0 : LBVERB << "sync global clock to " << time << ", drift "
1261 0 : << time - _impl->clock.getTime64() << std::endl;
1262 :
1263 0 : _impl->clock.set( time );
1264 0 : return true;
1265 : }
1266 :
1267 0 : bool Config::_cmdSwapObject( co::ICommand& cmd )
1268 : {
1269 0 : co::ObjectICommand command( cmd );
1270 0 : LBVERB << "Cmd swap object " << command << std::endl;
1271 :
1272 0 : const uint32_t requestID = command.read< uint32_t >();
1273 : co::Object* object =
1274 0 : reinterpret_cast< co::Object* >( command.read< void* >( ));
1275 :
1276 0 : LatencyObject* latencyObject = new LatencyObject( object->getChangeType(),
1277 0 : object->chooseCompressor(),
1278 0 : _impl->currentFrame + getLatency() + 1 );
1279 0 : getLocalNode()->swapObject( object, latencyObject );
1280 : {
1281 0 : lunchbox::ScopedFastWrite mutex( _impl->latencyObjects );
1282 0 : _impl->latencyObjects->push_back( latencyObject );
1283 : }
1284 0 : LBASSERT( requestID != LB_UNDEFINED_UINT32 );
1285 0 : getLocalNode()->serveRequest( requestID );
1286 0 : return true;
1287 : }
1288 : }
1289 :
1290 : #include <eq/fabric/config.ipp>
1291 : #include <eq/fabric/view.ipp>
1292 : #include <eq/fabric/observer.ipp>
1293 :
1294 : template class eq::fabric::Config< eq::Server, eq::Config, eq::Observer,
1295 : eq::Layout, eq::Canvas, eq::Node,
1296 : eq::ConfigVisitor >;
1297 :
1298 : /** @cond IGNORE */
1299 : template EQFABRIC_API std::ostream& eq::fabric::operator << ( std::ostream&,
1300 : const eq::Config::Super& );
1301 : /** @endcond */
1302 :
1303 : #define FIND_ID_TEMPLATE1( type ) \
1304 : template EQ_API void eq::Config::Super::find< type >( const uint128_t&, \
1305 : type** );
1306 :
1307 : FIND_ID_TEMPLATE1( eq::Window );
1308 : FIND_ID_TEMPLATE1( eq::Channel );
1309 : FIND_ID_TEMPLATE1( eq::Layout );
1310 : FIND_ID_TEMPLATE1( eq::Observer );
1311 : FIND_ID_TEMPLATE1( eq::Canvas );
1312 :
1313 : #define FIND_ID_TEMPLATE2( type ) \
1314 : template EQ_API type* eq::Config::Super::find< type >( const uint128_t& );
1315 :
1316 : FIND_ID_TEMPLATE2( eq::Window );
1317 : FIND_ID_TEMPLATE2( eq::Observer );
1318 : FIND_ID_TEMPLATE2( eq::Layout );
1319 : FIND_ID_TEMPLATE2( eq::View );
1320 : FIND_ID_TEMPLATE2( eq::Canvas );
1321 :
1322 : #define CONST_FIND_ID_TEMPLATE2( type ) \
1323 : template EQ_API const type* eq::Config::Super::find< type >( const uint128_t& ) const;
1324 :
1325 : CONST_FIND_ID_TEMPLATE2( eq::Window );
1326 : CONST_FIND_ID_TEMPLATE2( eq::Observer );
1327 : CONST_FIND_ID_TEMPLATE2( eq::Layout );
1328 : CONST_FIND_ID_TEMPLATE2( eq::View );
1329 : CONST_FIND_ID_TEMPLATE2( eq::Canvas );
1330 :
1331 : #define FIND_NAME_TEMPLATE1( type ) \
1332 : template EQ_API void \
1333 : eq::Config::Super::find< type >(const std::string&, const type** ) const;
1334 : FIND_NAME_TEMPLATE1( eq::Window );
1335 : FIND_NAME_TEMPLATE1( eq::Layout );
1336 : FIND_NAME_TEMPLATE1( eq::Observer );
1337 : FIND_NAME_TEMPLATE1( eq::Canvas );
1338 :
1339 :
1340 : #define CONST_FIND_NAME_TEMPLATE2( type ) \
1341 : template EQ_API const type* \
1342 : eq::Config::Super::find< type >( const std::string& ) const;
1343 :
1344 : CONST_FIND_NAME_TEMPLATE2( eq::Window );
1345 : CONST_FIND_NAME_TEMPLATE2( eq::Canvas );
1346 : CONST_FIND_NAME_TEMPLATE2( eq::Channel );
1347 : CONST_FIND_NAME_TEMPLATE2( eq::Layout );
1348 : CONST_FIND_NAME_TEMPLATE2( eq::Observer );
1349 : CONST_FIND_NAME_TEMPLATE2( eq::View );
1350 :
1351 : #define FIND_NAME_TEMPLATE2( type ) \
1352 : template EQ_API type* \
1353 : eq::Config::Super::find< type >( const std::string& );
1354 :
1355 : FIND_NAME_TEMPLATE2( eq::Window );
1356 : FIND_NAME_TEMPLATE2( eq::Canvas );
1357 : FIND_NAME_TEMPLATE2( eq::Channel );
1358 : FIND_NAME_TEMPLATE2( eq::Layout );
1359 : FIND_NAME_TEMPLATE2( eq::Observer );
1360 42 : FIND_NAME_TEMPLATE2( eq::View );
|