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