From 2c098fe0f9aa1fcd3a74002d77e31307eda8404a Mon Sep 17 00:00:00 2001 From: Raphael Dumusc Date: Mon, 16 Jan 2017 16:07:54 +0100 Subject: [PATCH] Add passive stereo support, upgrade to Deflect 0.13 --- .gitsubprojects | 2 +- doc/Changelog.md | 2 + examples/configuration_stereo.xml | 16 ++++ tests/cpp/core/ConfigurationTests.cpp | 41 +++++++++ tests/cpp/core/DeflectSerializationTests.cpp | 30 ++++++- tests/resources/CMakeLists.txt | 1 + tests/resources/configuration_stereo.xml | 16 ++++ tide/core/Configuration.cpp | 17 +++- tide/core/Configuration.h | 1 + tide/core/serialization/deflectTypes.h | 3 +- tide/master/MasterApplication.cpp | 86 +++++++++---------- tide/master/MasterConfiguration.cpp | 45 +++------- tide/master/network/MasterFromWallChannel.cpp | 2 + tide/wall/DataProvider.cpp | 6 +- tide/wall/DataProvider.h | 10 ++- tide/wall/PixelStreamUpdater.cpp | 20 ++++- tide/wall/PixelStreamUpdater.h | 14 +-- tide/wall/WallApplication.cpp | 6 +- tide/wall/WallConfiguration.cpp | 27 ++++-- tide/wall/WallConfiguration.h | 8 +- tide/wall/WallWindow.cpp | 2 +- 21 files changed, 247 insertions(+), 108 deletions(-) create mode 100644 examples/configuration_stereo.xml create mode 100644 tests/resources/configuration_stereo.xml diff --git a/.gitsubprojects b/.gitsubprojects index e7f3f09f..80ddf54a 100644 --- a/.gitsubprojects +++ b/.gitsubprojects @@ -1,5 +1,5 @@ # -*- mode: cmake -*- git_subproject(ZeroEQ https://github.com/HBPVIS/ZeroEQ.git 505caf8) -git_subproject(Deflect https://github.com/BlueBrain/Deflect.git 7ebb0a5) +git_subproject(Deflect https://github.com/BlueBrain/Deflect.git f9ad032) git_subproject(TUIO https://github.com/BlueBrain/TUIO.git 67a65ad) git_subproject(VirtualKeyboard https://github.com/rdumusc/QtFreeVirtualKeyboard.git b2f79e3) diff --git a/doc/Changelog.md b/doc/Changelog.md index d0d2d949..81428799 100644 --- a/doc/Changelog.md +++ b/doc/Changelog.md @@ -3,6 +3,8 @@ Changelog {#changelog} # Release 1.3 (git master) +* [122](https://github.com/BlueBrain/Tide/pull/122): + Added passive stereo rendering mode for pixel streams sent by Deflect 0.13. * [121](https://github.com/BlueBrain/Tide/pull/121): Pretty print FFMPEG log messages using Tide log style and reduced verbosity. * [119](https://github.com/BlueBrain/Tide/pull/119): diff --git a/examples/configuration_stereo.xml b/examples/configuration_stereo.xml new file mode 100644 index 00000000..ed98141f --- /dev/null +++ b/examples/configuration_stereo.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/tests/cpp/core/ConfigurationTests.cpp b/tests/cpp/core/ConfigurationTests.cpp index c6a99b36..c20279af 100644 --- a/tests/cpp/core/ConfigurationTests.cpp +++ b/tests/cpp/core/ConfigurationTests.cpp @@ -50,6 +50,7 @@ #define CONFIG_TEST_FILENAME "./configuration.xml" #define CONFIG_TEST_FILENAME_II "./configuration_default.xml" +#define CONFIG_TEST_FILENAME_STEREO "./configuration_stereo.xml" #define CONFIG_EXPECTED_BACKGROUND "/nfs4/bbp.epfl.ch/visualization/DisplayWall/media/background.png" #define CONFIG_EXPECTED_BACKGROUND_COLOR "#242424" @@ -117,6 +118,46 @@ BOOST_AUTO_TEST_CASE( test_wall_configuration ) BOOST_CHECK_EQUAL( config.getProcessCountForHost(), 3 ); BOOST_CHECK_EQUAL( config.getGlobalScreenIndex(), QPoint( 0, 2 )); BOOST_CHECK_EQUAL( config.getWindowPos(), QPoint( 0, 2160 )); + BOOST_CHECK( config.getStereoMode() == deflect::View::mono ); +} + +BOOST_AUTO_TEST_CASE( test_stereo_configuration ) +{ + Configuration config( CONFIG_TEST_FILENAME_STEREO ); + + BOOST_CHECK_EQUAL( config.getMullionHeight(), 0 ); + BOOST_CHECK_EQUAL( config.getMullionWidth(), -60 ); + + BOOST_CHECK_EQUAL( config.getScreenWidth(), 1920 ); + BOOST_CHECK_EQUAL( config.getScreenHeight(), 1200 ); + + BOOST_CHECK_EQUAL( config.getTotalWidth(), 1920 * 2 - 60 ); + BOOST_CHECK_EQUAL( config.getTotalHeight(), 1200 ); + + BOOST_CHECK_EQUAL( config.getTotalScreenCountX(), 2 ); + BOOST_CHECK_EQUAL( config.getTotalScreenCountY(), 1 ); + + const auto processIndexLeft = 1; // note: starts from 1, not 0 + WallConfiguration configLeft( CONFIG_TEST_FILENAME_STEREO, processIndexLeft ); + BOOST_REQUIRE_EQUAL( configLeft.getProcessIndex(), processIndexLeft ); + + BOOST_CHECK_EQUAL( configLeft.getHost().toStdString(), "localhost" ); + BOOST_CHECK_EQUAL( configLeft.getDisplay().toStdString(), ":0.0" ); + BOOST_CHECK_EQUAL( configLeft.getProcessCountForHost(), 4 ); + BOOST_CHECK_EQUAL( configLeft.getGlobalScreenIndex(), QPoint( 0, 0 )); + BOOST_CHECK_EQUAL( configLeft.getWindowPos(), QPoint( 0, 0 )); + BOOST_CHECK( configLeft.getStereoMode() == deflect::View::left_eye ); + + const auto processIndexRight = 2; // note: starts from 1, not 0 + WallConfiguration configRight( CONFIG_TEST_FILENAME_STEREO, processIndexRight ); + BOOST_REQUIRE_EQUAL( configRight.getProcessIndex(), processIndexRight ); + + BOOST_CHECK_EQUAL( configRight.getHost().toStdString(), "localhost" ); + BOOST_CHECK_EQUAL( configRight.getDisplay().toStdString(), ":0.1" ); + BOOST_CHECK_EQUAL( configRight.getProcessCountForHost(), 4 ); + BOOST_CHECK_EQUAL( configRight.getGlobalScreenIndex(), QPoint( 0, 0 )); + BOOST_CHECK_EQUAL( configRight.getWindowPos(), QPoint( 0, 0 )); + BOOST_CHECK( configRight.getStereoMode() == deflect::View::right_eye ); } BOOST_AUTO_TEST_CASE( test_master_configuration ) diff --git a/tests/cpp/core/DeflectSerializationTests.cpp b/tests/cpp/core/DeflectSerializationTests.cpp index dc25a685..a66f4e5c 100644 --- a/tests/cpp/core/DeflectSerializationTests.cpp +++ b/tests/cpp/core/DeflectSerializationTests.cpp @@ -41,6 +41,7 @@ #include namespace ut = boost::unit_test; +#include "serialization/includes.h" #include "serialization/deflectTypes.h" #include @@ -56,7 +57,6 @@ BOOST_AUTO_TEST_CASE( testSegementParametersSerialization ) params.width = 78; params.compressed = false; - // serialize std::stringstream stream; { @@ -78,3 +78,31 @@ BOOST_AUTO_TEST_CASE( testSegementParametersSerialization ) BOOST_CHECK_EQUAL( params.compressed, paramsDeserialized.compressed ); } +BOOST_AUTO_TEST_CASE( testFrameSerialization ) +{ + deflect::Frame frame; + frame.segments.push_back( deflect::Segment() ); + frame.segments.push_back( deflect::Segment() ); + frame.uri = "SomeUri"; + frame.view = deflect::View::right_eye; + + // serialize + std::stringstream stream; + { + boost::archive::binary_oarchive oa( stream ); + oa << frame; + } + + // deserialize + deflect::Frame frameDeserialized; + { + boost::archive::binary_iarchive ia( stream ); + ia >> frameDeserialized; + } + + BOOST_CHECK_EQUAL( frame.segments.size(), + frameDeserialized.segments.size( )); + BOOST_CHECK_EQUAL( frame.uri.toStdString(), + frameDeserialized.uri.toStdString() ); + BOOST_CHECK_EQUAL( (int)frame.view, (int)frameDeserialized.view ); +} diff --git a/tests/resources/CMakeLists.txt b/tests/resources/CMakeLists.txt index 5d5750c5..f7252fe0 100644 --- a/tests/resources/CMakeLists.txt +++ b/tests/resources/CMakeLists.txt @@ -11,6 +11,7 @@ set(TEST_RESOURCES select_test.htm configuration.xml configuration_default.xml + configuration_stereo.xml legacy.dcx reference_screenshot.png state_v0.dcx diff --git a/tests/resources/configuration_stereo.xml b/tests/resources/configuration_stereo.xml new file mode 100644 index 00000000..1a0d018b --- /dev/null +++ b/tests/resources/configuration_stereo.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/tide/core/Configuration.cpp b/tide/core/Configuration.cpp index 1f90ae26..04b4fc35 100644 --- a/tide/core/Configuration.cpp +++ b/tide/core/Configuration.cpp @@ -202,19 +202,28 @@ bool Configuration::getInt( const QXmlQuery& query, int& value ) const return ok; } -bool Configuration::getBool( const QXmlQuery& query, bool& value ) const +bool Configuration::getString( const QXmlQuery& query, QString& value ) const { QString queryResult; if( !query.evaluateTo( &queryResult )) return false; - queryResult = queryResult.remove( QRegExp( TRIM_REGEX )); - if( queryResult == "true" ) + value = queryResult.remove( QRegExp( TRIM_REGEX )); + return true; +} + +bool Configuration::getBool( const QXmlQuery& query, bool& value ) const +{ + QString result; + if( !getString( query, result )) + return false; + + if( result == "true" ) { value = true; return true; } - if( queryResult == "false" ) + if( result == "false" ) { value = false; return true; diff --git a/tide/core/Configuration.h b/tide/core/Configuration.h index a8a61a6d..465e0e97 100644 --- a/tide/core/Configuration.h +++ b/tide/core/Configuration.h @@ -147,6 +147,7 @@ class Configuration /** Evaluate the querry and set the result to value on success. */ bool getDouble( const QXmlQuery& query, double& value ) const; bool getInt( const QXmlQuery& query, int& value ) const; + bool getString( const QXmlQuery& query, QString& value ) const; bool getBool( const QXmlQuery& query, bool& value ) const; private: diff --git a/tide/core/serialization/deflectTypes.h b/tide/core/serialization/deflectTypes.h index bd6e631d..9261e7d8 100644 --- a/tide/core/serialization/deflectTypes.h +++ b/tide/core/serialization/deflectTypes.h @@ -54,10 +54,11 @@ namespace serialization { template -void serialize( Archive & ar, deflect::Frame& frame, const unsigned int ) +void serialize( Archive& ar, deflect::Frame& frame, const unsigned int ) { ar & frame.segments; ar & frame.uri; + ar & frame.view; } template< class Archive > diff --git a/tide/master/MasterApplication.cpp b/tide/master/MasterApplication.cpp index 815eb9a0..2b6bd110 100644 --- a/tide/master/MasterApplication.cpp +++ b/tide/master/MasterApplication.cpp @@ -70,7 +70,6 @@ #endif #include -#include #include #include @@ -139,8 +138,15 @@ void MasterApplication::load( const QString sessionFile ) void MasterApplication::_init() { _displayGroup.reset( new DisplayGroup( _config->getTotalSize( ))); + _pixelStreamWindowManager.reset( new PixelStreamWindowManager( *_displayGroup )); + _pixelStreamWindowManager->setAutoFocusNewWindows( + _options->getAutoFocusPixelStreams( )); + connect( _options.get(), &Options::autoFocusPixelStreamsChanged, + _pixelStreamWindowManager.get(), + &PixelStreamWindowManager::setAutoFocusNewWindows ); + _pixelStreamerLauncher.reset( new PixelStreamerLauncher( *_pixelStreamWindowManager, *_config )); @@ -244,17 +250,41 @@ void MasterApplication::_startDeflectServer() return; } - auto& dispatcher = _deflectServer->getPixelStreamDispatcher(); - - connect( &dispatcher, &deflect::FrameDispatcher::openPixelStream, + connect( _deflectServer.get(), &deflect::Server::pixelStreamOpened, _pixelStreamWindowManager.get(), &PixelStreamWindowManager::handleStreamStart ); - connect( &dispatcher, &deflect::FrameDispatcher::deletePixelStream, + + connect( _deflectServer.get(), &deflect::Server::pixelStreamClosed, _pixelStreamWindowManager.get(), &PixelStreamWindowManager::handleStreamEnd ); + connect( _pixelStreamWindowManager.get(), &PixelStreamWindowManager::streamWindowClosed, - &dispatcher, &deflect::FrameDispatcher::deleteStream ); + _deflectServer.get(), &deflect::Server::closePixelStream ); + + connect( _deflectServer.get(), &deflect::Server::receivedFrame, + _pixelStreamWindowManager.get(), + &PixelStreamWindowManager::updateStreamDimensions ); + + connect( _pixelStreamWindowManager.get(), + &PixelStreamWindowManager::requestFirstFrame, + _deflectServer.get(), &deflect::Server::requestFrame ); + + connect( _deflectServer.get(), &deflect::Server::registerToEvents, + _pixelStreamWindowManager.get(), + &PixelStreamWindowManager::registerEventReceiver ); + + connect( _pixelStreamWindowManager.get(), + &PixelStreamWindowManager::eventRegistrationReply, + _deflectServer.get(), &deflect::Server::replyToEventRegistration ); + + connect( _deflectServer.get(), &deflect::Server::receivedSizeHints, + _pixelStreamWindowManager.get(), + &PixelStreamWindowManager::updateSizeHints ); + + connect( _deflectServer.get(), &deflect::Server::receivedData, + _pixelStreamWindowManager.get(), + &PixelStreamWindowManager::sendDataToWindow ); } void MasterApplication::_setupMPIConnections() @@ -284,48 +314,12 @@ void MasterApplication::_setupMPIConnections() { _masterToWallChannel->sendAsync( markers ); }, Qt::DirectConnection ); - connect( &_deflectServer->getPixelStreamDispatcher(), - &deflect::FrameDispatcher::sendFrame, - _masterToWallChannel.get(), - &MasterToWallChannel::send ); - connect( &_deflectServer->getPixelStreamDispatcher(), - &deflect::FrameDispatcher::sendFrame, - _pixelStreamWindowManager.get(), - &PixelStreamWindowManager::updateStreamDimensions ); - connect( _deflectServer.get(), - &deflect::Server::registerToEvents, - _pixelStreamWindowManager.get(), - &PixelStreamWindowManager::registerEventReceiver ); - connect( _deflectServer.get(), &deflect::Server::receivedSizeHints, - _pixelStreamWindowManager.get(), - &PixelStreamWindowManager::updateSizeHints ); - connect( _deflectServer.get(), &deflect::Server::receivedData, - _pixelStreamWindowManager.get(), - &PixelStreamWindowManager::sendDataToWindow ); - - connect( _pixelStreamWindowManager.get(), - &PixelStreamWindowManager::streamWindowClosed, - _deflectServer.get(), &deflect::Server::onPixelStreamerClosed ); - connect( _pixelStreamWindowManager.get(), - &PixelStreamWindowManager::eventRegistrationReply, - _deflectServer.get(), - &deflect::Server::onEventRegistrationReply ); - connect( _pixelStreamWindowManager.get(), - &PixelStreamWindowManager::requestFirstFrame, - &_deflectServer->getPixelStreamDispatcher(), - &deflect::FrameDispatcher::requestFrame ); - - _pixelStreamWindowManager->setAutoFocusNewWindows( - _options->getAutoFocusPixelStreams( )); - connect( _options.get(), - &Options::autoFocusPixelStreamsChanged, - _pixelStreamWindowManager.get(), - &PixelStreamWindowManager::setAutoFocusNewWindows ); - connect( _masterFromWallChannel.get(), &MasterFromWallChannel::receivedRequestFrame, - &_deflectServer->getPixelStreamDispatcher(), - &deflect::FrameDispatcher::requestFrame ); + _deflectServer.get(), &deflect::Server::requestFrame ); + + connect( _deflectServer.get(), &deflect::Server::receivedFrame, + _masterToWallChannel.get(), &MasterToWallChannel::send ); connect( _masterFromWallChannel.get(), &MasterFromWallChannel::receivedScreenshot, diff --git a/tide/master/MasterConfiguration.cpp b/tide/master/MasterConfiguration.cpp index a808f311..9d5ea82f 100644 --- a/tide/master/MasterConfiguration.cpp +++ b/tide/master/MasterConfiguration.cpp @@ -48,7 +48,6 @@ namespace { const int DEFAULT_WEBSERVICE_PORT = 8888; -const QRegExp TRIM_REGEX( "[\\n\\t\\r]" ); const QString DEFAULT_URL( "http://www.google.com" ); const QString DEFAULT_WHITEBOARD_SAVE_FOLDER( "/tmp/" ); } @@ -87,39 +86,30 @@ void MasterConfiguration::loadMasterProcessInfo( QXmlQuery& query ) void MasterConfiguration::loadContentDirectory( QXmlQuery& query ) { - QString queryResult; - query.setQuery( "string(/configuration/dock/@directory)" ); - if( query.evaluateTo( &queryResult )) - _contentDir = queryResult.remove( QRegExp( TRIM_REGEX )); + getString( query, _contentDir ); if( _contentDir.isEmpty( )) _contentDir = QDir::homePath(); } void MasterConfiguration::loadSessionsDirectory( QXmlQuery& query ) { - QString queryResult; - query.setQuery( "string(/configuration/sessions/@directory)" ); - if( query.evaluateTo( &queryResult )) - _sessionsDir = queryResult.remove( QRegExp( TRIM_REGEX )); + getString( query, _sessionsDir ); if( _sessionsDir.isEmpty( )) _sessionsDir = QDir::homePath(); } void MasterConfiguration::loadLauncherSettings( QXmlQuery& query ) { - QString queryResult; - query.setQuery( "string(/configuration/launcher/@display)" ); - if( query.evaluateTo( &queryResult )) - _launcherDisplay = queryResult.remove( QRegExp( TRIM_REGEX )); + getString( query, _launcherDisplay ); + query.setQuery( "string(/configuration/launcher/@demoServiceUrl)" ); - if( query.evaluateTo( &queryResult )) - _demoServiceUrl = queryResult.remove( QRegExp( TRIM_REGEX )); + getString( query, _demoServiceUrl ); + query.setQuery( "string(/configuration/launcher/@demoServiceImageFolder)" ); - if( query.evaluateTo( &queryResult )) - _demoServiceImageFolder = queryResult.remove( QRegExp( TRIM_REGEX )); + getString( query, _demoServiceImageFolder ); } void MasterConfiguration::loadWebService( QXmlQuery& query ) @@ -130,44 +120,35 @@ void MasterConfiguration::loadWebService( QXmlQuery& query ) void MasterConfiguration::loadWhiteboard( QXmlQuery& query ) { - QString queryResult; query.setQuery( "string(/configuration/whiteboard/@saveUrl)" ); - if( query.evaluateTo( &queryResult )) - _whiteboardSaveUrl = queryResult.remove( QRegExp( TRIM_REGEX )); + getString( query, _whiteboardSaveUrl ); if( _whiteboardSaveUrl.isEmpty( )) _whiteboardSaveUrl = DEFAULT_WHITEBOARD_SAVE_FOLDER; } void MasterConfiguration::loadAppLauncher( QXmlQuery& query ) { - QString queryResult; query.setQuery( "string(/configuration/applauncher/@qml)" ); - if( query.evaluateTo( &queryResult )) - _appLauncherFile = queryResult.remove( QRegExp( TRIM_REGEX )); + getString( query, _appLauncherFile ); } void MasterConfiguration::loadWebBrowserStartURL( QXmlQuery& query ) { - QString queryResult; query.setQuery( "string(/configuration/webbrowser/@defaultURL)"); - if( query.evaluateTo( &queryResult )) - _webBrowserDefaultURL = queryResult.remove( QRegExp( TRIM_REGEX )); + getString( query, _webBrowserDefaultURL ); if( _webBrowserDefaultURL.isEmpty( )) _webBrowserDefaultURL = DEFAULT_URL; } void MasterConfiguration::loadBackgroundProperties( QXmlQuery& query ) { - QString queryResult; query.setQuery( "string(/configuration/background/@uri)" ); - if( query.evaluateTo( &queryResult )) - _backgroundUri = queryResult.remove( QRegExp( TRIM_REGEX )); + getString( query, _backgroundUri ); + QString queryResult; query.setQuery( "string(/configuration/background/@color)" ); - if( query.evaluateTo( &queryResult )) + if( getString( query, queryResult )) { - queryResult.remove( QRegExp( TRIM_REGEX )); - const QColor newColor( queryResult ); if( newColor.isValid( )) _backgroundColor = newColor; diff --git a/tide/master/network/MasterFromWallChannel.cpp b/tide/master/network/MasterFromWallChannel.cpp index ed18cd9a..bed6349b 100644 --- a/tide/master/network/MasterFromWallChannel.cpp +++ b/tide/master/network/MasterFromWallChannel.cpp @@ -72,8 +72,10 @@ void MasterFromWallChannel::processMessages() switch( result.message ) { case MPIMessageType::REQUEST_FRAME: + { emit receivedRequestFrame( serialization::get( _buffer )); break; + } case MPIMessageType::IMAGE: emit receivedScreenshot( serialization::get( _buffer ), result.src - 1 ); diff --git a/tide/wall/DataProvider.cpp b/tide/wall/DataProvider.cpp index 820581a4..d4ad6708 100644 --- a/tide/wall/DataProvider.cpp +++ b/tide/wall/DataProvider.cpp @@ -48,7 +48,9 @@ #include #include -DataProvider::DataProvider() {} +DataProvider::DataProvider( const deflect::View view ) + : _view{ view } +{} DataProvider::~DataProvider() { @@ -69,7 +71,7 @@ DataProvider::getStreamDataSource( const QString& uri ) if( !updater ) { - updater = std::make_shared(); + updater = std::make_shared( _view ); connect( updater.get(), &PixelStreamUpdater::requestFrame, this, &DataProvider::requestFrame ); _streamUpdaters[uri] = PixelStreamUpdaterWeakPtr( updater ); diff --git a/tide/wall/DataProvider.h b/tide/wall/DataProvider.h index 04a9a0f2..a57a87b9 100644 --- a/tide/wall/DataProvider.h +++ b/tide/wall/DataProvider.h @@ -55,7 +55,13 @@ class DataProvider : public QObject Q_DISABLE_COPY( DataProvider ) public: - DataProvider(); + /** + * Construct a data provider. + * @param view to use for stereo contents. + */ + explicit DataProvider( deflect::View view ); + + /** Destructor. */ ~DataProvider(); /** Get the data source for the given stream uri. */ @@ -76,6 +82,8 @@ public slots: void requestFrame( QString uri ); private: + const deflect::View _view; + typedef QFutureWatcher Watcher; QList _watchers; diff --git a/tide/wall/PixelStreamUpdater.cpp b/tide/wall/PixelStreamUpdater.cpp index afa2c171..bf4f8761 100644 --- a/tide/wall/PixelStreamUpdater.cpp +++ b/tide/wall/PixelStreamUpdater.cpp @@ -49,8 +49,8 @@ #include #include -PixelStreamUpdater::PixelStreamUpdater() - : _readyToSwap( true ) +PixelStreamUpdater::PixelStreamUpdater( deflect::View view ) + : _view( view ) { _swapSyncFrame.setCallback( std::bind( &PixelStreamUpdater::_onFrameSwapped, this, std::placeholders::_1 )); @@ -97,7 +97,14 @@ ImagePtr PixelStreamUpdater::getTileImage( const uint tileIndex ) const // turbojpeg handles need to be per thread, and this function may be // called from multiple threads static QThreadStorage< deflect::SegmentDecoder > decoder; - decoder.localData().decode( segment ); + try + { + decoder.localData().decode( segment ); + } + catch( const std::runtime_error& e ) + { + throw std::runtime_error( "Invalid stream tile" ); + } } return std::make_shared( _currentFrame, tileIndex ); @@ -133,7 +140,12 @@ void PixelStreamUpdater::getNextFrame() void PixelStreamUpdater::updatePixelStream( deflect::FramePtr frame ) { - _swapSyncFrame.update( frame ); + if( frame->view == _view || frame->view == deflect::View::mono || + ( _view == deflect::View::mono && + frame->view == deflect::View::left_eye )) + { + _swapSyncFrame.update( frame ); + } } void PixelStreamUpdater::_onFrameSwapped( deflect::FramePtr frame ) diff --git a/tide/wall/PixelStreamUpdater.h b/tide/wall/PixelStreamUpdater.h index 44409789..aac30126 100644 --- a/tide/wall/PixelStreamUpdater.h +++ b/tide/wall/PixelStreamUpdater.h @@ -57,8 +57,12 @@ class PixelStreamUpdater : public QObject, public DataSource Q_DISABLE_COPY( PixelStreamUpdater ) public: - /** Constructor. */ - PixelStreamUpdater(); + /** + * Constructor. + * @param view which the data source provides. Left and right views also + * include mono contents. + */ + PixelStreamUpdater( deflect::View view ); /** * @copydoc DataSource::getTileImage @@ -97,11 +101,11 @@ public slots: void requestFrame( QString uri ); private: - typedef SwapSyncObject SwapSyncFrame; - SwapSyncFrame _swapSyncFrame; + const deflect::View _view; + SwapSyncObject _swapSyncFrame; deflect::FramePtr _currentFrame; mutable QReadWriteLock _mutex; - bool _readyToSwap; + bool _readyToSwap = true; void _onFrameSwapped( deflect::FramePtr frame ); }; diff --git a/tide/wall/WallApplication.cpp b/tide/wall/WallApplication.cpp index 1a86f3d4..a5e4cdfd 100644 --- a/tide/wall/WallApplication.cpp +++ b/tide/wall/WallApplication.cpp @@ -149,9 +149,9 @@ void WallApplication::_initMPIConnection( MPIChannelPtr worldChannel ) if( _wallChannel->getRank() == 0 ) { - connect( &_window->getDataProvider(), - SIGNAL( requestFrame( QString )), - _toMasterChannel.get(), SLOT( sendRequestFrame( QString ))); + connect( &_window->getDataProvider(), &DataProvider::requestFrame, + _toMasterChannel.get(), + &WallToMasterChannel::sendRequestFrame ); } connect( &_mpiReceiveThread, SIGNAL( started( )), diff --git a/tide/wall/WallConfiguration.cpp b/tide/wall/WallConfiguration.cpp index 2635ad90..d8976bac 100644 --- a/tide/wall/WallConfiguration.cpp +++ b/tide/wall/WallConfiguration.cpp @@ -64,12 +64,12 @@ void WallConfiguration::_loadWallSettings( const int processIndex ) QString queryResult; - // get host + // read host query.setQuery( QString( "string(//process[%1]/@host)").arg( xpathIndex )); if( query.evaluateTo( &queryResult )) _host = queryResult.remove( QRegExp("[\\n\\t\\r]" )); - // get display (optional attribute) + // read display (optional attribute) query.setQuery( QString( "string(//process[%1]/@display)" ).arg( xpathIndex )); if( query.evaluateTo( &queryResult )) _display = queryResult.remove( QRegExp( "[\\n\\t\\r]" )); @@ -78,12 +78,12 @@ void WallConfiguration::_loadWallSettings( const int processIndex ) int value = 0; - // get number of tiles for my process + // read number of tiles for this process query.setQuery( QString( "string(count(//process[%1]/screen))" ).arg( xpathIndex )); if( !getInt( query, value ) || value != 1 ) throw std::runtime_error( "Expect exactly one screen per process" ); - // get number of wall processes on the same host + // read number of wall processes on the same host query.setQuery( QString( "string(count(//process[@host eq '%1']))" ).arg( _host )); if( !getInt( query, value ) || value < 1 ) throw std::runtime_error( "Could not determine the number of wall processes on that host" ); @@ -97,13 +97,25 @@ void WallConfiguration::_loadWallSettings( const int processIndex ) if( getInt( query, value )) _screenPosition.setY( value ); - query.setQuery( QString( "string(//process[%1]/screen/@i)" ).arg(xpathIndex)); + query.setQuery( QString( "string(//process[%1]/screen/@i)" ).arg( xpathIndex )); if( getInt( query, value )) _screenGlobalIndex.setX( value ); query.setQuery( QString( "string(//process[%1]/screen/@j)" ).arg( xpathIndex )); if( getInt( query, value )) _screenGlobalIndex.setY( value ); + + // read stereo mode + query.setQuery( QString( "string(//process[%1]/@stereo)" ).arg( xpathIndex )); + if( getString( query, queryResult )) + { + if( queryResult == "left" ) + _stereoMode = deflect::View::left_eye; + else if( queryResult == "right" ) + _stereoMode = deflect::View::right_eye; + else + _stereoMode = deflect::View::mono; + } } int WallConfiguration::getProcessIndex() const @@ -135,3 +147,8 @@ const QPoint& WallConfiguration::getWindowPos() const { return _screenPosition; } + +deflect::View WallConfiguration::getStereoMode() const +{ + return _stereoMode; +} diff --git a/tide/wall/WallConfiguration.h b/tide/wall/WallConfiguration.h index 46184e74..a49e8ee6 100644 --- a/tide/wall/WallConfiguration.h +++ b/tide/wall/WallConfiguration.h @@ -45,8 +45,7 @@ #include /** - * The WallConfiguration manages all the parameters needed to setup a Wall - * process. + * Read the parameters needed to setup a Wall process from an xml file. * * @warning: this class can only be used AFTER creating a QApplication. */ @@ -85,6 +84,9 @@ class WallConfiguration : public Configuration /** Get the coordinates of the screen in pixel units. */ const QPoint& getWindowPos() const; + /** @return the stereo mode for this process. */ + deflect::View getStereoMode() const; + private: const int _processIndex; @@ -95,6 +97,8 @@ class WallConfiguration : public Configuration QPoint _screenPosition; QPoint _screenGlobalIndex; + deflect::View _stereoMode = deflect::View::mono; + void _loadWallSettings( int processIndex ); }; diff --git a/tide/wall/WallWindow.cpp b/tide/wall/WallWindow.cpp index 582dcd05..c7fbfa87 100644 --- a/tide/wall/WallWindow.cpp +++ b/tide/wall/WallWindow.cpp @@ -76,7 +76,7 @@ WallWindow::WallWindow( const WallConfiguration& config, , _rootItem( nullptr ) , _uploadThread( new QThread ) , _uploader( new TextureUploader ) - , _provider( new DataProvider ) + , _provider( new DataProvider{ config.getStereoMode() } ) { connect( _provider, &DataProvider::imageLoaded, _uploader, &TextureUploader::uploadTexture );