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