From 743f6015c457d0ee079ad5f905a08d54df71a457 Mon Sep 17 00:00:00 2001 From: Stefan Eilemann Date: Thu, 2 Mar 2017 17:15:50 +0100 Subject: [PATCH] Support stereo image streaming using Deflect --- .gitexternals | 2 +- .gitsubprojects | 2 +- doc/Changelog.md | 2 + eq/channel.cpp | 1 + eq/deflect/eventHandler.cpp | 15 ++-- eq/deflect/proxy.cpp | 133 +++++++++++++++++++++++------------- eq/deflect/proxy.h | 5 +- eq/detail/channel.ipp | 10 +++ eq/eye.h | 31 +++++---- eq/fabric/eventType.h | 6 +- eq/fabric/eye.h | 34 +++++---- eq/resultImageListener.h | 5 +- eq/server/config.cpp | 1 - examples/eqPly/window.cpp | 10 ++- 14 files changed, 158 insertions(+), 99 deletions(-) diff --git a/.gitexternals b/.gitexternals index 3d0450d543..f8893aed7f 100644 --- a/.gitexternals +++ b/.gitexternals @@ -1,2 +1,2 @@ # -*- mode: cmake -*- -# CMake/common https://github.com/Eyescale/CMake.git 6a44f78 +# CMake/common https://github.com/Eyescale/CMake.git e3a2dba diff --git a/.gitsubprojects b/.gitsubprojects index abdf5a13f2..9a89d1820c 100644 --- a/.gitsubprojects +++ b/.gitsubprojects @@ -6,4 +6,4 @@ git_subproject(Pression https://github.com/Eyescale/Pression.git ae3dded) git_subproject(hwsd https://github.com/Eyescale/hwsd.git 2898f91) git_subproject(Collage https://github.com/Eyescale/Collage.git 206860e) git_subproject(GLStats https://github.com/Eyescale/GLStats.git 836dd5e) -git_subproject(Deflect https://github.com/BlueBrain/Deflect.git 24e97d7) +git_subproject(Deflect https://github.com/BlueBrain/Deflect.git 6c3d21c) diff --git a/doc/Changelog.md b/doc/Changelog.md index 865c083cc2..442685ad0c 100644 --- a/doc/Changelog.md +++ b/doc/Changelog.md @@ -3,6 +3,8 @@ Changelog {#Changelog} # git master +* [629](https://github.com/Eyescale/Equalizer/pull/629) + Support deflect stereo streaming * [619](https://github.com/Eyescale/Equalizer/pull/619) Fix running from an installed version * [611](https://github.com/Eyescale/Equalizer/pull/611) diff --git a/eq/channel.cpp b/eq/channel.cpp index 9c2785a56f..d35623b4d4 100644 --- a/eq/channel.cpp +++ b/eq/channel.cpp @@ -389,6 +389,7 @@ void Channel::frameStart( const uint128_t&, const uint32_t frameNumber ) void Channel::frameFinish( const uint128_t&, const uint32_t frameNumber ) { + _impl->frameFinish(); releaseFrame( frameNumber ); } diff --git a/eq/deflect/eventHandler.cpp b/eq/deflect/eventHandler.cpp index 2596fcfa8f..42a2faa382 100644 --- a/eq/deflect/eventHandler.cpp +++ b/eq/deflect/eventHandler.cpp @@ -165,20 +165,17 @@ void EventHandler::_processEvents( const Proxy* proxy ) while( _proxy->hasNewEvent( )) { - ::deflect::Event deflectEvent = _proxy->getEvent(); - - if( deflectEvent.type == ::deflect::Event::EVT_CLOSE ) - { - _proxy->stopRunning(); - window->getConfig()->sendEvent( EVENT_EXIT ); - break; - } - + const ::deflect::Event deflectEvent = _proxy->getEvent(); const float x = deflectEvent.mouseX * pvp.w; const float y = deflectEvent.mouseY * pvp.h; switch( deflectEvent.type ) { + case ::deflect::Event::EVT_CLOSE: + _proxy->stopRunning(); + window->processEvent( EVENT_EXIT ); + return; + case ::deflect::Event::EVT_KEY_PRESS: case ::deflect::Event::EVT_KEY_RELEASE: { diff --git a/eq/deflect/proxy.cpp b/eq/deflect/proxy.cpp index eb9b2b9a8c..8f1e5ee19a 100644 --- a/eq/deflect/proxy.cpp +++ b/eq/deflect/proxy.cpp @@ -38,23 +38,13 @@ namespace eq { namespace deflect { - -::deflect::Stream::Future make_ready_future( const bool value ) -{ - std::promise< bool > promise; - promise.set_value( value ); - return promise.get_future(); -} - class Proxy::Impl : public boost::noncopyable { public: - explicit Impl( Channel& channel ) - : _channel( channel ) - , _sendFuture( make_ready_future( false )) - , _running( false ) + explicit Impl( Channel& ch ) + : channel( ch ) { - const DrawableConfig& dc = _channel.getDrawableConfig(); + const DrawableConfig& dc = channel.getDrawableConfig(); if( dc.colorBits != 8 ) { LBWARN << "Can only stream 8-bit RGB(A) framebuffers to " @@ -64,40 +54,83 @@ class Proxy::Impl : public boost::noncopyable } const std::string& deflectHost = - _channel.getView()->getSAttribute( View::SATTR_DEFLECT_HOST ); + channel.getView()->getSAttribute( View::SATTR_DEFLECT_HOST ); const std::string& name = - _channel.getView()->getSAttribute( View::SATTR_DEFLECT_ID ); - _stream.reset( new ::deflect::Stream( name, deflectHost )); - if( !_stream->isConnected( )) + channel.getView()->getSAttribute( View::SATTR_DEFLECT_ID ); + stream.reset( new ::deflect::Stream( name, deflectHost )); + if( !stream->isConnected( )) { LBWARN << "Could not connect to Deflect host: " << deflectHost << std::endl; - return; + stream.reset(); } - - _running = true; - _sendFuture = make_ready_future( true ); } ~Impl() { - // wait for completion of previous send - _sendFuture.wait(); + for( size_t i = 0; i < NUM_EYES; ++i ) + if( _sendFuture[i].valid( )) + _sendFuture[i].wait(); + if( _finishFuture.valid( )) + _finishFuture.wait(); + } + + void notifyNewImage( Channel&, const Image& image ) + { + switch( channel.getEye( )) + { + case eq::EYE_LEFT: + _send( ::deflect::View::left_eye, EYE_LEFT_BIT, image ); + return; + + case eq::EYE_RIGHT: + _send( ::deflect::View::right_eye, EYE_RIGHT_BIT, image ); + return; + + default: + _send( ::deflect::View::mono, EYE_CYCLOP_BIT, image ); + return; + } + } - void notifyNewImage( Channel& channel, const Image& image ) + void finishFrame() { - LBASSERT( &channel == &_channel ); + if( _finishFuture.valid() && !_finishFuture.get( )) + stream.reset(); + if( !stream ) + return; + + for( size_t i = 0; i < NUM_EYES; ++i ) + { + if( _sendFuture[i].valid( )) + { + _finishFuture = stream->finishFrame(); + return; + } + } + } + + Channel& channel; + std::unique_ptr< ::deflect::Stream > stream; + std::unique_ptr< EventHandler > eventHandler; - // wait for completion of previous send - _running = _sendFuture.get(); +private: + void _send( const ::deflect::View view, const Eye eye, const Image& image ) + { + if( _sendFuture[eye].valid() && !_sendFuture[eye].get( )) + stream.reset(); + if( !stream ) + return; // copy pixels to perform swapYAxis() const size_t dataSize = image.getPixelDataSize( Frame::Buffer::color ); - _buffer.replace( image.getPixelPointer( Frame::Buffer::color ), dataSize); + _buffer[eye].replace( image.getPixelPointer( Frame::Buffer::color ), + dataSize ); const PixelViewport& pvp = image.getPixelViewport(); - ::deflect::ImageWrapper::swapYAxis( _buffer.getData(), pvp.w, pvp.h, - image.getPixelSize( Frame::Buffer::color )); + ::deflect::ImageWrapper::swapYAxis( _buffer[eye].getData(), pvp.w, + pvp.h, + image.getPixelSize( Frame::Buffer::color )); // determine image offset wrt global view const Viewport& vp = channel.getViewport(); @@ -106,20 +139,19 @@ class Proxy::Impl : public boost::noncopyable const int32_t offsX = vp.x * width; const int32_t offsY = height - (vp.y * height + vp.h * height); - ::deflect::ImageWrapper imageWrapper( _buffer.getData(), pvp.w, pvp.h, - ::deflect::BGRA, offsX, offsY ); + ::deflect::ImageWrapper imageWrapper( _buffer[eye].getData(), pvp.w, + pvp.h, ::deflect::BGRA, offsX, + offsY ); imageWrapper.compressionPolicy = ::deflect::COMPRESSION_ON; imageWrapper.compressionQuality = 100; + imageWrapper.view = view; - _sendFuture = _stream->asyncSend( imageWrapper ); + _sendFuture[eye] = stream->send( imageWrapper ); } - std::unique_ptr< ::deflect::Stream > _stream; - std::unique_ptr< EventHandler > _eventHandler; - Channel& _channel; - lunchbox::Bufferb _buffer; - ::deflect::Stream::Future _sendFuture; - bool _running; + lunchbox::Bufferb _buffer[NUM_EYES]; + ::deflect::Stream::Future _sendFuture[NUM_EYES]; + ::deflect::Stream::Future _finishFuture; }; Proxy::Proxy( Channel& channel ) @@ -131,48 +163,53 @@ Proxy::Proxy( Channel& channel ) Proxy::~Proxy() { - _impl->_channel.removeResultImageListener( this ); + _impl->channel.removeResultImageListener( this ); } void Proxy::notifyNewImage( Channel& channel, const Image& image ) { _impl->notifyNewImage( channel, image ); - if( !_impl->_eventHandler && _impl->_stream->registerForEvents( true )) + if( !_impl->eventHandler && _impl->stream->registerForEvents( true )) { - _impl->_eventHandler.reset( new EventHandler( this )); + _impl->eventHandler.reset( new EventHandler( this )); LBDEBUG << "Installed event handler for Deflect proxy" << std::endl; } } +void Proxy::notifyFinishFrame() +{ + _impl->finishFrame(); +} + Channel& Proxy::getChannel() { - return _impl->_channel; + return _impl->channel; } int Proxy::getSocketDescriptor() const { - return _impl->_stream->getDescriptor(); + return _impl->stream->getDescriptor(); } bool Proxy::hasNewEvent() const { - return _impl->_stream->hasEvent(); + return _impl->stream->hasEvent(); } bool Proxy::isRunning() const { - return _impl->_running; + return _impl->stream != nullptr; } void Proxy::stopRunning() { - _impl->_running = false; + _impl->stream.reset(); } ::deflect::Event Proxy::getEvent() const { - return _impl->_stream->getEvent(); + return _impl->stream->getEvent(); } } diff --git a/eq/deflect/proxy.h b/eq/deflect/proxy.h index a7074ada43..e62959cdfe 100644 --- a/eq/deflect/proxy.h +++ b/eq/deflect/proxy.h @@ -1,5 +1,5 @@ -/* Copyright (c) 2013-2015, Daniel Nachbaur +/* Copyright (c) 2013-2017, Daniel Nachbaur * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 2.1 as published @@ -39,6 +39,9 @@ class Proxy : public ResultImageListener /** Stream the given image to the Deflect host. */ void notifyNewImage( Channel& channel, const Image& image ) final; + /** Complete stream operations and show image on server. */ + void notifyFinishFrame() final; + /** @return the associated destination channel. */ Channel& getChannel(); diff --git a/eq/detail/channel.ipp b/eq/detail/channel.ipp index 5b147c3289..f650e10d74 100644 --- a/eq/detail/channel.ipp +++ b/eq/detail/channel.ipp @@ -110,6 +110,7 @@ public: downloadFramebuffer( channel, buffers ); for( ResultImageListener* listener : resultImageListeners ) listener->notifyNewImage( channel, framebufferImage ); + _finishImageListeners = true; if( view->getScreenshotBuffers() != eq::Frame::Buffer::none ) { @@ -119,6 +120,14 @@ public: } } + void frameFinish() + { + if( _finishImageListeners ) + for( ResultImageListener* listener : resultImageListeners ) + listener->notifyFinishFrame(); + _finishImageListeners = false; + } + void downloadFramebuffer( eq::Channel& channel, const eq::Frame::Buffer buffers ) { @@ -190,6 +199,7 @@ public: FileFrameWriter frameWriter; bool _updateFrameBuffer; + bool _finishImageListeners = false; }; } diff --git a/eq/eye.h b/eq/eye.h index 2a1a82f566..88d61d2696 100644 --- a/eq/eye.h +++ b/eq/eye.h @@ -1,16 +1,16 @@ -/* Copyright (c) 2007-2012, Stefan Eilemann - * 2010, Cedric Stalder +/* Copyright (c) 2007-2017, Stefan Eilemann + * Cedric Stalder * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 2.1 as published * by the Free Software Foundation. - * + * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. @@ -23,17 +23,20 @@ namespace eq { - using fabric::Eye; +using fabric::Eye; - /** @cond IGNORE */ - using fabric::EYE_UNDEFINED; - using fabric::EYE_CYCLOP; - using fabric::EYE_LEFT; - using fabric::EYE_RIGHT; - using fabric::EYES_STEREO; - using fabric::EYES_ALL; - using fabric::NUM_EYES; - /** @endcond */ +/** @cond IGNORE */ +using fabric::EYE_UNDEFINED; +using fabric::EYE_CYCLOP; +using fabric::EYE_LEFT; +using fabric::EYE_RIGHT; +using fabric::EYE_CYCLOP_BIT; +using fabric::EYE_LEFT_BIT; +using fabric::EYE_RIGHT_BIT; +using fabric::EYES_STEREO; +using fabric::EYES_ALL; +using fabric::NUM_EYES; +/** @endcond */ } #endif // EQ_EYE_H diff --git a/eq/fabric/eventType.h b/eq/fabric/eventType.h index ca67f2dc91..2cdc9ac184 100644 --- a/eq/fabric/eventType.h +++ b/eq/fabric/eventType.h @@ -53,19 +53,17 @@ enum EventType // Also update string table in event.cpp EVENT_MAGELLAN_AXIS = 30, //!< AxisEvent: SpaceMouse touched EVENT_MAGELLAN_BUTTON, //!< ButtonEvent: SpaceMouse button pressed - // Stateless Events + // Event EVENT_WINDOW_CLOSE = 40, //!< A window has been closed EVENT_WINDOW_HIDE, //!< A window is hidden EVENT_WINDOW_EXPOSE, //!< A window is dirty EVENT_EXIT, //!< Exit request from application or due to runtime error - - EVENT_STATISTIC, //!< Statistic event - /** Window pointer grabbed by system window */ EVENT_WINDOW_POINTER_GRAB, /** Window pointer to be released by system window */ EVENT_WINDOW_POINTER_UNGRAB, + EVENT_STATISTIC, //!< Statistic event /** * Observer moved (head tracking update). Contains observer originator diff --git a/eq/fabric/eye.h b/eq/fabric/eye.h index e7e8c76465..54876fa59a 100644 --- a/eq/fabric/eye.h +++ b/eq/fabric/eye.h @@ -27,25 +27,23 @@ namespace eq { namespace fabric { - /** - * Eye pass bit mask for which is enabled. - */ - enum Eye - { - EYE_CYCLOP_BIT = 0, //!< @internal - EYE_LEFT_BIT = 1, //!< @internal - EYE_RIGHT_BIT = 2, //!< @internal - EYE_UNDEFINED = 0, - EYE_CYCLOP = 1 << EYE_CYCLOP_BIT, //!< monoscopic 'middle' eye - EYE_LEFT = 1 << EYE_LEFT_BIT, //!< left eye - EYE_RIGHT = 1 << EYE_RIGHT_BIT, //!< right eye - EYE_LAST = EYE_RIGHT, //!< the last eye - NUM_EYES = 3, //!< @internal increase with each new enum - EYES_STEREO = EYE_LEFT | EYE_RIGHT, //!< left and right eye - EYES_ALL = 7 //!< all eyes - }; +/** Eye pass bit mask for which rendering is enabled. */ +enum Eye +{ + EYE_CYCLOP_BIT = 0, //!< @internal + EYE_LEFT_BIT = 1, //!< @internal + EYE_RIGHT_BIT = 2, //!< @internal + EYE_UNDEFINED = 0, + EYE_CYCLOP = 1 << EYE_CYCLOP_BIT, //!< monoscopic 'middle' eye + EYE_LEFT = 1 << EYE_LEFT_BIT, //!< left eye + EYE_RIGHT = 1 << EYE_RIGHT_BIT, //!< right eye + EYE_LAST = EYE_RIGHT, //!< the last eye + NUM_EYES = 3, //!< @internal increase with each new enum + EYES_STEREO = EYE_LEFT | EYE_RIGHT, //!< left and right eye + EYES_ALL = 7 //!< all eyes +}; - EQFABRIC_API std::ostream& operator << ( std::ostream& os, const Eye& eye ); +EQFABRIC_API std::ostream& operator << ( std::ostream& os, const Eye& eye ); } } diff --git a/eq/resultImageListener.h b/eq/resultImageListener.h index d31b73853b..7c6f72aa1b 100644 --- a/eq/resultImageListener.h +++ b/eq/resultImageListener.h @@ -1,5 +1,5 @@ -/* Copyright (c) 2015, Daniel Nachbaur +/* Copyright (c) 2015-2017, Daniel Nachbaur * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License version 2.1 as published @@ -49,6 +49,9 @@ class ResultImageListener */ virtual void notifyNewImage( Channel& channel, const Image& image ) = 0; + /** Notify on completion of a frame. */ + virtual void notifyFinishFrame() { /*nop*/ } + private: ResultImageListener( const ResultImageListener& ) = delete; ResultImageListener& operator=( const ResultImageListener& ) = delete; diff --git a/eq/server/config.cpp b/eq/server/config.cpp index d8663c0c7d..c411d908e8 100644 --- a/eq/server/config.cpp +++ b/eq/server/config.cpp @@ -853,7 +853,6 @@ bool Config::exit() event.serial = getSerial(); event.time = getServer()->getTime(); event.originator = getID(); - cmd << EVENT_EXIT << event; _needsFinish = false; diff --git a/examples/eqPly/window.cpp b/examples/eqPly/window.cpp index d66058e8b1..756dc30a3e 100644 --- a/examples/eqPly/window.cpp +++ b/examples/eqPly/window.cpp @@ -139,7 +139,15 @@ void Window::_loadLogo() GL_TEXTURE_RECTANGLE_ARB ); LBASSERT( _logoTexture ); - image.upload( eq::Frame::Buffer::color, _logoTexture, eq::Vector2i(), om ); + if( !image.upload( eq::Frame::Buffer::color, _logoTexture, eq::Vector2i(), + om )) + { + LBWARN << "Can't load overlay logo " << _logoTextureName << std::endl; + om.deleteEqTexture( _logoTextureName.c_str( )); + _logoTexture = nullptr; + return; + } + image.deleteGLObjects( om ); LBVERB << "Created logo texture of size " << _logoTexture->getWidth() << "x" << _logoTexture->getHeight() << std::endl;